# Project Files Guide A junior-friendly walk-through of every folder and file that matters in this repository. Read it alongside [README.md](README.md): the README tells you how to run the project, this guide tells you where things live once it is running. ## How the application is organised There are three parts: 1. **Backend** — Node.js + Express in [src/](src/). Talks to MariaDB, serves the API under `/api/v1/`, and serves the static frontend files. 2. **Frontend** — two single-page shells in [public/](public/): `user.html` for operators and `admin.html` for administrators. Both are ES-module based (no bundler) and use Bootstrap 5. 3. **Database** — MariaDB. Schema and seed data are in [sql/](sql/). Docker loads them automatically on a fresh volume. ### End-to-end request flow (simplified) 1. `docker compose up` starts three containers: `app`, `db`, `phpmyadmin`. 2. [src/server.js](src/server.js) boots Express and connects to MariaDB. 3. The browser loads `/` → `portal.html` (chooser page). 4. The user clicks a link and goes to `/login-admin` or `/login-user`. 5. After login, `/admin` or `/user` is served — both require a valid `auth_token` cookie (see [src/middleware/authMiddleware.js](src/middleware/authMiddleware.js)). 6. The frontend shell loads either `admin-app.js` or `user-app.js`, which hands off to `public/js/admin.js` or `public/js/user.js`. 7. The frontend calls `/api/v1/admin/all` to bulk-load every entity needed for rendering, and keeps working drafts in IndexedDB. ## Root files ### [package.json](package.json) Defines the Node project: scripts (`start`, `dev`, `test:environment`), dependencies (Express, MariaDB driver, cookie-parser, cors, dotenv), and the minimum Node version. ### [docker-compose.yml](docker-compose.yml) Spins up `app`, `db`, and `phpmyadmin` containers with the right volumes and ports for local development. ### [.env.example](.env.example) / `.env` `.env.example` documents the variables the backend expects. Copy it to `.env` and fill in real values; [src/config/env.js](src/config/env.js) loads them at startup and fails fast if a required one is missing. ### [.devcontainer/](.devcontainer/) VS Code Dev Containers definition so the editor opens directly inside the `app` container. ### [docker/mariadb/init/](docker/mariadb/init/) Mount point for extra init scripts. `sql/schema.sql` and `sql/seed.sql` are placed here by Docker Compose so they run the first time the DB volume is created. ## public/ — what the browser gets ### HTML shells - [public/portal.html](public/portal.html) — the neutral landing page at `/`. Two links: one to the user login, one to the admin login. - [public/login-admin.html](public/login-admin.html) and [public/login-user.html](public/login-user.html) — login forms. They POST to `/api/v1/auth/admin/login` or `/api/v1/auth/user/login` and follow the JSON `redirect` on success. - [public/admin.html](public/admin.html) — Bootstrap sidebar + main area for the admin console. Loads `admin-app.js`. The sidebar opens on the **Reports** panel by default; nav categories are ordered Reports → CheckLists → Sites → Users → Settings. - [public/user.html](public/user.html) — Bootstrap shell for the operator task workflow. Loads `user-app.js`. Contains a **Sync** button in the Assigned Tasks header that refreshes the task list from the server without a full page reload. ### Top-level scripts - [public/admin-app.js](public/admin-app.js) — opens the shared IndexedDB (`check-list-poc-db`), reads the cached image rules, then calls `initAdmin()` from [public/js/admin.js](public/js/admin.js). - [public/user-app.js](public/user-app.js) — opens the shared IndexedDB (`check-list-poc-db`) and calls `initUser()` from [public/js/user.js](public/js/user.js). `user.js` additionally opens a second database (`user-portal-db`) for per-task record data and images. ### Static assets - [public/styles.css](public/styles.css) — custom styles layered on top of Bootstrap 5. Includes `.admin-table` (full-width, striped header, hover rows), `.admin-table-compact` (denser column padding for wide entity tables), and `.admin-table-actions` (shrink-wraps the actions column so data columns take all remaining space). - [public/icon.svg](public/icon.svg) — PWA icon referenced by [public/manifest.webmanifest](public/manifest.webmanifest). ## public/js/ — frontend modules Each file has a single responsibility and imports only what it needs. - [public/js/constants.js](public/js/constants.js) — store names, API base path, timings. - [public/js/state.js](public/js/state.js) — tiny shared object holding the current IndexedDB handle. Kept deliberately small; most feature-specific state lives inside `admin.js` or `user.js`. - [public/js/api.js](public/js/api.js) — `fetchJson(path, options)` wrapper that prepends the versioned base path and turns `{ message }` error bodies into thrown errors. - [public/js/db.js](public/js/db.js) — opens and wraps `check-list-poc-db` (the shared IndexedDB used by both admin and user consoles) with generic CRUD helpers: get, put, delete, transaction. - [public/js/user-db.js](public/js/user-db.js) — opens and wraps `user-portal-db` (a second, separate IndexedDB used only by the user console). Stores per-task record answers, visit dates, and image `dataUrl` blobs keyed by numeric task ID. - [public/js/validation.js](public/js/validation.js) — pure validators for reports and image-rule forms. No DOM or state dependencies, so the same logic can be reused server-side. - [public/js/images.js](public/js/images.js) — image optimisation entry point. Detects Web Worker + `OffscreenCanvas` support and falls back to main-thread canvas when not available. Preserves the EXIF block so the backend gets the same metadata the user saw. - [public/js/image-worker.js](public/js/image-worker.js) — the worker implementation used by `images.js`. - [public/js/exif.js](public/js/exif.js) — lightweight JPEG EXIF parser (IFD0 + GPS tags) used by both the user flow and the admin lightbox. - [public/js/admin.js](public/js/admin.js) — the admin console controller. Navigation, render functions, and CRUD handlers for every admin category. The Users table includes a **Tasks** column with a badge showing how many tasks are assigned to each user (blue if > 0, grey if 0). - [public/js/user.js](public/js/user.js) — the operator workflow controller. Key responsibilities: - `loadFromServer()` — fetches `/api/v1/admin/all`; on `401` (stale cookie after a server restart) it redirects to `/login-user`; on any other failure it falls back to the IndexedDB cache. - `filterTasksByUser()` — keeps only tasks where `task.userId` matches the `?userId=` URL parameter. - `showDetailView(id)` — chains `maybeHydrateFromServer()` (seeds IndexedDB from the server if the task has no local data) → `maybeDownloadImages()` (fetches image blobs from the server if they were previously uploaded) → `renderTaskDetail()`. - `forceSyncWithServer()` — called by the Sync button; re-fetches live data without a page reload. - Record editing, image attachment, save-as-draft, and submit-as-final with image optimisation and validation. ## src/ — backend ### [src/server.js](src/server.js) Boot file. Imports the Express app, confirms the DB connection, starts listening, and wires up `SIGINT` / `SIGTERM` for a clean shutdown. ### [src/app.js](src/app.js) Express wiring. Middleware stack in mount order: 1. `cors()` — wide-open CORS for PoC use 2. `cookieParser()` — parses the `auth_token` cookie 3. `express.json({ limit: '50mb' })` — accepts large base64 image payloads 4. `noCacheHtml` — sets `Cache-Control: no-cache` on every HTML response so browsers revalidate page content after a Docker restart instead of serving a stale cached copy 5. API routes under `/api/v1/` 6. HTML page routes (`/`, `/login-admin`, `/login-user`, `/admin`, `/user`) 7. `express.static(public/)` with `setHeaders` applying `no-cache` for `.html` files; JS/CSS use default ETag revalidation 8. 404 + global error handlers `requireAnyAuth` is applied to `/api/v1/admin/*` and `/api/v1/reports/*`; `requireUserAuth` / `requireAdminAuth` gate the HTML workspaces. ### src/config/ - [src/config/env.js](src/config/env.js) — loads `.env` via `dotenv`, checks required keys, exposes a normalised `env` object. ### src/db/ - [src/db/pool.js](src/db/pool.js) — creates the shared MariaDB pool and exposes `query()` / `closePool()`. ### src/middleware/ - [src/middleware/authMiddleware.js](src/middleware/authMiddleware.js) — `requireAdminAuth`, `requireUserAuth`, `requireAnyAuth`. For API callers (detected via `req.originalUrl` / `Accept`) an unauthenticated response returns `401 JSON`; browser navigation is redirected to the relevant login page. - [src/middleware/errorHandler.js](src/middleware/errorHandler.js) — `notFoundHandler` and global `errorHandler` that convert everything to a consistent JSON shape. - [src/middleware/validateParams.js](src/middleware/validateParams.js) — regex / numeric URL-parameter guards used by route definitions. ### src/routes/ | File | Prefix | Notes | | --- | --- | --- | | [healthRoutes.js](src/routes/healthRoutes.js) | `/api/v1/health` | Liveness + DB ping. | | [authRoutes.js](src/routes/authRoutes.js) | `/api/v1/auth` | Admin/user login, logout, `me`. Issues the `auth_token` cookie. | | [adminRoutes.js](src/routes/adminRoutes.js) | `/api/v1/admin` | CRUD for every admin entity and `/all` bulk load. Gated by `requireAnyAuth`. | | [reportRoutes.js](src/routes/reportRoutes.js) | `/api/v1/reports` | Submit/get/list/delete reports and their images. Gated by `requireAnyAuth`. | | [configRoutes.js](src/routes/configRoutes.js) | `/api/v1/config` | Image-rules read/write (audit-logged) and export profile. | | [lookupRoutes.js](src/routes/lookupRoutes.js) | `/api/v1/lookups` | Reference lookup sets and values. | | [templateRoutes.js](src/routes/templateRoutes.js) | `/api/v1/templates` | Legacy template catalogue (kept for the seeded sample + smoke test). | ### src/services/ Thin layer between routes and SQL. Each file owns a domain. - [adminService.js](src/services/adminService.js) — everything under `admin_*` tables, plus the bulk `loadAllAdminData()` used by both the admin and user consoles. - [auditService.js](src/services/auditService.js) — writes to `audit_log` whenever something sensitive changes (report submit/delete, image rules update, template publish). - [authService.js](src/services/authService.js) — credential verification and an in-memory session `Map` with 24-hour expiry. Plain-text password comparison — PoC only. - [cacheService.js](src/services/cacheService.js) — generic LRU + TTL factory, pre-configured `templateCache`, `lookupCache`, `configCache`. - [configService.js](src/services/configService.js) — image rules and export profile queries. (Previous `app_config` helpers were removed along with the table.) - [lookupService.js](src/services/lookupService.js) — `lookup_sets` / `lookup_values`. - [reportService.js](src/services/reportService.js) — `reports` and `report_images`. Images are stored as `LONGBLOB` rows; list helpers group them by `record_id`. - [templateService.js](src/services/templateService.js) — template + `template_versions` queries and publish flow. ### src/utils/ - [asyncHandler.js](src/utils/asyncHandler.js) — wraps async route handlers so rejected promises reach the Express error middleware. - [json.js](src/utils/json.js) — safely parses JSON columns returned by MariaDB. ## sql/ ### [sql/schema.sql](sql/schema.sql) Defines every table the app touches: - Template catalogue: `templates`, `template_versions`. - Reference data: `lookup_sets`, `lookup_values`, `image_rules`, `export_profiles`. - Reports: `reports` (answers as JSON) and `report_images` (binary blobs). - Audit: `audit_log`. - Admin entities: `admin_categories`, `admin_sub_categories`, `admin_severities`, `admin_statuses`, `admin_handled_by`, `admin_projects`, `admin_processes`, `admin_users`, `admin_sites`, `admin_cl_records`, `admin_cl_templates`, `admin_cl_template_records` (join), `admin_tasks`. - Credentials: `admin_credentials`. ### [sql/seed.sql](sql/seed.sql) Inserts enough data for the frontend to boot: the sample `incoming-inspection` template, `pass-fail` / `draft-status` lookups, the default image-rules row, the default export profile, and seed admin/user credentials. ## scripts/ ### [scripts/test-environment.js](scripts/test-environment.js) Connectivity smoke test — hits `/api/v1/health`, `/api/v1/templates`, the DB directly, and phpMyAdmin. Useful after `docker compose up` and after any infrastructure change. ## Recommended reading order 1. [README.md](README.md) 2. [docker-compose.yml](docker-compose.yml) 3. [sql/schema.sql](sql/schema.sql) 4. [src/server.js](src/server.js) → [src/app.js](src/app.js) 5. [src/middleware/authMiddleware.js](src/middleware/authMiddleware.js) 6. [src/routes/adminRoutes.js](src/routes/adminRoutes.js) and [src/services/adminService.js](src/services/adminService.js) 7. [public/admin-app.js](public/admin-app.js) → [public/js/admin.js](public/js/admin.js) 8. [public/user-app.js](public/user-app.js) → [public/js/user.js](public/js/user.js) ## Quick "where do I look" rule - Page layout looks wrong → the relevant `.html` in [public/](public/) and [public/styles.css](public/styles.css). - Browser behaviour is wrong → `public/js/admin.js` or `public/js/user.js`, or the feature-specific module (`validation.js`, `images.js`, `exif.js`). - API endpoint is wrong → the matching file in [src/routes/](src/routes/), then the service it delegates to. - Data looks wrong → [sql/schema.sql](sql/schema.sql) and [sql/seed.sql](sql/seed.sql), plus the service file that owns the query. - App won't start → [src/server.js](src/server.js), [src/config/env.js](src/config/env.js), and [docker-compose.yml](docker-compose.yml).