Skip to content

Repository files navigation

javaBin Kids

Nettside for javaBin Kids — kode-arrangementer for barn i regi av javaBin, i forbindelse med JavaZone-konferansen.

Nettsiden presenterer arrangementer, håndterer påmelding med venteliste, og har et admin-panel for arrangørene.

Tech stack

LagTeknologi
FrontendSvelteKit 2 + Svelte 5 (runes)
BackendSvelteKit server routes (+page.server.ts, +server.ts)
DatabasePostgreSQL 17 via Drizzle ORM
ValideringZod
E-postSendGrid
Markdownmarked (kursbeskrivelser)
AuthCookie-basert sesjoner med bcrypt
InfraDocker Compose (dev + backup)

Kom i gang

Forutsetninger

  • Docker og Docker Compose
  • Node.js 22+ (for lokal utvikling uten Docker)

Med Docker (anbefalt)

# Start alt
docker compose up -d
# Kjør migrering og seed
docker compose exec app npx drizzle-kit migrate
docker compose exec app npx tsx seed.ts
# Appen kjører på http://localhost:5175

Uten Docker

# Forutsetter en kjørende PostgreSQL-instans
cp .env.example .env
# Rediger .env med din DATABASE_URL
npm install
npm run db:migrate
npm run db:seed
npm run dev

Miljøvariabler

Se .env.example:

VariabelBeskrivelse
DATABASE_URLPostgreSQL connection string
SENDGRID_API_KEYAPI-nøkkel fra sendgrid.com
ADMIN_USERNAMEBrukernavn for admin (brukes av seed)
ADMIN_PASSWORDPassord for admin (brukes av seed)
BASE_URLOffentlig URL for e-postlenker
EMAIL_SUBJECT_PREFIX(Valgfritt) Prefix som legges til alle e-post-emnelinjer. Nyttig for å skille test-e-poster fra produksjon, f.eks. [DEV] .

Prosjektstruktur

src/
├── lib/
│ ├── server/
│ │ ├── db/
│ │ │ ├── schema.ts # Drizzle-skjema (alle tabeller)
│ │ │ └── index.ts # DB-tilkobling
│ │ ├── ... # Komponenter og utils for server-side (validering, auth, e-post)
│ ├── components/
│ │ ├── ... # Frontend komponenter
│ └── toast.svelte.ts # Toast state management
├── routes/
│ ├── ... # Offentlige sider
│ ├── admin/ # Admin-panel (auth-beskyttet)
│ └── api/ # API-ruter levert av SvelteKit
└── app.css # Globale stiler (undervanns-tema)

Database

Tabeller

Drizzle-skjemaet ligger i src/lib/server/db/schema.ts. Alle timestamp-kolonner lagres uten tidssone, alle UUID-kolonner er gen_random_uuid()-defaulted der ikke annet er spesifisert.

events

Arrangementer — én rad per javaBin Kids-dag (f.eks. "javaBin Kids Vår 2026"). Kurs og forslag henger under et arrangement.

KolonneTypeBeskrivelse
arrangementIduuid, PKUnik ID
titletextVisningstittel
descriptiontextMarkdown-beskrivelse
datetimestampDato for arrangementet
locationtextSted/adresse
cancelledbooleanOm arrangementet er avlyst
registrationOpenstimestampStart for påmelding
registrationClosestimestampSlutt for påmelding
imageUrltext, nullableURL til forsidebilde (fra images-tabellen eller ekstern)
openForSubmissionsbooleanOm åpent for innsending av kursforslag
submissionDeadlinetimestamp, nullableFrist for innsending av forslag
createdAt, updatedAttimestampAuto

courses

Kurs som arrangeres innenfor et gitt arrangement. Hvert kurs har egen alders- og kapasitetsgrense.

KolonneTypeBeskrivelse
courseIduuid, PKUnik ID
arrangementIduuid, FK → eventsHvilket arrangement kurset tilhører (ON DELETE RESTRICT)
titletextKurstittel
introductiontextKort introduksjonstekst
descriptiontextFullstendig Markdown-beskrivelse
thumbnailUrltext, nullableURL til thumbnail
ageMin, ageMaxintegerAldersgruppe (inkluderende)
maxParticipantsintegerMaks antall påmeldte før venteliste
createdAt, updatedAttimestampAuto

submissions

Kursforslag sendt inn av eksterne foredragsholdere via forslagsskjemaet. Går gjennom admin-vurdering (submittedapproved/rejected).

KolonneTypeBeskrivelse
submissionIduuid, PKUnik ID
arrangementIduuid, FK → eventsHvilket arrangement forslaget er sendt til
statustextsubmitted, approved eller rejected
titletextKurstittel
descriptiontextMarkdown-beskrivelse
equipmentRequirementstext, nullableUtstyrsbehov
ageMin, ageMaxintegerForeslått aldersgruppe
maxParticipantsintegerForeslått maks antall
speakerNametextForedragsholders navn
speakerEmailtextE-post (brukes til mottatt/godkjent/avvist-mail)
speakerBiotextKort bio
editTokenuuidToken i redigeringslenke, lar foredragsholder endre forslaget uten innlogging
createdAt, updatedAttimestampAuto

registrations

Påmeldinger fra foreldre til et spesifikt kurs. Unik på (courseId, parentEmail, childName) for å hindre duplikater.

KolonneTypeBeskrivelse
registrationIduuid, PKUnik ID
courseIduuid, FK → coursesKurset barnet meldes på
parentNametextForelders navn
parentEmailtextE-post (brukes til bekreftelse/påminnelse)
parentPhonetextTelefonnummer
childNametextBarnets navn
childAgeintegerBarnets alder
statustextconfirmed, waitlisted eller cancelled
waitlistPositioninteger, nullablePosisjon på venteliste (1 = først i køen); null for confirmed/cancelled
consentGivenbooleanOm samtykke til databehandling er gitt
cancellationTokenuuidToken i bekreftelse/avmeldings-lenke — gjør at forelder kan avmelde uten innlogging
createdAt, updatedAttimestampAuto

adminUsers

Admin-brukere som kan logge inn i admin-panelet. Opprettes via npm run db:seed.

KolonneTypeBeskrivelse
adminUserIduuid, PKUnik ID
usernametext, uniqueBrukernavn
passwordHashtextbcrypt-hash av passord
createdAttimestampAuto

sessions

Aktive admin-sesjoner. Sesjons-ID-en lagres som HTTP-cookie og valideres på hver admin-request.

KolonneTypeBeskrivelse
sessionIduuid, PKID som lagres i cookien
adminUserIduuid, FK → adminUsersHvilken bruker sesjonen tilhører
expiresAttimestampUtløp (typisk 24 timer etter innlogging)
createdAttimestampAuto

siteContent

Nøkkel/verdi-tekst som admin kan redigere fra /admin/innhold. Brukes for forside-hero, om-siden osv.

KolonneTypeBeskrivelse
keytext, PKIdentifikator, f.eks. hero_title, om_content
contenttextFritekst/Markdown
updatedAttimestampAuto

images

Binær-lagring av opplastede bilder (thumbnails, forsidebilder). Serveres via /api/images/{imageId} som bruker mimeType som Content-Type.

KolonneTypeBeskrivelse
imageIduuid, PKUnik ID (del av serve-URL)
filenametextOpprinnelig filnavn
mimeTypetextimage/jpeg, image/png, osv.
databyteaRå bilde-bytes
createdAttimestampAuto

emailTemplates

Redigerbare e-postmaler for de åtte transaksjonelle e-postene (bekreftelse, venteliste, forslag-godkjent, osv.). Hvis en rad mangler eller et felt er tomt, faller email.ts tilbake på hardkodet default. Tekstfelter kan inneholde {{variabel}}-plassholdere og **fet tekst**.

KolonneTypeBeskrivelse
templateKeytext, PKÉn av confirmation, waitlist, promotion, cancellation, reminder, submissionReceived, submissionApproved, submissionRejected
subjecttextEmnelinje
headingtextOverskrift i e-post-kroppen
introTexttextParagrafer før info-bokser (tom linje = ny paragraf)
outroTexttextParagrafer etter knapp/info-bokser
buttonTexttext, nullableCTA-knappens tekst (kun for maler med knapp)
updatedAttimestampAuto

contactCards

Kontaktkort som vises på kontaktsiden, administreres fra /admin/innhold.

KolonneTypeBeskrivelse
contactCardIduuid, PKUnik ID
titletextKort-tittel
actionTypetextemail, link eller phone
actionValuetextE-post, URL eller telefonnummer
sortOrderintegerVisningsrekkefølge (stigende)
createdAt, updatedAttimestampAuto

Migreringer

Drizzle Kit håndterer migreringer. SQL-filer ligger i drizzle/migrations/.

# Generer ny migrering etter skjemaendring
npm run db:generate
# Kjør migreringer
npm run db:migrate

Seed

seed.ts oppretter en admin-bruker og et eksempel-arrangement med kurs:

npm run db:seed

Viktige konsepter

Påmeldingsflyt

  1. Bruker velger kurs og fyller ut skjema
  2. Server validerer (Zod), sjekker duplikat, sjekker kapasitet
  3. Plassering skjer i en DB-transaksjon med SELECT ... FOR UPDATE (row-level locking)
  4. Hvis plass: confirmed, ellers: waitlisted med posisjon
  5. E-postbekreftelse sendes (med kanselleringslenke)

Venteliste

  • Ved kansellering rykker neste person på ventelisten opp automatisk
  • Alle gjenværende ventelisteposisjoner dekrementeres
  • Den opprykkte personen får e-post

Admin-autentisering

  • Cookie-basert sesjoner lagret i sessions-tabellen
  • Sesjoner utløper etter 24 timer
  • +layout.server.ts under /admin sjekker sesjon og redirecter til login
  • Alle admin API-ruter validerer sesjon

Typesikkerhet

  • Drizzle ORM gir type-safe database-tilgang
  • SvelteKit genererer PageData-typer fra +page.server.ts load-funksjoner
  • Alle sider importerer PageData fra ./$types
  • Zod validerer all bruker-input på serveren

Visuelt design

Inspirert av JavaZone 2026 sitt undervanns-tema:

  • Mørk blågrønn gradient bakgrunn
  • Turkise overskrifter, gylne aksenter
  • Animerte bobler med interaktiv fiskefigur (poengteller i localStorage)
  • Responsivt, mobil-først design

Tester

npx vitest run

Tester dekker:

  • Zod-validering (validation.test.ts)
  • Rate limiter (rateLimit.test.ts)
  • Påmeldingslogikk (registration.test.ts)

Backup

Backup-containeren kjører automatisk som en del av Docker Compose.

Automatisk backup

  • Kjører pg_dump | gzip daglig kl. 03:00
  • Lagres til Docker-volumet backups
  • Backups eldre enn 14 dager slettes automatisk (konfigurerbart via BACKUP_KEEP_DAYS)

Manuell backup

docker compose exec backup bash -c \
'pg_dump | gzip > /backups/javabinkids-manual-$(date +%Y%m%d-%H%M%S).sql.gz'

Se eksisterende backups

docker compose exec backup ls -lh /backups/

Restore

# Finn ønsket backup
docker compose exec backup ls -lh /backups/
# Restore (erstatter all data i databasen)
docker compose exec backup bash -c \
'gunzip -c /backups/javabinkids-XXXXXXXX-XXXXXX.sql.gz | psql'

Se backup-logger

docker compose logs backup

Produksjon

Prosjektet inkluderer en Dockerfile for produksjonsbygg med multi-stage build:

docker build -t javabinkids .
docker run -p 3000:3000 \
-e DATABASE_URL=postgres://... \
-e SENDGRID_API_KEY=SG.... \
-e BASE_URL=https://kids.javabin.no \
javabinkids

npm scripts

ScriptBeskrivelse
npm run devStart dev-server
npm run buildProduksjonsbygg
npm run previewForhåndsvis produksjonsbygg
npm run checkTypeScript type-checking
npm run db:generateGenerer Drizzle-migrering
npm run db:migrateKjør migreringer
npm run db:seedSeed database

About

Nettside for javaBin Kids

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages