Architecture ============ GCMS is built as a layered Node.js application following a classic Model-Controller pattern, served by Express with real-time updates powered by Socket.io. This page documents the major components, how they fit together, and how a request flows through the system. High-level overview ------------------- .. mermaid:: flowchart TB Browser["Browser
(EJS views + JS)"] Listen["listen.js
(starts HTTP server)"] Server["server.js
(HTTP + Socket.io)"] App["app.js
(Express app)"] Routes["routes/
(URL → controller)"] Controllers["controllers/
(request handling)"] Models["models/
(SQL queries)"] Utils["utils/
(shared helpers)"] DB[("Supabase
PostgreSQL + Storage")] External["External services
Microsoft Graph
Google Gemini
Mailtrap"] Browser <--> Listen Listen --> Server Server --> App App --> Routes Routes --> Controllers Controllers --> Models Controllers --> Utils Models --> DB Utils --> DB Utils --> External Project layout -------------- The repository is organised by responsibility, not by feature: ============================ ==================================================== Folder Purpose ============================ ==================================================== ``routes/`` Express route definitions; map URLs to controllers ``controllers/`` Request handlers; validate input, call models, send responses ``models/`` SQL queries and database access logic ``utils/`` Cross-cutting concerns (auth, sessions, sockets, email, AI) ``views/`` EJS templates rendered server-side ``public/`` Static assets (CSS, client-side JS, images) ``db/`` Schema definition and seed scripts ``node_scripts/`` Standalone Node scripts (e.g. ``install_db.js``) ``new_tests/`` Jest unit and integration tests, one pair of files per user requirement ============================ ==================================================== The layered structure means most features touch one file per layer. For example, the task feature uses ``routes/taskRoutes.js``, ``controllers/taskControllers.js``, and ``models/taskModels.js``. Request flow ------------ A typical HTTP request flows through the application like this: 1. **Browser** sends a request (e.g. ``POST /api/tasks``) 2. **listen.js** is what's actively listening; Node hands the request to the HTTP server it started 3. **server.js** forwards the request to the Express app (and routes socket frames to the Socket.io handlers) 4. **app.js** routes it to the correct ``app.use`` mount 5. **Route file** matches the URL pattern and calls the controller 6. **Controller** validates the request, checks authentication, and calls one or more models 7. **Model** executes a parameterised SQL query against Supabase 8. **Controller** formats the result and sends an HTTP response 9. **Socket.io** (where relevant) broadcasts the change to other connected clients in the same project Entry points ------------ GCMS uses a three-file entry point that separates concerns cleanly and makes each layer independently testable. Tests can import the Express app, the HTTP+Socket.io server, or trigger live listening behaviour as needed without touching the others. app.js ~~~~~~ ``app.js`` is the testable Express application. It: - Loads environment variables from ``.env.auth`` - Configures Express middleware (JSON parsing, cookies, EJS templating, static assets) - Sets up sessions and authentication via the ``utils/`` helpers - Mounts each route file under the appropriate path (``/`` for page routes, ``/api`` for everything else) - Exports the configured app When ``NODE_ENV=test``, ``app.js`` injects a bypass middleware that populates ``req.user`` with a stub user, so integration tests can exercise authenticated routes without going through the full OAuth flow. server.js ~~~~~~~~~ ``server.js`` wraps the Express app in a Node HTTP server and attaches Socket.io to it. It: - Imports the configured Express app from ``app.js`` - Creates an HTTP server wrapping the app - Attaches Socket.io to the HTTP server - Initialises socket event handlers via ``utils/socket.js`` - Exports the configured server (but does **not** call ``listen``) This separation allows integration tests for socket features to import the server directly without binding to a port. listen.js ~~~~~~~~~ ``listen.js`` is the runtime entry point invoked by ``npm run dev`` and ``npm run prod``. Its single responsibility is to import the configured server and begin listening on port 3000. This means production code, integration tests, and socket tests can each pick the entry point that matches their needs without pulling in irrelevant behaviour. Routes ------ Route files (in ``routes/``) define the URL surface of the application. Each route file groups related endpoints — for example ``taskRoutes.js`` handles all task-related URLs. There are two categories of routes: **Page routes** (``pageRoutes.js``) Server-side rendered EJS views, mounted at ``/``. Examples: ``/``, ``/projects``, ``/projects/:id``, ``/profile``. **API routes** (everything else) JSON endpoints, mounted at ``/api``. Used by client-side JS for asynchronous operations. Grouped by resource: - ``authRoutes.js`` — Microsoft OAuth flow - ``userRoutes.js`` — user profile and account operations - ``projectRoutes.js`` — project CRUD - ``taskRoutes.js`` — task CRUD and assignment - ``calendarRoutes.js`` — meetings and Microsoft calendar sync - ``notificationsRoutes.js`` — notification fetch and read state - ``contributionsRoutes.js`` — contribution calculations - ``noteRoutes.js`` — sticky-note widgets on the project board - ``fileRoutes.js`` — shared file upload and listing - ``aiRoutes.js`` — AI assistant chat A complete list of endpoints with parameters and responses is in the :doc:`/api-reference/index`. Controllers ----------- Controllers (in ``controllers/``) contain the per-endpoint logic: - Validate the incoming request body and parameters - Verify the user is authenticated and authorised for the action - Call one or more model functions to read or write data - Shape the response and send it back to the client - Trigger notifications or socket broadcasts where appropriate Controllers do not contain SQL. All database access is delegated to the model layer. Models ------ Models (in ``models/``) are the only layer that talks to the database directly. Each model file exposes async functions that: - Build parameterised SQL queries (using either the ``pg`` pool or the Supabase JS client depending on the operation) - Execute the query and return the result - Handle row-level edge cases (not found, empty results, etc.) Keeping SQL in models means controllers stay clean and unit tests can mock the data layer without spinning up a database. Utils ----- The ``utils/`` folder holds cross-cutting helpers that don't belong to any single feature: ============================ ==================================================== File Purpose ============================ ==================================================== ``auth.js`` Passport.js Microsoft OAuth strategy and middleware ``session.js`` Express session configuration ``socket.js`` Socket.io event handlers and room management ``supabase.js`` Supabase JS client + ``pg`` pool initialisation ``gemini.js`` Google Gemini API client and prompt construction ``fileFetcher.js`` File download and Office text extraction ``emailSender.js`` Nodemailer transporter for notification emails ``emailConfig.js`` Mailtrap SMTP configuration ============================ ==================================================== Real-time updates ----------------- GCMS uses Socket.io for real-time features. ``utils/socket.js`` sets up the server-side socket and joins each authenticated client to a room for every project they belong to. When a relevant event occurs — a new chat message, a task status change, a widget moved on the project board — the controller emits a socket event to the project's room. All connected clients in that project receive the update and refresh their UI without a page reload. Socket events used: - ``chat:message`` — new chat message - ``task:update`` — task created, updated, or deleted - ``widget:update`` — widget added, moved, or removed - ``notification`` — new notification for a specific user Authentication flow ------------------- User authentication uses the OAuth 2.0 Authorization Code flow with Microsoft as the identity provider, implemented via Passport.js. .. mermaid:: sequenceDiagram participant User participant GCMS participant Microsoft User->>GCMS: Click "Sign in with Microsoft" GCMS->>Microsoft: Redirect to /authorize User->>Microsoft: Enter credentials Microsoft->>GCMS: Redirect to /api/auth/callback with code GCMS->>Microsoft: Exchange code for access token Microsoft->>GCMS: Access token + refresh token GCMS->>GCMS: Create or update user record GCMS->>User: Redirect to dashboard with session cookie The access token is stored in the session and used for subsequent Microsoft Graph API calls (e.g. creating calendar events). The refresh token is used to obtain a new access token when the current one expires. External integrations --------------------- GCMS integrates with four external services. See :doc:`/external-integrations/index` for full details. - **Microsoft Graph API** — authentication and calendar sync - **Google Gemini** — AI assistant - **Supabase** — database and file storage - **Mailtrap** — email notifications Testing ------- GCMS has a dedicated automated test suite with both unit and integration coverage. See :doc:`/testing/index` for the full testing strategy, how to run tests, and conventions for writing new ones.