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

+ +
+ +
+

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

+ +
+ +
+

NPM Scripts

+ +
+ +
+

Docker Image

+ +
+ +
+

Deploy Scripts

+ +
+ +
+

Config & Env Vars

+ +
+ +
+

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.
+
+
+
+

Contents

+ +
+
+

Notes

+ +
+
+ + 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

+ +
+ +
+

Registry

+ +
+ +
+

App Manifests

+ +
+ +
+

Runtime Environment

+ +
+ +
+

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

+ +
+
+ + 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; +}