diff --git a/code/client/config.ts b/code/client/config.ts index 676c4c15..fb438260 100644 --- a/code/client/config.ts +++ b/code/client/config.ts @@ -11,6 +11,7 @@ const firebaseConfig = { }; const app = initializeApp(firebaseConfig); + const auth = getAuth(app); const googleAuthProvider = new GoogleAuthProvider(); const githubAuthProvider = new GithubAuthProvider(); diff --git a/code/client/src/App.tsx b/code/client/src/App.tsx index d786e65e..2c02f6a9 100644 --- a/code/client/src/App.tsx +++ b/code/client/src/App.tsx @@ -4,13 +4,13 @@ import Header from '@ui/components/header/Header'; import Workspace from '@ui/pages/workspace/Workspace'; import NotFound from '@ui/pages/notfound/NotFound'; import './App.scss'; -import { ErrorProvider } from '@ui/contexts/error/ErrorContext'; +import { ErrorProvider } from '@/contexts/error/ErrorContext'; import Sidebar from '@ui/components/sidebar/Sidebar'; -import { WorkspaceProvider } from '@ui/contexts/workspace/WorkspaceContext'; +import { WorkspaceProvider } from '@/contexts/workspace/WorkspaceContext'; import Workspaces from '@ui/pages/workspaces/Workspaces'; -import { CommunicationProvider } from '@ui/contexts/communication/CommunicationContext'; +import { CommunicationProvider } from '@/contexts/communication/CommunicationContext'; import Home from '@ui/pages/home/Home'; -import AuthProvider from '@ui/contexts/auth/AuthContext'; +import AuthProvider from '@/contexts/auth/AuthContext'; import Profile from '@ui/pages/profile/Profile'; import Landing from '@ui/pages/landing/Landing'; @@ -18,9 +18,9 @@ function App() { return (
- - - + + +
@@ -66,9 +66,9 @@ function App() { } />
- - - + + +
); diff --git a/code/client/src/ui/contexts/auth/AuthContext.tsx b/code/client/src/contexts/auth/AuthContext.tsx similarity index 72% rename from code/client/src/ui/contexts/auth/AuthContext.tsx rename to code/client/src/contexts/auth/AuthContext.tsx index 025546e5..fb411afc 100644 --- a/code/client/src/ui/contexts/auth/AuthContext.tsx +++ b/code/client/src/contexts/auth/AuthContext.tsx @@ -1,9 +1,10 @@ import Cookies from 'js-cookie'; import { auth, githubAuthProvider, googleAuthProvider } from '@config'; import { createContext, ReactNode, useEffect, useState } from 'react'; -import { signInWithPopup, signOut, User, type AuthProvider as Provider } from 'firebase/auth'; -import useError from '@ui/contexts/error/useError'; +import { signInWithPopup, signOut, User, type AuthProvider as Provider, inMemoryPersistence } from 'firebase/auth'; +import useError from '@/contexts/error/useError'; import useAuthService from '@services/auth/useAuthService'; +import { useNavigate } from 'react-router-dom'; export type AuthContextType = { currentUser: User | null; @@ -29,14 +30,17 @@ export function AuthProvider({ children }: AuthProviderProps) { const [currentUser, setCurrentUser] = useState(null); const [loading, setLoading] = useState(true); const { publishError } = useError(); - const { registerUser } = useAuthService(); + const { sessionLogin, sessionLogout } = useAuthService(); + const navigate = useNavigate(); const loginWithProvider = async (provider: Provider) => { try { + auth.setPersistence(inMemoryPersistence); // for httpOnly cookies, do not persist any state client side const { user } = await signInWithPopup(auth, provider); - await registerUser(user.uid, { name: user.displayName!, email: user.email! }); - const token = await user.getIdToken(true); - Cookies.set('token', token, { expires: 1, secure: true, sameSite: 'Strict' }); + const userInfo = { name: user.displayName!, email: user.email! }; + const idToken = await user.getIdToken(); + const csrfToken = Cookies.get('csrfToken')!; + await sessionLogin(idToken, csrfToken, user.uid, userInfo); } catch (e) { publishError(e as Error); } @@ -47,15 +51,16 @@ export function AuthProvider({ children }: AuthProviderProps) { const loginWithGithub = () => loginWithProvider(githubAuthProvider); const logout = async () => { + console.log('logging out'); + await sessionLogout(); await signOut(auth); - Cookies.remove('token'); - window.location.href = '/'; + navigate('/'); }; const deleteAccount = async () => { + await sessionLogout(); await currentUser?.delete(); - Cookies.remove('token'); - window.location.href = '/'; + navigate('/'); }; useEffect(() => { diff --git a/code/client/src/ui/contexts/auth/useAuth.ts b/code/client/src/contexts/auth/useAuth.ts similarity index 60% rename from code/client/src/ui/contexts/auth/useAuth.ts rename to code/client/src/contexts/auth/useAuth.ts index e165f62a..9909a756 100644 --- a/code/client/src/ui/contexts/auth/useAuth.ts +++ b/code/client/src/contexts/auth/useAuth.ts @@ -1,5 +1,5 @@ import { useContext } from 'react'; -import { AuthContext, AuthContextType } from '@ui/contexts/auth/AuthContext'; +import { AuthContext, AuthContextType } from '@/contexts/auth/AuthContext'; export function useAuth(): AuthContextType { return useContext(AuthContext); diff --git a/code/client/src/ui/contexts/communication/CommunicationContext.tsx b/code/client/src/contexts/communication/CommunicationContext.tsx similarity index 100% rename from code/client/src/ui/contexts/communication/CommunicationContext.tsx rename to code/client/src/contexts/communication/CommunicationContext.tsx diff --git a/code/client/src/ui/contexts/communication/useCommunication.ts b/code/client/src/contexts/communication/useCommunication.ts similarity index 69% rename from code/client/src/ui/contexts/communication/useCommunication.ts rename to code/client/src/contexts/communication/useCommunication.ts index 6b5facaf..262be5c2 100644 --- a/code/client/src/ui/contexts/communication/useCommunication.ts +++ b/code/client/src/contexts/communication/useCommunication.ts @@ -1,6 +1,6 @@ import { Communication } from '@services/communication/communication'; import { useContext } from 'react'; -import { CommunicationContext } from '@ui/contexts/communication/CommunicationContext'; +import { CommunicationContext } from '@/contexts/communication/CommunicationContext'; export function useCommunication(): Communication { return useContext(CommunicationContext); diff --git a/code/client/src/ui/contexts/error/ErrorContext.tsx b/code/client/src/contexts/error/ErrorContext.tsx similarity index 100% rename from code/client/src/ui/contexts/error/ErrorContext.tsx rename to code/client/src/contexts/error/ErrorContext.tsx diff --git a/code/client/src/ui/contexts/error/useError.ts b/code/client/src/contexts/error/useError.ts similarity index 63% rename from code/client/src/ui/contexts/error/useError.ts rename to code/client/src/contexts/error/useError.ts index 9c960014..24509056 100644 --- a/code/client/src/ui/contexts/error/useError.ts +++ b/code/client/src/contexts/error/useError.ts @@ -1,5 +1,5 @@ import { useContext } from 'react'; -import { ErrorContext } from '@ui/contexts/error/ErrorContext'; +import { ErrorContext } from '@/contexts/error/ErrorContext'; const useError = () => useContext(ErrorContext); diff --git a/code/client/src/ui/contexts/workspace/WorkspaceContext.tsx b/code/client/src/contexts/workspace/WorkspaceContext.tsx similarity index 94% rename from code/client/src/ui/contexts/workspace/WorkspaceContext.tsx rename to code/client/src/contexts/workspace/WorkspaceContext.tsx index d616b556..0ed013bc 100644 --- a/code/client/src/ui/contexts/workspace/WorkspaceContext.tsx +++ b/code/client/src/contexts/workspace/WorkspaceContext.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { useState, createContext, useEffect } from 'react'; import { WorkspaceMeta } from '@notespace/shared/src/workspace/types/workspace'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; -import useError from '@ui/contexts/error/useError'; +import { useCommunication } from '@/contexts/communication/useCommunication'; +import useError from '@/contexts/error/useError'; import { useParams } from 'react-router-dom'; import useWorkspaceService from '@services/workspace/useWorkspaceService'; import useResources from '@domain/workspaces/useResources'; diff --git a/code/client/src/ui/contexts/workspace/useWorkspace.ts b/code/client/src/contexts/workspace/useWorkspace.ts similarity index 62% rename from code/client/src/ui/contexts/workspace/useWorkspace.ts rename to code/client/src/contexts/workspace/useWorkspace.ts index 5be486a4..3279011d 100644 --- a/code/client/src/ui/contexts/workspace/useWorkspace.ts +++ b/code/client/src/contexts/workspace/useWorkspace.ts @@ -1,5 +1,5 @@ import { useContext } from 'react'; -import { WorkspaceContext } from '@ui/contexts/workspace/WorkspaceContext'; +import { WorkspaceContext } from '@/contexts/workspace/WorkspaceContext'; const useWorkspace = () => useContext(WorkspaceContext); diff --git a/code/client/src/domain/workspaces/tree/useWorkspaceTree.ts b/code/client/src/domain/workspaces/tree/useWorkspaceTree.ts index 15efb9e0..0f11da62 100644 --- a/code/client/src/domain/workspaces/tree/useWorkspaceTree.ts +++ b/code/client/src/domain/workspaces/tree/useWorkspaceTree.ts @@ -1,6 +1,6 @@ import { useState } from 'react'; import { Resource } from '@notespace/shared/src/workspace/types/resource'; -import { Resources } from '@ui/contexts/workspace/WorkspaceContext'; +import { Resources } from '@/contexts/workspace/WorkspaceContext'; function useWorkspaceTree() { const [nodes, setNodes] = useState({}); diff --git a/code/client/src/domain/workspaces/tree/utils.ts b/code/client/src/domain/workspaces/tree/utils.ts index cfa4361e..ae82812a 100644 --- a/code/client/src/domain/workspaces/tree/utils.ts +++ b/code/client/src/domain/workspaces/tree/utils.ts @@ -1,5 +1,5 @@ import { TreeNode } from '@domain/workspaces/tree/types'; -import { Resources } from '@ui/contexts/workspace/WorkspaceContext'; +import { Resources } from '@/contexts/workspace/WorkspaceContext'; export function getTree(id: string, nodes: Resources): TreeNode { const node = nodes[id]; diff --git a/code/client/src/domain/workspaces/useResources.ts b/code/client/src/domain/workspaces/useResources.ts index c3323b28..46eb0ff8 100644 --- a/code/client/src/domain/workspaces/useResources.ts +++ b/code/client/src/domain/workspaces/useResources.ts @@ -1,7 +1,7 @@ import { ResourceType, Resource } from '@notespace/shared/src/workspace/types/resource'; import useResourceService from '@services/resource/useResourceService'; import useSocketListeners from '@services/communication/socket/useSocketListeners'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; +import { useCommunication } from '@/contexts/communication/useCommunication'; import useWorkspaceTree from '@domain/workspaces/tree/useWorkspaceTree'; function useResources() { diff --git a/code/client/src/domain/workspaces/useWorkspaces.ts b/code/client/src/domain/workspaces/useWorkspaces.ts index a01c988b..9d619a14 100644 --- a/code/client/src/domain/workspaces/useWorkspaces.ts +++ b/code/client/src/domain/workspaces/useWorkspaces.ts @@ -1,9 +1,9 @@ import { useEffect, useState } from 'react'; import { WorkspaceInputModel, WorkspaceMeta } from '@notespace/shared/src/workspace/types/workspace'; import useSocketListeners from '@services/communication/socket/useSocketListeners'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; +import { useCommunication } from '@/contexts/communication/useCommunication'; import useWorkspaceService from '@services/workspace/useWorkspaceService'; -import useError from '@ui/contexts/error/useError'; +import useError from '@/contexts/error/useError'; function useWorkspaces() { const { socket } = useCommunication(); diff --git a/code/client/src/domain/workspaces/utils.ts b/code/client/src/domain/workspaces/utils.ts index 6a87a10f..c71b9798 100644 --- a/code/client/src/domain/workspaces/utils.ts +++ b/code/client/src/domain/workspaces/utils.ts @@ -1,6 +1,6 @@ import { WorkspaceMeta } from '@notespace/shared/src/workspace/types/workspace'; import { DocumentResource, ResourceType } from '@notespace/shared/src/workspace/types/resource'; -import { Resources } from '@ui/contexts/workspace/WorkspaceContext'; +import { Resources } from '@/contexts/workspace/WorkspaceContext'; export function sortWorkspaces(workspaces: WorkspaceMeta[], column: string, ascending: boolean): WorkspaceMeta[] { return workspaces.sort((a, b) => { diff --git a/code/client/src/services/auth/authService.ts b/code/client/src/services/auth/authService.ts index b3800e64..fe5c513e 100644 --- a/code/client/src/services/auth/authService.ts +++ b/code/client/src/services/auth/authService.ts @@ -1,10 +1,13 @@ import { User, UserData } from '@notespace/shared/src/users/types'; import { HttpCommunication } from '@services/communication/http/httpCommunication'; -import Cookies from 'js-cookie'; function authService(http: HttpCommunication) { - async function registerUser(id: string, data: UserData) { - await http.post('/users', { id, ...data }); + async function sessionLogin(idToken: string, csrfToken: string, id: string, data: UserData) { + await http.post('/users/login', { idToken, csrfToken, id, ...data }); + } + + async function sessionLogout() { + await http.post('/users/logout'); } async function getUser(id: string): Promise { @@ -17,11 +20,11 @@ function authService(http: HttpCommunication) { async function deleteUser(id: string) { await http.delete(`/users/${id}`); - Cookies.remove('token'); } return { - registerUser, + sessionLogin, + sessionLogout, getUser, updateUser, deleteUser, diff --git a/code/client/src/services/auth/useAuthService.ts b/code/client/src/services/auth/useAuthService.ts index e4eb000f..96f4d235 100644 --- a/code/client/src/services/auth/useAuthService.ts +++ b/code/client/src/services/auth/useAuthService.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; +import { useCommunication } from '@/contexts/communication/useCommunication'; import authService from '@services/auth/authService'; function useAuthService() { diff --git a/code/client/src/services/resource/useResourceService.ts b/code/client/src/services/resource/useResourceService.ts index cf613c21..735c17f4 100644 --- a/code/client/src/services/resource/useResourceService.ts +++ b/code/client/src/services/resource/useResourceService.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; +import { useCommunication } from '@/contexts/communication/useCommunication'; import { useParams } from 'react-router-dom'; import resourceService from '@services/resource/resourceService'; diff --git a/code/client/src/services/workspace/useWorkspaceService.ts b/code/client/src/services/workspace/useWorkspaceService.ts index 01cad51a..3faf8cde 100644 --- a/code/client/src/services/workspace/useWorkspaceService.ts +++ b/code/client/src/services/workspace/useWorkspaceService.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; +import { useCommunication } from '@/contexts/communication/useCommunication'; import workspaceService from '@services/workspace/workspaceService'; function useWorkspaceService() { diff --git a/code/client/src/ui/components/header/Header.tsx b/code/client/src/ui/components/header/Header.tsx index 8bac00ee..5f4522d3 100644 --- a/code/client/src/ui/components/header/Header.tsx +++ b/code/client/src/ui/components/header/Header.tsx @@ -1,5 +1,5 @@ import './Header.scss'; -import { useAuth } from '@ui/contexts/auth/useAuth'; +import { useAuth } from '@/contexts/auth/useAuth'; import { Link } from 'react-router-dom'; function Header() { diff --git a/code/client/src/ui/components/sidebar/Sidebar.tsx b/code/client/src/ui/components/sidebar/Sidebar.tsx index 15d089ce..20491d45 100644 --- a/code/client/src/ui/components/sidebar/Sidebar.tsx +++ b/code/client/src/ui/components/sidebar/Sidebar.tsx @@ -1,7 +1,7 @@ import { IoMenu, IoTime } from 'react-icons/io5'; import { Link } from 'react-router-dom'; import { RiMenuFold2Line, RiMenuFoldLine, RiTeamFill } from 'react-icons/ri'; -import useWorkspace from '@ui/contexts/workspace/useWorkspace'; +import useWorkspace from '@/contexts/workspace/useWorkspace'; import useSidebarState from '@ui/components/sidebar/hooks/useSidebarState'; import WorkspaceTree from '@ui/components/sidebar/components/workspace-tree/WorkspaceTree'; import { IoMdSettings } from 'react-icons/io'; diff --git a/code/client/src/ui/components/sidebar/components/workspace-tree/WorkspaceTree.tsx b/code/client/src/ui/components/sidebar/components/workspace-tree/WorkspaceTree.tsx index 26a8ed80..5482f04a 100644 --- a/code/client/src/ui/components/sidebar/components/workspace-tree/WorkspaceTree.tsx +++ b/code/client/src/ui/components/sidebar/components/workspace-tree/WorkspaceTree.tsx @@ -3,7 +3,7 @@ import { WorkspaceMeta } from '@notespace/shared/src/workspace/types/workspace'; import { getTree } from '@domain/workspaces/tree/utils'; import { ResourceType } from '@notespace/shared/src/workspace/types/resource'; import { DragEvent, useState } from 'react'; -import { Resources, WorkspaceOperations } from '@ui/contexts/workspace/WorkspaceContext'; +import { Resources, WorkspaceOperations } from '@/contexts/workspace/WorkspaceContext'; import './WorkspaceTree.scss'; type WorkspaceTreeProps = { diff --git a/code/client/src/ui/pages/document/Document.tsx b/code/client/src/ui/pages/document/Document.tsx index bb71e3f5..2ec8c428 100644 --- a/code/client/src/ui/pages/document/Document.tsx +++ b/code/client/src/ui/pages/document/Document.tsx @@ -2,8 +2,8 @@ import Editor from '@ui/pages/document/components/editor/Editor'; import useFugue from '@domain/editor/fugue/useFugue'; import { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; -import useError from '@ui/contexts/error/useError'; +import { useCommunication } from '@/contexts/communication/useCommunication'; +import useError from '@/contexts/error/useError'; import useDocumentService from '@services/resource/useResourceService'; import useConnectors from '@domain/editor/connectors/useConnectors'; import { DocumentResource } from '@notespace/shared/src/workspace/types/resource'; diff --git a/code/client/src/ui/pages/landing/Landing.tsx b/code/client/src/ui/pages/landing/Landing.tsx index a3ba4268..f0bd21ca 100644 --- a/code/client/src/ui/pages/landing/Landing.tsx +++ b/code/client/src/ui/pages/landing/Landing.tsx @@ -1,4 +1,4 @@ -import { useAuth } from '@ui/contexts/auth/useAuth'; +import { useAuth } from '@/contexts/auth/useAuth'; import { useNavigate } from 'react-router-dom'; import { useEffect } from 'react'; import googleIcon from '@assets/images/google-icon.png'; diff --git a/code/client/src/ui/pages/profile/Profile.tsx b/code/client/src/ui/pages/profile/Profile.tsx index 88a2124f..500a75ef 100644 --- a/code/client/src/ui/pages/profile/Profile.tsx +++ b/code/client/src/ui/pages/profile/Profile.tsx @@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom'; import useAuthService from '@services/auth/useAuthService'; import { User } from '@notespace/shared/src/users/types'; import { formatDate } from '@/utils/utils'; -import { useAuth } from '@ui/contexts/auth/useAuth'; +import { useAuth } from '@/contexts/auth/useAuth'; import './Profile.scss'; function Profile() { diff --git a/code/client/src/ui/pages/workspace/Workspace.tsx b/code/client/src/ui/pages/workspace/Workspace.tsx index f3308410..d56b57c4 100644 --- a/code/client/src/ui/pages/workspace/Workspace.tsx +++ b/code/client/src/ui/pages/workspace/Workspace.tsx @@ -1,7 +1,7 @@ import { ResourceType } from '@notespace/shared/src/workspace/types/resource'; import DocumentView from '@ui/pages/workspace/components/DocumentView'; -import useError from '@ui/contexts/error/useError'; -import useWorkspace from '@ui/contexts/workspace/useWorkspace'; +import useError from '@/contexts/error/useError'; +import useWorkspace from '@/contexts/workspace/useWorkspace'; import { useEffect, useState } from 'react'; import DataTable from '@ui/components/table/DataTable'; import { FaPlus } from 'react-icons/fa'; diff --git a/code/client/src/ui/pages/workspaces/Workspaces.tsx b/code/client/src/ui/pages/workspaces/Workspaces.tsx index 7124ae89..c9fc0332 100644 --- a/code/client/src/ui/pages/workspaces/Workspaces.tsx +++ b/code/client/src/ui/pages/workspaces/Workspaces.tsx @@ -1,12 +1,12 @@ import useWorkspaces from '@domain/workspaces/useWorkspaces'; import WorkspaceView from '@ui/pages/workspaces/components/WorkspaceView'; import CreateWorkspaceDialog from '@ui/pages/workspaces/components/CreateWorkspaceDialog'; -import useError from '@ui/contexts/error/useError'; +import useError from '@/contexts/error/useError'; import DataTable from '@ui/components/table/DataTable'; import { MdDelete } from 'react-icons/md'; import { useEffect, useState } from 'react'; import { sortWorkspaces } from '@domain/workspaces/utils'; -import { useCommunication } from '@ui/contexts/communication/useCommunication'; +import { useCommunication } from '@/contexts/communication/useCommunication'; import './Workspaces.scss'; function Workspaces() { diff --git a/code/server/src/controllers/http/handlers/usersHandlers.ts b/code/server/src/controllers/http/handlers/usersHandlers.ts index c1524bad..233078c3 100644 --- a/code/server/src/controllers/http/handlers/usersHandlers.ts +++ b/code/server/src/controllers/http/handlers/usersHandlers.ts @@ -4,10 +4,28 @@ import { UsersService } from '@services/UsersService'; import { httpResponse } from '@controllers/http/utils/httpResponse'; import { UserData } from '@notespace/shared/src/users/types'; import { enforceAuth } from '@controllers/http/middlewares/authMiddleware'; +import admin from 'firebase-admin'; function usersHandlers(service: UsersService) { - const registerUser = async (req: Request, res: Response) => { - const { id, ...data } = req.body; + const sessionLogin = async (req: Request, res: Response) => { + const { id, idToken, csrfToken, ...data } = req.body; + // guard against CSRF attacks + if (csrfToken !== req.cookies.csrfToken) { + httpResponse.unauthorized(res).send(); + return; + } + // session login - create session cookie, verifying ID token in the process + try { + const expiresIn = 60 * 60 * 24 * 5 * 1000; // 5 days + const sessionCookie = await admin.auth().createSessionCookie(idToken, { expiresIn }); + const options = { maxAge: expiresIn, httpOnly: true, secure: true }; + res.cookie('session', sessionCookie, options); + } catch (e) { + httpResponse.unauthorized(res).send(); + return; + } + + // register user in database if not already registered try { const user = await service.getUser(id); if (user) { @@ -22,6 +40,11 @@ function usersHandlers(service: UsersService) { } }; + const sessionLogout = async (req: Request, res: Response) => { + res.clearCookie('session'); + httpResponse.noContent(res).send(); + }; + const getUser = async (req: Request, res: Response) => { const { id } = req.params; const user = await service.getUser(id); @@ -47,7 +70,8 @@ function usersHandlers(service: UsersService) { }; const router = PromiseRouter({ mergeParams: true }); - router.post('/', registerUser); + router.post('/login', sessionLogin); + router.post('/logout', sessionLogout); router.get('/:id', getUser); router.get('/', getUsers); router.put('/:id', enforceAuth, updateUser); diff --git a/code/server/src/controllers/http/middlewares/errorMiddleware.ts b/code/server/src/controllers/http/middlewares/errorMiddleware.ts index eea9dd6d..e3a921de 100644 --- a/code/server/src/controllers/http/middlewares/errorMiddleware.ts +++ b/code/server/src/controllers/http/middlewares/errorMiddleware.ts @@ -28,5 +28,4 @@ export default function errorMiddleware(error: Error, req: Request, res: Respons const message = response.statusCode === 500 ? 'Internal server error' : error.message; response.send({ error: message }); ErrorLogger.logError(error.message); - console.error(error.stack); } diff --git a/code/server/src/controllers/http/middlewares/loggingMiddleware.ts b/code/server/src/controllers/http/middlewares/loggingMiddleware.ts new file mode 100644 index 00000000..91464565 --- /dev/null +++ b/code/server/src/controllers/http/middlewares/loggingMiddleware.ts @@ -0,0 +1,9 @@ +import { NextFunction, Request, Response } from 'express'; +import { ControllersLogger } from '@src/utils/logging'; + +const logger = ControllersLogger('ws'); + +export default async function loggingMiddleware(req: Request, res: Response, next: NextFunction) { + logger.logInfo(`Request: ${req.method} ${req.url}`); + next(); +} diff --git a/code/server/src/controllers/http/router.ts b/code/server/src/controllers/http/router.ts index c07aeef1..e644d181 100644 --- a/code/server/src/controllers/http/router.ts +++ b/code/server/src/controllers/http/router.ts @@ -6,6 +6,7 @@ import errorMiddleware from '@controllers/http/middlewares/errorMiddleware'; import usersHandlers from '@controllers/http/handlers/usersHandlers'; import { Server } from 'socket.io'; import { authMiddleware } from '@controllers/http/middlewares/authMiddleware'; +import loggingMiddleware from '@controllers/http/middlewares/loggingMiddleware'; export default function (services: Services, io: Server) { if (!services) throw new Error('Services parameter is required'); @@ -13,6 +14,7 @@ export default function (services: Services, io: Server) { // automatically routes unhandled errors to error handling middleware const router = PromiseRouter(); router.use(express.urlencoded({ extended: true })); + router.use(loggingMiddleware); router.use(authMiddleware); router.use('/users', usersHandlers(services.users)); diff --git a/code/server/src/controllers/ws/initSocketEvents.ts b/code/server/src/controllers/ws/initSocketEvents.ts index ec681100..2e3f6feb 100644 --- a/code/server/src/controllers/ws/initSocketEvents.ts +++ b/code/server/src/controllers/ws/initSocketEvents.ts @@ -13,7 +13,7 @@ export default function initSocketEvents(events: Record) Object.entries(events).forEach(([event, handler]) => { socket.on(event, async data => { try { - logger.logInfo('Event: ' + event + ': ' + JSON.stringify(data)); + logger.logInfo('Event: ' + event); await handler(socket, data); } catch (e: any) { logger.logError(e); diff --git a/code/server/src/firebaseConfig.ts b/code/server/src/firebaseConfig.ts index 3fbade44..b425fd10 100644 --- a/code/server/src/firebaseConfig.ts +++ b/code/server/src/firebaseConfig.ts @@ -1,11 +1,20 @@ -import { cert, initializeApp, ServiceAccount } from 'firebase-admin/app'; -import serviceAccount from '@/firestore-key-5cddf-472039f8dbb6.json'; +import { config } from 'dotenv'; +import admin from 'firebase-admin'; import { getFirestore } from 'firebase-admin/firestore'; -initializeApp({ - credential: cert(serviceAccount as ServiceAccount), +config(); + +// decode the base64 encoded service account json +const serviceAccountBase64 = process.env.FIREBASE_SERVICE_ACCOUNT; +const serviceAccountBuffer = Buffer.from(serviceAccountBase64!, 'base64'); +const serviceAccount = JSON.parse(serviceAccountBuffer.toString('utf8')); + +// initialize firebase admin sdk +admin.initializeApp({ + credential: admin.credential.cert(serviceAccount), }); +// initialize firestore const db = getFirestore(); export default db; diff --git a/code/shared/src/workspace/types/workspace.ts b/code/shared/src/workspace/types/workspace.ts index a4c58349..aa4295c5 100644 --- a/code/shared/src/workspace/types/workspace.ts +++ b/code/shared/src/workspace/types/workspace.ts @@ -4,7 +4,7 @@ export type WorkspaceMeta = { name: string; id: string; createdAt: string; - members: number; + members: number | string[]; isPrivate: boolean; };