Fjern SpacetimeDB komplett (oppgave 22.4)
SpacetimeDB er nå helt fjernet fra Synops. Sanntid håndteres av PG LISTEN/NOTIFY + WebSocket i portvokteren (maskinrommet). Kode fjernet: - spacetimedb/ Rust-modul og spacetime.json - maskinrommet/src/stdb.rs (HTTP-klient for STDB-reducers) - frontend module_bindings/ (23 auto-genererte filer) - spacetimedb npm-avhengighet fra package.json - scripts/test-sanntid.sh (testet STDB-flyt) Infrastruktur: - Docker-container stoppet og fjernet fra docker-compose.yml - Caddy: fjernet /spacetime/* reverse proxy - maskinrommet-env.sh: fjernet STDB_IP og SPACETIMEDB_*-variabler - .env.example: fjernet SpacetimeDB-seksjoner Dokumentasjon oppdatert: - CLAUDE.md: stack, lagmodell, kjerneprinsipper, driftsmodell - docs/arkitektur.md: skrivestien, lesestien, datalag, teknologivalg - docs/retninger/datalaget.md: migrasjonshistorikk, status "fjernet" - 37 andre docs oppdatert (features, concepts, infra, ops, retninger) - Alle kode-kommentarer med STDB-referanser oppdatert Verifisert: maskinrommet bygger og starter OK, frontend bygger OK, helsesjekk returnerer 200. Caddy reloadet.
This commit is contained in:
parent
f418aec33b
commit
b5aa5bb243
106 changed files with 2930 additions and 6346 deletions
|
|
@ -21,10 +21,6 @@ AUTHENTIK_CLIENT_SECRET=
|
|||
# === SvelteKit ===
|
||||
AUTH_SECRET= # openssl rand -base64 33
|
||||
|
||||
# === SpacetimeDB ===
|
||||
# URL til SpacetimeDB-instansen (server eller lokal)
|
||||
VITE_SPACETIMEDB_URL=ws://sidelinja.org/spacetime
|
||||
|
||||
# === Whisper (STT) ===
|
||||
# URL til faster-whisper-server (Docker-internt)
|
||||
# WHISPER_URL=http://faster-whisper:8000
|
||||
|
|
|
|||
23
CLAUDE.md
23
CLAUDE.md
|
|
@ -33,7 +33,7 @@ CLAUDE.md er eneste startdokument. Alt annet ligger under `docs/`:
|
|||
- `universell_input.md` — Tre primitiver (input, mottak, kommunikasjon), noder+edges
|
||||
- `maskinrommet.md` — Rust-orkestrator: fang, prosesser, lever
|
||||
- `bruker_ikke_workspace.md` — Noder er sentrum: brukere, team, innhold er noder. Tilgangsmatrise fra edges.
|
||||
- `datalaget.md` — PG(+AGE) som graf og arkiv, SpacetimeDB som sanntidslag
|
||||
- `datalaget.md` — PG(+AGE) som graf og arkiv, PG LISTEN/NOTIFY + WebSocket som sanntidslag
|
||||
- `arbeidsflaten.md` — Spatial canvas med verktøy-paneler, drag-and-drop, kompatibilitetsmatrise
|
||||
- `unix_filosofi.md` — Maskinrommet som orkestrator, arbeid i CLI-verktøy, Claude og maskinrommet deler verktøykasse
|
||||
- `docs/primitiver/` — Spesifikasjoner for kjerneprimitivene:
|
||||
|
|
@ -61,10 +61,10 @@ CLAUDE.md er eneste startdokument. Alt annet ligger under `docs/`:
|
|||
- `ai_gateway.md` — LiteLLM som sentralisert AI-ruter (BYOK + fallback)
|
||||
- `api_grensesnitt.md` — Kommunikasjonskart: SvelteKit er web-API, Rust er worker
|
||||
- `jobbkø.md` — PostgreSQL-basert køsystem for bakgrunnsjobber
|
||||
- `synkronisering.md` — PostgreSQL ↔ SpacetimeDB dataflyt og eierskapsmodell
|
||||
- `synkronisering.md` — Historisk: PG ↔ SpacetimeDB dataflyt (utdatert, SpacetimeDB fjernet mars 2026)
|
||||
- `claude_agent.md` — Claude som chat-deltaker: arkitektur, triggere, sikkerhet
|
||||
- `observerbarhet.md` — Strukturert logging, metrikk-endepunkt (/metrics), AI-kostnad
|
||||
- `docs/erfaringer/` — Lærdommer fra v1 (adapter-mønster, Svelte 5, SpacetimeDB, Authentik)
|
||||
- `docs/erfaringer/` — Lærdommer fra v1 (adapter-mønster, Svelte 5, Authentik)
|
||||
- `reference/` — Kode fra v1 med gjenbruksverdi (Editor.svelte)
|
||||
- `ops/` — Repeterbare vedlikeholdsjobber (ryddejobb, doc-audit, drift-sjekk)
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ CLAUDE.md er eneste startdokument. Alt annet ligger under `docs/`:
|
|||
## Stack
|
||||
- **Orkestrator/Backend:** Rust (maskinrommet) — kjører native på hosten
|
||||
- **Frontend:** SvelteKit (TypeScript, PWA)
|
||||
- **Sanntid:** SpacetimeDB
|
||||
- **Sanntid:** PG LISTEN/NOTIFY + WebSocket (portvokteren)
|
||||
- **Database/Graf:** PostgreSQL (+Apache AGE ved behov)
|
||||
- **Binærlagring:** CAS (content-addressable store)
|
||||
- **AI:** Claude Code (chat-agent), LiteLLM (AI Gateway), faster-whisper (STT)
|
||||
|
|
@ -102,14 +102,13 @@ med Docker container-IPs.
|
|||
| Tjeneste | Begrunnelse |
|
||||
|----------|-------------|
|
||||
| **PostgreSQL** | Versjonsstyring, enkel oppgradering |
|
||||
| **SpacetimeDB** | Eksperimentelt, offisielt image |
|
||||
| **Authentik** | Kompleks stack (server + worker + Redis) |
|
||||
| **LiteLLM** | Ferdig image, sjelden oppdatering |
|
||||
| **faster-whisper** | Modellhåndtering, ferdig image |
|
||||
|
||||
### Kommunikasjon mellom lagene
|
||||
- Caddy (native) → Docker-tjenester: via localhost-porter
|
||||
(Authentik:9000, Forgejo:3000, SpacetimeDB:9080)
|
||||
(Authentik:9000, Forgejo:3000)
|
||||
- Caddy (native) → native tjenester: direkte localhost
|
||||
(maskinrommet:3100, SvelteKit:3200)
|
||||
- Maskinrommet (host) → Docker-tjenester: via container-IP
|
||||
|
|
@ -163,15 +162,15 @@ maskinrommet deler verktøykasse.
|
|||
## Lagmodell
|
||||
```
|
||||
GUI (SvelteKit) — spatial canvas med verktøy-paneler
|
||||
│ skriv │ les (sanntid, direkte WebSocket)
|
||||
▼ ▼
|
||||
Maskinrommet (Rust) SpacetimeDB ──→ GUI
|
||||
│ skriv │ les (sanntid via WebSocket)
|
||||
▼ ▲
|
||||
Maskinrommet (Rust) ───┘ PG LISTEN/NOTIFY → WebSocket → GUI
|
||||
│ orkestrerer
|
||||
▼
|
||||
CLI-verktøy (tools/) ←── Claude bruker de samme
|
||||
│
|
||||
▼
|
||||
Tjenester: PG+AGE, SpacetimeDB, CAS, Whisper, LiteLLM, LiveKit ...
|
||||
Tjenester: PG+AGE, CAS, Whisper, LiteLLM, LiveKit ...
|
||||
```
|
||||
|
||||
## Kjerneprinsipper
|
||||
|
|
@ -189,8 +188,8 @@ Tjenester: PG+AGE, SpacetimeDB, CAS, Whisper, LiteLLM, LiveKit ...
|
|||
Du ser dine edges. Tilgang via materialisert tilgangsmatrise.
|
||||
5. **Privat er default.** Input uten mottaker-edge er privat. Security
|
||||
by design, ikke konfigurasjon.
|
||||
6. **PG er arkivet, SpacetimeDB er nåtid.** Ingen eierskapskonflikt.
|
||||
To lag, to roller.
|
||||
6. **PG er sannhetskilden.** All data lever i PostgreSQL.
|
||||
Sanntid via PG LISTEN/NOTIFY + WebSocket i portvokteren.
|
||||
7. **Alt er paneler i en arbeidsflate.** Brukergrensesnittet er et spatial
|
||||
canvas der verktøy plasseres, sizes og arrangeres fritt. Hver feature
|
||||
er et panel i BlockShell. Interaksjon mellom paneler via drag-and-drop:
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ Synops.
|
|||
|
||||
Alt er noder og edges i en graf. En bruker er en node. Et team er en
|
||||
node. En mediefil er en node. Hva noe "er" bestemmes av edges, ikke
|
||||
av noden selv. Maskinrommet eier alle skrivinger. Frontend er et tynt
|
||||
lag som leser grafen fra SpacetimeDB.
|
||||
av noden selv. Maskinrommet eier alle skrivinger. Frontend leser
|
||||
sanntidsoppdateringer via WebSocket (PG LISTEN/NOTIFY).
|
||||
|
||||
## Lagmodell
|
||||
|
||||
|
|
@ -21,12 +21,12 @@ lag som leser grafen fra SpacetimeDB.
|
|||
│ Drag-and-drop mellom paneler │
|
||||
└────────┬──────────────────┬─────────────┘
|
||||
│ intensjoner │ les (sanntid)
|
||||
│ │ direkte WebSocket
|
||||
┌────────▼────────┐ ┌──────▼──────────┐
|
||||
│ Maskinrommet │ │ SpacetimeDB │
|
||||
│ (Rust) │ │ Hele grafen │
|
||||
│ Orkestrerer │ │ (noder+edges) │
|
||||
└──┬───────────┬──┘ └─────────────────┘
|
||||
│ │ WebSocket
|
||||
┌────────▼──────────────────┘
|
||||
│ Maskinrommet (Rust)
|
||||
│ Orkestrerer + WebSocket-hub
|
||||
│ PG LISTEN/NOTIFY → WS → GUI
|
||||
└──┬───────────┬──┘
|
||||
│ spawner │
|
||||
▼ ▼
|
||||
┌─────────┐ ┌─────┐┌─────┐┌─────────────┐
|
||||
|
|
@ -38,25 +38,25 @@ lag som leser grafen fra SpacetimeDB.
|
|||
```
|
||||
|
||||
### Skrivestien
|
||||
GUI → intensjon → Maskinrommet (Rust) → SpacetimeDB (instant) → PG (async)
|
||||
GUI → intensjon → Maskinrommet (Rust) → PG → NOTIFY → WebSocket → GUI
|
||||
|
||||
Frontend sender intensjoner (ikke data). Maskinrommet validerer,
|
||||
skriver til SpacetimeDB først for umiddelbar oppdatering, deretter
|
||||
persisterer til PG asynkront. Maskinrommet leser edges og bestemmer
|
||||
hvilke tjenester som trigges.
|
||||
Frontend sender intensjoner (ikke data). Maskinrommet validerer og
|
||||
skriver til PG. PG NOTIFY-triggere sender endringer via WebSocket
|
||||
til alle tilkoblede klienter i sanntid. Maskinrommet leser edges
|
||||
og bestemmer hvilke tjenester som trigges.
|
||||
|
||||
### Lesestien (sanntid)
|
||||
SpacetimeDB → GUI (direkte WebSocket)
|
||||
PG NOTIFY → Maskinrommet (WebSocket-hub) → GUI
|
||||
|
||||
SpacetimeDB holder hele grafen — alle noder og edges. Frontend
|
||||
abonnerer via WebSocket med edge-filtre. Visninger er spørringer
|
||||
mot STDB, ikke forhåndsdefinerte API-endepunkter.
|
||||
Maskinrommet lytter på PG LISTEN/NOTIFY-kanaler og videresender
|
||||
relevante endringer via WebSocket til tilkoblede klienter, filtrert
|
||||
på tilgangsmatrisen.
|
||||
|
||||
### Lesestien (tunge spørringer)
|
||||
GUI → Maskinrommet (Rust) → PG
|
||||
|
||||
Søk, statistikk, semantisk søk (pgvector), graftraversering
|
||||
(AGE/Cypher). For operasjoner der STDB ikke er egnet.
|
||||
(AGE/Cypher).
|
||||
|
||||
## Datamodell
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ bestemmes utelukkende av dens edges:
|
|||
- Node uten edges = løs tanke
|
||||
|
||||
### Visninger er spørringer
|
||||
Visninger er spørringer mot SpacetimeDB med edge-filtre:
|
||||
Visninger er spørringer mot node-/edge-stores med edge-filtre:
|
||||
- Chat = noder med kanal-edge, sortert på tid
|
||||
- Kanban = noder med board-edge, gruppert på status
|
||||
- Kalender = noder med dato-edge, på tidslinje
|
||||
|
|
@ -139,7 +139,7 @@ i `tools/` — maskinrommet spawner riktig verktøy fra jobbkøen.
|
|||
|
||||
Kjerneansvar (forblir i maskinrommet):
|
||||
- Auth + tilgangskontroll
|
||||
- Intentions (validering, edge-logikk, STDB+PG-skriving)
|
||||
- Intentions (validering, edge-logikk, PG-skriving)
|
||||
- Jobbkø (polling, retry, dead letter)
|
||||
- CAS-forvaltning og pruning
|
||||
|
||||
|
|
@ -173,13 +173,9 @@ Deling er å legge til edges.
|
|||
## Datalag
|
||||
|
||||
### PostgreSQL
|
||||
Persistent backup og arkiv. Alle noder og edges. Fulltekstsøk,
|
||||
Eneste datakilde. Alle noder og edges. Fulltekstsøk,
|
||||
pgvector (semantisk søk), JSONB. Apache AGE for Cypher ved behov.
|
||||
|
||||
### SpacetimeDB
|
||||
Holder hele grafen — alle noder og edges. Frontend abonnerer via
|
||||
WebSocket. Maskinrommet skriver hit først for umiddelbar oppdatering,
|
||||
PG synkroniseres asynkront.
|
||||
PG LISTEN/NOTIFY-triggere sender sanntidsoppdateringer.
|
||||
|
||||
### CAS (Content-Addressable Store)
|
||||
Binærdata (lyd, bilde, video) lagret med hash. TTL basert på
|
||||
|
|
@ -193,7 +189,7 @@ Tredjepartstjenester kjører i **Docker**. Prinsipp: Docker for det vi
|
|||
ikke bygger selv, native for det vi har full kontroll over.
|
||||
|
||||
**Native (systemd):** Caddy, maskinrommet (Rust), SvelteKit.
|
||||
**Docker:** PostgreSQL, SpacetimeDB, Authentik, LiteLLM, faster-whisper.
|
||||
**Docker:** PostgreSQL, Authentik, LiteLLM, faster-whisper.
|
||||
|
||||
Maskinrommet og Caddy kjører native fordi de trenger direkte tilgang
|
||||
til host-ressurser (CLI-verktøy, TLS-konfig). Docker-tjenester
|
||||
|
|
@ -207,7 +203,7 @@ eksponerer porter på localhost.
|
|||
| CLI-verktøy | Rust/Shell | Native | Prosessering (transcribe, render, audio, AI). Delt mellom maskinrommet og Claude |
|
||||
| Frontend | SvelteKit | Native (systemd) | PWA, SSR, spatial canvas med verktøy-paneler |
|
||||
| Database | PostgreSQL | Docker | Versjonsstyring, enkel oppgradering |
|
||||
| Sanntid | SpacetimeDB | Docker | Eksperimentelt, offisielt image |
|
||||
| Sanntid | PG LISTEN/NOTIFY + WebSocket | Native (i maskinrommet) | Ingen ekstra avhengighet, sanntid fra PG |
|
||||
| Binærlagring | CAS (filsystem) | Native | Enkel, deduplisering, ingen ekstern avhengighet |
|
||||
| AI Gateway | LiteLLM | Docker | Ferdig image, sjelden oppdatering |
|
||||
| AI Agent | Claude Code CLI | Native | Chat-deltaker, spawnes av maskinrommet |
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ publisering.
|
|||
|
||||
#### Varslingsmekanisme
|
||||
|
||||
- **Sanntidsvarsel via STDB:** Maskinrommet skriver en varslingsnode
|
||||
som frontend abonnerer på. Vises som banner/toast i alle aktive
|
||||
klienter umiddelbart
|
||||
- **Sanntidsvarsel via WebSocket:** Maskinrommet skriver en varslingsnode
|
||||
som frontend abonnerer på via WebSocket. Vises som banner/toast i alle
|
||||
aktive klienter umiddelbart
|
||||
- **Varslingstyper:**
|
||||
- `info` — generell melding (f.eks. "Ny funksjonalitet tilgjengelig")
|
||||
- `warning` — planlagt vedlikehold med nedtelling
|
||||
|
|
@ -100,10 +100,9 @@ Vises for alle med `visibility: open`. Forsvinner automatisk etter
|
|||
|
||||
Sanntidsoversikt over systemtilstand.
|
||||
|
||||
- **Tjeneste-status:** PG, STDB, Caddy, Authentik, LiteLLM, Whisper, LiveKit — oppe/nede/degradert
|
||||
- **Tjeneste-status:** PG, Caddy, Authentik, LiteLLM, Whisper, LiveKit — oppe/nede/degradert
|
||||
- **Metrikker:** CPU, minne, disk, nettverkstrafikk
|
||||
- **PG-helse:** Tilkoblingspool, aktive spørringer, replikerings-lag (fremtidig)
|
||||
- **STDB-helse:** Minnebruk, antall abonnenter, graf-størrelse
|
||||
- **Logg-tilgang:** Siste feil og advarsler fra alle tjenester, filtrerbart
|
||||
- **Backup-status:** Siste vellykkede backup per type, neste planlagte kjøring
|
||||
|
||||
|
|
@ -118,7 +117,7 @@ Sanntidsoversikt over systemtilstand.
|
|||
- **Frontend:** `/admin/health` — dashboard med tjenestekort (opp/nede/degradert med
|
||||
latens), system-metrikker med progress-bars, PG-tilkoblinger og DB-størrelse,
|
||||
backup-status, og filtrerbar logg-visning
|
||||
- **Tjeneste-sjekker:** PG (SQL ping), STDB (noop-kall), Caddy (admin-API),
|
||||
- **Tjeneste-sjekker:** PG (SQL ping), Caddy (admin-API),
|
||||
Authentik (health-endpoint), LiteLLM/Whisper/LiveKit (HTTP health). Alle kjøres
|
||||
parallelt med 5s timeout
|
||||
- **Metrikker:** CPU load via `/proc/loadavg`, minne via `/proc/meminfo`,
|
||||
|
|
|
|||
|
|
@ -310,14 +310,14 @@ Terminal (Claude/Vegard) Web (SvelteKit)
|
|||
│
|
||||
↕ synk
|
||||
│
|
||||
SpacetimeDB → WebSocket → sanntid i frontend
|
||||
PG LISTEN/NOTIFY → WebSocket → sanntid i frontend
|
||||
```
|
||||
|
||||
| Grensesnitt | Leser fra | Skriver til |
|
||||
|-------------|-----------|-------------|
|
||||
| `synops-tasks` CLI | PG direkte | PG direkte |
|
||||
| Web (KanbanTrait) | SpacetimeDB | Maskinrommet → PG + STDB |
|
||||
| `synops-respond` | PG | PG + STDB (via maskinrommet) |
|
||||
| Web (KanbanTrait) | WebSocket (PG) | Maskinrommet → PG |
|
||||
| `synops-respond` | PG | PG (via maskinrommet) |
|
||||
|
||||
Ingen nytt UI trengs — arbeidstavlen er bare en `collection`-node
|
||||
med kanban-trait. Vegard ser den på arbeidsflaten ved siden av
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Et fullverdig virtuelt møterom for Sidelinjas redaksjon. Bygget på LiveKit for
|
|||
2. Under møtet er følgende verktøy tilgjengelige:
|
||||
- **Video/lyd** — LiveKit-strøm mellom deltakere
|
||||
- **Whiteboard** — Frihåndstavle for skisser (se `docs/features/whiteboard.md`)
|
||||
- **Delt scratchpad** — SpacetimeDB-drevet tekstfelt for kjappe notater
|
||||
- **Delt scratchpad** — Sanntids tekstfelt for kjappe notater
|
||||
- **Aha-markør** — Markerer viktige øyeblikk med tidsstempel
|
||||
- **Off-the-record** — Pauser AI-lytting og opptak
|
||||
3. Whisper transkriberer møtet via live transkripsjonspipelinen (se `docs/features/live_transkripsjon.md`).
|
||||
|
|
@ -23,6 +23,7 @@ Et fullverdig virtuelt møterom for Sidelinjas redaksjon. Bygget på LiveKit for
|
|||
| Feature | Rolle i Møterommet |
|
||||
|---|---|
|
||||
| LiveKit | WebRTC lyd/video |
|
||||
| Lydmixer | Volumslidere per deltaker, mute, sound pads (se `docs/features/lydmixer.md`) |
|
||||
| Live transkripsjon | Whisper-pipeline for møtetranskripsjon (se `docs/features/live_transkripsjon.md`) |
|
||||
| Live AI (møte-modus) | Referat, action points, tråding (se `docs/features/live_ai.md`) |
|
||||
| Whiteboard | Frihåndstavle for visuell brainstorming (se `docs/features/whiteboard.md`) |
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Redaksjonen er den daglige arbeidsflaten for Sidelinjas team. Her planlegges epi
|
|||
## 2. Brukeropplevelse
|
||||
|
||||
### 2.1 Tema-bassenget
|
||||
Alle pågående "Saker" vises i en oversikt. PostgreSQL er kilden til sannhet, SpacetimeDB holder aktive temaer i minnet for sanntidsoppdateringer.
|
||||
Alle pågående "Saker" vises i en oversikt. PostgreSQL er kilden til sannhet, med sanntidsoppdateringer via LISTEN/NOTIFY + WebSocket.
|
||||
|
||||
### 2.2 Trådet Chat
|
||||
Hver melding tilhører et Tema. Meldinger støtter tråder (svar-på-svar) og rike mentions med `#`-tags og `@`-mentions. Se `docs/features/chat.md` for teknisk spesifikasjon.
|
||||
|
|
@ -16,7 +16,7 @@ Hver melding tilhører et Tema. Meldinger støtter tråder (svar-på-svar) og ri
|
|||
Episoder fungerer som containere. Brukerne drar Temaer fra bassenget inn i en episodes kjøreplan med drag-and-drop. Se `docs/features/kanban.md` for teknisk spesifikasjon.
|
||||
|
||||
### 2.4 Show Notes
|
||||
Et kollaborativt tekstfelt koblet til et Tema. Enkle "Operational Transformation"-aktige oppdateringer (eller felt-låsing) håndteres i SpacetimeDB-modulen. Synkes til PostgreSQL for persistens.
|
||||
Et kollaborativt tekstfelt koblet til et Tema. Lagres i PostgreSQL med sanntidssynk via WebSocket.
|
||||
|
||||
### 2.5 AI-behandling av tekst
|
||||
Brukere limer inn uformatert tekst fra nettet i editoren, trykker AI-knappen (✨) og velger handling (rens, oppsummer, trekk ut fakta). Resultatet publiseres som en ny melding med foreslåtte graf-koblinger. Se `docs/proposals/editor.md` § "AI-behandling — universell knapp".
|
||||
|
|
@ -32,5 +32,5 @@ Brukere limer inn uformatert tekst fra nettet i editoren, trykker AI-knappen (
|
|||
|
||||
## 4. Instruks for Claude Code
|
||||
* Bruk SvelteKit for Drag-and-Drop. Unngå tunge biblioteker hvis native HTML5 Drag and Drop er tilstrekkelig.
|
||||
* SpacetimeDB er "State Manager". Frontend speiler SpacetimeDB sin tilstand — ikke bygg kompleks lokal state.
|
||||
* All tilgang er styrt via node_access-matrisen. SpacetimeDB-tilkoblinger bærer brukerens identitet, og tilgang avgjøres av edges til samlings-noder.
|
||||
* Frontend mottar sanntidsdata via WebSocket — ikke bygg kompleks lokal state.
|
||||
* All tilgang er styrt via node_access-matrisen. WebSocket-tilkoblinger bærer brukerens identitet, og tilgang avgjøres av edges til samlings-noder.
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ Synops (collection, visibility: readable)
|
|||
│ └── (work_items)
|
||||
│
|
||||
└── Synops Erfaringer (collection)
|
||||
├── SpacetimeDB-integrasjon (content)
|
||||
├── SpacetimeDB-integrasjon (content, historisk)
|
||||
├── Authentik OIDC (content)
|
||||
└── ...
|
||||
```
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Det virtuelle podcast-studioet er Sidelinjas innspillingsmiljø. LiveKit håndte
|
|||
2. Høykvalitetslyd streames mellom deltakerne via WebRTC.
|
||||
3. I bakgrunnen transkriberer Whisper lydstrømmen i chunks (~5 sek) via live transkripsjonspipelinen (se `docs/features/live_transkripsjon.md`).
|
||||
4. AI-assistenten analyserer transkripsjonen for entiteter (NER) og slår opp i Kunnskapsgrafen. Relevante faktoider popper lydløst opp på skjermen (se `docs/features/live_ai.md`, studio-modus).
|
||||
5. Programlederne kan trykke **Aha-markør** for å markere viktige øyeblikk. Tidsstempelet lagres i SpacetimeDB, koblet til episoden.
|
||||
5. Programlederne kan trykke **Aha-markør** for å markere viktige øyeblikk. Tidsstempelet lagres i PG og synkes til frontend via WebSocket, koblet til episoden.
|
||||
6. Etter innspilling skyves lydfilen inn i Podcastfabrikken for full transkripsjon og publisering (se `docs/concepts/podcastfabrikken.md`).
|
||||
|
||||
## 3. Komponenter
|
||||
|
|
@ -17,9 +17,10 @@ Det virtuelle podcast-studioet er Sidelinjas innspillingsmiljø. LiveKit håndte
|
|||
| Feature | Rolle i Studioet |
|
||||
|---|---|
|
||||
| LiveKit | WebRTC lyd/video mellom deltakere |
|
||||
| Lydmixer | Volumslidere, mute, sound pads, stemmeeffekter, EQ (se `docs/features/lydmixer.md`) |
|
||||
| Live transkripsjon | Whisper `small` for lav latens, ~1s forsinkelse (se `docs/features/live_transkripsjon.md`) |
|
||||
| Live AI (studio-modus) | NER + faktoid-oppslag fra Kunnskapsgrafen (se `docs/features/live_ai.md`) |
|
||||
| Aha-markør | Manuell markering av viktige øyeblikk, lagres i SpacetimeDB |
|
||||
| Aha-markør | Manuell markering av viktige øyeblikk, lagres i PG |
|
||||
|
||||
## 4. Avgrensning
|
||||
- Studioet er for **innspilling**, ikke redigering. Klipping/postproduksjon skjer utenfor Sidelinja.
|
||||
|
|
@ -27,6 +28,6 @@ Det virtuelle podcast-studioet er Sidelinjas innspillingsmiljø. LiveKit håndte
|
|||
- Aha-markøren deles med Møterommet (se `docs/concepts/møterommet.md`), men i studio-konteksten brukes den primært til klippepunkter.
|
||||
|
||||
## 5. Utviklingsfaser
|
||||
1. Bygg SpacetimeDB-lytter i frontend + dummy faktoid-push for å verifisere UI.
|
||||
1. Bygg WebSocket-lytter i frontend + dummy faktoid-push for å verifisere UI.
|
||||
2. Koble Whisper til et offline lydopptak, kjør NER/oppslag mot PostgreSQL.
|
||||
3. Koble LiveKit-strømmen til Whisper for sanntid.
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ For å unngå "Blank Canvas"-syndromet startes plattformen med anerkjente rammev
|
|||
Valgomaten er bygget som en to-trinns trakt for å maksimere viral spredning samtidig som dataintegriteten bevares.
|
||||
|
||||
### 2.1 Forsiden: Friksjonsfri & Anonym
|
||||
* **Mekanikk:** Ingen registrering kreves for å starte. SvelteKit genererer en anonym UUID som lagres i `localStorage` og brukes mot SpacetimeDB.
|
||||
* **Mekanikk:** Ingen registrering kreves for å starte. SvelteKit genererer en anonym UUID som lagres i `localStorage`.
|
||||
* **UX:** Et rent kortstokk-grensesnitt (Tinder-stil swipe). Påstand på forsiden, brukeren velger Enig/Uenig.
|
||||
* **Tre-trinns kalibrering (OKCupid-inspirert):**
|
||||
1. Hva mener du?
|
||||
2. Hva ønsker du at kandidaten skal mene?
|
||||
3. Hvor viktig er dette for deg?
|
||||
* **Dealbreakers (Negative Veto):** Brukere kan sette absolutte grenser (f.eks. "Uansett hvor enige vi er om skatt, matcher jeg aldri med en som er for/mot vindkraft").
|
||||
* **Sanntid:** SpacetimeDB beregner PCA (Prinsipalkomponentanalyse) og oppdaterer brukerens posisjon lynraskt i minnet for hvert swipe.
|
||||
* **Sanntid:** PCA (Prinsipalkomponentanalyse) beregnes og oppdaterer brukerens posisjon for hvert swipe.
|
||||
|
||||
### 2.2 Baksiden: "Snu kortet" (Krever innlogging)
|
||||
For avansert interaksjon må brukeren logge inn via Authentik (SSO). Den anonyme sesjonen flettes da automatisk med brukerprofilen. På baksiden av kortet finner man:
|
||||
|
|
@ -50,7 +50,7 @@ Et spørsmål i PostgreSQL kan *aldri* endres (`UPDATE`) etter at folk har begyn
|
|||
|
||||
### 4.2 Opprettelse av nye spørsmål og akser (Sandkassen)
|
||||
* **Inkubator:** Innloggede brukere kan opprette nye spørsmål eller definere helt nye akser (ved å angi to motpoler). Disse havner i en "Sandkasse" og vises ikke på forsiden før de har fått nok organisk engasjement fra andre innloggede brukere.
|
||||
* **Vekting via Graph Edges:** Koblingen mellom et spørsmål og en akse lagres som en relasjon i `graph_edges`. Når brukere "stemmer opp" at et spørsmål tilhører en spesifikk akse, øker feltet `confidence`. SpacetimeDB bruker denne `confidence`-scoren som multiplikator i match-algoritmen.
|
||||
* **Vekting via Graph Edges:** Koblingen mellom et spørsmål og en akse lagres som en relasjon i `graph_edges`. Når brukere "stemmer opp" at et spørsmål tilhører en spesifikk akse, øker feltet `confidence`. Denne `confidence`-scoren brukes som multiplikator i match-algoritmen.
|
||||
|
||||
## 5. Visualisering & Sidelinja Explorer
|
||||
|
||||
|
|
@ -74,9 +74,9 @@ Av kostnads- og ytelseshensyn skjer all AI-bruk asynkront i backend via jobbkøe
|
|||
|---|---|
|
||||
| **SvelteKit (Klient)** | UX, Anonym UUID-håndtering i `localStorage`, kortstokk-swipe-grensesnitt, "lazy loading" av baksiden ved innlogging, visning av Heatmaps via Sidelinja Explorer. |
|
||||
| **Authentik** | SSO for brukere, kandidater og redaksjon. Sammenfletting av anonym UUID med ekte bruker-ID. |
|
||||
| **SpacetimeDB** | Sanntids match-kalkulering (Rust Reducers), swiping-logikk, PCA-beregning, og "Multiplayer"-rom ("Sofagruppa"). Holder kun aktive sesjoner i minnet. |
|
||||
| **Sanntid (WebSocket)** | Match-kalkulering, swiping-logikk, PCA-beregning. Sanntidsoppdateringer via PG LISTEN/NOTIFY + WebSocket. |
|
||||
| **PostgreSQL** | Kunnskapsgrafen (`nodes`, `graph_edges`). Permanent lagring av spørsmål, akser, versjonshistorikk og aggregerte data for Sidelinja Explorer. |
|
||||
| **Rust Worker (Sync)** | Synkroniserer batcher av svar fra SpacetimeDB over til PostgreSQL-lagringen via standard sync-mekanismen (se `synkronisering.md`). |
|
||||
| **PostgreSQL** | Eneste datakilde for spørsmål, akser, svar og kunnskapsgraf. |
|
||||
| **Rust Worker (AI)** | Kjører `valgomat_generate_profile` og `valgomat_moderation` via jobbkøen. |
|
||||
|
||||
## 8. Dataklassifisering (ref. docs/arkitektur.md 2.2)
|
||||
|
|
@ -84,22 +84,20 @@ Av kostnads- og ytelseshensyn skjer all AI-bruk asynkront i backend via jobbkøe
|
|||
| Data | Kategori | Detaljer |
|
||||
|---|---|---|
|
||||
| Spørsmål, akser, versjonshistorikk | Kritisk (PG) | Brukergenerert innhold, krever backup |
|
||||
| Individuelle svar (aggregert) | Kritisk (PG) | Synket fra SpacetimeDB |
|
||||
| Aktive sesjoner, live PCA-state | Flyktig (SpacetimeDB) | Tåler tap — bruker svarer på nytt |
|
||||
| Individuelle svar (aggregert) | Kritisk (PG) | Lagret direkte i PG |
|
||||
| AI-genererte kandidatprofiler | Avledet (PG) | Kan regenereres fra partiprogrammer |
|
||||
|
||||
## 9. Skaleringsrisiko
|
||||
|
||||
### PCA i SpacetimeDB
|
||||
PCA-beregning i SpacetimeDB er minnekrevende og udokumentert for store datasett. Ved tusenvis av samtidige brukere med 50+ akser kan minnebruken eksplodere. Tiltak:
|
||||
- **Materialized Views i PostgreSQL** for Sidelinja Explorer — aldri kjør tunge aggregeringer on-the-fly. Oppdater views via nattlig jobb eller etter batch-sync fra SpacetimeDB.
|
||||
- **Batch-sync med størrelsesbegrensning** — sync_outbox kan bli bottleneck ved høy svaraktivitet. Vurder dedikert sync-frekvens for valgomat-data.
|
||||
- **PCA-fallback i PG** — hvis SpacetimeDB-minnebruk overstiger terskel, flytt PCA-beregning til PostgreSQL (via `pg_stat_statements` for kostnadsmåling) med lengre oppdateringsintervall.
|
||||
- **Overvåk tidlig** — legg til valgomat-spesifikke metrikker i `/admin/observability` (antall aktive sesjoner, PCA-beregningstid, SpacetimeDB-minnebruk for valgomat-tabeller).
|
||||
### PCA-beregning
|
||||
PCA-beregning er potensielt tung ved mange brukere og akser. Tiltak:
|
||||
- **Materialized Views i PostgreSQL** for Sidelinja Explorer — aldri kjør tunge aggregeringer on-the-fly. Oppdater views via nattlig jobb.
|
||||
- **PCA i PG** — beregnes i PostgreSQL med caching og oppdateringsintervall.
|
||||
- **Overvåk tidlig** — legg til valgomat-spesifikke metrikker i `/admin/observability` (antall aktive sesjoner, PCA-beregningstid).
|
||||
|
||||
## 10. Instruks for Claude Code
|
||||
1. **Datamodell:** Utvid enum `node_type` i PostgreSQL med `valgomat_question` og `valgomat_axis`. Bruk `graph_edges` med `relation_type = 'AFFECTS_AXIS'` og oppdater `confidence`-feltet for å håndtere crowdsourcet vekting av spørsmål opp mot ulike akser.
|
||||
2. **SpacetimeDB Reducers:** Implementer innkommende events som `SubmitSwipe`, `SuggestAxis`, og match-algoritmen i Rust inne i SpacetimeDB. Pass på at reducere støtter anonym `session_id`.
|
||||
2. **Swiping-logikk:** Implementer `SubmitSwipe`, `SuggestAxis` og match-algoritmen i maskinrommet. Støtt anonym `session_id`.
|
||||
3. **State Management:** SvelteKit skal ikke kreve innlogging for forsiden. Implementer Auth-guards slik at opprettelse av spørsmål, stemmegivning på andres forslag og visning av kommentarer gir `403 Forbidden` for uautoriserte og trigger Authentik-flyten.
|
||||
4. **Explorer API:** Bygg aggregerte Materialized Views i PostgreSQL for Sidelinja Explorer for å unngå tung on-the-fly kalkulering av Heatmaps over millioner av rader.
|
||||
5. **Jobbtyper:** Registrer `valgomat_generate_profile` og `valgomat_moderation` som jobbtyper i jobbkøen (se `jobbkø.md`). Bruk `sidelinja/rutine` som modellalias.
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ Maskinrommet:
|
|||
6. Logg forbruk i ai_usage_log
|
||||
│
|
||||
▼
|
||||
Frontend mottar oppdatering via SpacetimeDB
|
||||
Frontend mottar oppdatering via WebSocket (PG LISTEN/NOTIFY)
|
||||
```
|
||||
|
||||
### 6.2 Drag-and-drop integrasjon
|
||||
|
|
|
|||
|
|
@ -30,13 +30,13 @@ Canvas-primitiv (felles)
|
|||
Whiteboard (consumer)
|
||||
├── Tegneverktøy: penn, linje, rektangel, tekst
|
||||
├── Strøk-modell: SVG paths / canvas paths
|
||||
└── SpacetimeDB: strøk-synkronisering
|
||||
└── Sanntid: strøk-synkronisering via WebSocket
|
||||
|
||||
Storyboard (consumer)
|
||||
├── Kort-rendering: <MessageBox> i kompakt modus
|
||||
├── Status-modell: Klar / Tatt opp / Droppet / Arkivert
|
||||
├── Portal-soner: overføringsmekanikk til andre blokker
|
||||
└── SpacetimeDB: kort-posisjon + status-synkronisering
|
||||
└── Sanntid: kort-posisjon + status-synkronisering via WebSocket
|
||||
```
|
||||
|
||||
## 2. Kamera-modell
|
||||
|
|
@ -167,9 +167,9 @@ Enhver blokk i `BlockShell` kan gå i fullskjerm. Dette er en generell feature,
|
|||
- **Escape:** Trykk Esc eller klikk "minimer"-knapp for å gå tilbake
|
||||
- **URL-state:** Fullskjerm-tilstand lagres ikke i URL — det er en visuell modus, ikke en side
|
||||
|
||||
## 8. SpacetimeDB-integrasjon
|
||||
## 8. Sanntidsintegrasjon
|
||||
|
||||
Canvas-primitivet selv har ingen SpacetimeDB-kobling — det er consumer-ens ansvar. Men primitivet eksponerer events som consumeren kan koble til SpacetimeDB:
|
||||
Canvas-primitivet selv har ingen sanntidskobling — det er consumer-ens ansvar. Men primitivet eksponerer events som consumeren kan koble til WebSocket:
|
||||
|
||||
```typescript
|
||||
interface CanvasEvents {
|
||||
|
|
@ -180,7 +180,7 @@ interface CanvasEvents {
|
|||
}
|
||||
```
|
||||
|
||||
Storyboard-consumeren bruker `onObjectMove` til å kalle en SpacetimeDB-reducer for å synkronisere posisjon til andre klienter.
|
||||
Storyboard-consumeren bruker `onObjectMove` til å oppdatere PG via maskinrommet, som propagerer endringen til andre klienter via WebSocket.
|
||||
|
||||
## 9. Bygger på
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ Storyboard-consumeren bruker `onObjectMove` til å kalle en SpacetimeDB-reducer
|
|||
|
||||
### Fase 2: Storyboard som første consumer
|
||||
- `<StoryboardCard>` rendrer meldingsboks-kort på canvaset
|
||||
- SpacetimeDB-synk for posisjon og status
|
||||
- Sanntidssynk for posisjon og status via WebSocket
|
||||
- Portal-soner for overføring
|
||||
|
||||
### Fase 3: Whiteboard-migrering
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
**Filsti:** `docs/features/chat.md`
|
||||
|
||||
## 1. Konsept
|
||||
En universell, sanntids meldingskomponent bygget på SpacetimeDB. Chat er ikke bundet til én kontekst — den kan knyttes til enhver node i Kunnskapsgrafen via **channels**. Ulike konsepter bruker chat med ulik konfigurasjon, men all infrastruktur er delt.
|
||||
En universell, sanntids meldingskomponent. Chat er ikke bundet til én kontekst — den kan knyttes til enhver node i Kunnskapsgrafen via **channels**. Ulike konsepter bruker chat med ulik konfigurasjon, men all infrastruktur er delt. Sanntid via PG LISTEN/NOTIFY + WebSocket.
|
||||
|
||||
## 2. Channels-modellen
|
||||
En **channel** er en meldingsstrøm knyttet til en vilkårlig node (parent) i grafen. Channelen er selv en node (`node_type = 'channel'`), noe som betyr at den deltar i grafen og arver tilgangsstyring via `node_access`-matrisen.
|
||||
|
|
@ -74,14 +74,15 @@ messages (
|
|||
)
|
||||
```
|
||||
|
||||
### 3.2 SpacetimeDB (sanntid)
|
||||
SpacetimeDB holder aktive channels' meldinger i minnet som varm cache foran PG. Ved oppstart gjør worker warmup fra PG → SpacetimeDB (per-kanal konfigurasjon). Nye meldinger sendes via SpacetimeDB-reducers og kringkastes til alle tilkoblede klienter. Synkes til PostgreSQL med ~1 sek forsinkelse (se `docs/infra/synkronisering.md`).
|
||||
### 3.2 Sanntid
|
||||
Nye meldinger skrives til PG og propageres via LISTEN/NOTIFY + WebSocket
|
||||
til alle tilkoblede klienter i sanntid.
|
||||
|
||||
## 4. Mentions & Autocomplete
|
||||
Kun aktive når `config.mentions = true`.
|
||||
|
||||
* **Trigger-tegn:** `#` (Temaer/Aktører fra Kunnskapsgrafen), `@` (brukere/redaksjonsmedlemmer), `/` (kommandoer).
|
||||
* **Filtrering:** Svelte-klienten filtrerer den lokale SpacetimeDB-cachen umiddelbart. Skriver man `#Ha...` vises en klikkbar liste ("Hans Petter Sjøli", "Høyre").
|
||||
* **Filtrering:** Svelte-klienten filtrerer den lokale cachen umiddelbart. Skriver man `#Ha...` vises en klikkbar liste ("Hans Petter Sjøli", "Høyre").
|
||||
* **Grafkobling:** Ved `#`-mention opprettes automatisk `MENTIONS`-edges i `graph_edges` mellom meldingen og den nevnte noden.
|
||||
* **Mobil-optimalisert:** Autocomplete-listen er tappbar og tilpasset mindre skjermer.
|
||||
|
||||
|
|
@ -119,16 +120,10 @@ Channels med `config.ttl_days` satt til et tall får sine meldinger automatisk s
|
|||
|
||||
### Ferdig (mars 2026)
|
||||
- **ChatBlock.svelte:** Adapter-mønster via `createChat()` factory. Bruker `chat.edit()`, `chat.delete()`, `chat.react()` — ingen direkte PG API-kall.
|
||||
- **SpacetimeDB-adapter (`spacetime.svelte.ts`):** Ren SpacetimeDB-adapter. All data fra SpacetimeDB (historikk via warmup + sanntid). Reaksjoner fra `message_reaction`-tabellen.
|
||||
- **PG-adapter (`pg.svelte.ts`):** Polling hvert 3. sek. Readonly fallback når SpacetimeDB ikke er konfigurert.
|
||||
- **Factory (`create.svelte.ts`):** Velger adapter basert på `VITE_SPACETIMEDB_URL`. SSR-safe med `browser`-guard.
|
||||
- **WebSocket-adapter:** Sanntidsdata via PG LISTEN/NOTIFY + WebSocket.
|
||||
- **Shared types (`types.ts`):** `ChatConnection` interface med `send`, `edit`, `delete`, `react`, `readonly`.
|
||||
- **SpacetimeDB Rust-modul (`spacetimedb/src/lib.rs`):** `ChatMessage`, `MessageReaction`, `SyncOutbox`-tabeller. Reducers: `send_message`, `delete_message`, `edit_message`, `add_reaction`, `remove_reaction`, `load_messages`, `load_reactions`, `clear_channel`, `mark_synced`.
|
||||
- **Worker warmup (`worker/src/warmup.rs`):** PG → SpacetimeDB ved oppstart. Per-kanal konfig (all/messages/days/none). Trådbasert henting.
|
||||
- **Worker sync (`worker/src/sync.rs`):** SpacetimeDB → PG hvert sekund. Insert/delete/update meldinger + reaksjoner.
|
||||
- **Admin-side (`/admin/channels`):** Per-kanal warmup-konfigurasjon.
|
||||
- **Tråder:** Komplett trådvisning med datogruppering, autoscroll og visuell skillelinje mellom tråder.
|
||||
- **Reaksjoner:** Via SpacetimeDB-reducers, synket til PG.
|
||||
- **Reaksjoner:** Lagret i PG, propagert via WebSocket.
|
||||
- **Meldingskollaps:** Lange meldinger begrenses til 2 linjer med "Vis mer"/"Vis mindre".
|
||||
- **AI-behandling:** Meldinger kan AI-behandles (✨-knapp, eldre modell). Revisjons-toggle viser original vs. AI-versjon. Markdown-rendering for AI-output. NB: Erstattes av frittstående AI-verktøy på arbeidsflaten — se `docs/features/ai_verktoy.md`.
|
||||
- **Konvertering:** Meldinger kan opprettes som kanban-kort eller kalenderhendelse (dialog sier "Opprett", ikke "Konverter" — meldingen beholdes i chatten).
|
||||
|
|
@ -144,13 +139,12 @@ Channels med `config.ttl_days` satt til et tall får sine meldinger automatisk s
|
|||
|
||||
### Gjenstår
|
||||
- **Vedlegg, TTL** — avventer implementering.
|
||||
- **Tilgangsfiltrering:** SpacetimeDB-laget må filtrere basert på `node_access`-matrisen.
|
||||
- **Pin/konvertering:** Går fortsatt direkte til PG API (ikke via SpacetimeDB).
|
||||
- **Pin/konvertering:** Gjenstår.
|
||||
|
||||
## 11. Instruks for Claude Code
|
||||
* **Opprettelsesrekkefølge:** Opprett `nodes`-rad → `channels`-rad → (for meldinger) `nodes`-rad → `messages`-rad. Alt i én transaksjon.
|
||||
* **Channel-opprettelse:** Når en Tema, Episode eller Møte opprettes, opprett alltid en default-channel i samme transaksjon.
|
||||
* **Mentions-parsing:** Skjer i sync-workeren ved persistering til PG. Parser mention-UUIDs fra HTML body og oppretter `graph_edges`.
|
||||
* **Config-respekt:** Frontend-komponenten må lese `channel.config` og slå av/på UI-elementer. `channels.config` inneholder også `warmup_mode`/`warmup_value` for SpacetimeDB-oppvarming.
|
||||
* **PG er autoritativ** — SpacetimeDB er varm cache. Frontend snakker kun med SpacetimeDB.
|
||||
* **Config-respekt:** Frontend-komponenten må lese `channel.config` og slå av/på UI-elementer.
|
||||
* **PG er eneste datakilde.** Sanntid via LISTEN/NOTIFY + WebSocket.
|
||||
* **Tilgang styres via `node_access`-matrisen.** Channels arver tilgang fra sin parent-node via edges.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ som ikke er delt med andre via edges. Fungerer som en kronologisk logg
|
|||
over tanker, notater og idéer som kun er synlige for eieren.
|
||||
|
||||
## 2. Status
|
||||
**Implementert med nodes+edges (mars 2026).** Sanntid via SpacetimeDB.
|
||||
**Implementert med nodes+edges (mars 2026).** Sanntid via PG LISTEN/NOTIFY + WebSocket.
|
||||
|
||||
### Implementert
|
||||
- Frontend: `/diary` route med dagbok-visning
|
||||
|
|
@ -55,9 +55,9 @@ POST /intentions/create_edge
|
|||
`/diary` → `frontend/src/routes/diary/+page.svelte`
|
||||
|
||||
### Datakilde
|
||||
SpacetimeDB sanntidsabonnement via `nodeStore` og `edgeStore`.
|
||||
WebSocket-sanntidsdata via `nodeStore` og `edgeStore`.
|
||||
Ingen backend-query — all filtrering skjer i frontend basert på
|
||||
SpacetimeDB-data som allerede er lastet.
|
||||
data mottatt via initial sync + WebSocket.
|
||||
|
||||
### UI-struktur
|
||||
- Header med tilbake-lenke til mottak og innlegg-teller
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Månedsbasert kalendervisning for redaksjonell planlegging. Hendelser er nodes i
|
|||
|
||||
## 2. Status
|
||||
**Kalendervisning implementert (mars 2026).** Bruker `scheduled`-edges i stedet for
|
||||
separat `calendar_events`-tabell. Abonnement, ICS-eksport og SpacetimeDB-sync gjenstår.
|
||||
separat `calendar_events`-tabell. Abonnement og ICS-eksport gjenstår.
|
||||
|
||||
### Implementert
|
||||
- **Fase 1 (v1, mars 2025):** PG-adapter med `calendars` + `calendar_events` (legacy)
|
||||
|
|
@ -22,7 +22,7 @@ separat `calendar_events`-tabell. Abonnement, ICS-eksport og SpacetimeDB-sync gj
|
|||
- Hendelsesliste under rutenett for gjeldende måned
|
||||
- Lenke fra mottak-siden med hendelsesteller
|
||||
- Tilgang via `nodeVisibility` (respekterer `node_access`-matrise)
|
||||
- Sanntidsoppdatering via SpacetimeDB-subscriptions
|
||||
- Sanntidsoppdatering via WebSocket (PG LISTEN/NOTIFY)
|
||||
|
||||
### Gjenstår — Fase 2
|
||||
- Kobling til kanban-kort (vis deadline på kalender)
|
||||
|
|
@ -33,7 +33,6 @@ separat `calendar_events`-tabell. Abonnement, ICS-eksport og SpacetimeDB-sync gj
|
|||
- Abonnementsmodell (kalender → kalender via graph_edges)
|
||||
- Personlige vs. delte kalendere (via samlings-noder)
|
||||
- ICS/CalDAV-eksport
|
||||
- SpacetimeDB-modul + hybrid-adapter
|
||||
- Varsler/påminnelser via jobbkøen
|
||||
|
||||
## 3. Datamodell (implementert)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ episodeplanlegging i Redaksjonen, men også mottaker av AI-genererte
|
|||
action points fra Møterommet.
|
||||
|
||||
## 2. Status
|
||||
**Implementert med nodes+edges (mars 2026).** Sanntid via SpacetimeDB.
|
||||
**Implementert med nodes+edges (mars 2026).** Sanntid via PG LISTEN/NOTIFY + WebSocket.
|
||||
|
||||
### Implementert
|
||||
- Board = samlings-node (`node_kind: 'collection'`, `metadata.board: true`)
|
||||
|
|
@ -18,7 +18,7 @@ action points fra Møterommet.
|
|||
- Backend: `POST /intentions/update_edge` for statusendring
|
||||
- Backend: `GET /query/board?board_id=...` for board-spørring
|
||||
- Frontend: `/board/[id]` route med HTML5 drag-and-drop
|
||||
- Sanntid via SpacetimeDB edge-subscriptions (ingen polling)
|
||||
- Sanntid via PG LISTEN/NOTIFY + WebSocket (ingen polling)
|
||||
- Opprett kort direkte i kolonne (tittel-input)
|
||||
- Oppretting av nye brett fra mottak-siden
|
||||
|
||||
|
|
@ -97,5 +97,5 @@ med `submitted_to`-edge til samlingen, inkludert forfatterinfo.
|
|||
* Board er en collection-node med `metadata.board: true`.
|
||||
* Status er en `status`-edge (kort → board) med `metadata.value`.
|
||||
* Bruk native HTML5 Drag and Drop, unngå tunge biblioteker.
|
||||
* Sanntid via SpacetimeDB edge-subscriptions.
|
||||
* Sanntid via PG LISTEN/NOTIFY + WebSocket.
|
||||
* Tilgang styres via `node_access`-matrisen.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Brukes i Studioet (se `docs/concepts/studioet.md`). En "virtuell co-host" som dy
|
|||
1. Live transkripsjon (se `docs/features/live_transkripsjon.md`) leverer tekst-chunks.
|
||||
2. Rust-tjenesten analyserer for egennavn (Named Entity Recognition).
|
||||
3. Lynraskt oppslag i PostgreSQL: `SELECT * FROM factoids JOIN actors... WHERE actor.name = $1`.
|
||||
4. Treff dytter `LiveFactoidEvent` inn i SpacetimeDB.
|
||||
4. Treff dytter `LiveFactoidEvent` via WebSocket til frontend.
|
||||
5. SvelteKit-studio viser faktoiden lydløst i en egen boks.
|
||||
|
||||
### 2.2 Lagring
|
||||
|
|
@ -55,7 +55,7 @@ Studio-modus har en **kill switch** — en synlig "Stopp AI"-knapp i studio-gren
|
|||
|
||||
Nødvendig fordi AI-en kan dytte feil eller irrelevante faktoider under live innspilling. Programlederen må kunne slå den av uten å forlate studio-viewet.
|
||||
|
||||
Kill switch-status (`ai_enabled: bool`) lagres på LiveKit-rommet i SpacetimeDB og synkes til alle klienter i rommet.
|
||||
Kill switch-status (`ai_enabled: bool`) lagres i kommunikasjonsnoden metadata i PG og synkes til alle klienter via WebSocket.
|
||||
|
||||
## 5. Instruks for Claude Code
|
||||
* Begge moduser deler samme Whisper-pipeline — ikke dupliser transkripsjonskode.
|
||||
|
|
|
|||
|
|
@ -63,14 +63,15 @@ Eksempler på standard-pads: jingle/intro, applaus, latter, dramatisk pause,
|
|||
|
||||
### 3.4 Delt mixer-kontroll (flerbruker)
|
||||
Alle deltakere i rommet kan se og bruke mixeren samtidig. Mixer-state
|
||||
synkroniseres i sanntid via SpacetimeDB, slik at volumendringer, mutes,
|
||||
effekttogles og pad-avspilling reflekteres hos alle klienter umiddelbart.
|
||||
synkroniseres i sanntid via PG LISTEN/NOTIFY + WebSocket, slik at
|
||||
volumendringer, mutes, effekttogles og pad-avspilling reflekteres hos
|
||||
alle klienter umiddelbart.
|
||||
|
||||
| Element | Synkronisering |
|
||||
|---|---|
|
||||
| **Volumslider** | STDB: `MixerChannel`-tabell med `gain`-verdi per kanal |
|
||||
| **Mute** | STDB: `is_muted` boolean per kanal |
|
||||
| **Effekt av/på** | STDB: `active_effects` JSON per kanal |
|
||||
| **Volumslider** | PG: `mixer_channels`-tabell med `gain`-verdi per kanal |
|
||||
| **Mute** | PG: `is_muted` boolean per kanal |
|
||||
| **Effekt av/på** | PG: `active_effects` JSON per kanal |
|
||||
| **Pad-trigger** | LiveKit Data Message (lav latens) |
|
||||
| **Pad-konfig** | Node metadata (persistent, sjelden endring) |
|
||||
|
||||
|
|
@ -83,35 +84,18 @@ effekttogles og pad-avspilling reflekteres hos alle klienter umiddelbart.
|
|||
rolle-system (owner/admin/member-edges).
|
||||
|
||||
**Konflikthåndtering:** Last-write-wins. Volumslidere er kontinuerlige
|
||||
verdier som oppdateres via STDB-reducers. Ved samtidig endring av samme
|
||||
verdier som oppdateres via maskinrommet. Ved samtidig endring av samme
|
||||
kanal vinner siste skriving — i praksis uproblematisk fordi endringer
|
||||
er visuelt synlige for alle og deltakerne koordinerer naturlig.
|
||||
|
||||
**SpacetimeDB-tabeller:**
|
||||
**PG-tabell:** `mixer_channels` med NOTIFY-trigger for sanntidspropagering via WebSocket.
|
||||
|
||||
```rust
|
||||
#[spacetimedb::table(accessor = mixer_channel, public)]
|
||||
pub struct MixerChannel {
|
||||
#[primary_key]
|
||||
pub id: String, // "{room_id}:{target_user_id}"
|
||||
pub room_id: String, // "communication_{node_uuid}"
|
||||
pub target_user_id: String, // hvem kanalen tilhører
|
||||
pub gain: f64, // 0.0–1.5
|
||||
pub is_muted: bool,
|
||||
pub active_effects: String, // JSON: {"fat_bottom": true, "robot": false, ...}
|
||||
pub role: String, // "editor" | "viewer" — tilgangskontroll per kanal
|
||||
pub updated_by: String, // hvem som sist endret
|
||||
pub updated_at: Timestamp,
|
||||
}
|
||||
```
|
||||
|
||||
**STDB Reducers:**
|
||||
- `create_mixer_channel(room_id, target_user_id, updated_by)` — idempotent opprettelse
|
||||
- `set_gain(room_id, target_user_id, gain, updated_by)` — clamped 0.0–1.5, viewer-sjekk
|
||||
- `set_mute(room_id, target_user_id, is_muted, updated_by)` — viewer-sjekk
|
||||
- `toggle_effect(room_id, target_user_id, effect_name, updated_by)` — JSON-toggle
|
||||
- `delete_mixer_channel(room_id, target_user_id)` — opprydding ved disconnect
|
||||
- `set_mixer_role(room_id, target_user_id, role, updated_by)` — sett editor/viewer
|
||||
**API-endepunkter (maskinrommet):**
|
||||
- `POST /intentions/create_mixer_channel` — idempotent opprettelse
|
||||
- `POST /intentions/set_gain` — clamped 0.0–1.5, viewer-sjekk
|
||||
- `POST /intentions/set_mute` — viewer-sjekk
|
||||
- `POST /intentions/toggle_effect` — JSON-toggle
|
||||
- `POST /intentions/set_mixer_role` — sett editor/viewer
|
||||
|
||||
Mixer-kanaler ryddes automatisk ved `close_live_room` og `clear_all`.
|
||||
|
||||
|
|
@ -213,7 +197,7 @@ Lydmixeren aktiveres via `mixer`-traitet på en samlings-node. Krever at
|
|||
- [x] Master fader og master mute
|
||||
|
||||
### Fase B: Delt mixer-kontroll
|
||||
- [x] SpacetimeDB: `MixerChannel`-tabell + reducers (set_gain, set_mute, toggle_effect, set_mixer_role)
|
||||
- [x] PG: `mixer_channels`-tabell + NOTIFY-trigger + maskinrommet-endepunkter
|
||||
- [x] Frontend abonnerer på mixer-state, oppdaterer Web Audio-graf ved endringer
|
||||
- [x] Visuell feedback: alle ser sliders bevege seg i sanntid
|
||||
- [x] Tilgangskontroll: eier/admin kan sette deltaker til "viewer" (kun observere)
|
||||
|
|
@ -229,7 +213,7 @@ Lydmixeren aktiveres via `mixer`-traitet på en samlings-node. Krever at
|
|||
- [x] Fat bottom (lowshelf filter, +8dB @ 200Hz)
|
||||
- [x] Sparkle (highshelf filter, +4dB @ 10kHz)
|
||||
- [x] Exciter (WaveShaperNode soft-clip + highshelf @ 3.5kHz)
|
||||
- [x] Per-kanal av/på-toggles for hver effekt (synkronisert via STDB `active_effects`)
|
||||
- [x] Per-kanal av/på-toggles for hver effekt (synkronisert via PG `active_effects`)
|
||||
- [x] Preset-konfigurasjon: "Av", "Podcast-stemme" (bass+luft), "Radio-stemme" (bass+luft+exciter)
|
||||
- [x] Highpass-filter (80Hz) alltid aktiv for rumble-fjerning
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
Et enkelt notatverktøy med automatisk lagring. Brukes som scratchpad i ulike kontekster — show notes, møtenotater, research-notater. Notater er nodes i kunnskapsgrafen og kan kobles til andre noder.
|
||||
|
||||
## 2. Status
|
||||
**PG-adapter ferdig og deployet (mars 2025).** Rich text og SpacetimeDB-sync gjenstår.
|
||||
**PG-adapter ferdig og deployet (mars 2025).** Rich text gjenstår.
|
||||
|
||||
### Implementert
|
||||
- Migrering `0004_notes.sql`: `notes`-tabell (FK→nodes)
|
||||
|
|
@ -21,7 +21,6 @@ Et enkelt notatverktøy med automatisk lagring. Brukes som scratchpad i ulike ko
|
|||
- Versjonering / undo-historikk
|
||||
- Kobling til andre noder (temaer, episoder, aktører)
|
||||
- Flerbruker-redigering (conflict resolution)
|
||||
- SpacetimeDB-modul + hybrid-adapter
|
||||
- Eksport (Markdown, PDF)
|
||||
|
||||
## 3. Datamodell (implementert)
|
||||
|
|
|
|||
|
|
@ -168,33 +168,10 @@ med forklaring ved inkompatibilitet.
|
|||
Factory-funksjon `createBlockReceiver(toolType)` oppretter en `BlockReceiver`
|
||||
for en gitt verktøy-type.
|
||||
|
||||
## 5. SpacetimeDB-integrasjon
|
||||
## 5. Sanntidsintegrasjon
|
||||
|
||||
Plasseringsdata for sanntidskontekster (storyboard, kanban) eies av SpacetimeDB:
|
||||
|
||||
```rust
|
||||
#[table(name = message_placement, public)]
|
||||
pub struct MessagePlacement {
|
||||
#[primary_key]
|
||||
pub id: String,
|
||||
pub message_id: String,
|
||||
pub context_type: String,
|
||||
pub context_id: String,
|
||||
pub entered_at: Timestamp,
|
||||
pub position_json: String, // JSON-serialisert posisjon
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn place_message(ctx: &ReducerContext, placement: MessagePlacement) { ... }
|
||||
|
||||
#[reducer]
|
||||
pub fn remove_placement(ctx: &ReducerContext, message_id: String, context_type: String, context_id: String) { ... }
|
||||
|
||||
#[reducer]
|
||||
pub fn move_on_canvas(ctx: &ReducerContext, placement_id: String, new_position_json: String) { ... }
|
||||
```
|
||||
|
||||
Sync-workeren persisterer til PG `message_placements`-tabellen.
|
||||
Plasseringsdata lagres i PG `message_placements`-tabellen. Endringer
|
||||
propageres via LISTEN/NOTIFY + WebSocket for sanntidsoppdatering i frontend.
|
||||
|
||||
## 6. Responsivt design
|
||||
|
||||
|
|
@ -207,7 +184,7 @@ Sync-workeren persisterer til PG `message_placements`-tabellen.
|
|||
- **Meldingsboks** (`meldingsboks.md`): Alle overførte objekter er meldingsbokser
|
||||
- **Kunnskapsgraf** (`kunnskapsgraf_og_relasjoner.md`): Plasseringer er relasjoner i grafen
|
||||
- **BlockShell** / PageGrid: Verktøy-panel-rammen som rendrer mottakssoner
|
||||
- **SpacetimeDB** (`synkronisering.md`): Sanntidssynk av plasseringer
|
||||
- **PG LISTEN/NOTIFY + WebSocket**: Sanntidssynk av plasseringer
|
||||
|
||||
## 8. Konsekvenser for eksisterende kode
|
||||
|
||||
|
|
@ -222,9 +199,9 @@ Sync-workeren persisterer til PG `message_placements`-tabellen.
|
|||
|
||||
`message_placements` er ny. Eksisterende `kanban_card_view` og `calendar_event_view` lever parallelt inntil migrering.
|
||||
|
||||
### 8.3 SpacetimeDB-modul
|
||||
### 8.3 Sanntid
|
||||
|
||||
Ny tabell `message_placement` med reducers for place/remove/move.
|
||||
PG NOTIFY-trigger på `message_placements` for sanntidspropagering via WebSocket.
|
||||
|
||||
## 9. Implementasjonsstatus
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ En interaktiv graf-visning i SvelteKit som gjør Kunnskapsgrafen visuelt naviger
|
|||
|
||||
## 3. Datakilde
|
||||
* **SvelteKit server-side:** Henter grafdata via Recursive CTE-spørringer mot PostgreSQL og returnerer `{ "nodes": [...], "edges": [...] }` til klienten.
|
||||
* **SpacetimeDB:** Brukes ikke for graf-visualisering — dette er historiske data som lever i PostgreSQL.
|
||||
* Graf-visualisering er basert på historiske data som lever i PostgreSQL.
|
||||
|
||||
## 4. Instruks for Claude Code
|
||||
* Design JSON-responsen slik at den lett kan mates inn i graf-visualiseringsbiblioteker (D3.js, Vis.js).
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Et delt, sanntids tegnebrett for frihåndsskisser, diagrammer og visuell brainst
|
|||
| **Personlig** | Kun brukeren selv | Privat skisse, kan deles til et Tema senere |
|
||||
|
||||
## 3. Sanntidssynkronisering
|
||||
* **SpacetimeDB** synkroniserer strøk (penseltype, farge, koordinater) mellom alle deltakere i sanntid.
|
||||
* PG LISTEN/NOTIFY + WebSocket synkroniserer strøk (penseltype, farge, koordinater) mellom alle deltakere i sanntid.
|
||||
* Hvert whiteboard har en unik ID og er en node i grafen.
|
||||
* Tilgangskontroll følger konteksten: møterom-deltakere, tema-medlemmer, eller kun eieren for personlige tavler.
|
||||
|
||||
|
|
@ -23,12 +23,11 @@ Et delt, sanntids tegnebrett for frihåndsskisser, diagrammer og visuell brainst
|
|||
* **Implementering:** HTML Canvas eller SVG i SvelteKit. Vurder et lett bibliotek (f.eks. tldraw, Excalidraw) hvis frihåndskvaliteten krever det — men foretrekk egenutviklet for å holde avhengigheter nede.
|
||||
|
||||
## 5. Lagring og Eksport
|
||||
* **Sanntidsdata (SpacetimeDB):** Strøk-historikk holdes i minnet så lenge tavlen er aktiv.
|
||||
* **Sanntidsdata:** Strøk-historikk lagres i PG og propageres via WebSocket.
|
||||
* **Eksport (PostgreSQL + filsystem):** Når tavlen "lukkes" eller deles, rendres den til PNG eller SVG og lagres som en `media_file`. Referansen knyttes til konteksten (melding, møte) via `message_attachments` eller tilsvarende.
|
||||
* **Dataklassifisering:** Strøk-data i SpacetimeDB er flyktig (kategori 4). Eksporterte bilder er avledet (kategori 3) — kan gjenskapes fra strøk-data så lenge tavlen er aktiv, men etter lukking er bildet den permanente kopien.
|
||||
|
||||
## 6. Instruks for Claude Code
|
||||
* Whiteboard-komponenten skal være en gjenbrukbar Svelte-komponent som kan mountes i møterom, chat og som frittstående side.
|
||||
* SpacetimeDB-tabellen for strøk bør være enkel: `whiteboard_id`, `stroke_data` (JSON), `user_id`, `timestamp`.
|
||||
* PG-tabellen for strøk bør være enkel: `whiteboard_id`, `stroke_data` (JSON), `user_id`, `timestamp`. NOTIFY-trigger for sanntid.
|
||||
* Ikke bygg et fullverdig tegneprogram — start med frihåndstegning, viskelær og farger. Utvid ved behov.
|
||||
* Tilgang til whiteboards styres via `node_access`-matrisen.
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ Oppdater en spec-node.
|
|||
}
|
||||
```
|
||||
|
||||
Oppdaterer noden i STDB + PG. Logger endringen i `ai_usage_log`
|
||||
Oppdaterer noden i PG. Logger endringen i `ai_usage_log`
|
||||
med `job_type: 'spec_update'`. Oppretter `revision`-node med
|
||||
forrige versjon for historikk.
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ Send et svar i en samtale (erstatter dagens direkte STDB-skriving).
|
|||
}
|
||||
```
|
||||
|
||||
Maskinrommet håndterer node-opprettelse, edges, og STDB-synk.
|
||||
Maskinrommet håndterer node-opprettelse og edges.
|
||||
Metadata kan inkludere kildereferanser som frontend kan vise.
|
||||
|
||||
#### `POST /agent/suggest_edges`
|
||||
|
|
@ -144,7 +144,7 @@ godkjenner. Over en viss confidence-terskel kan de auto-godkjennes.
|
|||
## Autentisering
|
||||
|
||||
Agent-endepunktene bruker agent-token fra `agent_identities`-tabellen.
|
||||
Samme token som brukes for STDB-tilkobling. Header:
|
||||
Header:
|
||||
|
||||
```
|
||||
Authorization: Bearer <agent_token>
|
||||
|
|
@ -161,7 +161,7 @@ Med Agent API endres flyten:
|
|||
|
||||
### Før (nåværende)
|
||||
```
|
||||
melding → agent_respond-jobb → bygg prompt (SQL) → claude -p → parse svar → skriv STDB+PG
|
||||
melding → agent_respond-jobb → bygg prompt (SQL) → claude -p → parse svar → skriv PG
|
||||
```
|
||||
|
||||
### Etter (med Agent API)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Maskinrommet eier alle skrivinger: det validerer, skriver til PG,
|
|||
og orkestrerer konsekvenser.
|
||||
|
||||
Sanntid: PG LISTEN/NOTIFY → maskinrommet → WebSocket `/ws` → frontend.
|
||||
SpacetimeDB er under utfasing — frontend bruker kun portvokterens WebSocket (Fase M2).
|
||||
Frontend bruker portvokterens WebSocket for sanntid.
|
||||
Tunge spørringer (søk, statistikk, graftraversering) går via maskinrommet → PG.
|
||||
|
||||
## 2. Kommunikasjonskart
|
||||
|
|
@ -57,7 +57,7 @@ Tunge spørringer (søk, statistikk, graftraversering) går via maskinrommet →
|
|||
## 5. Implementerte endepunkter
|
||||
|
||||
### Offentlige
|
||||
- `GET /health` — Helsesjekk. Verifiserer PG- og STDB-tilkobling.
|
||||
- `GET /health` — Helsesjekk. Verifiserer PG-tilkobling.
|
||||
|
||||
### WebSocket (sanntid, oppgave 22.1–22.2)
|
||||
- `GET /ws?token=<JWT>` — WebSocket-oppgradering for sanntidsstrøm.
|
||||
|
|
@ -73,8 +73,8 @@ Tunge spørringer (søk, statistikk, graftraversering) går via maskinrommet →
|
|||
|
||||
### Autentiserte (krever `Authorization: Bearer <JWT>`)
|
||||
- `GET /me` — Returnerer autentisert brukers `node_id` og `authentik_sub`.
|
||||
- `POST /intentions/create_node` — Opprett node. Skriv til STDB (instant),
|
||||
spawn async PG-skriving, returner `node_id` umiddelbart.
|
||||
- `POST /intentions/create_node` — Opprett node. Skriv til PG,
|
||||
returner `node_id` umiddelbart.
|
||||
- Body (JSON): `{ node_kind?, title?, content?, visibility?, metadata? }`
|
||||
- Defaults: `node_kind="content"`, `visibility="hidden"`, andre felter tomme
|
||||
- Respons: `{ node_id: "<uuid>" }`
|
||||
|
|
@ -110,12 +110,12 @@ Tunge spørringer (søk, statistikk, graftraversering) går via maskinrommet →
|
|||
### LiveKit / Sanntidslyd (oppgave 11.2)
|
||||
- `POST /intentions/join_communication` — Koble til sanntidslyd i en kommunikasjonsnode.
|
||||
Validerer deltaker-tilgang (owner/member_of/host_of-edge eller via alias).
|
||||
Genererer LiveKit access token (JWT), oppretter rom i STDB, oppdaterer node-metadata.
|
||||
Genererer LiveKit access token (JWT), oppdaterer node-metadata.
|
||||
- Body (JSON): `{ communication_id, role? }` (role: "publisher" | "subscriber", default "publisher")
|
||||
- Respons: `{ livekit_room_name, livekit_token, livekit_url, identity, participants[] }`
|
||||
- Frontend bruker `livekit_token` + `livekit_url` til å koble livekit-client SDK.
|
||||
- `POST /intentions/leave_communication` — Forlat sanntidsrom.
|
||||
Fjerner deltaker fra STDB live-rom.
|
||||
Fjerner deltaker fra live-rom.
|
||||
- Body (JSON): `{ communication_id }`
|
||||
- Respons: `{ status: "left" }`
|
||||
- `POST /intentions/close_communication` — Steng sanntidsrom (krever owner/admin).
|
||||
|
|
@ -124,7 +124,7 @@ Tunge spørringer (søk, statistikk, graftraversering) går via maskinrommet →
|
|||
- Respons: `{ status: "closed" }`
|
||||
|
||||
### Mixer-kanaler (oppgave 22.2)
|
||||
Erstatter SpacetimeDB-reducers for delt mixer-tilstand i LiveKit-rom.
|
||||
Delt mixer-tilstand i LiveKit-rom.
|
||||
Skriver til PG `mixer_channels`-tabell; NOTIFY-trigger propagerer til WS.
|
||||
- `POST /intentions/create_mixer_channel` — Opprett mixer-kanal for deltaker i rom.
|
||||
- Body: `{ room_id, target_user_id }`
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
**Filsti:** `docs/infra/backup.md`
|
||||
|
||||
Synops sin backup-strategi bygger på én innsikt: **PostgreSQL er den
|
||||
eneste autoriteten.** SpacetimeDB er en sanntidscache som gjenoppbygges
|
||||
fra PG ved behov. Media-filer i CAS er innholdsadresserte og immutable.
|
||||
eneste autoriteten.** Media-filer i CAS er innholdsadresserte og immutable.
|
||||
|
||||
## Arkitektur
|
||||
|
||||
|
|
@ -15,8 +14,7 @@ PostgreSQL (autoritativ kilde)
|
|||
│ └──→ /srv/synops/backup/pg/sidelinja_YYYYMMDD_HHMMSS.dump
|
||||
│ └──→ Rotasjon: 30 dager
|
||||
│
|
||||
└──→ SpacetimeDB (sanntidscache)
|
||||
└──→ Gjenoppbygges fra PG ved krasj (warmup)
|
||||
└──→ Sanntid via PG LISTEN/NOTIFY → WebSocket
|
||||
```
|
||||
|
||||
## 1. PG-dump (daglig)
|
||||
|
|
@ -44,28 +42,10 @@ docker exec sidelinja-postgres-1 pg_restore --list /tmp/test.dump
|
|||
docker exec sidelinja-postgres-1 rm /tmp/test.dump
|
||||
```
|
||||
|
||||
## 2. STDB-gjenoppbygging ved krasj
|
||||
## 2. Sanntid
|
||||
|
||||
**Modul:** `maskinrommet/src/stdb_monitor.rs`
|
||||
|
||||
SpacetimeDB er en sanntidscache. Hvis den krasjer, tapes ingen data
|
||||
fordi all skriving går gjennom maskinrommet som skriver til PG først
|
||||
(asynkront, men alltid). Gjenoppbygging skjer automatisk:
|
||||
|
||||
### Ved oppstart
|
||||
Maskinrommet kjører `warmup::run()` i `main.rs` — laster alle noder,
|
||||
edges og node_access fra PG til STDB.
|
||||
|
||||
### Ved krasj under drift
|
||||
`stdb_monitor` kjører i bakgrunnen og sjekker STDB hvert 30. sekund:
|
||||
|
||||
1. **Oppdager** at STDB ikke svarer (var oppe, nå nede)
|
||||
2. **Venter** opptil 10 minutter på at containeren restarter
|
||||
3. **Kjører warmup** (PG → STDB) når STDB svarer igjen
|
||||
4. **Logger** hele hendelsesforløpet
|
||||
|
||||
Prosessen er automatisk og krever ingen manuell inngripen så lenge
|
||||
Docker restarter containeren (restart-policy: `unless-stopped`).
|
||||
Sanntid leveres via PG LISTEN/NOTIFY + WebSocket i portvokteren.
|
||||
Ingen separat sanntidstjeneste å gjenoppbygge — PG er eneste datakilde.
|
||||
|
||||
## 3. Restore fra backup
|
||||
|
||||
|
|
@ -79,7 +59,7 @@ docker cp /srv/synops/backup/pg/sidelinja_YYYYMMDD.dump sidelinja-postgres-1:/tm
|
|||
docker exec sidelinja-postgres-1 pg_restore -U sidelinja -d sidelinja --clean /tmp/restore.dump
|
||||
docker exec sidelinja-postgres-1 rm /tmp/restore.dump
|
||||
|
||||
# Start maskinrommet (warmup laster PG → STDB automatisk)
|
||||
# Start maskinrommet
|
||||
sudo systemctl start maskinrommet
|
||||
```
|
||||
|
||||
|
|
@ -88,7 +68,7 @@ Ved total serversvikt (ny VPS):
|
|||
1. Installer OS og Docker (se `docs/setup/produksjon.md`)
|
||||
2. Start PG-container
|
||||
3. Restore dump (se over)
|
||||
4. Start maskinrommet (warmup håndterer STDB)
|
||||
4. Start maskinrommet
|
||||
5. Avledede data (segmenter, søkeindeks) regenereres fra kildene
|
||||
|
||||
## 4. Overvåking
|
||||
|
|
@ -98,12 +78,11 @@ Health-dashboardet (`/admin/health`) viser backup-status:
|
|||
- **stale** — dump-fil er eldre enn 25 timer
|
||||
- **missing** — ingen dump-filer funnet
|
||||
|
||||
Metrikk-endepunktet (`/metrics`) inkluderer STDB-status som del av
|
||||
Metrikk-endepunktet (`/metrics`) inkluderer tjeneste-status som del av
|
||||
helsesjekken.
|
||||
|
||||
## 5. Hva som IKKE backupes (bevisst)
|
||||
|
||||
- **SpacetimeDB** — sanntidscache, gjenoppbygges fra PG
|
||||
- **Redis** — cache, regenereres automatisk
|
||||
- **Caddy-data** — sertifikater regenereres av Let's Encrypt
|
||||
- **Whisper-modeller** — re-download fra HuggingFace
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ Bruker sender melding (via frontend)
|
|||
→ kaller: claude -p "<prompt>" --output-format json
|
||||
→ oppretter svar-node i PG, logger ai_usage + resource_usage
|
||||
→ returnerer JSON med reply_node_id + response_text
|
||||
→ maskinrommet: skriver til STDB (sanntidsvisning)
|
||||
→ frontend viser melding i sanntid via STDB WebSocket
|
||||
→ PG NOTIFY propagerer til frontend via WebSocket
|
||||
→ frontend viser melding i sanntid
|
||||
```
|
||||
|
||||
Ansvarsdeling (unix-filosofi):
|
||||
- **Maskinrommet:** Auth, kill switch, rate limiting, loop-prevensjon, STDB-skriving
|
||||
- **Maskinrommet:** Auth, kill switch, rate limiting, loop-prevensjon
|
||||
- **synops-respond:** Kontekst-henting, prompt-bygging, claude-kall, PG-skriving
|
||||
|
||||
Latens: ~3-5 sekunder fra melding til svar.
|
||||
|
|
|
|||
|
|
@ -53,17 +53,17 @@ CLI-verktøy (`Command::new("synops-X")`) som gjør selve jobben.
|
|||
- Payload-parsing og validering
|
||||
- Sikkerhetskontroller (kill switch, rate limiting, loop-prevensjon for agent)
|
||||
- Voice/model-oppslag fra node metadata (orchestrator-logikk)
|
||||
- STDB-synk etter CLI fullført (sanntidsvisning)
|
||||
- PG NOTIFY etter CLI fullført (sanntidsvisning via WebSocket)
|
||||
- Jobbstatus-håndtering (complete/fail/retry)
|
||||
|
||||
| Jobbtype | CLI-verktøy | Maskinrommet beholder |
|
||||
|---|---|---|
|
||||
| `whisper_transcribe` | `synops-transcribe` | — |
|
||||
| `agent_respond` | `synops-respond` | Kill switch, rate limit, loop-prevensjon, STDB-synk |
|
||||
| `agent_respond` | `synops-respond` | Kill switch, rate limit, loop-prevensjon |
|
||||
| `suggest_edges` | `synops-suggest-edges` | — |
|
||||
| `summarize_communication` | `synops-summarize` | — |
|
||||
| `tts_generate` | `synops-tts` | Voice-oppslag fra node metadata, STDB-synk |
|
||||
| `audio_process` | `synops-audio` | STDB-synk |
|
||||
| `tts_generate` | `synops-tts` | Voice-oppslag fra node metadata |
|
||||
| `audio_process` | `synops-audio` | — |
|
||||
| `render_article` | `synops-render` | — |
|
||||
| `render_index` | `synops-render` | — |
|
||||
| `ai_process` | *(inline — mangler CLI)* | Alt (planlagt migrasjon) |
|
||||
|
|
@ -223,7 +223,7 @@ tabell med alle felter, retry/avbryt-knapper. Poller hvert 5. sekund.
|
|||
|
||||
**Migrasjon:** `014_resource_governor.sql` — `job_priority_rules` + `disk_status_log`
|
||||
|
||||
- Valgfritt: SpacetimeDB-event ved statusendring slik at UI kan vise fremdrift i sanntid (f.eks. "Transkriberer... 2/3 forsøk")
|
||||
- Jobbstatus-endringer propageres til frontend via PG NOTIFY → WebSocket (f.eks. "Transkriberer... 2/3 forsøk")
|
||||
|
||||
## 8. Instruks for Claude Code
|
||||
- Én binær: `sidelinja-worker`. Én Rust-crate med polling-loop + handler-dispatch
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ kan den prosessere ubesvarte meldinger.
|
|||
|
||||
Portvokteren eksponerer `/health` som sjekker:
|
||||
- PG-tilkobling
|
||||
- STDB-tilkobling
|
||||
- LiteLLM-tilgjengelighet
|
||||
- Disk-status
|
||||
|
||||
|
|
@ -107,7 +106,7 @@ En `reader` kan spørre `@bot` om informasjon, men ikke trigge
|
|||
| Alle LLM-er nede | synops-respond exit != 0 | Statisk "utilgjengelig" + work_item | Vet at meldingen er mottatt |
|
||||
| Portvokteren nede | Systemd healthcheck → restart | CLI fungerer for Claude Code | Web-brukere venter, terminal funker |
|
||||
| PG nede | Connection refused | Alt stopper | Eneste reelle SPOF |
|
||||
| STDB nede | Reconnect-loop | PG-fallback for lesing, skriving bufres | Sanntid borte, data trygt |
|
||||
| WebSocket nede | Portvokteren restarter | Frontend rekobler automatisk | Sanntid midlertidig borte, data trygt |
|
||||
|
||||
## PG som eneste SPOF
|
||||
|
||||
|
|
@ -118,7 +117,6 @@ gjenoppbygging fra backup er veldokumentert
|
|||
|
||||
Mitigering:
|
||||
- Automatisk PG-dump (se oppgave 12.2)
|
||||
- STDB kan gjenoppbygges fra PG
|
||||
- `synops-snapshot` sikrer at docs-fallback finnes i repo
|
||||
- CAS-filer er uavhengig av PG (filsystem)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
# Synkronisering: SpacetimeDB ↔ PostgreSQL
|
||||
|
||||
> **UTGÅTT (mars 2026).** SpacetimeDB er fjernet fra prosjektet.
|
||||
> Sanntid leveres nå via PG LISTEN/NOTIFY + WebSocket i portvokteren.
|
||||
> Se `docs/infra/api_grensesnitt.md` for gjeldende arkitektur.
|
||||
> Dokumentet beholdes som historisk referanse.
|
||||
|
||||
## Konsept
|
||||
|
||||
SpacetimeDB holder hele grafen (alle noder og edges) i minne.
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ Fravær av en trait betyr at funksjonaliteten er deaktivert. Ingen boolean
|
|||
|---|---|---|
|
||||
| `editor` | TipTap med presets (longform, note, chat, code) | Validering av dokumentstruktur |
|
||||
| `versioning` | Revisjonshistorikk, diff, rollback-knapp | Snapshot ved signifikante endringer |
|
||||
| `collaboration` | Samtidig redigering, markører, inline-kommentarer | OT/CRDT via STDB |
|
||||
| `collaboration` | Samtidig redigering, markører, inline-kommentarer | OT/CRDT via WebSocket |
|
||||
| `translation` | Språkvelger, side-ved-side-visning | AI-oversettelse via jobbkø |
|
||||
| `templates` | Mal-velger ved ny node | Mal-noder i samlingen |
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ Fravær av en trait betyr at funksjonaliteten er deaktivert. Ingen boolean
|
|||
|
||||
| Trait | Frontend | Backend |
|
||||
|---|---|---|
|
||||
| `chat` | Sanntidsmeldinger, tråder, reaksjoner | STDB-synk, TTL-håndtering |
|
||||
| `chat` | Sanntidsmeldinger, tråder, reaksjoner | WebSocket-synk, TTL-håndtering |
|
||||
| `forum` | Trådet diskusjon, sortering (nyeste/populære/ubesvarte) | Tråd-indeksering |
|
||||
| `comments` | Kommentarfelt under publisert innhold | Moderasjonskø, evt. anonym input |
|
||||
| `guest_input` | Gjeste-lenke-generering, svar-oversikt | Token-generering, CAS-upload, rate limiting |
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ Når en idé modnes nok til å bli implementert, skrives en full spec i `docs/fe
|
|||
| [Podcast Time Machine](podcast_time_machine.md) | Lav | Høy | Segmenter, Caddy byte-range, Live AI |
|
||||
| [Meme Generator](meme_generator.md) | Lav | Høy | Whiteboard, transkripsjon, AI Gateway |
|
||||
| [Valgomat Roast](valgomat_roast.md) | Lav | Middels | Valgomat, kunnskapsgraf |
|
||||
| [Live Audience Q&A](live_audience_qa.md) | Middels | Høy | Valgomat, LiveKit, SpacetimeDB |
|
||||
| [Live Audience Q&A](live_audience_qa.md) | Middels | Høy | Valgomat, LiveKit, WebSocket |
|
||||
| [Guest Prep Simulator](guest_prep_simulator.md) | Middels | Høy | Kunnskapsgraf, AI Gateway |
|
||||
| [Debate Club](debate_club.md) | Middels | Middels | Kunnskapsgraf, AI Gateway, jobbkø |
|
||||
| [Ghost Host TTS](ghost_host_tts.md) | Stor | Høy | LiveKit, AI Gateway, ny TTS-infra |
|
||||
|
|
@ -48,7 +48,7 @@ Når en idé modnes nok til å bli implementert, skrives en full spec i `docs/fe
|
|||
| [Flow Meter](flow_meter.md) | Lav | Middels | Storyboard |
|
||||
| [Emotion Tags](emotion_tags.md) | Lav | Middels | Meldingsboks, kanban, storyboard |
|
||||
| **Samarbeid** | | | |
|
||||
| [Collaborative Cursors](collaborative_cursors.md) | Lav | Middels | SpacetimeDB, Svelte |
|
||||
| [Collaborative Cursors](collaborative_cursors.md) | Lav | Middels | WebSocket, Svelte |
|
||||
| [Card Heat Map](card_heat_map.md) | Lav | Middels | Meldingsboks, kanban/storyboard |
|
||||
|
||||
**Forfremmet til feature:** [Meldingsboks](../features/meldingsboks.md) — universell diskusjonsprimitiv. [Artikkel-publisering](artikkel_publisering.md) → Fase 14 / `docs/concepts/publisering.md`. [Tekst-primitiv](tekst_primitiv.md) — realisert i nodearkitekturen.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
# Komponerbare sider (Dashboard-komposisjon)
|
||||
|
||||
> **Superseded:** Konseptet er videreført og utvidet i retningen
|
||||
> "Arbeidsflaten" (`docs/retninger/arbeidsflaten.md`). Den spatial
|
||||
> workspace-modellen med frie verktøy-paneler og drag-and-drop erstatter
|
||||
> grid-baserte dashboard-komposisjoner. Dokumentet er bevart som
|
||||
> historisk referanse — mye av tenkningen rundt blokker, resize og
|
||||
> maximize er videreført i arbeidsflate-retningen.
|
||||
|
||||
## Idé
|
||||
Brukere ser ferdige sider (Redaksjonen, Studioet, etc.), men admin kan komponere egne sider fra tilgjengelige byggeklosser — chat, kanban, statistikk, graf-visning, whiteboard, osv.
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ andre dokumenter. En retning kan også forkastes eller parkeres.
|
|||
| [Universell input og mottak](universell_input.md) | **Besluttet** | Én multimodal input-primitiv, én mottaksflate, kommunikasjonsnoder. Edges definerer alt. |
|
||||
| [Maskinrommet](maskinrommet.md) | **Besluttet** | Én Rust-tjeneste: fang, prosesser, lever. Eier all skriving. Edge-drevet ressursorkestrering. |
|
||||
| [Noder er sentrum](bruker_ikke_workspace.md) | **Besluttet** | Alt er noder (brukere, team, innhold). Edges definerer relasjoner og tilgang. Materialisert tilgangsmatrise for RLS. |
|
||||
| [Datalaget](datalaget.md) | **Revidert** | PG er eneste datakilde. Sanntid via LISTEN/NOTIFY + WebSocket. SpacetimeDB fases ut. CAS for binærdata, AGE ved behov. |
|
||||
| [Datalaget](datalaget.md) | **Revidert** | PG er eneste datakilde. Sanntid via LISTEN/NOTIFY + WebSocket. CAS for binærdata, AGE ved behov. |
|
||||
| [Arbeidsflaten](arbeidsflaten.md) | **Besluttet** | Spatial canvas med verktøy-paneler. Drag-and-drop skaper nye noder med edges. |
|
||||
| [Unix-filosofi](unix_filosofi.md) | **Besluttet** | Maskinrommet orkestrerer, CLI-verktøy gjør jobben. Claude deler verktøykasse. |
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ spesifikk samling.
|
|||
- **Navigasjon:** Tilgjengelig via "Min flate"-knapp på mottak, og i
|
||||
kontekst-velger-dropdown på samlingssider
|
||||
- **Provisjonering:** Backend oppretter workspace-node + owner-edge ved
|
||||
første forespørsel. STDB for instant synk, async PG for persistens.
|
||||
første forespørsel. PG-skriving med NOTIFY for sanntidsoppdatering.
|
||||
|
||||
## `source_material`-edge
|
||||
|
||||
|
|
@ -185,8 +185,9 @@ Gir artikler sporbar lineage: du kan alltid se *hvor* materialet kom fra.
|
|||
- **[Noder er sentrum](bruker_ikke_workspace.md):** Verktøy er visninger
|
||||
av grafen. Arbeidsflaten er brukerens personlige arrangement av disse
|
||||
visningene.
|
||||
- **[Datalaget](datalaget.md):** SpacetimeDB driver sanntidsoppdatering
|
||||
mellom paneler. Drag-and-drop oppretter noder/edges som synkes instant.
|
||||
- **[Datalaget](datalaget.md):** PG LISTEN/NOTIFY + WebSocket driver
|
||||
sanntidsoppdatering mellom paneler. Drag-and-drop oppretter noder/edges
|
||||
som propageres via sanntidsstrømmen.
|
||||
|
||||
## Hva ville vært annerledes
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
# Datalaget
|
||||
|
||||
**Status: Besluttet. Revidert mars 2026 — SpacetimeDB fases ut.**
|
||||
**Status: Besluttet. Revidert mars 2026 — SpacetimeDB fjernet.**
|
||||
|
||||
> PostgreSQL er eneste datakilde. Sanntid via PG `LISTEN/NOTIFY`
|
||||
> og WebSocket i portvokteren. CAS lagrer binærdata.
|
||||
> Apache AGE legges til ved behov for Cypher-traverseringer.
|
||||
> SpacetimeDB fases ut — se migrasjonsplan.
|
||||
|
||||
## Lagmodell
|
||||
|
||||
|
|
@ -105,61 +104,25 @@ Portvokteren holder:
|
|||
- Map av node_id → synlige noder (fra node_access)
|
||||
- Ved NOTIFY: filtrer på tilgang, push til relevante klienter
|
||||
|
||||
Enklere enn STDB-synk: én retning (PG → klient), ingen
|
||||
reducer-logikk, ingen konsistensproblemer.
|
||||
Én retning (PG → klient), ingen reducer-logikk, ingen
|
||||
konsistensproblemer.
|
||||
|
||||
## Hvorfor SpacetimeDB fases ut
|
||||
## Historikk: SpacetimeDB (fjernet mars 2026)
|
||||
|
||||
SpacetimeDB var et godt eksperiment. Det løste sanntid elegant
|
||||
i prototype-fasen. Men for produksjon på én server:
|
||||
SpacetimeDB ble brukt som sanntidslag i v1/prototype. Det løste
|
||||
sanntid elegant, men for produksjon på én server skapte det
|
||||
unødvendig kompleksitet:
|
||||
|
||||
- **Synk-kompleksitet.** PG ↔ STDB synk er en egen feilkategori.
|
||||
Erfaringsdocs (adapter_moenster.md, spacetimedb_integrasjon.md)
|
||||
dokumenterer smerten.
|
||||
- **Dobbelt vedlikehold.** STDB-modul med reducers må holdes i
|
||||
synk med PG-skjema. Endring i nodes-tabellen → to steder.
|
||||
- **Ekstra SPOF.** Enda en tjeneste å overvåke, restarte, debugge.
|
||||
- **Synk-kompleksitet.** PG ↔ STDB synk var en egen feilkategori.
|
||||
- **Dobbelt vedlikehold.** STDB-modul med reducers måtte holdes i
|
||||
synk med PG-skjema.
|
||||
- **Ekstra SPOF.** Enda en tjeneste å overvåke og restarte.
|
||||
- **Unødvendig for skalaen.** PG LISTEN/NOTIFY + WebSocket gir
|
||||
~5ms latency. STDB ga ~0.01ms. Forskjellen er umerkelig for
|
||||
brukere.
|
||||
- **CLI-verktøy forenkles.** Bare PG-tilkobling, ingen STDB-klient.
|
||||
~5ms latency — umerkelig forskjell for brukere.
|
||||
|
||||
## Migrasjonsplan: STDB → PG LISTEN/NOTIFY
|
||||
|
||||
### Fase M1: WebSocket-lag i portvokteren ✅
|
||||
Implementert LISTEN/NOTIFY-lytter og WebSocket-endepunkt i
|
||||
portvokteren. PG-triggers for nodes, edges og access.
|
||||
Frontend koblet til begge (STDB + nytt WS) i parallell.
|
||||
|
||||
### Fase M2: Frontend-migrering ✅
|
||||
Frontend bruker nå kun portvokterens WebSocket. SpacetimeDB-klient
|
||||
fjernet. Reactive stores oppdateres direkte fra WS-meldinger.
|
||||
Berikede events: portvokteren henter full raddata fra PG etter
|
||||
NOTIFY (ikke bare ID) slik at stores kan oppdateres uten ekstra
|
||||
API-kall. Mixer-kanaler migrert fra STDB til PG-tabell med
|
||||
tilhørende NOTIFY-trigger og HTTP API-endepunkter.
|
||||
|
||||
### Fase M3: Fjern skrivestien til STDB ✅
|
||||
Portvokteren skriver kun til PG. STDB-skrivestien er fjernet.
|
||||
Alle intensjoner (create/update/delete node/edge) skriver
|
||||
synkront til PG. NOTIFY-triggere er eneste push-mekanisme.
|
||||
Warmup (PG→STDB) og STDB-monitor er fjernet. StdbClient er
|
||||
fjernet fra AppState. Job-handlere (agent, audio, tts, ai_process)
|
||||
synker ikke lenger til STDB — PG NOTIFY dekker sanntid.
|
||||
|
||||
### Fase M4: Fjern STDB
|
||||
- Stopp SpacetimeDB Docker-container
|
||||
- Fjern STDB-modul (spacetimedb/)
|
||||
- Fjern STDB-klient fra portvokteren
|
||||
- Fjern STDB-avhengigheter fra frontend
|
||||
- Fjern synkroniserings-kode
|
||||
- Oppdater docs (synkronisering.md → arkiver)
|
||||
- Oppdater CLAUDE.md
|
||||
|
||||
### Fase M5: Opprydding
|
||||
- Slett erfaringsdocs som kun gjelder STDB
|
||||
- Oppdater alle docs-referanser til STDB
|
||||
- Fjern Docker-konfig for SpacetimeDB
|
||||
SpacetimeDB ble faset ut i fire steg: WebSocket-lag, frontend-
|
||||
migrering, fjern skrivestien, fjern alt. Se erfaringsdocs for
|
||||
lærdommer: `docs/erfaringer/spacetimedb_integrasjon.md`.
|
||||
|
||||
## Forhold til andre retninger
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ Lever resultat i riktig modalitet til riktig mottaker:
|
|||
- Lyd (TTS-opplesning, lydstream)
|
||||
- Video/bilde (stream, thumbnail)
|
||||
- Strukturert data (noder, edges tilbake i grafen)
|
||||
- Push (SpacetimeDB-reducer)
|
||||
- Push (WebSocket via PG LISTEN/NOTIFY)
|
||||
|
||||
## Maskinrommet eier all skriving
|
||||
|
||||
|
|
@ -41,9 +41,9 @@ Frontend sender intensjoner. Maskinrommet utfører.
|
|||
```
|
||||
Frontend: "legg til Trond i møtet"
|
||||
→ Maskinrommet validerer
|
||||
→ Skriver edge til SpacetimeDB (instant)
|
||||
→ Skriver edge til PG
|
||||
→ Oppdaterer tilgangsmatrise
|
||||
→ Persisterer til PG (asynk)
|
||||
→ PG NOTIFY → WebSocket → frontend oppdateres i sanntid
|
||||
→ Reagerer på konsekvensene (koble inn LiveKit, starte transkripsjon)
|
||||
```
|
||||
|
||||
|
|
@ -51,8 +51,7 @@ Alt i én operasjon. Maskinrommet er ikke reaktivt i en pub/sub-
|
|||
forstand — det orkestrerer hele sekvensen. Enklere å forstå,
|
||||
enklere å debugge.
|
||||
|
||||
Skrivestien: validering → SpacetimeDB (instant) → PG (asynk).
|
||||
Se [synkronisering](../infra/synkronisering.md).
|
||||
Skrivestien: validering → PG → NOTIFY → WebSocket (sanntid).
|
||||
|
||||
## Edge-drevet ressursorkestrering
|
||||
|
||||
|
|
@ -185,13 +184,13 @@ Compute-separasjon er en konfigurasjon, ikke en arkitekturendring.
|
|||
## Evolusjon: Maskinrommet → Portvokteren
|
||||
|
||||
Maskinrommet ble bygget som en monolitt — auth, validering,
|
||||
prosessering, jobbkø, STDB-synk i én binær. Med unix-filosofi-
|
||||
prosessering, jobbkø i én binær. Med unix-filosofi-
|
||||
retningen (se `docs/retninger/unix_filosofi.md`) flyttes all
|
||||
prosessering til CLI-verktøy. Det som blir igjen er:
|
||||
|
||||
1. **Auth** — JWT-validering, "hvem er denne requesten?"
|
||||
2. **HTTP-ruting** — frontend → riktig CLI-verktøy
|
||||
3. **STDB-synk** — push PG-endringer til SpacetimeDB
|
||||
3. **Sanntid** — PG LISTEN/NOTIFY → WebSocket til frontend
|
||||
4. **Jobbkø-dispatch** — poll PG, spawn CLI-verktøy
|
||||
|
||||
Dette er en **portvokter**, ikke et maskinrom. Når uttynningen er
|
||||
|
|
@ -219,5 +218,5 @@ Maskinrommet er infrastrukturen *under* de tre primitivene i
|
|||
|
||||
- [Noder er sentrum](bruker_ikke_workspace.md) — maskinrommet
|
||||
eier tilgangsmatrise-oppdatering
|
||||
- [Datalaget](datalaget.md) — maskinrommet skriver SpacetimeDB
|
||||
først, PG asynk
|
||||
- [Datalaget](datalaget.md) — maskinrommet skriver PG,
|
||||
sanntid via LISTEN/NOTIFY + WebSocket
|
||||
|
|
|
|||
|
|
@ -6,15 +6,14 @@
|
|||
## Observasjoner
|
||||
|
||||
Sidelinja har vokst organisk. Vi har bygget chat, kanban, kalender, notater,
|
||||
kunnskapsgraf — hver som sin feature, hver med sin spec. SpacetimeDB ble lagt til
|
||||
for sanntid, men arkitekturen er fortsatt "PostgreSQL-app med sanntidskrydder."
|
||||
kunnskapsgraf — hver som sin feature, hver med sin spec. Sanntid ble lagt til,
|
||||
men arkitekturen var fortsatt "PostgreSQL-app med sanntidskrydder."
|
||||
|
||||
Resultatet:
|
||||
- **Forum-følelsen.** Ting er organisert i tråder, kort, lister. Brukeren
|
||||
navigerer mellom sider. Det føles som et tradisjonelt verktøy med litt polish.
|
||||
- **Databasespenning.** PG og SpacetimeDB har et komplisert eierforhold.
|
||||
SpacetimeDB-loven løser grensesnittet, men ikke det underliggende spørsmålet:
|
||||
hva *er* primæropplevelsen?
|
||||
- **Arkitekturspenning.** Spørsmålet om hva *primæropplevelsen* er
|
||||
— sanntid eller tradisjonell webapp — forblir åpent.
|
||||
- **Feature-fragmentering.** Chat, kanban, whiteboard, notater — hver lever i sin
|
||||
boks. "Universell overføring" og "meldingsboks" prøver å lime dem sammen, men
|
||||
utgangspunktet er fortsatt separate primitiver.
|
||||
|
|
@ -34,34 +33,22 @@ Forskjellen er subtil men fundamental:
|
|||
|
||||
## Hva ville vært annerledes?
|
||||
|
||||
### SpacetimeDB som verden, PG som arkiv
|
||||
SpacetimeDB er ikke en "sanntidskache foran PG" — det er verdenen brukerne
|
||||
lever i. PG er arkivet som husker hva som har skjedd.
|
||||
### Sanntidslaget og arkivet
|
||||
PG er eneste datakilde. Sanntid leveres via PG LISTEN/NOTIFY + WebSocket
|
||||
i portvokteren. Arkivet og sanntidslaget er ikke to separate systemer —
|
||||
PG er begge deler.
|
||||
|
||||
Rollene blir klare og adskilte:
|
||||
- **SpacetimeDB** = sanntidslaget. Aktivt samarbeid, live interaksjon,
|
||||
ting som skjer *nå*.
|
||||
- **PostgreSQL** = arkivet. Alt som noensinne har skjedd. Søk, historikk,
|
||||
statistikk, revisjon.
|
||||
Ikke alt trenger sanntid. En kunnskapsgraf-utforsker, et søk i gamle
|
||||
episoder, en statistikkside, en offentlig publisert artikkel — disse
|
||||
bruker tradisjonelle API-kall mot PG. Sanntidsstrømmen er for det som
|
||||
er levende: chat, whiteboard, live redigering.
|
||||
|
||||
Men viktig: sanntidslaget er *bare* et sanntidslag. Ikke alt trenger
|
||||
sanntid. En kunnskapsgraf-utforsker, et søk i gamle episoder, en
|
||||
statistikkside, en offentlig publisert artikkel — disse snakker rett med
|
||||
PG-arkivet som tradisjonelle nettsider. De trenger ikke gå gjennom
|
||||
SpacetimeDB. Begge lagene leser og skriver til det samme arkivet.
|
||||
|
||||
Dermed har vi to parallelle overflater:
|
||||
- **Sanntidsopplevelsen** (via SpacetimeDB) — for alt som er levende,
|
||||
aktivt, samarbeidende. Chat, whiteboard, live redigering.
|
||||
- **Tradisjonelt lag** (rett mot PG) — for alt som er retrospektivt,
|
||||
To overflater, én datakilde:
|
||||
- **Sanntidsopplevelsen** (via WebSocket) — for alt som er levende,
|
||||
aktivt, samarbeidende.
|
||||
- **Tradisjonelt lag** (API-kall mot PG) — for alt som er retrospektivt,
|
||||
utforskende, statisk. Arkiv, søk, publisering, statistikk.
|
||||
|
||||
Dataflyt mellom dem: ting som oppstår i sanntidslaget synkes til PG.
|
||||
Ting i PG kan løftes inn i sanntidslaget når de blir aktive igjen.
|
||||
Men det er ingen eierskapskonflikt — de to lagene har fundamentalt
|
||||
forskjellige roller. Det er ikke to konkurrerende sannheter, det er
|
||||
*nåtid* og *arkiv*, med to overflater som passer til hver sin rolle.
|
||||
|
||||
### Rommet som primitiv, ikke siden
|
||||
I dag navigerer brukeren mellom `/chat`, `/kanban`, `/kalender`. I "rom"-modellen
|
||||
er brukeren alltid *et sted*, og funksjonalitet er lag som kan slås av og på:
|
||||
|
|
@ -121,22 +108,17 @@ systemet tar vare på det riktig sted basert på kontekst og synlighet.
|
|||
Input-metode (tekst, voice, tegning) og synlighet (privat, delt, publisert)
|
||||
er ortogonale egenskaper. Ingen av dem bør diktere *hva* innholdet blir.
|
||||
|
||||
### SpacetimeDB som naturlig motor
|
||||
SpacetimeDB tenker "dette eksisterer i verden nå" — ikke "lagre dette i
|
||||
riktig tabell." Å trylle frem et whiteboard er naturlig i en verden-modell:
|
||||
det er bare et nytt objekt med en tilstand. I PG-modellen må du opprette
|
||||
rader, definere relasjoner, sette opp persistens. SpacetimeDB låner seg
|
||||
til flytende, formløs interaksjon på en måte PG ikke gjør.
|
||||
|
||||
PG briljerer i rollen som arkiv: relasjonelle spørringer over historikk,
|
||||
fulltekstsøk, pgvector for semantisk søk, aggregeringer og statistikk.
|
||||
Når du spør "hva snakket vi om i mars?" er det PG som svarer. Når du
|
||||
spør "hva skjer nå?" er det SpacetimeDB.
|
||||
### PG som eneste kilde, WebSocket som sanntidslag
|
||||
PG briljerer som både arkiv og sanntidskilde: relasjonelle spørringer
|
||||
over historikk, fulltekstsøk, pgvector for semantisk søk, aggregeringer
|
||||
og statistikk. LISTEN/NOTIFY + WebSocket gir sanntidsoppdatering uten
|
||||
et ekstra system. Å trylle frem et whiteboard er bare å opprette en node
|
||||
— WebSocket-strømmen sørger for at alle ser det umiddelbart.
|
||||
|
||||
## Spenninger og åpne spørsmål
|
||||
|
||||
- **Ytelse.** En alltid-på sanntidsopplevelse krever mer av både klient og
|
||||
server enn tradisjonelle sideinnlastinger. Er SpacetimeDB klar for dette?
|
||||
server enn tradisjonelle sideinnlastinger.
|
||||
- **Kompleksitet.** "Alt er et rom" høres elegant ut, men kan bli kaotisk.
|
||||
Hvordan unngår vi at det blir uoversiktlig?
|
||||
- **Discovery vs fokus.** "Alt kan bli hva som helst" er kraftig, men kan
|
||||
|
|
@ -146,9 +128,9 @@ spør "hva skjer nå?" er det SpacetimeDB.
|
|||
- **~~Gradvis overgang.~~** *(Løst — se "Innebygd utviklingsstrategi" nedenfor.)*
|
||||
- **Solo-bruk.** Mye av verdien i "rom" kommer fra å være der sammen. Hvordan
|
||||
føles det for én person som jobber alene?
|
||||
- **Er det vi allerede har?** Meldingsboks-konseptet, universell overføring og
|
||||
SpacetimeDB-loven peker allerede i denne retningen. Kanskje dette ikke er en
|
||||
ny retning, men en artikulering av det vi ubevisst har bygget mot?
|
||||
- **Er det vi allerede har?** Meldingsboks-konseptet og universell overføring
|
||||
peker allerede i denne retningen. Kanskje dette ikke er en ny retning,
|
||||
men en artikulering av det vi ubevisst har bygget mot?
|
||||
- **Inspirasjon.** Spillverdener (MMO-lobbyer, shared spaces), Figma (alle i
|
||||
samme canvas), tldraw, Gather.town. Hva kan vi lære fra disse?
|
||||
|
||||
|
|
@ -157,31 +139,24 @@ spør "hva skjer nå?" er det SpacetimeDB.
|
|||
To-lags-modellen gir en viktig implikasjon for utviklingen: **innebygd fallback
|
||||
og to veier inn til alt.**
|
||||
|
||||
Ny funksjonalitet kan alltid starte i det tradisjonelle laget — rett mot PG,
|
||||
vanlig request/response, kjent terreng. Den fungerer med en gang. Når det
|
||||
senere gir verdi (samarbeid, sanntid, live interaksjon) kan den løftes inn
|
||||
i sanntidslaget. Men den trenger ikke det for å være nyttig.
|
||||
Ny funksjonalitet kan alltid starte som tradisjonell request/response mot PG.
|
||||
Sanntidsoppdatering kommer gratis via LISTEN/NOTIFY + WebSocket — ingen
|
||||
separat "løfting" til et sanntidslag nødvendig.
|
||||
|
||||
Dette betyr:
|
||||
- **Ingenting vi har bygget er bortkastet.** Eksisterende PG-baserte features
|
||||
er ikke "gammel arkitektur" — de er det tradisjonelle laget, og det er et
|
||||
fullverdig lag.
|
||||
- **Ingen stor omskriving.** Retningen er ikke en migrasjon med en frist. Den
|
||||
er en utviklingsstrategi: bygg tradisjonelt, løft til sanntid ved behov.
|
||||
- **Risikoen er lav.** Hvis SpacetimeDB skuffer, har vi fortsatt et komplett
|
||||
tradisjonelt system. Hvis det leverer, får ting gradvis en rikere opplevelse.
|
||||
- **Primitivene er nøkkelen.** Så lenge meldingsboksen og kunnskapsgrafen er
|
||||
fleksible nok, kan begge lag bruke dem. Arkitekturen holder uavhengig av
|
||||
hvilket lag en feature lever i.
|
||||
er fullverdige og har sanntid via WebSocket.
|
||||
- **Ingen stor omskriving.** Retningen er en utviklingsstrategi: bygg mot PG,
|
||||
sanntid følger automatisk.
|
||||
- **Risikoen er lav.** PG er stabil og velprøvd.
|
||||
- **Primitivene er nøkkelen.** Så lenge noder, edges og kunnskapsgrafen er
|
||||
fleksible nok, holder arkitekturen uavhengig av kontekst.
|
||||
|
||||
## Kritisk vurdering
|
||||
|
||||
### SpacetimeDB er en ung teknologi
|
||||
Å lene seg tungt på SpacetimeDB er et veddemål. Hvis prosjektet stagnerer,
|
||||
endrer API, eller har skaleringstak vi ikke ser ennå — er vi eksponert.
|
||||
*Mildnet* av at det tradisjonelle laget mot PG alltid finnes som fallback:
|
||||
SpacetimeDB er ikke alt-eller-ingenting, men et lag for det som trenger
|
||||
sanntid. Resten lever trygt mot PG uansett.
|
||||
### Sanntidslaget er PG-basert
|
||||
SpacetimeDB ble fjernet (mars 2026). Sanntid leveres nå via PG
|
||||
LISTEN/NOTIFY + WebSocket. Én datakilde, ingen synkroniseringskompleksitet.
|
||||
|
||||
### "Formløs input" er vanskelig i praksis
|
||||
Det høres elegant ut, men noen må bestemme hva noe *blir*. AI? Brukeren
|
||||
|
|
@ -195,20 +170,17 @@ hverdagen.
|
|||
En samtale fra i går som fortsatt er relevant — er den "nå" eller "arkiv"?
|
||||
Et kanban-kort som har stått stille i to uker? Vi trenger regler for når ting
|
||||
flyttes mellom lagene, og de reglene kan bli like komplekse som
|
||||
SpacetimeDB-loven de erstatter. "Tid og arkiv" er renere *i prinsippet*,
|
||||
reglene de erstatter. "Tid og arkiv" er renere *i prinsippet*,
|
||||
men i praksis er "aktiv" et spektrum, ikke en binær tilstand.
|
||||
|
||||
### Solo-bruk er underadressert
|
||||
Vegard er primærbruker. "Rom"-konseptet henter mye av sin kraft fra
|
||||
tilstedeværelse og samarbeid. For én person som produserer podcast er det
|
||||
en reell risiko at sanntidslaget er overhead sammenlignet med et godt
|
||||
organisert tradisjonelt verktøy. *Mildnet* av to-lags-modellen: solo-bruk
|
||||
kan lene seg tyngre på det tradisjonelle PG-laget, og sanntidslaget
|
||||
aktiveres når det faktisk gir verdi (live innspilling, samarbeid).
|
||||
organisert tradisjonelt verktøy. *Mildnet* av at PG-basert sanntid har null overhead — WebSocket
|
||||
leverer oppdateringer uten ekstra lag å vedlikeholde.
|
||||
|
||||
### Omfanget
|
||||
*Betydelig mildnet* av utviklingsstrategien ovenfor. Retningen krever ikke
|
||||
en omskriving — den er kompatibel med inkrementell utvikling. Den
|
||||
gjenværende risikoen er mer subtil: at vi bruker mental energi på å
|
||||
vurdere "bør dette være sanntid?" for hver feature i stedet for å bare
|
||||
bygge. Pragmatisk default bør være: bygg tradisjonelt, løft senere.
|
||||
*Betydelig mildnet* av at sanntid nå er innebygd i arkitekturen via
|
||||
PG LISTEN/NOTIFY + WebSocket. Ingen vurdering "bør dette være sanntid?"
|
||||
nødvendig — alt som skrives til PG propageres automatisk.
|
||||
|
|
|
|||
|
|
@ -44,17 +44,17 @@ er form-basert og eksplisitt. Brukeren "administrerer innhold" mer enn
|
|||
de "jobber sammen i et miljø."
|
||||
|
||||
### Sanntid som tillegg
|
||||
SpacetimeDB er lagt til for å gi sanntidsoppdatering, men arkitekturen
|
||||
er PostgreSQL-først. Sanntid er noe som *skjer med* tradisjonelle
|
||||
operasjoner, ikke noe som er *grunnlaget* for opplevelsen.
|
||||
Sanntid (opprinnelig via SpacetimeDB, nå PG LISTEN/NOTIFY + WebSocket)
|
||||
er lagt til, men arkitekturen er PostgreSQL-først. Sanntid er noe som
|
||||
*skjer med* tradisjonelle operasjoner, ikke noe som er *grunnlaget*
|
||||
for opplevelsen.
|
||||
|
||||
## Spenninger
|
||||
|
||||
### To sannhetskilder
|
||||
PG og SpacetimeDB har et komplisert forhold. SpacetimeDB-loven definerer
|
||||
klare regler for hvem som eier hva, men selve eksistensen av loven vitner
|
||||
om en arkitektonisk spenning: vi har to systemer som begge vil være
|
||||
primærkilde, og vi bruker konvensjoner for å holde dem fra å kollidere.
|
||||
### To sannhetskilder (historisk, nå løst)
|
||||
PG og SpacetimeDB hadde et komplisert forhold. Denne spenningen er
|
||||
løst ved fjerning av SpacetimeDB (mars 2026) — PG er nå eneste
|
||||
datakilde, med sanntid via LISTEN/NOTIFY + WebSocket.
|
||||
|
||||
### Ambisiøs bunn, forsiktig topp
|
||||
Meldingsboksen og kunnskapsgrafen åpner for opplevelser vi ikke leverer
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ som knyttes til resultatet.
|
|||
### Én pipeline
|
||||
|
||||
All input går gjennom samme tekniske pipeline:
|
||||
maskinrommet → SpacetimeDB (instant) → PG (asynk).
|
||||
maskinrommet → PG → NOTIFY → WebSocket (sanntid).
|
||||
|
||||
Konteksten bestemmer routing, ikke en teknisk modus:
|
||||
|
||||
|
|
@ -260,8 +260,8 @@ Alle leser fra samme graf. Ingen har "sin egen" data.
|
|||
|
||||
- [Noder er sentrum](bruker_ikke_workspace.md) — visibility,
|
||||
tilgangsmatrise, aliaser
|
||||
- [Datalaget](datalaget.md) — SpacetimeDB holder hele grafen,
|
||||
PG persisterer asynkront
|
||||
- [Datalaget](datalaget.md) — PG er eneste datakilde,
|
||||
sanntid via LISTEN/NOTIFY + WebSocket
|
||||
- [Maskinrommet](maskinrommet.md) — validering, routing, CAS,
|
||||
tunge jobber (Whisper, TTS, AI)
|
||||
- [Rom, ikke forum](rom_ikke_forum.md) — kommunikasjonsnoden
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ Maskinrommet (Rust)
|
|||
│ └── ...
|
||||
│
|
||||
▼ direkte
|
||||
PG, STDB, CAS
|
||||
PG, CAS
|
||||
```
|
||||
|
||||
Claude har tilgang til hele `tools/`-katalogen og kan kjøre alt direkte:
|
||||
|
|
@ -109,7 +109,7 @@ Ikke en big-bang refaktor. Gradvis utbryting:
|
|||
|
||||
Kjernen som ikke bør brytes ut:
|
||||
- Auth-middleware (JWT-validering, node-oppslag)
|
||||
- Intentions (validering, STDB+PG-skriving, edge-logikk)
|
||||
- Intentions (validering, PG-skriving, edge-logikk)
|
||||
- Jobbkø (polling, retry, dead letter)
|
||||
- Tilgangskontroll (node_access, recompute_access)
|
||||
- Health-endepunkt
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
> produksjonsserveren via Claude Code. Dokumentet beholdes som referanse
|
||||
> i tilfelle lokalt utviklingsmiljø gjeninnføres.
|
||||
|
||||
Det lokale miljøet var et **utviklingsmiljø for kode**. Frontend (SvelteKit) ble kjørt lokalt med HMR, Rust ble bygd lokalt. Alle tjenester (PG, SpacetimeDB, AI Gateway, etc.) kjørte på produksjonsserveren — ingen lokal Docker-replika.
|
||||
Det lokale miljøet var et **utviklingsmiljø for kode**. Frontend (SvelteKit) ble kjørt lokalt med HMR, Rust ble bygd lokalt. Alle tjenester (PG, AI Gateway, etc.) kjørte på produksjonsserveren — ingen lokal Docker-replika.
|
||||
|
||||
## Hva som gjøres hvor
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ newgrp docker
|
|||
```bash
|
||||
sudo mkdir -p /srv/synops/{config,data,media,logs}
|
||||
sudo mkdir -p /srv/synops/config/{caddy,authentik}
|
||||
sudo mkdir -p /srv/synops/data/{postgres,spacetimedb,forgejo,authentik}
|
||||
sudo mkdir -p /srv/synops/data/{postgres,forgejo,authentik}
|
||||
sudo mkdir -p /srv/synops/data/whisper-models
|
||||
sudo mkdir -p /srv/synops/media/podcast
|
||||
sudo mkdir -p /srv/synops/logs/caddy
|
||||
|
|
@ -80,7 +80,6 @@ Resultat:
|
|||
│ └── authentik/
|
||||
├── data/
|
||||
│ ├── postgres/
|
||||
│ ├── spacetimedb/
|
||||
│ ├── forgejo/
|
||||
│ ├── whisper-models/
|
||||
│ └── authentik/
|
||||
|
|
@ -128,11 +127,6 @@ AUTHENTIK_ISSUER=https://auth.sidelinja.org/application/o/sidelinja/
|
|||
AUTHENTIK_CLIENT_ID=<fra Authentik OIDC-provider>
|
||||
AUTHENTIK_CLIENT_SECRET=<fra Authentik OIDC-provider>
|
||||
|
||||
# === SpacetimeDB ===
|
||||
SPACETIMEDB_URL=http://spacetimedb:3000
|
||||
SPACETIMEDB_DATABASE=synops
|
||||
SPACETIMEDB_TOKEN=<generert av spacetime identity token>
|
||||
|
||||
# === Whisper (STT) ===
|
||||
# Modell lastes ned automatisk ved oppstart. large-v3 gir best norsk kvalitet.
|
||||
# Ved GPU: bytt image til fedirz/faster-whisper-server:latest-cuda og WHISPER__COMPUTE_TYPE=float16
|
||||
|
|
@ -156,8 +150,7 @@ Tjenestene startes i rekkefølge fordi noen avhenger av andre. Alle defineres i
|
|||
5. **Forgejo:** Start med Authentik som OAuth2-provider, opprett organisasjon og repo
|
||||
|
||||
### Lag B: Sanntid (krever nettverk)
|
||||
6. **SpacetimeDB:** Start, verifiser tilkobling
|
||||
7. **LiveKit:** Start, verifiser at WebRTC fungerer
|
||||
6. **LiveKit:** Start, verifiser at WebRTC fungerer
|
||||
|
||||
### Lag C: Applikasjon (krever alt over)
|
||||
8. **SvelteKit:** Bygg og start container, verifiser at frontenden laster
|
||||
|
|
@ -187,7 +180,6 @@ services:
|
|||
postgres: # data:/srv/synops/data/postgres
|
||||
authentik: # SSO for alle domener, på auth.sidelinja.org
|
||||
forgejo: # data:/srv/synops/data/forgejo, på git.sidelinja.org
|
||||
spacetimedb: # data:/srv/synops/data/spacetimedb
|
||||
maskinrommet: # Rust/axum API, intern port 3100, proxyet via Caddy
|
||||
livekit: # Intern port, proxyet via Caddy
|
||||
sveltekit: # Intern port, proxyet via Caddy
|
||||
|
|
@ -205,11 +197,6 @@ auth.sidelinja.org {
|
|||
|
||||
# === Sidelinja (hovedapplikasjon) ===
|
||||
sidelinja.org {
|
||||
# SpacetimeDB (WebSocket) — handle_path stripper prefix
|
||||
handle_path /spacetime/* {
|
||||
reverse_proxy spacetimedb:3000
|
||||
}
|
||||
|
||||
# LiveKit signaling (WebSocket upgrade)
|
||||
handle_path /livekit/* {
|
||||
reverse_proxy livekit:7880
|
||||
|
|
@ -401,7 +388,6 @@ echo "$(date): Off-site backup ferdig" >> /srv/synops/logs/backup-offsite.log
|
|||
- **Avledede data i PG** (ren tekst, segmenter, søkeindeks) — regenereres fra Git
|
||||
- **Logger** — rulleres med logrotate, arkiveres separat ved behov
|
||||
- **Whisper-modeller** — re-download fra HuggingFace
|
||||
- **SpacetimeDB** — sanntidsdata synkes til PG, in-memory state er flyktig
|
||||
|
||||
### 11.8 Restore-prosedyre
|
||||
```bash
|
||||
|
|
@ -454,8 +440,7 @@ sudo journalctl -u maskinrommet -f
|
|||
```
|
||||
|
||||
Env-filen (`/tmp/maskinrommet.env`) genereres automatisk av
|
||||
`scripts/maskinrommet-env.sh` med Docker container-IPs for PG og STDB.
|
||||
Ved oppstart laster maskinrommet hele grafen fra PG inn i STDB (warmup).
|
||||
`scripts/maskinrommet-env.sh` med Docker container-IPs for PG.
|
||||
Caddy (Docker) proxyer `api.sidelinja.org` til `host.docker.internal:3100`.
|
||||
|
||||
Dockerfile (`maskinrommet/Dockerfile`) beholdes for referanse, men brukes
|
||||
|
|
@ -470,7 +455,7 @@ ikke i produksjon.
|
|||
- [ ] Git push til Forgejo fungerer
|
||||
|
||||
### Lag B-C
|
||||
- [x] `https://api.sidelinja.org/health` returnerer `{"status":"ok"}` med PG og STDB tilkoblet (verifisert 2026-03-17)
|
||||
- [x] `https://api.sidelinja.org/health` returnerer `{"status":"ok"}` med PG tilkoblet (verifisert 2026-03-17)
|
||||
- [x] `https://api.sidelinja.org/me` returnerer 401 uten token (verifisert 2026-03-17)
|
||||
- [x] `https://sidelinja.org` laster SvelteKit-appen (deployet 2025-03-15)
|
||||
- [x] `https://sidelinja.org/api/health` returnerer 200
|
||||
|
|
@ -478,6 +463,5 @@ ikke i produksjon.
|
|||
- [x] Chat: meldinger sendes og vises med riktig brukernavn (verifisert 2025-03-15)
|
||||
- [ ] `https://synops.no` viser placeholder
|
||||
- [ ] `https://vegard.info` svarer
|
||||
- [ ] SpacetimeDB: WebSocket-tilkobling fra nettleser fungerer
|
||||
- [x] LiveKit: Container kjører, signaling proxyet via Caddy (verifisert 2026-03-17)
|
||||
- [ ] Media: `curl -I https://sidelinja.org/media/podcast/test.mp3` returnerer `Accept-Ranges: bytes`
|
||||
|
|
|
|||
|
|
@ -7,7 +7,3 @@ AUTH_TRUST_HOST=true
|
|||
|
||||
# Maskinrommet API
|
||||
MASKINROMMET_URL=https://api.sidelinja.org
|
||||
|
||||
# SpacetimeDB (sanntids WebSocket-tilkobling)
|
||||
VITE_SPACETIMEDB_URL=wss://sidelinja.org/spacetime
|
||||
VITE_SPACETIMEDB_MODULE=synops
|
||||
|
|
|
|||
138
frontend/package-lock.json
generated
138
frontend/package-lock.json
generated
|
|
@ -18,7 +18,6 @@
|
|||
"@tiptap/starter-kit": "^3.20.4",
|
||||
"d3": "^7.9.0",
|
||||
"livekit-client": "^2.17.3",
|
||||
"spacetimedb": "^2.0.4",
|
||||
"wavesurfer.js": "^7.12.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -2205,26 +2204,6 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||
|
|
@ -2888,12 +2867,6 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/headers-polyfill": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz",
|
||||
"integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
|
|
@ -3357,18 +3330,6 @@
|
|||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/obug": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
||||
|
|
@ -3457,21 +3418,6 @@
|
|||
"preact": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-changeset": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.0.tgz",
|
||||
|
|
@ -3676,22 +3622,6 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz",
|
||||
"integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
|
|
@ -3812,15 +3742,6 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-stable-stringify": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
|
|
@ -3871,59 +3792,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spacetimedb": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/spacetimedb/-/spacetimedb-2.0.4.tgz",
|
||||
"integrity": "sha512-7GiZerC9SKXvHvcaOLCgLEjFL4XOcG30k5f/ogA9QqBuD+tO/om6DfhIplo5dUg976gfqxr3Hsp5XtY2pPSCKw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"headers-polyfill": "^4.0.3",
|
||||
"object-inspect": "^1.13.4",
|
||||
"prettier": "^3.3.3",
|
||||
"pure-rand": "^7.0.1",
|
||||
"safe-stable-stringify": "^2.5.0",
|
||||
"statuses": "^2.0.2",
|
||||
"url-polyfill": "^1.1.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=17.0.0",
|
||||
"@tanstack/react-query": "^5.0.0",
|
||||
"react": "^18.0.0 || ^19.0.0-0 || ^19.0.0",
|
||||
"svelte": "^4.0.0 || ^5.0.0",
|
||||
"undici": "^6.19.2",
|
||||
"vue": "^3.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@tanstack/react-query": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"svelte": {
|
||||
"optional": true
|
||||
},
|
||||
"undici": {
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
|
|
@ -4069,12 +3937,6 @@
|
|||
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/url-polyfill": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.14.tgz",
|
||||
"integrity": "sha512-p4f3TTAG6ADVF3mwbXw7hGw+QJyw5CnNGvYh5fCuQQZIiuKUswqcznyV3pGDP9j0TSmC4UvRKm8kl1QsX1diiQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
"@tiptap/starter-kit": "^3.20.4",
|
||||
"d3": "^7.9.0",
|
||||
"livekit-client": "^2.17.3",
|
||||
"spacetimedb": "^2.0.4",
|
||||
"wavesurfer.js": "^7.12.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -629,7 +629,7 @@ export interface CreateAnnouncementResponse {
|
|||
node_id: string;
|
||||
}
|
||||
|
||||
/** Opprett et systemvarsel (vises for alle klienter umiddelbart via STDB). */
|
||||
/** Opprett et systemvarsel (vises for alle klienter umiddelbart via WebSocket). */
|
||||
export function createAnnouncement(
|
||||
accessToken: string,
|
||||
data: CreateAnnouncementRequest
|
||||
|
|
@ -1283,7 +1283,7 @@ export async function fetchNodeUsage(
|
|||
}
|
||||
|
||||
// =============================================================================
|
||||
// Mixer-kanaler (oppgave 22.2 — erstatter STDB-reducers)
|
||||
// Mixer-kanaler
|
||||
// =============================================================================
|
||||
|
||||
export async function createMixerChannel(
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
let shareError = $state('');
|
||||
let shareSaving = $state(false);
|
||||
|
||||
// --- Derived: AI-preset noder fra STDB ---
|
||||
// --- Derived: AI-preset noder fra store ---
|
||||
const presets = $derived.by(() => {
|
||||
const all = nodeStore.byKind('ai_preset');
|
||||
return all.sort((a, b) => {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
collectionNode: Node | undefined;
|
||||
/** Current user's node ID */
|
||||
userId: string | undefined;
|
||||
/** Whether STDB is connected */
|
||||
/** Whether WebSocket is connected */
|
||||
connected: boolean;
|
||||
/** Active trait names on this collection */
|
||||
traitNames: string[];
|
||||
|
|
@ -259,7 +259,7 @@
|
|||
</div>
|
||||
|
||||
{#if connected}
|
||||
<span class="context-status context-status-ok" title="Tilkoblet SpacetimeDB">●</span>
|
||||
<span class="context-status context-status-ok" title="Tilkoblet sanntid">●</span>
|
||||
{:else}
|
||||
<span class="context-status" title="{connectionState.current}">●</span>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export interface Rect {
|
|||
height: number;
|
||||
}
|
||||
|
||||
/** Events emitted by the canvas for consumer integration (e.g. SpacetimeDB sync) */
|
||||
/** Events emitted by the canvas for consumer integration (e.g. WebSocket sync) */
|
||||
export interface CanvasEvents {
|
||||
onObjectMove?: (id: string, x: number, y: number) => void;
|
||||
onObjectResize?: (id: string, w: number, h: number) => void;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@
|
|||
});
|
||||
|
||||
// =========================================================================
|
||||
// Scheduled events from SpacetimeDB
|
||||
// Scheduled events from WebSocket store
|
||||
// =========================================================================
|
||||
|
||||
interface ScheduledEvent {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* WebSocket-tilkobling til portvokteren (maskinrommet).
|
||||
*
|
||||
* Erstatter SpacetimeDB-klient i Fase M2. Kobler til /ws-endepunktet,
|
||||
* Kobler til /ws-endepunktet,
|
||||
* mottar initial_sync og inkrementelle events, og oppdaterer reactive stores.
|
||||
*
|
||||
* Usage:
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
id: __t.string(),
|
||||
sourceId: __t.string(),
|
||||
targetId: __t.string(),
|
||||
edgeType: __t.string(),
|
||||
metadata: __t.string(),
|
||||
system: __t.bool(),
|
||||
createdBy: __t.string(),
|
||||
};
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
roomId: __t.string(),
|
||||
targetUserId: __t.string(),
|
||||
updatedBy: __t.string(),
|
||||
};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
id: __t.string(),
|
||||
nodeKind: __t.string(),
|
||||
title: __t.string(),
|
||||
content: __t.string(),
|
||||
visibility: __t.string(),
|
||||
metadata: __t.string(),
|
||||
createdBy: __t.string(),
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
id: __t.string(),
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
roomId: __t.string(),
|
||||
targetUserId: __t.string(),
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
subjectId: __t.string(),
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
subjectId: __t.string(),
|
||||
objectId: __t.string(),
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
id: __t.string(),
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default __t.row({
|
||||
id: __t.string().primaryKey(),
|
||||
sourceId: __t.string().name("source_id"),
|
||||
targetId: __t.string().name("target_id"),
|
||||
edgeType: __t.string().name("edge_type"),
|
||||
metadata: __t.string(),
|
||||
system: __t.bool(),
|
||||
createdAt: __t.timestamp().name("created_at"),
|
||||
createdBy: __t.string().name("created_by"),
|
||||
});
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
// This was generated using spacetimedb cli version 2.0.5 (commit d60138999206c06c776829072f46b5d1c1101f7e).
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
DbConnectionBuilder as __DbConnectionBuilder,
|
||||
DbConnectionImpl as __DbConnectionImpl,
|
||||
SubscriptionBuilderImpl as __SubscriptionBuilderImpl,
|
||||
TypeBuilder as __TypeBuilder,
|
||||
Uuid as __Uuid,
|
||||
convertToAccessorMap as __convertToAccessorMap,
|
||||
makeQueryBuilder as __makeQueryBuilder,
|
||||
procedureSchema as __procedureSchema,
|
||||
procedures as __procedures,
|
||||
reducerSchema as __reducerSchema,
|
||||
reducers as __reducers,
|
||||
schema as __schema,
|
||||
t as __t,
|
||||
table as __table,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type DbConnectionConfig as __DbConnectionConfig,
|
||||
type ErrorContextInterface as __ErrorContextInterface,
|
||||
type Event as __Event,
|
||||
type EventContextInterface as __EventContextInterface,
|
||||
type Infer as __Infer,
|
||||
type QueryBuilder as __QueryBuilder,
|
||||
type ReducerEventContextInterface as __ReducerEventContextInterface,
|
||||
type RemoteModule as __RemoteModule,
|
||||
type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,
|
||||
type SubscriptionHandleImpl as __SubscriptionHandleImpl,
|
||||
} from "spacetimedb";
|
||||
|
||||
// Import all reducer arg schemas
|
||||
import ClearAllReducer from "./clear_all_reducer";
|
||||
import CreateEdgeReducer from "./create_edge_reducer";
|
||||
import CreateNodeReducer from "./create_node_reducer";
|
||||
import DeleteEdgeReducer from "./delete_edge_reducer";
|
||||
import DeleteNodeReducer from "./delete_node_reducer";
|
||||
import DeleteNodeAccessReducer from "./delete_node_access_reducer";
|
||||
import DeleteNodeAccessForSubjectReducer from "./delete_node_access_for_subject_reducer";
|
||||
import UpdateEdgeReducer from "./update_edge_reducer";
|
||||
import UpdateNodeReducer from "./update_node_reducer";
|
||||
import UpsertNodeAccessReducer from "./upsert_node_access_reducer";
|
||||
import CreateMixerChannelReducer from "./create_mixer_channel_reducer";
|
||||
import SetGainReducer from "./set_gain_reducer";
|
||||
import SetMuteReducer from "./set_mute_reducer";
|
||||
import ToggleEffectReducer from "./toggle_effect_reducer";
|
||||
import DeleteMixerChannelReducer from "./delete_mixer_channel_reducer";
|
||||
import SetMixerRoleReducer from "./set_mixer_role_reducer";
|
||||
|
||||
// Import all procedure arg schemas
|
||||
|
||||
// Import all table schema definitions
|
||||
import EdgeRow from "./edge_table";
|
||||
import MixerChannelRow from "./mixer_channel_table";
|
||||
import NodeRow from "./node_table";
|
||||
import NodeAccessRow from "./node_access_table";
|
||||
|
||||
/** Type-only namespace exports for generated type groups. */
|
||||
|
||||
/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */
|
||||
const tablesSchema = __schema({
|
||||
edge: __table({
|
||||
name: 'edge',
|
||||
indexes: [
|
||||
{ accessor: 'id', name: 'edge_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'source_id', name: 'edge_source_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'sourceId',
|
||||
] },
|
||||
{ accessor: 'target_id', name: 'edge_target_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'targetId',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'edge_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, EdgeRow),
|
||||
mixer_channel: __table({
|
||||
name: 'mixer_channel',
|
||||
indexes: [
|
||||
{ accessor: 'id', name: 'mixer_channel_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'room_id', name: 'mixer_channel_room_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'roomId',
|
||||
] },
|
||||
{ accessor: 'target_user_id', name: 'mixer_channel_target_user_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'targetUserId',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'mixer_channel_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, MixerChannelRow),
|
||||
node: __table({
|
||||
name: 'node',
|
||||
indexes: [
|
||||
{ accessor: 'id', name: 'node_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'node_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, NodeRow),
|
||||
node_access: __table({
|
||||
name: 'node_access',
|
||||
indexes: [
|
||||
{ accessor: 'id', name: 'node_access_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'id',
|
||||
] },
|
||||
{ accessor: 'object_id', name: 'node_access_object_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'objectId',
|
||||
] },
|
||||
{ accessor: 'subject_id', name: 'node_access_subject_id_idx_btree', algorithm: 'btree', columns: [
|
||||
'subjectId',
|
||||
] },
|
||||
],
|
||||
constraints: [
|
||||
{ name: 'node_access_id_key', constraint: 'unique', columns: ['id'] },
|
||||
],
|
||||
}, NodeAccessRow),
|
||||
});
|
||||
|
||||
/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */
|
||||
const reducersSchema = __reducers(
|
||||
__reducerSchema("clear_all", ClearAllReducer),
|
||||
__reducerSchema("create_edge", CreateEdgeReducer),
|
||||
__reducerSchema("create_node", CreateNodeReducer),
|
||||
__reducerSchema("delete_edge", DeleteEdgeReducer),
|
||||
__reducerSchema("delete_node", DeleteNodeReducer),
|
||||
__reducerSchema("delete_node_access", DeleteNodeAccessReducer),
|
||||
__reducerSchema("delete_node_access_for_subject", DeleteNodeAccessForSubjectReducer),
|
||||
__reducerSchema("update_edge", UpdateEdgeReducer),
|
||||
__reducerSchema("update_node", UpdateNodeReducer),
|
||||
__reducerSchema("upsert_node_access", UpsertNodeAccessReducer),
|
||||
__reducerSchema("create_mixer_channel", CreateMixerChannelReducer),
|
||||
__reducerSchema("set_gain", SetGainReducer),
|
||||
__reducerSchema("set_mute", SetMuteReducer),
|
||||
__reducerSchema("toggle_effect", ToggleEffectReducer),
|
||||
__reducerSchema("delete_mixer_channel", DeleteMixerChannelReducer),
|
||||
__reducerSchema("set_mixer_role", SetMixerRoleReducer),
|
||||
);
|
||||
|
||||
/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */
|
||||
const proceduresSchema = __procedures(
|
||||
);
|
||||
|
||||
/** The remote SpacetimeDB module schema, both runtime and type information. */
|
||||
const REMOTE_MODULE = {
|
||||
versionInfo: {
|
||||
cliVersion: "2.0.5" as const,
|
||||
},
|
||||
tables: tablesSchema.schemaType.tables,
|
||||
reducers: reducersSchema.reducersType.reducers,
|
||||
...proceduresSchema,
|
||||
} satisfies __RemoteModule<
|
||||
typeof tablesSchema.schemaType,
|
||||
typeof reducersSchema.reducersType,
|
||||
typeof proceduresSchema
|
||||
>;
|
||||
|
||||
/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */
|
||||
export const tables: __QueryBuilder<typeof tablesSchema.schemaType> = __makeQueryBuilder(tablesSchema.schemaType);
|
||||
|
||||
/** The reducers available in this remote SpacetimeDB module. */
|
||||
export const reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);
|
||||
|
||||
/** The context type returned in callbacks for all possible events. */
|
||||
export type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The context type returned in callbacks for reducer events. */
|
||||
export type ReducerEventContext = __ReducerEventContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The context type returned in callbacks for subscription events. */
|
||||
export type SubscriptionEventContext = __SubscriptionEventContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The context type returned in callbacks for error events. */
|
||||
export type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;
|
||||
/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */
|
||||
export type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;
|
||||
|
||||
/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */
|
||||
export class SubscriptionBuilder extends __SubscriptionBuilderImpl<typeof REMOTE_MODULE> {}
|
||||
|
||||
/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */
|
||||
export class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}
|
||||
|
||||
/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */
|
||||
export class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {
|
||||
/** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */
|
||||
static builder = (): DbConnectionBuilder => {
|
||||
return new DbConnectionBuilder(REMOTE_MODULE, (config: __DbConnectionConfig<typeof REMOTE_MODULE>) => new DbConnection(config));
|
||||
};
|
||||
|
||||
/** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */
|
||||
override subscriptionBuilder = (): SubscriptionBuilder => {
|
||||
return new SubscriptionBuilder(this);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default __t.row({
|
||||
id: __t.string().primaryKey(),
|
||||
roomId: __t.string().name("room_id"),
|
||||
targetUserId: __t.string().name("target_user_id"),
|
||||
gain: __t.f64(),
|
||||
isMuted: __t.bool().name("is_muted"),
|
||||
activeEffects: __t.string().name("active_effects"),
|
||||
role: __t.string(),
|
||||
updatedBy: __t.string().name("updated_by"),
|
||||
updatedAt: __t.timestamp().name("updated_at"),
|
||||
});
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default __t.row({
|
||||
id: __t.string().primaryKey(),
|
||||
subjectId: __t.string().name("subject_id"),
|
||||
objectId: __t.string().name("object_id"),
|
||||
access: __t.string(),
|
||||
viaEdge: __t.string().name("via_edge"),
|
||||
});
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default __t.row({
|
||||
id: __t.string().primaryKey(),
|
||||
nodeKind: __t.string().name("node_kind"),
|
||||
title: __t.string(),
|
||||
content: __t.string(),
|
||||
visibility: __t.string(),
|
||||
metadata: __t.string(),
|
||||
createdAt: __t.timestamp().name("created_at"),
|
||||
createdBy: __t.string().name("created_by"),
|
||||
});
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
roomId: __t.string(),
|
||||
targetUserId: __t.string(),
|
||||
gain: __t.f64(),
|
||||
updatedBy: __t.string(),
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
roomId: __t.string(),
|
||||
targetUserId: __t.string(),
|
||||
role: __t.string(),
|
||||
updatedBy: __t.string(),
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
roomId: __t.string(),
|
||||
targetUserId: __t.string(),
|
||||
isMuted: __t.bool(),
|
||||
updatedBy: __t.string(),
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
roomId: __t.string(),
|
||||
targetUserId: __t.string(),
|
||||
effectName: __t.string(),
|
||||
updatedBy: __t.string(),
|
||||
};
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export const Edge = __t.object("Edge", {
|
||||
id: __t.string(),
|
||||
sourceId: __t.string(),
|
||||
targetId: __t.string(),
|
||||
edgeType: __t.string(),
|
||||
metadata: __t.string(),
|
||||
system: __t.bool(),
|
||||
createdAt: __t.timestamp(),
|
||||
createdBy: __t.string(),
|
||||
});
|
||||
export type Edge = __Infer<typeof Edge>;
|
||||
|
||||
export const Node = __t.object("Node", {
|
||||
id: __t.string(),
|
||||
nodeKind: __t.string(),
|
||||
title: __t.string(),
|
||||
content: __t.string(),
|
||||
visibility: __t.string(),
|
||||
metadata: __t.string(),
|
||||
createdAt: __t.timestamp(),
|
||||
createdBy: __t.string(),
|
||||
});
|
||||
export type Node = __Infer<typeof Node>;
|
||||
|
||||
export const MixerChannel = __t.object("MixerChannel", {
|
||||
id: __t.string(),
|
||||
roomId: __t.string(),
|
||||
targetUserId: __t.string(),
|
||||
gain: __t.f64(),
|
||||
isMuted: __t.bool(),
|
||||
activeEffects: __t.string(),
|
||||
role: __t.string(),
|
||||
updatedBy: __t.string(),
|
||||
updatedAt: __t.timestamp(),
|
||||
});
|
||||
export type MixerChannel = __Infer<typeof MixerChannel>;
|
||||
|
||||
export const NodeAccess = __t.object("NodeAccess", {
|
||||
id: __t.string(),
|
||||
subjectId: __t.string(),
|
||||
objectId: __t.string(),
|
||||
access: __t.string(),
|
||||
viaEdge: __t.string(),
|
||||
});
|
||||
export type NodeAccess = __Infer<typeof NodeAccess>;
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import { type Infer as __Infer } from "spacetimedb";
|
||||
|
||||
// Import all procedure arg schemas
|
||||
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import { type Infer as __Infer } from "spacetimedb";
|
||||
|
||||
// Import all reducer arg schemas
|
||||
import ClearAllReducer from "../clear_all_reducer";
|
||||
import CreateEdgeReducer from "../create_edge_reducer";
|
||||
import CreateNodeReducer from "../create_node_reducer";
|
||||
import DeleteEdgeReducer from "../delete_edge_reducer";
|
||||
import DeleteNodeReducer from "../delete_node_reducer";
|
||||
import DeleteNodeAccessReducer from "../delete_node_access_reducer";
|
||||
import DeleteNodeAccessForSubjectReducer from "../delete_node_access_for_subject_reducer";
|
||||
import UpdateEdgeReducer from "../update_edge_reducer";
|
||||
import UpdateNodeReducer from "../update_node_reducer";
|
||||
import UpsertNodeAccessReducer from "../upsert_node_access_reducer";
|
||||
import CreateMixerChannelReducer from "../create_mixer_channel_reducer";
|
||||
import SetGainReducer from "../set_gain_reducer";
|
||||
import SetMuteReducer from "../set_mute_reducer";
|
||||
import ToggleEffectReducer from "../toggle_effect_reducer";
|
||||
import DeleteMixerChannelReducer from "../delete_mixer_channel_reducer";
|
||||
import SetMixerRoleReducer from "../set_mixer_role_reducer";
|
||||
|
||||
export type ClearAllParams = __Infer<typeof ClearAllReducer>;
|
||||
export type CreateEdgeParams = __Infer<typeof CreateEdgeReducer>;
|
||||
export type CreateNodeParams = __Infer<typeof CreateNodeReducer>;
|
||||
export type DeleteEdgeParams = __Infer<typeof DeleteEdgeReducer>;
|
||||
export type DeleteNodeParams = __Infer<typeof DeleteNodeReducer>;
|
||||
export type DeleteNodeAccessParams = __Infer<typeof DeleteNodeAccessReducer>;
|
||||
export type DeleteNodeAccessForSubjectParams = __Infer<typeof DeleteNodeAccessForSubjectReducer>;
|
||||
export type UpdateEdgeParams = __Infer<typeof UpdateEdgeReducer>;
|
||||
export type UpdateNodeParams = __Infer<typeof UpdateNodeReducer>;
|
||||
export type UpsertNodeAccessParams = __Infer<typeof UpsertNodeAccessReducer>;
|
||||
export type CreateMixerChannelParams = __Infer<typeof CreateMixerChannelReducer>;
|
||||
export type SetGainParams = __Infer<typeof SetGainReducer>;
|
||||
export type SetMuteParams = __Infer<typeof SetMuteReducer>;
|
||||
export type ToggleEffectParams = __Infer<typeof ToggleEffectReducer>;
|
||||
export type DeleteMixerChannelParams = __Infer<typeof DeleteMixerChannelReducer>;
|
||||
export type SetMixerRoleParams = __Infer<typeof SetMixerRoleReducer>;
|
||||
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
id: __t.string(),
|
||||
edgeType: __t.string(),
|
||||
metadata: __t.string(),
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
id: __t.string(),
|
||||
nodeKind: __t.string(),
|
||||
title: __t.string(),
|
||||
content: __t.string(),
|
||||
visibility: __t.string(),
|
||||
metadata: __t.string(),
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
TypeBuilder as __TypeBuilder,
|
||||
t as __t,
|
||||
type AlgebraicTypeType as __AlgebraicTypeType,
|
||||
type Infer as __Infer,
|
||||
} from "spacetimedb";
|
||||
|
||||
export default {
|
||||
subjectId: __t.string(),
|
||||
objectId: __t.string(),
|
||||
access: __t.string(),
|
||||
viaEdge: __t.string(),
|
||||
};
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* Lokale type-definisjoner for noder, edges, access og mixer-kanaler.
|
||||
*
|
||||
* Erstatter SpacetimeDB module_bindings/types.ts.
|
||||
* Feltnavnene matcher JSON-formatet fra portvokterens WebSocket (camelCase).
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@
|
|||
return 'text-neutral-500';
|
||||
}
|
||||
|
||||
const allServices = ['maskinrommet', 'caddy', 'postgres', 'spacetimedb', 'authentik', 'litellm', 'whisper', 'livekit'];
|
||||
const allServices = ['maskinrommet', 'caddy', 'postgres', 'authentik', 'litellm', 'whisper', 'livekit'];
|
||||
</script>
|
||||
|
||||
<div class="min-h-screen bg-neutral-950 text-neutral-100 p-4 sm:p-8">
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@
|
|||
}
|
||||
|
||||
// =========================================================================
|
||||
// Scheduled events from SpacetimeDB
|
||||
// Scheduled events from WebSocket store
|
||||
// =========================================================================
|
||||
|
||||
interface ScheduledEvent {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
};
|
||||
|
||||
// =========================================================================
|
||||
// Kort fra SpacetimeDB — noder med submitted_to-edge til samlingen
|
||||
// Kort fra WebSocket store — noder med submitted_to-edge til samlingen
|
||||
// =========================================================================
|
||||
|
||||
interface CardData {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
const connected = $derived(connectionState.current === 'connected');
|
||||
const mediaNodeId = $derived($page.params.id ?? '');
|
||||
|
||||
// Media node from STDB
|
||||
// Media node from store
|
||||
const mediaNode = $derived(connected ? nodeStore.get(mediaNodeId) : undefined);
|
||||
const metadata = $derived.by(() => {
|
||||
if (!mediaNode?.metadata) return null;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
});
|
||||
});
|
||||
|
||||
// Also try to read layout from STDB node (for real-time sync)
|
||||
// Also try to read layout from WS node store (for real-time sync)
|
||||
const workspaceNode = $derived(
|
||||
workspaceNodeId && connected ? nodeStore.get(workspaceNodeId) : undefined
|
||||
);
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
let layout = $state<WorkspaceLayout>({ panels: [] });
|
||||
let layoutInitialized = $state(false);
|
||||
|
||||
// When workspace node appears in STDB (after creation), load its layout
|
||||
// When workspace node appears in store (after creation), load its layout
|
||||
$effect(() => {
|
||||
if (!workspaceNode || layoutInitialized) return;
|
||||
try {
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(async () => {
|
||||
try {
|
||||
// Read current metadata from STDB node
|
||||
// Read current metadata from node store
|
||||
const currentMeta = workspaceNode
|
||||
? JSON.parse(workspaceNode.metadata ?? '{}')
|
||||
: {};
|
||||
|
|
@ -460,7 +460,7 @@
|
|||
</div>
|
||||
|
||||
{#if connected}
|
||||
<span class="context-status context-status-ok" title="Tilkoblet SpacetimeDB">●</span>
|
||||
<span class="context-status context-status-ok" title="Tilkoblet sanntid">●</span>
|
||||
{:else}
|
||||
<span class="context-status" title="{connectionState.current}">●</span>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -758,8 +758,8 @@ async fn resolve_silence_cuts(
|
|||
|
||||
// ─── Jobbhåndterer — delegerer til synops-audio CLI ────────────────
|
||||
//
|
||||
// Maskinrommet beholder: STDB-synk for sanntidsvisning.
|
||||
// CLI gjør: FFmpeg-prosessering, CAS-lagring, PG-skriving, ressurslogging.
|
||||
// PG NOTIFY-triggere sender sanntidsoppdateringer.
|
||||
|
||||
/// Synops-audio binary path.
|
||||
fn audio_bin() -> String {
|
||||
|
|
@ -771,7 +771,7 @@ fn audio_bin() -> String {
|
|||
///
|
||||
/// Spawner synops-audio med --write for å gjøre alt arbeidet:
|
||||
/// FFmpeg-prosessering, CAS-lagring, PG-skriving, ressurslogging.
|
||||
/// Maskinrommet gjør etterpå STDB-synk for sanntidsvisning.
|
||||
/// PG NOTIFY-triggere sender sanntidsoppdateringer til klienter.
|
||||
///
|
||||
/// Payload:
|
||||
/// ```json
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Felles hjelpefunksjoner for å spawne CLI-verktøy fra jobbkø-handlere.
|
||||
//
|
||||
// Mønsteret: maskinrommet orkestrerer (payload-parsing, sikkerhetskontroller,
|
||||
// STDB-synk), CLI-verktøyet gjør jobben (API-kall, DB-skriving, prosessering).
|
||||
// Mønsteret: maskinrommet orkestrerer (payload-parsing, sikkerhetskontroller),
|
||||
// CLI-verktøyet gjør jobben (API-kall, DB-skriving, prosessering).
|
||||
// Stdout → jobbresultat (JSON), stderr → feillogg, exitkode → status.
|
||||
//
|
||||
// Ref: docs/retninger/unix_filosofi.md
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Serverhelse-dashboard — tjeneste-status, metrikker, backup-status, logg-tilgang.
|
||||
//
|
||||
// Sjekker alle tjenester i stacken (PG, STDB, Caddy, Authentik, LiteLLM,
|
||||
// Sjekker alle tjenester i stacken (PG, Caddy, Authentik, LiteLLM,
|
||||
// Whisper, LiveKit) og samler system-metrikker (CPU, minne, disk).
|
||||
//
|
||||
// Ref: docs/concepts/adminpanelet.md § 4 "Serverhelse", oppgave 15.6
|
||||
|
|
@ -373,10 +373,9 @@ fn read_service_logs(service: &str, max_lines: usize) -> Vec<LogEntry> {
|
|||
"maskinrommet" | "caddy" | "sveltekit" => {
|
||||
format!("journalctl -u {service} --no-pager -n {max_lines} --output=short-iso 2>/dev/null")
|
||||
}
|
||||
"postgres" | "spacetimedb" | "authentik" | "litellm" | "whisper" | "livekit" => {
|
||||
"postgres" | "authentik" | "litellm" | "whisper" | "livekit" => {
|
||||
let container = match service {
|
||||
"postgres" => "sidelinja-postgres-1",
|
||||
"spacetimedb" => "sidelinja-spacetimedb-1",
|
||||
"authentik" => "sidelinja-authentik-server-1",
|
||||
"litellm" => "sidelinja-ai-gateway-1",
|
||||
"whisper" => "sidelinja-faster-whisper-1",
|
||||
|
|
@ -473,7 +472,7 @@ pub async fn health_logs(
|
|||
read_service_logs(service, max_lines)
|
||||
} else {
|
||||
// Alle tjenester, siste linjer fra hver
|
||||
let services = ["maskinrommet", "caddy", "postgres", "spacetimedb", "authentik", "litellm", "whisper", "livekit"];
|
||||
let services = ["maskinrommet", "caddy", "postgres", "authentik", "litellm", "whisper", "livekit"];
|
||||
let per_service = (max_lines / services.len()).max(10);
|
||||
let mut all = Vec::new();
|
||||
for svc in &services {
|
||||
|
|
|
|||
|
|
@ -457,8 +457,7 @@ pub struct CreateNodeResponse {
|
|||
|
||||
/// POST /intentions/create_node
|
||||
///
|
||||
/// Validerer input, skriver til STDB (instant), spawner async PG-skriving.
|
||||
/// Returnerer node_id umiddelbart.
|
||||
/// Validerer input, skriver til PG. NOTIFY-triggere sender sanntidsoppdateringer.
|
||||
///
|
||||
/// Hvis `context_id` er satt, opprettes automatisk en `belongs_to`-edge
|
||||
/// fra den nye noden til kontekstnoden. Kontekstnoden må eksistere og
|
||||
|
|
@ -1179,7 +1178,7 @@ pub struct DeleteNodeResponse {
|
|||
|
||||
/// POST /intentions/delete_node
|
||||
///
|
||||
/// Sletter en node og alle dens edges (CASCADE i PG, eksplisitt i STDB).
|
||||
/// Sletter en node og alle dens edges (CASCADE i PG).
|
||||
/// Krever at brukeren er created_by eller har owner/admin-edge til noden.
|
||||
pub async fn delete_node(
|
||||
State(state): State<AppState>,
|
||||
|
|
@ -3264,7 +3263,7 @@ pub struct RoomParticipantInfo {
|
|||
///
|
||||
/// Kobler en bruker til sanntidslyd i en kommunikasjonsnode.
|
||||
/// Validerer tilgang (bruker må ha member_of/owner/host_of-edge),
|
||||
/// genererer LiveKit-token, oppdaterer STDB med live-status.
|
||||
/// genererer LiveKit-token, oppdaterer PG med live-status.
|
||||
pub async fn join_communication(
|
||||
State(state): State<AppState>,
|
||||
user: AuthUser,
|
||||
|
|
@ -3521,7 +3520,7 @@ pub struct CloseCommunicationResponse {
|
|||
/// POST /intentions/close_communication
|
||||
///
|
||||
/// Stenger et sanntidsrom. Krever owner/admin-tilgang.
|
||||
/// Fjerner alle deltakere fra STDB, oppdaterer metadata til "ended".
|
||||
/// Oppdaterer metadata til "ended".
|
||||
pub async fn close_communication(
|
||||
State(state): State<AppState>,
|
||||
user: AuthUser,
|
||||
|
|
@ -3734,7 +3733,7 @@ pub struct CreateAnnouncementResponse {
|
|||
/// POST /intentions/create_announcement
|
||||
///
|
||||
/// Oppretter en systemvarslingsnode med `visibility: open` slik at alle
|
||||
/// aktive klienter ser varselet via STDB umiddelbart.
|
||||
/// aktive klienter ser varselet via WebSocket umiddelbart.
|
||||
///
|
||||
/// Kun autentiserte brukere kan opprette varsler (MVP — full admin-sjekk
|
||||
/// legges til når admin-rollesystemet er på plass).
|
||||
|
|
@ -3817,8 +3816,8 @@ pub struct ExpireAnnouncementResponse {
|
|||
|
||||
/// POST /intentions/expire_announcement
|
||||
///
|
||||
/// Fjerner (sletter) en systemvarslingsnode. Sletter fra STDB først
|
||||
/// (umiddelbar fjerning fra alle klienter), deretter fra PG.
|
||||
/// Fjerner (sletter) en systemvarslingsnode fra PG.
|
||||
/// WebSocket NOTIFY sørger for umiddelbar fjerning fra alle klienter.
|
||||
///
|
||||
/// Kun eier (created_by) eller admin kan fjerne varsler.
|
||||
pub async fn expire_announcement(
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ pub mod resource_usage;
|
|||
pub mod resources;
|
||||
mod rss;
|
||||
mod serving;
|
||||
#[allow(dead_code)]
|
||||
mod stdb; // Beholdt som død kode — fjernes i oppgave 22.4
|
||||
pub mod summarize;
|
||||
pub mod ws;
|
||||
pub mod mixer;
|
||||
|
|
@ -257,7 +255,7 @@ async fn main() {
|
|||
.route("/custom-domain/sok", get(custom_domain::serve_custom_domain_search))
|
||||
.route("/custom-domain/om", get(custom_domain::serve_custom_domain_about))
|
||||
.route("/custom-domain/{article_id}", get(custom_domain::serve_custom_domain_article))
|
||||
// Mixer-kanaler (oppgave 22.2 — erstatter STDB-reducers)
|
||||
// Mixer-kanaler
|
||||
.route("/intentions/create_mixer_channel", post(mixer::create_mixer_channel))
|
||||
.route("/intentions/set_gain", post(mixer::set_gain))
|
||||
.route("/intentions/set_mute", post(mixer::set_mute))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
//! Mixer-kanaler — HTTP API for delt lydmixer-tilstand.
|
||||
//!
|
||||
//! Erstatter SpacetimeDB-reducers for mixer (createMixerChannel, setGain,
|
||||
//! setMute, toggleEffect, setMixerRole). Skriver direkte til PG;
|
||||
//! NOTIFY-trigger propagerer endringer til WebSocket-klienter.
|
||||
//!
|
||||
//! Ref: oppgave 22.2 (SpacetimeDB-migrering)
|
||||
//! Skriver direkte til PG; NOTIFY-trigger propagerer endringer til
|
||||
//! WebSocket-klienter.
|
||||
|
||||
use axum::{extract::State, http::StatusCode, Json};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ fn edge_type_to_access_level(edge_type: &str) -> Option<&'static str> {
|
|||
/// Handler: pg_insert_edge
|
||||
///
|
||||
/// Håndterer tilgangsgivende edges (owner/admin/member_of/reader) med
|
||||
/// recompute_access i transaksjon, og synker til STDB. For belongs_to-edges
|
||||
/// recompute_access i transaksjon. For belongs_to-edges
|
||||
/// trigges artikkelrendering hvis target er en publiseringssamling.
|
||||
pub async fn handle_insert_edge(
|
||||
job: &JobRow,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Tunge spørringer — lesestien via PostgreSQL med RLS.
|
||||
//
|
||||
// For søk, statistikk, og graf-traversering brukes PG direkte (ikke STDB).
|
||||
// For søk, statistikk, og graf-traversering brukes PG direkte.
|
||||
// Alle spørringer kjøres med SET LOCAL ROLE synops_reader, som er underlagt
|
||||
// RLS-policies. Brukerens node_id settes som sesjonsvariabel.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,466 +0,0 @@
|
|||
// SpacetimeDB HTTP-klient for maskinrommet.
|
||||
//
|
||||
// Kaller STDB-reducere via HTTP JSON API.
|
||||
// Maskinrommet eier all skriving — denne klienten er eneste vei inn.
|
||||
//
|
||||
// API-format: POST /v1/database/{db}/call/{reducer}
|
||||
// Body: JSON-objekt med navngitte parametre.
|
||||
// Auth: Bearer-token fra STDB-identitet.
|
||||
//
|
||||
// Ref: docs/retninger/datalaget.md, docs/infra/synkronisering.md
|
||||
|
||||
use reqwest::Client;
|
||||
use serde::Serialize;
|
||||
|
||||
/// SpacetimeDB-klient som kaller reducere via HTTP.
|
||||
#[derive(Clone)]
|
||||
pub struct StdbClient {
|
||||
client: Client,
|
||||
base_url: String,
|
||||
database: String,
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl StdbClient {
|
||||
/// Opprett ny klient. `base_url` er STDB-serverens URL (f.eks. "http://spacetimedb:3000").
|
||||
pub fn new(base_url: &str, database: &str, token: &str) -> Self {
|
||||
Self {
|
||||
client: Client::new(),
|
||||
base_url: base_url.trim_end_matches('/').to_string(),
|
||||
database: database.to_string(),
|
||||
token: token.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Hent en ny identitet og token fra STDB-serveren.
|
||||
/// Brukes ved oppstart hvis ingen token er konfigurert.
|
||||
pub async fn create_identity(base_url: &str) -> Result<(String, String), StdbError> {
|
||||
let client = Client::new();
|
||||
let url = format!("{}/v1/identity", base_url.trim_end_matches('/'));
|
||||
let resp = client.post(&url).send().await?;
|
||||
|
||||
if !resp.status().is_success() {
|
||||
return Err(StdbError::Http(format!(
|
||||
"Kunne ikke opprette identitet: {}",
|
||||
resp.status()
|
||||
)));
|
||||
}
|
||||
|
||||
let body: serde_json::Value = resp.json().await?;
|
||||
let identity = body["identity"]
|
||||
.as_str()
|
||||
.ok_or_else(|| StdbError::Http("Mangler identity i respons".into()))?
|
||||
.to_string();
|
||||
let token = body["token"]
|
||||
.as_str()
|
||||
.ok_or_else(|| StdbError::Http("Mangler token i respons".into()))?
|
||||
.to_string();
|
||||
|
||||
Ok((identity, token))
|
||||
}
|
||||
|
||||
/// Kall en reducer med navngitte parametre (JSON-objekt).
|
||||
async fn call_reducer<T: Serialize>(&self, reducer: &str, args: &T) -> Result<(), StdbError> {
|
||||
let url = format!(
|
||||
"{}/v1/database/{}/call/{}",
|
||||
self.base_url, self.database, reducer
|
||||
);
|
||||
|
||||
let resp = self
|
||||
.client
|
||||
.post(&url)
|
||||
.bearer_auth(&self.token)
|
||||
.json(args)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
Ok(())
|
||||
} else {
|
||||
let status = resp.status();
|
||||
let body = resp.text().await.unwrap_or_default();
|
||||
Err(StdbError::Reducer {
|
||||
reducer: reducer.to_string(),
|
||||
status: status.as_u16(),
|
||||
message: body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Node-operasjoner
|
||||
// =========================================================================
|
||||
|
||||
pub async fn create_node(
|
||||
&self,
|
||||
id: &str,
|
||||
node_kind: &str,
|
||||
title: &str,
|
||||
content: &str,
|
||||
visibility: &str,
|
||||
metadata: &str,
|
||||
created_by: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
id: &'a str,
|
||||
node_kind: &'a str,
|
||||
title: &'a str,
|
||||
content: &'a str,
|
||||
visibility: &'a str,
|
||||
metadata: &'a str,
|
||||
created_by: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"create_node",
|
||||
&Args {
|
||||
id,
|
||||
node_kind,
|
||||
title,
|
||||
content,
|
||||
visibility,
|
||||
metadata,
|
||||
created_by,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_node(
|
||||
&self,
|
||||
id: &str,
|
||||
node_kind: &str,
|
||||
title: &str,
|
||||
content: &str,
|
||||
visibility: &str,
|
||||
metadata: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
id: &'a str,
|
||||
node_kind: &'a str,
|
||||
title: &'a str,
|
||||
content: &'a str,
|
||||
visibility: &'a str,
|
||||
metadata: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"update_node",
|
||||
&Args {
|
||||
id,
|
||||
node_kind,
|
||||
title,
|
||||
content,
|
||||
visibility,
|
||||
metadata,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_node(&self, id: &str) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
id: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer("delete_node", &Args { id }).await
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Edge-operasjoner
|
||||
// =========================================================================
|
||||
|
||||
pub async fn create_edge(
|
||||
&self,
|
||||
id: &str,
|
||||
source_id: &str,
|
||||
target_id: &str,
|
||||
edge_type: &str,
|
||||
metadata: &str,
|
||||
system: bool,
|
||||
created_by: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
id: &'a str,
|
||||
source_id: &'a str,
|
||||
target_id: &'a str,
|
||||
edge_type: &'a str,
|
||||
metadata: &'a str,
|
||||
system: bool,
|
||||
created_by: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"create_edge",
|
||||
&Args {
|
||||
id,
|
||||
source_id,
|
||||
target_id,
|
||||
edge_type,
|
||||
metadata,
|
||||
system,
|
||||
created_by,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_edge(
|
||||
&self,
|
||||
id: &str,
|
||||
edge_type: &str,
|
||||
metadata: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
id: &'a str,
|
||||
edge_type: &'a str,
|
||||
metadata: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer("update_edge", &Args { id, edge_type, metadata })
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_edge(&self, id: &str) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
id: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer("delete_edge", &Args { id }).await
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// NodeAccess-operasjoner
|
||||
// =========================================================================
|
||||
|
||||
pub async fn upsert_node_access(
|
||||
&self,
|
||||
subject_id: &str,
|
||||
object_id: &str,
|
||||
access: &str,
|
||||
via_edge: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
subject_id: &'a str,
|
||||
object_id: &'a str,
|
||||
access: &'a str,
|
||||
via_edge: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"upsert_node_access",
|
||||
&Args {
|
||||
subject_id,
|
||||
object_id,
|
||||
access,
|
||||
via_edge,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_node_access(
|
||||
&self,
|
||||
subject_id: &str,
|
||||
object_id: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
subject_id: &'a str,
|
||||
object_id: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer("delete_node_access", &Args { subject_id, object_id })
|
||||
.await
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Live-rom (LiveKit)
|
||||
// =========================================================================
|
||||
|
||||
pub async fn create_live_room(
|
||||
&self,
|
||||
room_id: &str,
|
||||
communication_id: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
room_id: &'a str,
|
||||
communication_id: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer("create_live_room", &Args { room_id, communication_id })
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn add_room_participant(
|
||||
&self,
|
||||
room_id: &str,
|
||||
user_id: &str,
|
||||
display_name: &str,
|
||||
role: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
room_id: &'a str,
|
||||
user_id: &'a str,
|
||||
display_name: &'a str,
|
||||
role: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"add_room_participant",
|
||||
&Args { room_id, user_id, display_name, role },
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn remove_room_participant(
|
||||
&self,
|
||||
room_id: &str,
|
||||
user_id: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
room_id: &'a str,
|
||||
user_id: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer("remove_room_participant", &Args { room_id, user_id })
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn close_live_room(&self, room_id: &str) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
room_id: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer("close_live_room", &Args { room_id }).await
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Placement-operasjoner (message_placements)
|
||||
// =========================================================================
|
||||
|
||||
/// Plasser en melding i en kontekst. Idempotent (upsert).
|
||||
pub async fn place_message(
|
||||
&self,
|
||||
id: &str,
|
||||
message_id: &str,
|
||||
context_type: &str,
|
||||
context_id: &str,
|
||||
position_json: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
id: &'a str,
|
||||
message_id: &'a str,
|
||||
context_type: &'a str,
|
||||
context_id: &'a str,
|
||||
position_json: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"place_message",
|
||||
&Args { id, message_id, context_type, context_id, position_json },
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Fjern en meldings plassering fra en kontekst.
|
||||
pub async fn remove_placement(
|
||||
&self,
|
||||
message_id: &str,
|
||||
context_type: &str,
|
||||
context_id: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
message_id: &'a str,
|
||||
context_type: &'a str,
|
||||
context_id: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"remove_placement",
|
||||
&Args { message_id, context_type, context_id },
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Flytt en plassering (oppdater posisjon).
|
||||
pub async fn move_on_canvas(
|
||||
&self,
|
||||
placement_id: &str,
|
||||
new_position_json: &str,
|
||||
) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Args<'a> {
|
||||
placement_id: &'a str,
|
||||
new_position_json: &'a str,
|
||||
}
|
||||
|
||||
self.call_reducer(
|
||||
"move_on_canvas",
|
||||
&Args { placement_id, new_position_json },
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Vedlikehold
|
||||
// =========================================================================
|
||||
|
||||
/// Tøm alle noder og edges. Brukes ved warmup for å unngå duplikater.
|
||||
pub async fn clear_all(&self) -> Result<(), StdbError> {
|
||||
#[derive(Serialize)]
|
||||
struct Empty {}
|
||||
|
||||
self.call_reducer("clear_all", &Empty {}).await
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Feilhåndtering
|
||||
// =============================================================================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StdbError {
|
||||
/// HTTP-transportfeil (nettverk, timeout)
|
||||
Http(String),
|
||||
/// Reducer returnerte feil (400, 500, etc.)
|
||||
Reducer {
|
||||
reducer: String,
|
||||
status: u16,
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for StdbError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
StdbError::Http(msg) => write!(f, "STDB HTTP-feil: {msg}"),
|
||||
StdbError::Reducer {
|
||||
reducer,
|
||||
status,
|
||||
message,
|
||||
} => write!(f, "STDB reducer {reducer} feilet ({status}): {message}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for StdbError {}
|
||||
|
||||
impl From<reqwest::Error> for StdbError {
|
||||
fn from(e: reqwest::Error) -> Self {
|
||||
StdbError::Http(e.to_string())
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ fn tts_bin() -> String {
|
|||
///
|
||||
/// Spawner synops-tts med --write for å gjøre alt arbeidet:
|
||||
/// ElevenLabs-kall, CAS-lagring, PG-skriving, ressurslogging.
|
||||
/// Maskinrommet gjør etterpå STDB-synk for sanntidsvisning.
|
||||
/// PG NOTIFY-triggere sender sanntidsoppdateringer til klienter.
|
||||
pub async fn handle_tts_job(
|
||||
job: &JobRow,
|
||||
db: &PgPool,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
//! og `mixer_channel_changed` kanaler i PostgreSQL og videresender relevante
|
||||
//! endringer via WebSocket til tilkoblede klienter, filtrert på tilgangsmatrisen.
|
||||
//!
|
||||
//! Fase M2: Frontend bruker kun denne WebSocket-tilkoblingen (SpacetimeDB fjernet).
|
||||
//! Events berikes med full raddata fra PG slik at klienten kan oppdatere stores direkte.
|
||||
//! Ref: docs/retninger/datalaget.md
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ faktisk kode. Mer fokusert enn ryddejobben — kun docs.
|
|||
### 3. Tekniske detaljer
|
||||
- [ ] Stemmer database-skjema beskrevet i docs med faktiske migrasjoner?
|
||||
- [ ] Stemmer API-endepunkter beskrevet i docs med faktiske routes?
|
||||
- [ ] Stemmer SpacetimeDB-tabeller/reducere i docs med modulen?
|
||||
- [ ] Stemmer WebSocket-/sanntids-beskrivelser i docs med implementasjonen?
|
||||
|
||||
### 4. Setup-docs
|
||||
- [ ] `docs/setup/lokal.md` — fungerer stegene fortsatt?
|
||||
|
|
|
|||
|
|
@ -29,10 +29,7 @@ men ikke implementert.
|
|||
- [ ] Er det env-vars i `.env` som er utdaterte?
|
||||
- [ ] Er secrets rotert der de bør være?
|
||||
|
||||
### 5. SpacetimeDB-modul
|
||||
- [ ] Er SpacetimeDB-modulen publisert med siste endringer?
|
||||
|
||||
### 6. Caddy / reverse proxy
|
||||
### 5. Caddy / reverse proxy
|
||||
- [ ] Er Caddyfile i repo synk med `/srv/synops/config/caddy/Caddyfile`?
|
||||
- [ ] Er det nye subdomener eller routes som mangler?
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ fjerne utdaterte referanser, og sikre at dokumentasjon stemmer med virkeligheten
|
|||
- [ ] Ubrukte SvelteKit-routes (mapper i `web/src/routes/` uten innhold eller med stub)
|
||||
- [ ] Ubrukte komponenter (filer i `web/src/lib/components/` som ikke importeres)
|
||||
- [ ] Ubrukte Rust-moduler i worker
|
||||
- [ ] Ubrukte SpacetimeDB-reducere eller tabeller
|
||||
- [ ] Gamle migrations som bør dokumenteres eller konsolideres
|
||||
- [ ] `package.json` / `Cargo.toml` — ubrukte dependencies
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Slettes når Synops er oppe og stabilt.
|
|||
| authentik-worker | goauthentik/server:latest | SSO bakgrunn | JA |
|
||||
| redis | redis:7-alpine | Cache for Authentik | JA |
|
||||
| forgejo | forgejo:10 | Git | JA |
|
||||
| spacetimedb | clockworklabs/spacetime:latest | Sanntid | FJERNES (tom v1-modul) |
|
||||
| ~~spacetimedb~~ | ~~clockworklabs/spacetime:latest~~ | ~~Sanntid~~ | FJERNET (mars 2026) |
|
||||
| web | sidelinja-web (bygget) | SvelteKit v1 | FJERNES |
|
||||
| worker | sidelinja-worker (bygget) | Rust worker v1 | FJERNES |
|
||||
| ai-gateway | litellm:main-stable | AI-ruter | BEHOLDES (uavhengig av app) |
|
||||
|
|
@ -23,7 +23,7 @@ Slettes når Synops er oppe og stabilt.
|
|||
- git.sidelinja.org → forgejo:3000
|
||||
- sidelinja.org → web:3000 + /media/* (filservering) + JSON access log
|
||||
- vegard.info → placeholder
|
||||
- rt.sidelinja.org → spacetimedb:3000
|
||||
- ~~rt.sidelinja.org → spacetimedb:3000~~ (fjernet)
|
||||
|
||||
## PG init-script
|
||||
`/srv/sidelinja/config/postgres/init/01-create-databases.sql`
|
||||
|
|
@ -34,7 +34,7 @@ kjøres kun ved *første* PG-oppstart.
|
|||
## Dockerfiles (referanse)
|
||||
### web/Dockerfile
|
||||
- node:20-alpine, to-stegs bygg
|
||||
- VITE_SPACETIMEDB_URL som build-arg
|
||||
- ~~VITE_SPACETIMEDB_URL som build-arg~~ (fjernet)
|
||||
- `npm run build` → `node build` på port 3000
|
||||
|
||||
### worker/Dockerfile
|
||||
|
|
|
|||
|
|
@ -9,16 +9,12 @@ read_env() { grep "^$1=" "$ENV_FILE" | head -1 | cut -d= -f2; }
|
|||
container_ip() { docker inspect "$1" --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'; }
|
||||
|
||||
PG_IP=$(container_ip sidelinja-postgres-1)
|
||||
STDB_IP=$(container_ip sidelinja-spacetimedb-1)
|
||||
WHISPER_IP=$(container_ip sidelinja-faster-whisper-1 2>/dev/null || echo "")
|
||||
AI_GW_IP=$(container_ip sidelinja-ai-gateway-1 2>/dev/null || echo "")
|
||||
LIVEKIT_IP=$(container_ip sidelinja-livekit-1 2>/dev/null || echo "")
|
||||
|
||||
cat > /tmp/maskinrommet.env <<EOF
|
||||
DATABASE_URL=postgres://$(read_env POSTGRES_USER):$(read_env POSTGRES_PASSWORD)@${PG_IP}:5432/synops
|
||||
SPACETIMEDB_URL=http://${STDB_IP}:3000
|
||||
SPACETIMEDB_DATABASE=$(read_env SPACETIMEDB_DATABASE)
|
||||
SPACETIMEDB_TOKEN=$(read_env SPACETIMEDB_TOKEN)
|
||||
AUTHENTIK_ISSUER=$(read_env AUTHENTIK_ISSUER)
|
||||
AUTHENTIK_CLIENT_ID=$(read_env AUTHENTIK_CLIENT_ID)
|
||||
BIND_ADDR=0.0.0.0:3100
|
||||
|
|
|
|||
5268
scripts/synops.md
5268
scripts/synops.md
File diff suppressed because it is too large
Load diff
|
|
@ -1,141 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Test sanntidsflyt: STDB node/edge CRUD via HTTP API
|
||||
#
|
||||
# Verifiserer at maskinrommet kan skrive til STDB, og at data
|
||||
# er tilgjengelig for subscribers (grunnlaget for at to faner
|
||||
# kan se hverandres endringer i sanntid).
|
||||
#
|
||||
# Kjøres fra lokal maskin. SSH-er til serveren for å nå Docker-nettverket.
|
||||
#
|
||||
# Bruk: ./scripts/test-sanntid.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SERVER="claude@157.180.81.26"
|
||||
NETWORK="sidelinja_sidelinja-net"
|
||||
STDB_URL="http://spacetimedb:3000/v1/database/synops"
|
||||
VEGARD_NODE="a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
|
||||
|
||||
# Les STDB-token fra .env
|
||||
STDB_TOKEN=$(ssh "$SERVER" "grep SPACETIMEDB_TOKEN /srv/synops/.env | cut -d= -f2-")
|
||||
|
||||
if [ -z "$STDB_TOKEN" ]; then
|
||||
echo "FEIL: Fant ikke SPACETIMEDB_TOKEN i .env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEST_NODE_ID="test-sanntid-$(date +%s)"
|
||||
TEST_EDGE_ID="test-edge-$(date +%s)"
|
||||
|
||||
# Hjelpefunksjon: kall STDB reducer via curl i Docker-nettverket
|
||||
stdb_call() {
|
||||
local reducer=$1
|
||||
local data=$2
|
||||
ssh "$SERVER" "docker run --rm --network $NETWORK curlimages/curl:latest \
|
||||
-s -w '%{http_code}' -X POST $STDB_URL/call/$reducer \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer $STDB_TOKEN' \
|
||||
-d '$data'" 2>/dev/null
|
||||
}
|
||||
|
||||
# Hjelpefunksjon: spør STDB via SQL
|
||||
stdb_sql() {
|
||||
ssh "$SERVER" "docker exec sidelinja-spacetimedb-1 spacetime sql synops \"$1\"" 2>/dev/null | grep -v WARNING
|
||||
}
|
||||
|
||||
echo "=== Sanntidstest: STDB node/edge CRUD ==="
|
||||
echo ""
|
||||
|
||||
# 1. Opprett testnode
|
||||
echo -n "1. Oppretter testnode ($TEST_NODE_ID)... "
|
||||
RESULT=$(stdb_call "create_node" "{\"id\": \"$TEST_NODE_ID\", \"node_kind\": \"content\", \"title\": \"Sanntidstest\", \"content\": \"Automatisk test av sanntidsflyt\", \"visibility\": \"hidden\", \"metadata\": \"{}\", \"created_by\": \"$VEGARD_NODE\"}")
|
||||
if [ "$RESULT" = "200" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL (HTTP $RESULT)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Verifiser node finnes i STDB
|
||||
echo -n "2. Verifiserer node i STDB... "
|
||||
NODE_RESULT=$(stdb_sql "SELECT id FROM node WHERE id = '$TEST_NODE_ID'")
|
||||
if echo "$NODE_RESULT" | grep -q "$TEST_NODE_ID"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL: Node ikke funnet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. Opprett owner-edge (Vegard → testnode)
|
||||
echo -n "3. Oppretter owner-edge... "
|
||||
RESULT=$(stdb_call "create_edge" "{\"id\": \"$TEST_EDGE_ID\", \"source_id\": \"$VEGARD_NODE\", \"target_id\": \"$TEST_NODE_ID\", \"edge_type\": \"owner\", \"metadata\": \"{}\", \"system\": false, \"created_by\": \"$VEGARD_NODE\"}")
|
||||
if [ "$RESULT" = "200" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL (HTTP $RESULT)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. Verifiser edge finnes
|
||||
echo -n "4. Verifiserer edge i STDB... "
|
||||
EDGE_RESULT=$(stdb_sql "SELECT id FROM edge WHERE id = '$TEST_EDGE_ID'")
|
||||
if echo "$EDGE_RESULT" | grep -q "$TEST_EDGE_ID"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL: Edge ikke funnet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 5. Oppdater noden
|
||||
echo -n "5. Oppdaterer nodetittel... "
|
||||
RESULT=$(stdb_call "update_node" "{\"id\": \"$TEST_NODE_ID\", \"node_kind\": \"content\", \"title\": \"Sanntidstest (oppdatert)\", \"content\": \"Automatisk test av sanntidsflyt\", \"visibility\": \"hidden\", \"metadata\": \"{}\"}")
|
||||
if [ "$RESULT" = "200" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL (HTTP $RESULT)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 6. Verifiser oppdateringen
|
||||
echo -n "6. Verifiserer oppdatert tittel... "
|
||||
TITLE_RESULT=$(stdb_sql "SELECT title FROM node WHERE id = '$TEST_NODE_ID'")
|
||||
if echo "$TITLE_RESULT" | grep -q "oppdatert"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL: Tittel ikke oppdatert"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 7. Slett testnode (kaskade-sletter edges)
|
||||
echo -n "7. Sletter testnode (kaskaderer edges)... "
|
||||
RESULT=$(stdb_call "delete_node" "{\"id\": \"$TEST_NODE_ID\"}")
|
||||
if [ "$RESULT" = "200" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL (HTTP $RESULT)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 8. Verifiser at node og edge er borte
|
||||
echo -n "8. Verifiserer opprydding... "
|
||||
NODE_GONE=$(stdb_sql "SELECT id FROM node WHERE id = '$TEST_NODE_ID'" | grep -c "$TEST_NODE_ID" || true)
|
||||
EDGE_GONE=$(stdb_sql "SELECT id FROM edge WHERE id = '$TEST_EDGE_ID'" | grep -c "$TEST_EDGE_ID" || true)
|
||||
if [ "$NODE_GONE" = "0" ] && [ "$EDGE_GONE" = "0" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FEIL: Data ikke ryddet opp"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Alle 8 tester bestått ==="
|
||||
echo ""
|
||||
echo "Backend-sanntidsflyt fungerer: STDB mottar og serverer"
|
||||
echo "node/edge-operasjoner korrekt. WebSocket-subscribers"
|
||||
echo "(frontend-faner) vil motta disse endringene i sanntid."
|
||||
echo ""
|
||||
echo "For å teste i browser:"
|
||||
echo " 1. Start frontend: cd frontend && npm run dev"
|
||||
echo " 2. Åpne to faner: http://localhost:5173"
|
||||
echo " 3. Logg inn i begge"
|
||||
echo " 4. Skriv en node i fane 1, se den dukke opp i fane 2"
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"server": "synops-server",
|
||||
"database": "synops",
|
||||
"module-path": "./spacetimedb"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue