Files
x3 a407a1381a
ci / backend (push) Canceled after 0s
ci / frontend (push) Canceled after 0s
docs: setup, hosting, API, architecture and roadmap
- SETUP: local dev guide (env vars, DB migrate, backend/frontend, troubleshooting)
- HOSTING: bare-metal guide (systemd + Nginx + TLS + backups) and Docker Compose
  guide (deploy, reverse proxy, updates, backup/restore)
- API: REST contract with curl examples and Socket.IO event protocol
- ARCHITECTURE: stack, data model, auth model, realtime design, theming
- ROADMAP: webapp MVP/next steps plus Tauri desktop clients for Windows, macOS
  and Linux with release-binary plan
2026-09-22 13:33:22 +02:00

2.1 KiB

Architecture

Stack

  • Frontend: Vue 3 (Composition API + <script setup>), Vite, Vue Router, Pinia, Axios, Socket.IO client, TipTap editor. Theme via CSS variables (data-theme).
  • Backend: Node.js 20, Express 4, Socket.IO 4, pg (node-postgres), JWT, bcryptjs.
  • DB: PostgreSQL 14+. Single schema file (backend/src/db/schema.sql), applied via npm run migrate or psql.

Data model

users (id, email unique, name, password_hash)
  └─< workspace_members (workspace_id, user_id, role) >─┐
workspaces (id, name, owner_id -> users) <───────────────┘
  └─< pages (id, workspace_id, parent_id self-FK, title, icon, cover,
             content_json JSONB, content_html TEXT, position, is_archived)
        └─< comments (id, page_id, author_id, body)
  • Nesting via parent_id self-reference (ON DELETE CASCADE removes subtrees).
  • Owner auto-added as workspace_members(role=owner) via trigger.
  • updated_at maintained by trigger.

Auth & authorization

  • Register/login return a signed JWT (sub=user.id). Client stores it in localStorage and sends Authorization: Bearer.
  • Every workspace/page route resolves the caller's membership role server-side; viewers are blocked from mutations (403).
  • Socket.IO handshake verifies JWT; page:join and page:edit re-check membership and role against the DB (never trust client role).

Realtime design (MVP)

  • One Socket.IO room per page (page:<id>). Edits are debounced client-side (~800ms), persisted via REST PATCH /pages/:id, then broadcast page:updated.
  • page:edit socket events also persist server-side for clients that only use sockets.
  • Presence: server keeps room -> socketId -> user map, emits presence:update.
  • No CRDT/OT in MVP — last-write-wins. Concurrent edits converge on the last saved version; remote updates are ignored while the local user is the sender (from). See ROADMAP for planned Yjs-based conflict-free editing.

Theming

  • useThemeStore (light/dark/system), persisted in localStorage, applied as document.documentElement.dataset.theme. All colors are CSS variables.