Omnyx Backend Migrationsplan
Ziel
Migration von PHP + MySQL zu Bun + Hono + Drizzle ORM für eine Hybrid-Architektur:
- Lokal: SQLite (Desktop/Mobile, offline-fähig)
- Server: PostgreSQL (Docker, Multi-User)
- Desktop: Tauri + Bun-Sidecar
- Mobile: Capacitor + SQLite via WASM
Tech Stack
| Komponente |
Technologie |
| Runtime |
Bun (TypeScript-nativ, bun:sqlite built-in) |
| Server |
Hono (leicht, Zod-Validation) |
| ORM |
Drizzle ORM (bun:sqlite / node-postgres) |
| DB lokal |
bun:sqlite |
| DB Server |
PostgreSQL (Docker) |
| Desktop |
Tauri + Bun-Sidecar |
| Mobile |
Capacitor + @libsql/client-web (WASM) |
Projektstruktur (separates Repo)
Datenbank-Tabellen
media
| Spalte |
Typ |
Anmerkung |
| id |
integer |
PK autoIncrement |
| title |
text |
NOT NULL |
| year |
integer |
NOT NULL |
| poster |
text |
nullable |
| banner |
text |
nullable |
| description |
text |
nullable |
| rating |
real |
nullable |
| category |
text |
nullable |
| type |
text |
default 'Movie' |
| status |
text |
default 'Released' |
| aspect_ratio |
text |
nullable |
| runtime |
integer |
nullable |
| director |
text |
nullable |
| writer |
text |
nullable |
| release_date |
text |
nullable |
| source |
text |
nullable |
| created_at |
text |
default datetime() |
| updated_at |
text |
default datetime() |
| genres |
text |
JSON-Array |
| tags |
text |
JSON-Array |
| studios |
text |
JSON-Array |
| categories |
text |
JSON-Array |
| series |
text |
JSON-Array |
| platforms |
text |
JSON-Array |
| developers |
text |
JSON-Array |
| completion_status |
text |
nullable |
| play_count |
integer |
default 0 |
| last_activity |
text |
nullable |
| playtime |
integer |
default 0 |
episodes
| Spalte |
Typ |
| id |
integer PK |
| media_id |
integer FK → media |
| season |
integer |
| episode_number |
integer |
| title |
text |
| description |
text |
| air_date |
text |
| duration |
integer |
| thumbnail |
text |
tracks
| Spalte |
Typ |
| id |
integer PK |
| media_id |
integer FK → media |
| track_number |
integer |
| title |
text |
| duration |
text (format: "M:SS") |
| artist |
text |
cast (staff)
| Spalte |
Typ |
| id |
integer PK |
| name |
text NOT NULL |
| cleanname |
text nullable |
| photo |
text nullable |
| bio |
text nullable |
| birth_date |
text nullable |
| birth_place |
text nullable |
| occupations |
text (JSON-Array) |
| created_at |
text |
| updated_at |
text |
| media_types |
text (JSON-Array) |
| bust_size |
integer nullable |
| cup_size |
text nullable |
| waist_size |
integer nullable |
| hip_size |
integer nullable |
| height |
integer nullable |
| weight |
integer nullable |
| hair_color |
text nullable |
| eye_color |
text nullable |
| ethnicity |
text nullable |
adult_specifics
| Spalte |
Typ |
| id |
integer PK |
| cast_id |
integer FK → cast |
| tattoos |
text nullable |
| piercings |
text nullable |
| measurements |
text nullable |
| shoe_size |
integer nullable |
media_cast_staff (Junction)
| Spalte |
Typ |
| media_id |
integer FK |
| cast_id |
integer FK |
| role |
text |
| character_name |
text nullable |
| character_image |
text nullable |
| PK |
(media_id, cast_id) |
cast_media_filmography (Junction)
| Spalte |
Typ |
| cast_id |
integer FK |
| media_id |
integer FK |
| role |
text |
| character_name |
text nullable |
settings
| Spalte |
Typ |
| id |
integer PK |
| enabled_categories |
text (JSON-Array) |
| items_per_page |
integer default 20 |
| grid_item_size |
integer default 5 |
| default_view |
text default 'grid' |
| show_adult_content |
integer (boolean) default 0 |
| auto_play_trailers |
integer (boolean) default 0 |
| language |
text default 'en' |
| theme |
text default 'system' |
| jellyfin_library_mappings |
text nullable |
| page_title |
text nullable |
| favicon |
text nullable |
| custom_colors |
text nullable (JSON) |
| created_at |
text |
| updated_at |
text |
API-Endpoints (1:1 von PHP)
Media
Cast
Settings
Response-Format (identisch zu PHP)
DB-Connection (Driver-Abstraktion)
Desktop-Bundle (Tauri)
- Bun-Binary via
bun build --compile (~50MB Single-File)
- Tauri startet Binary als Sidecar
- React-Frontend (produktions-Build) in Tauri-WebView
- API auf
localhost:3001
Mobile (Capacitor + WASM SQLite)
- SQLite läuft direkt im WebView via
@libsql/client/web
- Drizzle-Queries identisch zum Backend
- Kein separater Server nötig
- Optional: Sync mit Server bei Internet-Verbindung
Docker-Deployment
Status (23.05.2026)
| Phase |
Beschreibung |
Status |
| P1 |
Backend-Projekt (Bun + Hono + Drizzle) |
✅ Fertig |
| P2 |
Hono-Routen (Media, Cast, Settings) |
✅ Fertig |
| P3 |
Tests + TypeScript-Lint |
✅ Fertig |
| P4 |
Docker-Config aktualisiert |
✅ Fertig |
| P5 |
Tauri Desktop (Bun-Sidecar) |
✅ Fertig |
| P6 |
PostgreSQL-Support + Deployment |
⏳ Ausstehend |
| P7 |
Sync-Logik (Hybrid) |
⏳ Optional |
| P8 |
Capacitor Mobile (lokales SQLite) |
⏳ Ausstehend |
Aktuelle Struktur
Nächste Schritte
- PostgreSQL-Support (P6) — pg-Treiber für Drizzle, docker-compose mit Postgres
- Capacitor Mobile (P8) — Android-App mit embedded SQLite via WASM
- Sticky Footer Bug — Footer klebt nicht am unteren Rand bei kurzen Seiten
- PUT /api/cast/:id um adult-Felder erweitern (height, weight etc. aktuell nicht updatbar)