From 66650d63acb2cfb9b97a1bb36372a80e58d03473 Mon Sep 17 00:00:00 2001 From: Tero Halla-aho Date: Mon, 24 Nov 2025 17:31:21 +0200 Subject: [PATCH] docs: move docs directory and render plantuml svgs --- .gitignore | 4 +- PROGRESS.md | 5 +- docs/architecture.html | 80 ++++++++++++++++++++++ docs/build.html | 76 +++++++++++++++++++++ docs/drawio/architecture.drawio | 52 ++++++++++++++ docs/drawio/infra.drawio | 64 ++++++++++++++++++ docs/index.html | 34 ++++++++++ docs/infra.html | 74 ++++++++++++++++++++ docs/plantuml/auth-register-login.puml | 19 ++++++ docs/plantuml/auth-register-login.svg | 1 + docs/plantuml/listing-create-approve.puml | 18 +++++ docs/plantuml/listing-create-approve.svg | 1 + docs/plantuml/listing-removal.puml | 18 +++++ docs/plantuml/listing-removal.svg | 1 + docs/plantuml/profile-update.puml | 10 +++ docs/plantuml/profile-update.svg | 1 + docs/sequences.html | 47 +++++++++++++ docs/style.css | 82 +++++++++++++++++++++++ 18 files changed, 583 insertions(+), 4 deletions(-) create mode 100644 docs/architecture.html create mode 100644 docs/build.html create mode 100644 docs/drawio/architecture.drawio create mode 100644 docs/drawio/infra.drawio create mode 100644 docs/index.html create mode 100644 docs/infra.html create mode 100644 docs/plantuml/auth-register-login.puml create mode 100644 docs/plantuml/auth-register-login.svg create mode 100644 docs/plantuml/listing-create-approve.puml create mode 100644 docs/plantuml/listing-create-approve.svg create mode 100644 docs/plantuml/listing-removal.puml create mode 100644 docs/plantuml/listing-removal.svg create mode 100644 docs/plantuml/profile-update.puml create mode 100644 docs/plantuml/profile-update.svg create mode 100644 docs/sequences.html create mode 100644 docs/style.css diff --git a/.gitignore b/.gitignore index 8af99a7..457ba4c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,7 @@ deploy/.last-image creds/ k3s.yaml -# Local-only documentation -docs-local/ +# Local-only documentation (now tracked in docs/) /lib/generated/prisma @@ -29,6 +28,7 @@ docs-local/ bin/ lib/python*/ lib64/ +lib64 pyvenv.cfg tsconfig.tsbuildinfo .lock diff --git a/PROGRESS.md b/PROGRESS.md index 1180f89..fc7206d 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -39,13 +39,14 @@ - Amenities expanded: electric vehicle charging (free/paid) and air conditioning; cover image selectable per listing and used in cards. - Home page shows a rolling feed of latest listings; navbar + CTA link to browse. - Listing creation form captures address details, coordinates, amenities (incl. EV/AC), and cover image choice. +- Documentation moved to `docs/`; PlantUML diagrams rendered to SVG and embedded in docs pages (draw.io sources kept for architecture/infra). - HTTPS redirect middleware applied to staging/prod ingress. - FI/EN localization with navbar language toggle; UI strings translated; Approvals link shows pending count badge. - Soft rejection/removal states for users/listings with timestamps; owner listing removal; login redirects home; listing visibility hides removed/not-published. - Profile page now allows editing name and password (email immutable). -- Docs: Added local docs in `docs-local/` (tracked, not shipped) with HTML + PlantUML sequences + draw.io diagrams. Ignored from deploy via runtime paths; kept in git. +- Docs: Added docs in `docs/` (tracked, not shipped) with HTML + PlantUML sequences + draw.io diagrams. Ignored from deploy via runtime paths; kept in git. To resume: -1) If desired, render diagrams locally: PlantUML in `docs-local/plantuml`, draw.io in `docs-local/drawio`. +1) If desired, render diagrams locally: PlantUML in `docs/plantuml`, draw.io in `docs/drawio`. 2) Keep registry health in mind; current pushes work (`1763994382` deployed). 3) Future app work: translations polish, more listing fields, admin tooling, or registry hardening. diff --git a/docs/architecture.html b/docs/architecture.html new file mode 100644 index 0000000..3c7da6c --- /dev/null +++ b/docs/architecture.html @@ -0,0 +1,80 @@ + + + + + Logical Architecture + + + +
+

Logical Architecture

+
Next.js App Router, Prisma/Postgres, role-based auth, email verification, approvals.
+
+
+
+

Components

+
    +
  • Web: Next.js app (App Router), server-rendered pages, client hooks for auth state.
  • +
  • API routes: Authentication, admin approvals, listings CRUD (soft-delete), profile update.
  • +
  • Data: Postgres via Prisma (models: User, Listing, ListingTranslation, ListingImage, VerificationToken).
  • +
  • Mail: SMTP (smtp.sohva.org) + DKIM signing for verification emails.
  • +
  • Auth: Email/password, verified+approved requirement, JWT session cookie (session_token), roles.
  • +
+
+ +
+

Layers Diagram

+

Source: docs/drawio/architecture.drawio. Edit with draw.io and export locally.

+
+ +
+

Domain Model Snapshot

+
+
erDiagram
+  USER ||--o{ LISTING : owns
+  USER ||--o{ LISTING : approves
+  LISTING ||--|{ LISTINGTRANSLATION : has
+  LISTING ||--o{ LISTINGIMAGE : has
+
+  USER {
+    string id
+    string email
+    string passwordHash
+    Role role
+    UserStatus status
+    datetime emailVerifiedAt
+    datetime approvedAt
+    datetime rejectedAt
+    datetime removedAt
+  }
+  LISTING {
+    string id
+    ListingStatus status
+    datetime approvedAt
+    datetime rejectedAt
+    datetime removedAt
+    string country
+    string region
+    string city
+  }
+  LISTINGTRANSLATION {
+    string id
+    string slug
+    string title
+    string locale
+  }
+  LISTINGIMAGE {
+    string id
+    string url
+  }
+
+
+
+ +
+

Auth Flow (High-Level)

+

See PlantUML source: docs/plantuml/auth-register-login.puml. Render locally with PlantUML.

+
+
+ + diff --git a/docs/build.html b/docs/build.html new file mode 100644 index 0000000..da92956 --- /dev/null +++ b/docs/build.html @@ -0,0 +1,76 @@ + + + + + Build & Deploy + + + +
+

Build & Deploy Pipeline

+
Node/Next build, Docker multi-stage, registry push, kubectl rollout.
+
+
+
+

Build Inputs

+
    +
  • Source: Next.js app with TypeScript and Prisma.
  • +
  • Env: .env (local), K8s Secret lomavuokraus-web-secrets in cluster.
  • +
  • Prisma schema: prisma/schema.prisma, migrations in prisma/migrations/.
  • +
+
+ +
+

NPM Scripts

+
    +
  • npm run lintnext lint
  • +
  • npm run buildnext build (used inside Docker and locally)
  • +
+
+ +
+

Docker Image

+
    +
  • Multi-stage Dockerfile: +
      +
    • deps: npm ci
    • +
    • builder: copy source, npx prisma generate, npm run build
    • +
    • runner: Node 20 bookworm-slim, copy standalone + static
    • +
    +
  • +
  • Tags: numeric (git SHA-derived) + :latest.
  • +
  • Scan: Trivy runs post-build if available.
  • +
+
+ +
+

Deploy Scripts

+
    +
  • deploy/build.sh → build image, write deploy/.last-image.
  • +
  • deploy/push.sh → push image.
  • +
  • deploy/deploy.sh → envsubst k8s/app.yaml, kubectl apply, rollout.
  • +
  • Environment wrappers: +
      +
    • deploy/deploy-staging.sh
    • +
    • deploy/deploy-prod.sh
    • +
    +
  • +
+
+ +
+

Config & Env Vars

+
    +
  • From ConfigMap (public): NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_API_BASE, APP_ENV.
  • +
  • From Secret: DB URL, AUTH_SECRET, SMTP, DKIM, etc.
  • +
  • App env resolution: process.env.* in Next server code.
  • +
+
+ +
+

Pipeline Diagram

+

For visuals, edit/export docs/drawio/architecture.drawio or create a dedicated pipeline page in draw.io.

+
+
+ + diff --git a/docs/drawio/architecture.drawio b/docs/drawio/architecture.drawio new file mode 100644 index 0000000..6619d7a --- /dev/null +++ b/docs/drawio/architecture.drawio @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/drawio/infra.drawio b/docs/drawio/infra.drawio new file mode 100644 index 0000000..c127fc2 --- /dev/null +++ b/docs/drawio/infra.drawio @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..7dd8381 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,34 @@ + + + + + Lomavuokraus Docs + + + +
+

Lomavuokraus Documentation

+
Docs tracked in git, not deployed with the app.
+
+
+ +
+

Notes

+
    +
  • Docs live in docs/ (tracked, not shipped).
  • +
  • Sequence diagrams: PlantUML sources in docs/plantuml.
  • +
  • Architecture/infra diagrams: draw.io sources in docs/drawio.
  • +
  • Generate locally: PlantUML CLI/JAR, draw.io desktop/CLI exports.
  • +
+
+
+ + diff --git a/docs/infra.html b/docs/infra.html new file mode 100644 index 0000000..03958f7 --- /dev/null +++ b/docs/infra.html @@ -0,0 +1,74 @@ + + + + + Infrastructure + + + +
+

Infrastructure Overview

+
+ Hetzner k3s cluster, Traefik ingress, cert-manager TLS, private registry, staging/prod namespaces. +
+
+
+
+

Cluster & Namespaces

+
    +
  • Single-node k3s (Hetzner hel1 cx22) at 157.180.66.64.
  • +
  • Namespaces: lomavuokraus-prod, lomavuokraus-staging.
  • +
  • Ingress controller: Traefik (k3s default).
  • +
  • cert-manager v1.15.3 with ClusterIssuers: +
      +
    • letsencrypt-prod (ACME prod)
    • +
    • letsencrypt-staging (ACME staging for test certs)
    • +
    +
  • +
  • DNS: lomavuokraus.fi, staging.lomavuokraus.fi, api.lomavuokraus.fi -> cluster IP.
  • +
+
+ +
+

Registry

+
    +
  • Private registry: registry.halla-aho.net/thalla/lomavuokraus-web.
  • +
  • Credentials stored outside repo (creds/), image pull secret registry-halla in staging/prod namespaces.
  • +
  • Images tagged with git SHA-derived numeric tag and :latest.
  • +
+
+ +
+

App Manifests

+
    +
  • k8s/app.yaml templated via envsubst in deploy scripts.
  • +
  • Objects: +
      +
    • ConfigMap: lomavuokraus-web-config (public env).
    • +
    • Deployment: 2 replicas, container port 3000, liveness/readiness on /api/health.
    • +
    • Service: ClusterIP on port 80.
    • +
    • Ingress: Traefik class, TLS via cert-manager, HTTPS redirect middleware.
    • +
    • Traefik Middleware: https-redirect to force HTTPS.
    • +
    +
  • +
  • Secrets: lomavuokraus-web-secrets in cluster (not in repo).
  • +
+
+ +
+

Runtime Environment

+
    +
  • Next.js 14.2.33 (App Router) running via Node.js 20 in Docker.
  • +
  • PostgreSQL DB at 46.62.203.202 (DATABASE_URL in .env, not committed).
  • +
  • SMTP: smtp.sohva.org, DKIM key under creds/dkim/....
  • +
  • Session auth: signed JWT cookie session_token; roles: USER, ADMIN, USER_MODERATOR, LISTING_MODERATOR.
  • +
+
+ +
+

Traffic Flow Diagram

+

Source: docs/drawio/infra.drawio (edit with draw.io, export PNG locally).

+
+
+ + diff --git a/docs/plantuml/auth-register-login.puml b/docs/plantuml/auth-register-login.puml new file mode 100644 index 0000000..8e1b32b --- /dev/null +++ b/docs/plantuml/auth-register-login.puml @@ -0,0 +1,19 @@ +@startuml +title User registration, verification, login, approval +actor User +participant "Next API" as API +database Postgres as DB +participant SMTP as Mail +actor Admin + +User -> API: POST /api/auth/register\n(email, password, name) +API -> DB: create User (status=PENDING)\ncreate VerificationToken +API -> Mail: send verification email +User -> API: POST /api/auth/verify (token) +API -> DB: set emailVerifiedAt +Admin -> API: POST /api/admin/users/approve +API -> DB: set status=ACTIVE, approvedAt +User -> API: POST /api/auth/login +API -> DB: validate password + status +API --> User: session_token cookie (JWT) +@enduml diff --git a/docs/plantuml/auth-register-login.svg b/docs/plantuml/auth-register-login.svg new file mode 100644 index 0000000..8b4e09b --- /dev/null +++ b/docs/plantuml/auth-register-login.svg @@ -0,0 +1 @@ +User registration, verification, login, approvalUser registration, verification, login, approvalUserNext APIPostgresSMTPAdminUserUserNext APINext APIPostgresPostgresSMTPSMTPAdminAdminPOST /api/auth/register(email, password, name)create User (status=PENDING)create VerificationTokensend verification emailPOST /api/auth/verify (token)set emailVerifiedAtPOST /api/admin/users/approveset status=ACTIVE, approvedAtPOST /api/auth/loginvalidate password + statussession_token cookie (JWT) \ No newline at end of file diff --git a/docs/plantuml/listing-create-approve.puml b/docs/plantuml/listing-create-approve.puml new file mode 100644 index 0000000..5396aa8 --- /dev/null +++ b/docs/plantuml/listing-create-approve.puml @@ -0,0 +1,18 @@ +@startuml +title Listing creation and admin approval +actor Owner +participant "Next API" as API +database Postgres as DB +actor Admin +actor User + +Owner -> API: POST /api/listings\n(slug, details, images) +API -> DB: create Listing\nstatus=PENDING (or PUBLISHED if auto) +Admin -> API: GET /api/admin/pending +API -> DB: fetch pending listings +Admin -> API: POST /api/admin/listings/approve\n(action=approve|reject|remove) +API -> DB: update status, timestamps +User -> API: GET /listings/[slug] +API -> DB: fetch translation\nstatus=PUBLISHED and not removed +API --> User: render listing +@enduml diff --git a/docs/plantuml/listing-create-approve.svg b/docs/plantuml/listing-create-approve.svg new file mode 100644 index 0000000..e9d3880 --- /dev/null +++ b/docs/plantuml/listing-create-approve.svg @@ -0,0 +1 @@ +Listing creation and admin approvalListing creation and admin approvalOwnerNext APIPostgresAdminUserOwnerOwnerNext APINext APIPostgresPostgresAdminAdminUserUserPOST /api/listings(slug, details, images)create Listingstatus=PENDING (or PUBLISHED if auto)GET /api/admin/pendingfetch pending listingsPOST /api/admin/listings/approve(action=approve|reject|remove)update status, timestampsGET /listings/[slug]fetch translationstatus=PUBLISHED and not removedrender listing \ No newline at end of file diff --git a/docs/plantuml/listing-removal.puml b/docs/plantuml/listing-removal.puml new file mode 100644 index 0000000..14dddc6 --- /dev/null +++ b/docs/plantuml/listing-removal.puml @@ -0,0 +1,18 @@ +@startuml +title Listing removal by owner or moderator +actor Owner +participant "Next API" as API +database Postgres as DB +actor Moderator + +Owner -> API: POST /api/listings/remove (listingId) +API -> DB: verify owner or moderator\nfetch listing +API -> DB: set status=REMOVED\npublished=false\nremovedAt/by +Moderator -> API: POST /api/admin/listings/approve\n(action=remove) +API -> DB: same status change if via admin path +Owner -> API: GET /api/listings/mine +API -> DB: fetch listings for owner (includes REMOVED) +Public -> API: GET /listings/[slug] +API -> DB: filter out removed listings +API --> Public: not found if removed +@enduml diff --git a/docs/plantuml/listing-removal.svg b/docs/plantuml/listing-removal.svg new file mode 100644 index 0000000..e3a0259 --- /dev/null +++ b/docs/plantuml/listing-removal.svg @@ -0,0 +1 @@ +Listing removal by owner or moderatorListing removal by owner or moderatorOwnerNext APIPostgresModeratorPublicOwnerOwnerNext APINext APIPostgresPostgresModeratorModeratorPublicPublicPOST /api/listings/remove (listingId)verify owner or moderatorfetch listingset status=REMOVEDpublished=falseremovedAt/byPOST /api/admin/listings/approve(action=remove)same status change if via admin pathGET /api/listings/minefetch listings for owner (includes REMOVED)GET /listings/[slug]filter out removed listingsnot found if removed \ No newline at end of file diff --git a/docs/plantuml/profile-update.puml b/docs/plantuml/profile-update.puml new file mode 100644 index 0000000..5422bc4 --- /dev/null +++ b/docs/plantuml/profile-update.puml @@ -0,0 +1,10 @@ +@startuml +title Profile update (name/password) +actor User +participant "Next API" as API +database Postgres as DB + +User -> API: PATCH /api/me\n(name?, password?) +API -> DB: update name/passwordHash\n(email immutable) +API --> User: updated profile payload +@enduml diff --git a/docs/plantuml/profile-update.svg b/docs/plantuml/profile-update.svg new file mode 100644 index 0000000..7bb6c2d --- /dev/null +++ b/docs/plantuml/profile-update.svg @@ -0,0 +1 @@ +Profile update (name/password)Profile update (name/password)UserNext APIPostgresUserUserNext APINext APIPostgresPostgresPATCH /api/me(name?, password?)update name/passwordHash(email immutable)updated profile payload \ No newline at end of file diff --git a/docs/sequences.html b/docs/sequences.html new file mode 100644 index 0000000..fb51e09 --- /dev/null +++ b/docs/sequences.html @@ -0,0 +1,47 @@ + + + + + Feature Sequences + + + +
+

Sequence Diagrams

+
PlantUML sources for key flows; render locally.
+
+
+
+

User Registration & Verification

+

PlantUML: docs/plantuml/auth-register-login.puml

+ User registration and verification sequence +
+ +
+

Listing Creation & Approval

+

PlantUML: docs/plantuml/listing-create-approve.puml

+ Listing creation and approval sequence +
+ +
+

Listing Removal by Owner/Moderator

+

PlantUML: docs/plantuml/listing-removal.puml

+ Listing removal sequence +
+ +
+

Profile Update (Name/Password)

+

PlantUML: docs/plantuml/profile-update.puml

+ Profile update sequence +
+ +
+

Rendering instructions

+
    +
  • PlantUML: plantuml docs/plantuml/*.puml (local PlantUML/Java or Docker).
  • +
  • Draw.io: open docs/drawio/*.drawio in the desktop app to edit/export PNG/SVG locally.
  • +
+
+
+ + diff --git a/docs/style.css b/docs/style.css new file mode 100644 index 0000000..af53da2 --- /dev/null +++ b/docs/style.css @@ -0,0 +1,82 @@ +body { + font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + margin: 0; + padding: 0; + background: #0f172a; + color: #e2e8f0; +} +a { + color: #38bdf8; +} +header { + padding: 24px 32px; + background: linear-gradient(135deg, #1e293b, #0f172a 60%); + border-bottom: 1px solid #1f2937; +} +h1, +h2, +h3 { + color: #f8fafc; +} +main { + padding: 24px 32px 48px; + display: grid; + gap: 16px; + max-width: 1200px; +} +.card { + background: #111827; + border: 1px solid #1f2937; + border-radius: 14px; + padding: 20px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25); +} +.meta { + color: #cbd5e1; + font-size: 14px; +} +.grid { + display: grid; + gap: 12px; +} +.two-col { + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} +pre { + background: #0b1220; + padding: 12px; + border-radius: 10px; + overflow-x: auto; + border: 1px solid #1f2937; +} +code { + font-family: "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + color: #cbd5e1; +} +.diagram { + background: #0b1220; + border: 1px solid #1f2937; + border-radius: 12px; + padding: 12px; +} +.badge { + display: inline-block; + background: #38bdf8; + color: #0f172a; + padding: 4px 8px; + border-radius: 999px; + font-weight: 600; + font-size: 12px; +} +ul { + padding-left: 18px; +} + +.diagram-img { + width: 100%; + max-width: 960px; + margin-top: 10px; + border: 1px solid #1f2937; + border-radius: 10px; + background: #0b1220; +}