# Teach Digibord-webapp (Fastify + static frontend) met PostgreSQL, gecontaineriseerd en via Forgejo Actions automatisch uitgerold: `dev` → test-VM, release → productie-VM. ## Structuur ``` teach/ ├─ public/index.html # de digibord-app (voorheen teach.html) ├─ src/server.js # Fastify: serveert de app + /api + DB-pool ├─ db/001_init.sql # initieel Postgres-schema ├─ Dockerfile # productie-image (non-root, healthcheck) ├─ compose.yaml # lokaal draaien (app + postgres) ├─ deploy/compose.deploy.yaml # test/prod: pullt image uit de registry ├─ .forgejo/workflows/ │ ├─ dev.yaml # push naar dev → build + deploy test │ └─ release.yaml # release → build + deploy prod └─ .env.example ``` ## Lokaal draaien ```bash cp .env.example .env # pas POSTGRES_PASSWORD en DATABASE_URL aan docker compose up --build ``` App op http://localhost:3000, healthcheck op `/healthz`, DB-check op `/readyz`. ## Architectuurkeuzes - **Container i.p.v. losse static site**: omdat er een database bijkomt, is een backend nodig (browser praat niet rechtstreeks met Postgres). De Fastify-app serveert de HTML én biedt de `/api`, en praat met de DB. - **Forgejo container registry**: CI bouwt de image één keer en pusht die; beide VM's pullen exact dezelfde geteste image. Geen build op de productie-VM. - **Pangolin/Traefik verzorgt de publieke HTTPS-ingang**. De meegeleverde nginx vormt de interne proxylaag en luistert standaard op poort `8081`, zodat de Pangolin/Traefik-route deze via het VM-/containernetwerk kan bereiken. De Fastify-app vertrouwt precies twee proxy-hops. ## Eenmalige setup ### 1. Repo aanmaken in Forgejo Maak een leeg repo (bv. `ramon/teach`) en push (zie onderaan). ### 2. Registry / Actions variabelen en secrets Onder **Settings → Actions → Variables** van het repo (of org): | Variable | Waarde | Uitleg | |------------|------------------|---------------------------------| | `REGISTRY` | `10.0.20.22:3000`| Host van je Forgejo = registry | De image heet dan `10.0.20.22:3000/bes-r/teach`. Onder **Settings → Actions → Secrets**: | Secret | Uitleg | |--------------------|--------------------------------------------------------------| | `REGISTRY_USER` | Forgejo-gebruiker met package-write rechten | | `REGISTRY_TOKEN` | Token/wachtwoord voor die gebruiker (scope: packages) | | `TEST_HOST` | IP/hostname van de test-VM | | `TEST_USER` | SSH-gebruiker op de test-VM | | `TEST_SSH_KEY` | Private SSH-key (deploy key) voor de test-VM | | `TEST_DEPLOY_PATH` | Pad op de test-VM met `compose.deploy.yaml` + `.env` + `db/` | | `PROD_HOST` | IP/hostname van de productie-VM | | `PROD_USER` | SSH-gebruiker op de productie-VM | | `PROD_SSH_KEY` | Private SSH-key voor de productie-VM | | `PROD_DEPLOY_PATH` | Pad op de productie-VM met de deploy-bestanden | > De runner moet de `docker`-CLI kunnen gebruiken (host-socket of docker-in-docker) > en `actions/checkout` + `appleboy/ssh-action` kunnen ophalen. Pas `runs-on` in > de workflows aan naar het label van jouw runner als dat niet `ubuntu-latest` is. ### 2b. HTTP-registry toestaan (insecure-registries) Je Forgejo draait op **`http://10.0.20.22:3000`** (platte HTTP). Docker weigert HTTP-registries tenzij je ze expliciet toestaat. Doe dit op **drie** plekken: de **runner-host** (die pusht) en **beide VM's** (die pullen). Op elke Docker-host: ```bash # /etc/docker/daemon.json { "insecure-registries": ["10.0.20.22:3000"] } ``` ```bash sudo systemctl restart docker ``` > Zet je Forgejo later achter HTTPS met een echt domein, dan kan deze stap weg > en gebruik je dat domein als `REGISTRY`. ### 3. Deploy-map op elke VM Op zowel de test- als de productie-VM, in het pad dat je bij `*_DEPLOY_PATH` opgeeft: ```bash mkdir -p teach && cd teach # kopieer deze twee uit de repo: # deploy/compose.deploy.yaml -> compose.deploy.yaml # db/ -> db/ # maak een .env aan: cat > .env <<'EOF' IMAGE=10.0.20.22:3000/bes-r/teach:dev # prod: laat CI dit op :vX.Y.Z zetten POSTGRES_DB=teach POSTGRES_USER=teach POSTGRES_PASSWORD= DATABASE_URL=postgres://teach:@db:5432/teach APP_PORT=3000 WEB_BIND_IP=0.0.0.0 WEB_PORT=8081 TRUST_PROXY_HOPS=2 SUPER_USER=beheerder SUPER_PASS= EOF ``` CI werkt bij elke deploy de `IMAGE=`-regel bij, pullt en herstart. ### 4. Pangolin/Traefik publiceren Publiceer `http://:8081` via Pangolin/Traefik en laat daar TLS beëindigen. De interne nginx behoudt `X-Forwarded-Proto: https`, waarna de app Secure/HttpOnly/SameSite-cookies, HSTS en overige securityheaders gebruikt. - Beperk poort 8081 met de hostfirewall tot het Pangolin/Traefik- of tunnelnetwerk. - Pas `TRUST_PROXY_HOPS` alleen aan wanneer de proxyketen werkelijk verandert. - Bij een aparte Traefik-container kan een gedeeld intern Docker-netwerk nodig zijn; zet `WEB_BIND_IP` niet ruimer dan noodzakelijk. - `SUPER_PASS` is alleen nodig bij een lege database, wordt nooit gelogd en moet via de beveiligde deploy-omgeving worden aangeleverd. - De overgang naar v0.3.03-beta trekt bestaande browsersessies bewust in. ## Workflow / branching - Werk op feature-branches, merge naar **`dev`** → automatisch naar test. - Tevreden? Maak een **release** (tag `vX.Y.Z`) → automatisch naar productie. ## Push naar Forgejo (eerste keer) ```bash git remote add origin http://10.0.20.22:3000/bes-r/teach.git git push -u origin main git push -u origin dev ``` ## Volgende stap: data uit localStorage naar de database De frontend bewaart borden/gebruikers nu nog in `localStorage`. Om echt een gedeelde database te gebruiken, bouwen we `/api`-endpoints (boards, folders, users) in `src/server.js` en laten we de frontend die aanroepen i.p.v. `localStorage`. Het schema in `db/001_init.sql` is daarvoor het startpunt.