|
| 1 | +# javaBin Kids |
| 2 | + |
| 3 | +Nettside for javaBin Kids — kode-arrangementer for barn i regi av [javaBin](https://java.no), i forbindelse med [JavaZone](https://javazone.no)-konferansen. |
| 4 | + |
| 5 | +Nettsiden presenterer arrangementer, håndterer påmelding med venteliste, og har et admin-panel for arrangørene. |
| 6 | + |
| 7 | +## Tech stack |
| 8 | + |
| 9 | +| Lag | Teknologi | |
| 10 | +|-----|-----------| |
| 11 | +| Frontend | [SvelteKit 2](https://svelte.dev/docs/kit) + [Svelte 5](https://svelte.dev) (runes) | |
| 12 | +| Backend | SvelteKit server routes (`+page.server.ts`, `+server.ts`) | |
| 13 | +| Database | PostgreSQL 17 via [Drizzle ORM](https://orm.drizzle.team) | |
| 14 | +| Validering | [Zod](https://zod.dev) | |
| 15 | +| E-post | [Resend](https://resend.com) | |
| 16 | +| Markdown | [marked](https://marked.js.org) (kursbeskrivelser) | |
| 17 | +| Auth | Cookie-basert sesjoner med bcrypt | |
| 18 | +| Infra | Docker Compose (dev + backup) | |
| 19 | + |
| 20 | +## Kom i gang |
| 21 | + |
| 22 | +### Forutsetninger |
| 23 | + |
| 24 | +- [Docker](https://docker.com) og Docker Compose |
| 25 | +- Node.js 22+ (for lokal utvikling uten Docker) |
| 26 | + |
| 27 | +### Med Docker (anbefalt) |
| 28 | + |
| 29 | +```bash |
| 30 | +# Start alt |
| 31 | +docker compose up -d |
| 32 | + |
| 33 | +# Kjør migrering og seed |
| 34 | +docker compose exec app npx drizzle-kit migrate |
| 35 | +docker compose exec app npx tsx seed.ts |
| 36 | + |
| 37 | +# Appen kjører på http://localhost:5175 |
| 38 | +``` |
| 39 | + |
| 40 | +### Uten Docker |
| 41 | + |
| 42 | +```bash |
| 43 | +# Forutsetter en kjørende PostgreSQL-instans |
| 44 | +cp .env.example .env |
| 45 | +# Rediger .env med din DATABASE_URL |
| 46 | + |
| 47 | +npm install |
| 48 | +npm run db:migrate |
| 49 | +npm run db:seed |
| 50 | +npm run dev |
| 51 | +``` |
| 52 | + |
| 53 | +### Miljøvariabler |
| 54 | + |
| 55 | +Se `.env.example`: |
| 56 | + |
| 57 | +| Variabel | Beskrivelse | |
| 58 | +|----------|-------------| |
| 59 | +| `DATABASE_URL` | PostgreSQL connection string | |
| 60 | +| `RESEND_API_KEY` | API-nøkkel fra [resend.com](https://resend.com) | |
| 61 | +| `ADMIN_USERNAME` | Brukernavn for admin (brukes av seed) | |
| 62 | +| `ADMIN_PASSWORD` | Passord for admin (brukes av seed) | |
| 63 | +| `BASE_URL` | Offentlig URL for e-postlenker | |
| 64 | + |
| 65 | +## Prosjektstruktur |
| 66 | + |
| 67 | +``` |
| 68 | +src/ |
| 69 | +├── lib/ |
| 70 | +│ ├── server/ |
| 71 | +│ │ ├── db/ |
| 72 | +│ │ │ ├── schema.ts # Drizzle-skjema (alle tabeller) |
| 73 | +│ │ │ └── index.ts # DB-tilkobling |
| 74 | +│ │ ├── ... # Komponenter og utils for server-side (validering, auth, e-post) |
| 75 | +│ ├── components/ |
| 76 | +│ │ ├── ... # Frontend komponenter |
| 77 | +│ └── toast.svelte.ts # Toast state management |
| 78 | +├── routes/ |
| 79 | +│ ├── ... # Offentlige sider |
| 80 | +│ ├── admin/ # Admin-panel (auth-beskyttet) |
| 81 | +│ └── api/ # API-ruter levert av SvelteKit |
| 82 | +└── app.css # Globale stiler (undervanns-tema) |
| 83 | +``` |
| 84 | + |
| 85 | +## Database |
| 86 | + |
| 87 | +### Tabeller |
| 88 | + |
| 89 | +| Tabell | Beskrivelse | |
| 90 | +|-----------------|-------------| |
| 91 | +| `events` | Arrangementer med dato, sted, registreringsperiode | |
| 92 | +| `courses` | Kurs innenfor et arrangement (aldersgruppe, kapasitet) | |
| 93 | +| `registrations` | Påmeldinger med status (confirmed/waitlisted/cancelled) | |
| 94 | +| `adminUsers` | Admin-brukere (opprettet via seed) | |
| 95 | +| `sessions` | Admin-sesjoner (cookie-basert) | |
| 96 | +| `siteContent` | Nøkkel/verdi-par for redigerbart sideinnhold | |
| 97 | +| `contactCards` | Kontaktkort for kontaktsiden | |
| 98 | + |
| 99 | +### Migreringer |
| 100 | + |
| 101 | +Drizzle Kit håndterer migreringer. SQL-filer ligger i `drizzle/migrations/`. |
| 102 | + |
| 103 | +```bash |
| 104 | +# Generer ny migrering etter skjemaendring |
| 105 | +npm run db:generate |
| 106 | + |
| 107 | +# Kjør migreringer |
| 108 | +npm run db:migrate |
| 109 | +``` |
| 110 | + |
| 111 | +### Seed |
| 112 | + |
| 113 | +`seed.ts` oppretter en admin-bruker og et eksempel-arrangement med kurs: |
| 114 | + |
| 115 | +```bash |
| 116 | +npm run db:seed |
| 117 | +``` |
| 118 | + |
| 119 | +## Viktige konsepter |
| 120 | + |
| 121 | +### Påmeldingsflyt |
| 122 | + |
| 123 | +1. Bruker velger kurs og fyller ut skjema |
| 124 | +2. Server validerer (Zod), sjekker duplikat, sjekker kapasitet |
| 125 | +3. Plassering skjer i en DB-transaksjon med `SELECT ... FOR UPDATE` (row-level locking) |
| 126 | +4. Hvis plass: `confirmed`, ellers: `waitlisted` med posisjon |
| 127 | +5. E-postbekreftelse sendes (med kanselleringslenke) |
| 128 | + |
| 129 | +### Venteliste |
| 130 | + |
| 131 | +- Ved kansellering rykker neste person på ventelisten opp automatisk |
| 132 | +- Alle gjenværende ventelisteposisjoner dekrementeres |
| 133 | +- Den opprykkte personen får e-post |
| 134 | + |
| 135 | +### Admin-autentisering |
| 136 | + |
| 137 | +- Cookie-basert sesjoner lagret i `sessions`-tabellen |
| 138 | +- Sesjoner utløper etter 24 timer |
| 139 | +- `+layout.server.ts` under `/admin` sjekker sesjon og redirecter til login |
| 140 | +- Alle admin API-ruter validerer sesjon |
| 141 | + |
| 142 | +### Typesikkerhet |
| 143 | + |
| 144 | +- Drizzle ORM gir type-safe database-tilgang |
| 145 | +- SvelteKit genererer `PageData`-typer fra `+page.server.ts` load-funksjoner |
| 146 | +- Alle sider importerer `PageData` fra `./$types` |
| 147 | +- Zod validerer all bruker-input på serveren |
| 148 | + |
| 149 | +### Visuelt design |
| 150 | + |
| 151 | +Inspirert av JavaZone 2026 sitt undervanns-tema: |
| 152 | +- Mørk blågrønn gradient bakgrunn |
| 153 | +- Turkise overskrifter, gylne aksenter |
| 154 | +- Animerte bobler med interaktiv fiskefigur (poengteller i localStorage) |
| 155 | +- Responsivt, mobil-først design |
| 156 | + |
| 157 | +## Tester |
| 158 | + |
| 159 | +```bash |
| 160 | +npx vitest run |
| 161 | +``` |
| 162 | + |
| 163 | +Tester dekker: |
| 164 | +- Zod-validering (`validation.test.ts`) |
| 165 | +- Rate limiter (`rateLimit.test.ts`) |
| 166 | +- Påmeldingslogikk (`registration.test.ts`) |
| 167 | + |
| 168 | +## Backup |
| 169 | + |
| 170 | +Backup-containeren kjører automatisk som en del av Docker Compose. |
| 171 | + |
| 172 | +### Automatisk backup |
| 173 | + |
| 174 | +- Kjører `pg_dump | gzip` daglig kl. 03:00 |
| 175 | +- Lagres til Docker-volumet `backups` |
| 176 | +- Backups eldre enn 14 dager slettes automatisk (konfigurerbart via `BACKUP_KEEP_DAYS`) |
| 177 | + |
| 178 | +### Manuell backup |
| 179 | + |
| 180 | +```bash |
| 181 | +docker compose exec backup bash -c \ |
| 182 | + 'pg_dump | gzip > /backups/javabinkids-manual-$(date +%Y%m%d-%H%M%S).sql.gz' |
| 183 | +``` |
| 184 | + |
| 185 | +### Se eksisterende backups |
| 186 | + |
| 187 | +```bash |
| 188 | +docker compose exec backup ls -lh /backups/ |
| 189 | +``` |
| 190 | + |
| 191 | +### Restore |
| 192 | + |
| 193 | +```bash |
| 194 | +# Finn ønsket backup |
| 195 | +docker compose exec backup ls -lh /backups/ |
| 196 | + |
| 197 | +# Restore (erstatter all data i databasen) |
| 198 | +docker compose exec backup bash -c \ |
| 199 | + 'gunzip -c /backups/javabinkids-XXXXXXXX-XXXXXX.sql.gz | psql' |
| 200 | +``` |
| 201 | + |
| 202 | +### Se backup-logger |
| 203 | + |
| 204 | +```bash |
| 205 | +docker compose logs backup |
| 206 | +``` |
| 207 | + |
| 208 | +## Produksjon |
| 209 | + |
| 210 | +Prosjektet inkluderer en `Dockerfile` for produksjonsbygg med multi-stage build: |
| 211 | + |
| 212 | +```bash |
| 213 | +docker build -t javabinkids . |
| 214 | +docker run -p 3000:3000 \ |
| 215 | + -e DATABASE_URL=postgres://... \ |
| 216 | + -e RESEND_API_KEY=re_... \ |
| 217 | + -e BASE_URL=https://kids.javabin.no \ |
| 218 | + javabinkids |
| 219 | +``` |
| 220 | + |
| 221 | +## npm scripts |
| 222 | + |
| 223 | +| Script | Beskrivelse | |
| 224 | +|--------|-------------| |
| 225 | +| `npm run dev` | Start dev-server | |
| 226 | +| `npm run build` | Produksjonsbygg | |
| 227 | +| `npm run preview` | Forhåndsvis produksjonsbygg | |
| 228 | +| `npm run check` | TypeScript type-checking | |
| 229 | +| `npm run db:generate` | Generer Drizzle-migrering | |
| 230 | +| `npm run db:migrate` | Kjør migreringer | |
| 231 | +| `npm run db:seed` | Seed database | |
0 commit comments