mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
206 lines
8.5 KiB
Markdown
206 lines
8.5 KiB
Markdown
---
|
|
description: "PostgreSQL-databaseschema, tabellen, migraties en back-upprocedures voor SnapOtter."
|
|
i18n_source_hash: a68264552836
|
|
i18n_provenance: machine
|
|
i18n_output_hash: 0c2305505e16
|
|
i18n_hash_version: 2
|
|
---
|
|
|
|
# Database {#database}
|
|
|
|
SnapOtter gebruikt PostgreSQL 17 met [Drizzle ORM](https://orm.drizzle.team/) (pg-core / node-postgres) voor gegevensopslag. Het schema is gedefinieerd in `apps/api/src/db/schema.ts`.
|
|
|
|
De verbinding wordt geconfigureerd via de omgevingsvariabele `DATABASE_URL` (standaard `postgres://snapotter:snapotter@postgres:5432/snapotter`). In Docker Compose slaat de Postgres-container zijn gegevens op in het benoemde volume `SnapOtter-pgdata`.
|
|
|
|
## Tabellen {#tables}
|
|
|
|
### users {#users}
|
|
|
|
Slaat gebruikersaccounts op. Wordt bij de eerste run automatisch aangemaakt op basis van `DEFAULT_USERNAME` en `DEFAULT_PASSWORD`.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `username` | varchar | Uniek, vereist |
|
|
| `passwordHash` | varchar | scrypt-hash |
|
|
| `role` | varchar | `admin`, `editor` of `user` |
|
|
| `mustChangePassword` | boolean | Vlag voor geforceerde wachtwoordreset |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
| `updatedAt` | timestamp | Tijd van laatste update |
|
|
|
|
### sessions {#sessions}
|
|
|
|
Actieve aanmeldsessies. Elke rij koppelt een sessietoken aan een gebruiker.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `id` | varchar | Primaire sleutel (sessietoken) |
|
|
| `userId` | uuid | Vreemde sleutel naar `users.id` |
|
|
| `expiresAt` | timestamp | Vervaltijd |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
|
|
### teams {#teams}
|
|
|
|
Groepen om gebruikers te organiseren. Beheerders kunnen gebruikers aan teams toewijzen.
|
|
|
|
| Kolom | Type | Beschrijving |
|
|
|--------|------|-------------|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `name` | varchar (uniek, max. 50 tekens) | Teamnaam |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
|
|
### api_keys {#api-keys}
|
|
|
|
API-sleutels voor programmatische toegang. De onbewerkte sleutel wordt eenmalig getoond bij aanmaken; alleen de hash wordt opgeslagen.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `userId` | uuid | Vreemde sleutel naar `users.id` |
|
|
| `keyHash` | varchar | scrypt-hash van de sleutel |
|
|
| `name` | varchar | Door de gebruiker opgegeven label |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
| `lastUsedAt` | timestamp | Bijgewerkt bij elk geauthenticeerd verzoek |
|
|
|
|
Sleutels beginnen met het voorvoegsel `si_` gevolgd door 96 hexadecimale tekens (48 willekeurige bytes).
|
|
|
|
### pipelines {#pipelines}
|
|
|
|
Opgeslagen toolketens die gebruikers in de UI aanmaken.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `name` | varchar | Pipelinenaam |
|
|
| `description` | varchar | Optionele beschrijving |
|
|
| `steps` | jsonb | Array van `{ toolId, settings }`-objecten |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
|
|
### user_files {#user-files}
|
|
|
|
Persistente bestandsbibliotheek. Een opgeslagen bewerking wordt standaard als een onafhankelijke root-rij ingevoegd ("opslaan als nieuw": `version` 1, `parentId` null, zodat het origineel in de lijst blijft staan), of als een aan de bovenliggende rij gekoppelde versie wanneer je het origineel overschrijft (`parentId` ingesteld, `version` opgehoogd, waarmee het wordt vervangen). De kolom `toolChain` registreert welke tools zijn toegepast.
|
|
|
|
| Kolom | Type | Beschrijving |
|
|
|--------|------|-------------|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `userId` | uuid | FK naar users (CASCADE DELETE) |
|
|
| `originalName` | varchar | Oorspronkelijke bestandsnaam bij upload |
|
|
| `storedName` | varchar | Bestandsnaam op schijf |
|
|
| `mimeType` | varchar | MIME-type |
|
|
| `size` | integer | Bestandsgrootte in bytes |
|
|
| `width` | integer | Breedte van de afbeelding in px |
|
|
| `height` | integer | Hoogte van de afbeelding in px |
|
|
| `version` | integer | Versienummer (1 = origineel) |
|
|
| `parentId` | uuid of null | FK naar user_files (bovenliggende versie) |
|
|
| `toolChain` | jsonb | Tool-ID's die op volgorde zijn toegepast om deze versie te maken |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
|
|
### jobs {#jobs}
|
|
|
|
Volgt verwerkingsjobs voor voortgangsrapportage en opschoning.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `type` | varchar | Tool- of pipeline-identifier |
|
|
| `status` | varchar | `queued`, `processing`, `completed` of `failed` |
|
|
| `progress` | real | Fractie van 0.0-1.0 |
|
|
| `inputFiles` | jsonb | Array van invoerbestandspaden |
|
|
| `outputPath` | varchar | Pad naar het resultaatbestand |
|
|
| `settings` | jsonb | Gebruikte toolinstellingen |
|
|
| `error` | varchar | Foutmelding bij mislukken |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
| `completedAt` | timestamp | Voltooiingstijd |
|
|
|
|
### settings {#settings}
|
|
|
|
Sleutel-waardeopslag voor serverbrede instellingen die beheerders vanuit de UI kunnen wijzigen.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `key` | varchar | Primaire sleutel |
|
|
| `value` | varchar | Instellingswaarde |
|
|
| `updatedAt` | timestamp | Tijd van laatste update |
|
|
|
|
### roles {#roles}
|
|
|
|
Aangepaste rollen met granulaire rechten.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `name` | varchar | Unieke rolnaam |
|
|
| `description` | varchar | Optionele beschrijving |
|
|
| `permissions` | jsonb | Array van rechtenstrings |
|
|
| `createdAt` | timestamp | Aanmaaktijd |
|
|
|
|
### audit_log {#audit-log}
|
|
|
|
Logboek van beveiligingsrelevante acties.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `id` | uuid | Primaire sleutel |
|
|
| `userId` | uuid | FK naar users |
|
|
| `action` | varchar | Actietype |
|
|
| `details` | jsonb | Actiespecifieke gegevens |
|
|
| `createdAt` | timestamp | Tijd van de actie |
|
|
|
|
### user_preferences {#user-preferences}
|
|
|
|
UI-status per gebruiker, gesleuteld op voorkeursnaam. Bewaart de vastgezette tools van de startpagina, die via `PUT /api/v1/preferences` worden geschreven.
|
|
|
|
| Kolom | Type | Opmerkingen |
|
|
|---|---|---|
|
|
| `userId` | text | FK naar users, cascaderend verwijderen. Samen met `key` de primaire sleutel |
|
|
| `key` | text | Naam van de voorkeur. Samen met `userId` de primaire sleutel |
|
|
| `value` | jsonb | Inhoud van de voorkeur |
|
|
| `updatedAt` | timestamp | Laatste schrijfactie |
|
|
|
|
## Migraties {#migrations}
|
|
|
|
Drizzle verzorgt schemamigraties. Migratiebestanden staan in `apps/api/drizzle/`. Tijdens ontwikkeling:
|
|
|
|
```bash
|
|
cd apps/api
|
|
npx drizzle-kit generate # generate a migration from schema changes
|
|
npx drizzle-kit migrate # apply pending migrations
|
|
```
|
|
|
|
In productie worden openstaande migraties automatisch toegepast bij het opstarten.
|
|
|
|
## Back-up en herstel {#backup-and-restore}
|
|
|
|
De relationele database bevindt zich in het `SnapOtter-pgdata`-volume van de Postgres-container, niet in het `/data`-volume van de app.
|
|
|
|
**Logische back-up met validatie (aanbevolen)**
|
|
|
|
```bash
|
|
# Dump into PostgreSQL's portable custom archive format
|
|
docker exec SnapOtter-postgres \
|
|
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
|
|
test -s snapotter.dump
|
|
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
|
|
|
|
# Restore into a fresh/disposable target first and fail on the first SQL error
|
|
docker exec -i SnapOtter-postgres \
|
|
pg_restore --exit-on-error --clean --if-exists --no-owner \
|
|
-U snapotter -d snapotter < snapotter.dump
|
|
```
|
|
|
|
Deze databasedump bevat geen opgeslagen bibliotheekobjecten in `/data/files` of de duurzame BullMQ-status in Redis. Maak een back-up en herstel deze met de gecoördineerde procedure in [Beveiliging en verharding](/nl/guide/security#backup-and-recovery).
|
|
|
|
**Koude volumemomentopname**
|
|
|
|
```bash
|
|
# Stop every service first, then use your storage platform to snapshot the
|
|
# PostgreSQL, app-data, and Redis volumes as one crash-consistent set.
|
|
docker compose -f docker/docker-compose.yml stop
|
|
```
|
|
|
|
Kopieer geen live PostgreSQL-gegevensmap met `tar`. Stel volumenamen voor voorvoegsels samen per project, dus los de gekoppelde volume-ID's van `docker inspect` of uw opslagplatform op in plaats van het letterlijke label `SnapOtter-pgdata` aan te nemen.
|
|
|
|
### Migreren vanaf 1.x (SQLite) {#migrating-from-1-x-sqlite}
|
|
|
|
Upgraden vanaf SnapOtter 1.x heeft een eigen gids: zie [Upgraden van 1.x naar 2.0](./upgrading). Kort gezegd: hergebruik je bestaande `/data`-volume, en 2.0 detecteert en importeert `/data/snapotter.db` automatisch bij de eerste keer opstarten (of stel `SQLITE_MIGRATE_PATH` in om er expliciet naar te verwijzen). Maak eerst een back-up van het volledige `/data`-volume, niet alleen van `snapotter.db`: 1.x gebruikt de SQLite WAL-modus, dus een gestopte container laat vaak het grootste deel van zijn gegevens in `snapotter.db-wal` staan naast een bijna leeg `snapotter.db`.
|