305 lines
14 KiB
Markdown
305 lines
14 KiB
Markdown
# 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).
|