Files
SnapOtter/apps/docs/id/guide/database.md
T
SnapOtterandGitHub d10d0f544f fix: release QA hardening across processing, media, security, and CI gates (#649)
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.
2026-07-27 15:37:30 +08:00

8.1 KiB

description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
description i18n_source_hash i18n_provenance i18n_output_hash i18n_hash_version
Skema database PostgreSQL, tabel, migrasi, dan prosedur pencadangan untuk SnapOtter. a68264552836 machine e23431b3174a 2

Database

SnapOtter menggunakan PostgreSQL 17 dengan Drizzle ORM (pg-core / node-postgres) untuk persistensi data. Skema didefinisikan di apps/api/src/db/schema.ts.

Koneksi dikonfigurasi melalui variabel lingkungan DATABASE_URL (default postgres://snapotter:snapotter@postgres:5432/snapotter). Di Docker Compose, kontainer Postgres menyimpan datanya di volume bernama SnapOtter-pgdata.

Tabel

users

Menyimpan akun pengguna. Dibuat otomatis pada saat pertama kali dijalankan dari DEFAULT_USERNAME dan DEFAULT_PASSWORD.

Kolom Tipe Catatan
id uuid Primary key
username varchar Unik, wajib
passwordHash varchar hash scrypt
role varchar admin, editor, atau user
mustChangePassword boolean Flag reset kata sandi paksa
createdAt timestamp Waktu pembuatan
updatedAt timestamp Waktu pembaruan terakhir

sessions

Sesi login aktif. Setiap baris mengaitkan token sesi ke seorang pengguna.

Kolom Tipe Catatan
id varchar Primary key (token sesi)
userId uuid Foreign key ke users.id
expiresAt timestamp Waktu kedaluwarsa
createdAt timestamp Waktu pembuatan

teams

Grup untuk mengorganisasi pengguna. Admin dapat menetapkan pengguna ke tim.

Kolom Tipe Deskripsi
id uuid Primary key
name varchar (unik, maks 50 karakter) Nama tim
createdAt timestamp Waktu pembuatan

api_keys

API key untuk akses secara programatik. Kunci mentah ditampilkan sekali saat pembuatan; hanya hash yang disimpan.

Kolom Tipe Catatan
id uuid Primary key
userId uuid Foreign key ke users.id
keyHash varchar hash scrypt dari kunci
name varchar Label yang diberikan pengguna
createdAt timestamp Waktu pembuatan
lastUsedAt timestamp Diperbarui pada setiap permintaan terautentikasi

Kunci diberi awalan si_ diikuti oleh 96 karakter heksadesimal (48 byte acak).

pipelines

Rangkaian tool tersimpan yang dibuat pengguna di UI.

Kolom Tipe Catatan
id uuid Primary key
name varchar Nama pipeline
description varchar Deskripsi opsional
steps jsonb Array objek { toolId, settings }
createdAt timestamp Waktu pembuatan

user_files

Pustaka file persisten. Secara default, sebuah editan yang disimpan dimasukkan sebagai baris akar independen ("simpan sebagai baru": version 1, parentId null, sehingga file asli tetap terdaftar), atau sebagai versi yang tertaut ke induk ketika Anda menimpa file asli (parentId diisi, version dinaikkan, menggantikannya). Kolom toolChain mencatat tool yang diterapkan.

Kolom Tipe Deskripsi
id uuid Primary key
userId uuid FK ke users (CASCADE DELETE)
originalName varchar Nama file unggahan asli
storedName varchar Nama file pada disk
mimeType varchar Tipe MIME
size integer Ukuran file dalam byte
width integer Lebar gambar dalam px
height integer Tinggi gambar dalam px
version integer Nomor versi (1 = asli)
parentId uuid atau null FK ke user_files (versi induk)
toolChain jsonb ID tool yang diterapkan secara berurutan untuk menghasilkan versi ini
createdAt timestamp Waktu pembuatan

jobs

Melacak job pemrosesan untuk pelaporan progres dan pembersihan.

Kolom Tipe Catatan
id uuid Primary key
type varchar Identifikasi tool atau pipeline
status varchar queued, processing, completed, atau failed
progress real Fraksi 0.0-1.0
inputFiles jsonb Array path file input
outputPath varchar Path ke file hasil
settings jsonb Pengaturan tool yang digunakan
error varchar Pesan kesalahan jika gagal
createdAt timestamp Waktu pembuatan
completedAt timestamp Waktu penyelesaian

settings

Penyimpanan key-value untuk pengaturan seluruh server yang dapat diubah admin dari UI.

Kolom Tipe Catatan
key varchar Primary key
value varchar Nilai pengaturan
updatedAt timestamp Waktu pembaruan terakhir

roles

Peran kustom dengan izin granular.

Kolom Tipe Catatan
id uuid Primary key
name varchar Nama peran unik
description varchar Deskripsi opsional
permissions jsonb Array string izin
createdAt timestamp Waktu pembuatan

audit_log

Log aksi yang relevan dengan keamanan.

Kolom Tipe Catatan
id uuid Primary key
userId uuid FK ke users
action varchar Tipe aksi
details jsonb Data khusus aksi
createdAt timestamp Waktu aksi

user_preferences

Status UI per pengguna, dikunci berdasarkan nama preferensi. Menyimpan alat yang disematkan di halaman beranda, yang ditulis melalui PUT /api/v1/preferences.

Kolom Tipe Catatan
userId text FK ke users, menghapus secara berantai. Primary key bersama key
key text Nama preferensi. Primary key bersama userId
value jsonb Muatan preferensi
updatedAt timestamp Penulisan terakhir

Migrasi

Drizzle menangani migrasi skema. File migrasi berada di apps/api/drizzle/. Selama pengembangan:

cd apps/api
npx drizzle-kit generate   # generate a migration from schema changes
npx drizzle-kit migrate    # apply pending migrations

Di produksi, migrasi yang tertunda diterapkan secara otomatis saat startup.

Cadangkan dan pulihkan

Basis data relasional berada di volume SnapOtter-pgdata container Postgres, bukan volume /data aplikasi.

Cadangan logis dengan validasi (disarankan)

# 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

Dump database ini tidak berisi objek perpustakaan yang disimpan di /data/files atau status BullMQ yang tahan lama di Redis. Cadangkan dan pulihkan dengan prosedur terkoordinasi di Keamanan & Pengerasan.

Snapshot volume dingin

# 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

Jangan menyalin direktori data PostgreSQL langsung dengan tar. Tulis nama volume awalan berdasarkan proyek, jadi selesaikan ID volume yang terpasang dari docker inspect atau platform penyimpanan Anda daripada menggunakan label literal SnapOtter-pgdata.

Migrasi dari 1.x (SQLite)

Memutakhirkan dari SnapOtter 1.x memiliki panduannya sendiri: lihat Memutakhirkan dari 1.x ke 2.0. Singkatnya, gunakan kembali volume /data Anda yang ada dan 2.0 otomatis mendeteksi serta mengimpor /data/snapotter.db pada boot pertama (atau atur SQLITE_MIGRATE_PATH untuk menunjuk ke sana secara eksplisit). Cadangkan seluruh volume /data terlebih dahulu, bukan hanya snapotter.db: 1.x menggunakan mode SQLite WAL, sehingga kontainer yang dihentikan sering meninggalkan sebagian besar datanya di snapotter.db-wal di samping snapotter.db yang hampir kosong.