# Tilbudgivern — System Architecture Tilbudgivern is an AI-assisted quote calculator for the Danish carpentry trade (tømrerfaget). One Node/Express process serves the API and the built React frontend; there is no separate frontend host and no microservices. ```mermaid flowchart TB Browser["Browser (React SPA)"] subgraph Host["This machine — also the production server"] PM2["PM2 process: tilbudgivern-unified"] Server["backend/unified-server.js\nExpress on :4032"] Static["frontend/build/\n(static React bundle)"] Socket["Socket.IO\n(WebSocket, real-time updates)"] DB[("MariaDB\ndatabase: tilbudgivern")] PM2 --> Server Server -- serves --> Static Server --- Socket Server --> DB end OpenAI["OpenAI GPT-4 API\n(quote generation, budget-capped)"] Ordrestyring["Ordrestyring GraphQL API\ncustomers, cases, offers, calendar, hours"] Stark["Stark CSV catalog import"] Bygma["Bygma price book / install manuals"] Browser <-- "HTTPS + WebSocket" --> Server Server -- REST/HTTPS --> OpenAI Server -- GraphQL --> Ordrestyring Server -- CSV upload --> Stark Server -- scrape/import --> Bygma ``` ## Why one process `backend/unified-server.js` is a single ~400K-line Express app: it mounts modular routers from `backend/src/routes/*.js` and `backend/routes/*.js`, but also defines roughly 180 routes directly inline in the file itself (see [backend.md](./backend.md)). This is a real characteristic of the codebase, not a diagram simplification — treat the file as the de facto entry point and router table combined. ## Deployment model - **This host is production.** There is no separate deploy target and no CI/CD pipeline that ships on merge. - Process manager: **PM2**, process name `tilbudgivern-unified`. - Deploying = pulling `main`, rebuilding the frontend into `frontend/build/`, `npm ci` in `backend/`, then `pm2 restart tilbudgivern-unified`. See the root `CLAUDE.md` "Deployment" section for the exact command sequence and health-check curls. - Daily DB backups via cron (02:00, 7-day retention). - A GitHub Actions SSH/rsync deploy workflow was removed — it targeted secrets that were never configured and every run of it failed historically. If a real second deploy target appears later, that workflow's structure (versioned release dirs, atomic symlink swap, pre-deploy backup, smoke tests) is a reasonable starting point, per `CLAUDE.md`. ## Companion documents - [backend.md](./backend.md) — routes, services, request flow, auth - [frontend.md](./frontend.md) — component structure, view-switch pattern, auth context - [data.md](./data.md) — database schema, table inventory, migration model