* feat: move login credentials to a DB-backed users table with an admin management page Replaces the hardcoded AUTH_USERNAME/AUTH_PASSWORD login check with a new auth_accounts table (bcrypt-hashed passwords, admin/user roles). Adds admin-only /api/users CRUD routes and a "Brugere" admin page in the frontend for managing logins without redeploying. Removes the unused, unmounted duplicate login route in src/routes/auth.js. * docs: add architecture codemaps with diagrams for the whole system Adds codemaps/architecture.md, backend.md, frontend.md, and data.md — Mermaid-diagrammed design documentation verified against the live codebase and database rather than assumed from CLAUDE.md. Covers the unified-server.js request flow (mounted routers + ~183 inline routes), 68 backend services grouped by domain, the frontend's state-driven view-switch (no React Router in practice despite BrowserRouter being present), and the full 122-table DB schema with the auth_accounts vs unrelated users table naming trap flagged explicitly. Links added from the root README. Co-Authored-By: Claude Sonnet 5 <[email protected]> * feat: ship canonical roof quote workflow * fix: keep migration dry-run idempotent * [verified] feat: complete Smart Pakker management * [verified] fix: ignore blank task dependencies * [verified] fix: align package duplication with schema * [verified] fix: enforce Discord status limits * [verified] fix: link Smart Pakke materials safely * [verified] fix: harden material link review * [verified] feat: improve material matching * fix: scope pitch validation to roof packages * fix: support canonical snapshots on production schema * [verified] fix: hide internal package metadata from PDF * [verified] feat: deliver sales-ready customer PDF * [verified] feat: ship sales-ready PDF with AI overview * [verified] fix: authenticate project list requests * [verified] fix: refresh project-list authentication * [verified] fix: open existing project details * [verified] fix: keep roof components searchable in builder * [verified] fix: expose all Smart Package categories * [verified] fix: authenticate project creation * [verified] feat: make Smart Pakker the universal project flow * [verified] feat: preview Smart Package contents * [verified] test: keep generic release isolated from downpipe work * feat: add first-class Smart Pakke rentals * [verified] feat: add gutter and downpipe smart packages * [verified] fix: prepare six-house gutter quote flow * [verified] fix: open generic quotes without roof geometry * [verified] fix: review generic quotes with authenticated APIs * [verified] fix: calculate generic Smart Package quotes * [verified] fix: return generic calculation breakdown * feat: checkpoint generic signed snapshot validation with red-green tests * feat: complete fail-closed generic quote approval and customer PDF flow * feat: use generic signed snapshot in final review * feat: redesign generic quote final review * fix: harden generic review summaries * feat: add auditable six-house package basis * [verified] feat: finish auditable Smart Pakke UI * [verified] fix: bind auditable quantity and price bases * [verified] fix: keep six-house basis across package versions * [verified] fix: complete smart package discovery management * [verified] fix: simplify composition and generic scope * [verified] test: keep explicit roof contracts fail closed * [verified] fix: harden generic quote snapshots * fix: make generic quote delivery customer safe * [verified] fix: secure package catalog reads * [verified] fix: close workspace provenance blockers * fix: harden customer document language boundary * [verified] fix: secure smart package internal reads * fix: version package child mutations atomically * feat: add generic customer quote text flow * [verified] fix: allow manual customer numbers * [verified] fix: expose optional roof geometry * [verified] fix: rebase hydrated packages after geometry edits * [verified] feat: add free editable site area map * [verified] fix: harden map recovery and geocoding gate * fix: bind map quantities to authoritative geometry * fix: release geocoder lock before dispatch * fix: separate roof and site geometry provenance * fix: revoke stale admin authorization * fix: migrate task geometry basis * fix: make backend CI dependency-complete * ci: seed isolated e2e login account * fix: allow clean database bootstrap * fix: skip indexes for optional tables * test: use canonical mansard geometry in e2e * [verified] fix(auth): enforce live operator boundary * fix: fail close Ordrestyring offer transport * fix(frontend): authenticate customer project requests * fix: align canonical roof type contract * [verified] fix: reconcile legacy package labor safely * [verified] fix: audit site geometry deletion * docs: add PR 31 reviewer guide * docs: synchronize Obsidian vault * docs: sync integrated reviewer guide to Obsidian * ci: seed isolated auth account explicitly * fix: close offer bootstrap and service readiness gaps * fix: authenticate protected package callers * fix: provision initial admin and disable generic send * [verified] fix: close final quote release blockers * [verified] fix: seed gutter packages before deployment --------- Co-authored-by: alexpolo1 <[email protected]> Co-authored-by: Claude Sonnet 5 <[email protected]>
7.9 KiB
Backend Structure
Entry point: backend/unified-server.js. It mounts modular routers and defines ~180 routes inline in the same file (verified by grepping app.get/post/put/patch/delete('/api/...') occurrences). Both patterns are live in production — inline routes are not legacy cruft to be migrated, they're simply how a large share of this API is built.
Mounted routers
| Mount path | File | Purpose |
|---|---|---|
/api/enhanced |
src/routes/enhancedFeatures.js |
Roof-type package search, add package to project (loaded in a try/catch at startup, not top-level) |
/api/smart-packages |
src/routes/smartPackagesRoutes.js |
Smart Package CRUD, management view, recalculation (also try/catch-loaded) |
/api/stark |
src/routes/starkImport.js |
Stark CSV catalog upload + import history/status (also try/catch-loaded) |
/api/customer-projects |
src/routes/customerProjects.js |
Customer + project CRUD, the core project lifecycle |
/api/ai |
src/routes/ai.js |
AI feature flags, subscription usage, support-draft generation |
/api/users |
src/routes/users.js |
New (2026-09-04). Admin-only user management CRUD |
/api/mobile |
src/routes/mobileOrders.js |
Field/mobile intake against Ordrestyring order history |
/api/analytics |
src/routes/analytics.js |
KPIs, employees, customers pulled from the Ordrestyring GraphQL layer |
/api/product-telemetry |
src/routes/productTelemetry.js |
Frontend product-usage event tracking + scorecards |
/api/ordrestyring |
routes/ordrestyring.js |
Quote submission, order status/list against Ordrestyring |
/api/ordrestyring/offers |
routes/offers.js |
Offer creation in Ordrestyring |
/api/calendar |
routes/calendar.js |
Calendar read + sync |
/api/ordrestyring/case |
routes/cases.js |
Case detail / work breakdown |
/api/visual-reports |
routes/visualTestReports.js |
Visual test report browsing (admin-API-key gated) |
/api/admin/logs |
routes/adminLogs.js |
Structured log browsing (admin-API-key gated) |
/api/health |
src/routes/healthDashboard.js |
DB/server health metrics |
/api/client-errors |
routes/clientErrors.js |
Frontend error/session reporting intake |
/api/support |
src/routes/supportTickets.js |
osTicket support worklist/ticket creation |
Note there are two routes directories: backend/src/routes/ (newer) and backend/routes/ (older, still actively mounted — ordrestyring.js, offers.js, calendar.js, cases.js, adminLogs.js, clientErrors.js, visualTestReports.js). Both are live; the split is historical, not a deprecation boundary.
Dead route files (unmounted — do not assume these are live)
These files exist in backend/src/routes/ but are not required anywhere in the codebase (verified by grepping every require('./src/routes/<name>') call site): bygmaPrisbog.js, categories.js, cleanup.js, pricing.js, quotes.js, uploads.js, webPrices.js. Same situation applied to auth.js, which was deleted 2026-09-04 for exactly this reason — its logic had silently diverged from the real, inline /api/auth/login handler in unified-server.js. Before editing any of these seven files, confirm first whether they're actually reachable; as of this writing, none are.
Inline routes in unified-server.js (representative, not exhaustive)
/api/auth/login, /api/auth (legacy no-op), /api/pdf/generate, /api/web-prices/search, plus ~180 more (183 app.<verb>('/api/...') calls total, verified 2026-09-04) covering pricing, geometry, quote generation, PDF, and Ordrestyring glue. Grep unified-server.js for app\.(get|post|put|patch|delete)\('/api/ to enumerate the current full list — it changes often enough that a static list here would go stale immediately.
Services (backend/src/services/, 68 files)
Grouped by domain:
AI / quote generation — openaiService.js, projectAiService.js, aiSuggestionService.js, aiValidationJobService.js, aiFeatureFlagService.js, codexGenerationService.js, codexCliValidationService.js, quoteRealismService.js, quoteTemplateService.js, quoteBenchmarkService.js, quoteEconomicsService.js
Smart packages — smartPackageService.js, smartPackageManagementService.js, smartPackageDefaultsService.js, smartPackageIntegrityService.js, smartPackageWorkspaceService.js, smartPackageMaterialMatchService.js, smartPackageExcelImportService.js, smartPackageExcelValidationService.js, historicalSmartPackageSearchService.js, packageService.js, materialPackageService.js (an older, parallel package system — see data.md)
Material pricing / import — starkImportService.js, bygmaImportService.js, bygmaPrisbogImportService.js, bygmaScraperService.js, haandvaerkPriserImportService.js, priceImportService.js, pricingService.js, materialPriceStatusService.js, installationManualService.js, webPriceService.js
Project lifecycle — customerProjectService.js, projectCalculationService.js, projectLaborService.js, projectMaterialService.js, projectExperienceService.js, projectFlowValidationService.js, projectQuoteGenerationService.js, roofGeometryService.js, advancedGeometryService.js, timeCalculatorService.js, orderSuggestionService.js, taskCategorizationService.js, planningService.js
Ordrestyring sync/integration — ordrestyringService.js, ordrestyringSyncService.js, ordrestyringQuoteService.js, ordrestyringMoneyService.js, orderStatusService.js, graphqlClient.js, enhancedOrderDataService.js, mobileOrderService.js
PDF / documents — pdfGenerationService.js, pdfResponseService.js, documentParserService.js, ocrService.js, excelMappingService.js
Auth / users — userService.js (new, 2026-09-04, bcrypt + auth_accounts)
Platform / infra — databaseService.js (MariaDB pool + query wrapper; also runs ~37 inline CREATE TABLE IF NOT EXISTS statements at startup — see data.md), databaseCleanupService.js, bootstrapServiceContainer.js, dynamicImportService.js, productTelemetryService.js, googleSearchService.js, repositoryStatusDiscordService.js, supportDraftService.js
Request flow and auth
flowchart LR
Req["Incoming request"] --> R{"Router or\ninline handler?"}
R -- "mounted router" --> MW["verifyToken\n(backend/src/middleware/auth.js)"]
R -- "inline in\nunified-server.js" --> MW
MW -- "valid JWT" --> Admin{"needs admin?"}
MW -- "missing/invalid" --> Reject["401/403"]
Admin -- "yes: requireAdmin" --> Svc["Service layer\n(backend/src/services/*)"]
Admin -- "no" --> Svc
Svc --> DBS["databaseService.query()"]
DBS --> DB[("MariaDB")]
Not every route requires auth — verifyToken is applied per-router/per-endpoint, not globally. Some admin surfaces use a separate optionalAdminApiKeyGuard (API-key based) rather than JWT — see the /api/visual-reports and /api/admin/logs mounts above.
Auth accounts — the auth_accounts / users naming trap
As of 2026-09-04, login credentials moved out of hardcoded env vars into a real table: auth_accounts (id, username, password_hash [bcrypt], role enum admin/user, last_login_at), managed by userService.js and src/routes/users.js.
This is deliberately not called users. A pre-existing, unrelated table named users already exists — it holds employee records synced from Ordrestyring (first_name, last_name, init, email; written by ordrestyringSyncService.js). It has nothing to do with login. If you're adding auth-related columns or queries, make sure you're touching auth_accounts, not users.
The live login handler is the inline POST /api/auth/login in unified-server.js (not a separate router file) — it looks up auth_accounts via userService.findByUsername, verifies with bcrypt, and issues a JWT carrying { username, id, role }. requireAdmin middleware gates /api/users/* on role === 'admin'.