Files
abdulive/packages/db/migrations/001_init.sql
T
ZeasyAlexandClaude Opus 5 d07c18441b S1: Einrichtungs-Assistent, Twitch-Auth, Datenbank und Chat-Aufnahme
Zugangsdaten kommen jetzt aus der App statt aus einer .env: ein
Einrichtungs-Assistent auf http://localhost:4792/setup nimmt Client-ID,
Secret und OBS-Passwort entgegen, der OAuth-Rueckruf laeuft auf denselben
Server. Gespeichert wird verschlueselt (AES-256-GCM) in einem Volume,
bewusst nicht in der Datenbank - die wird auf die Windows-Seite gesichert.
Siehe ADR-0005.

Technikbasis vereinfacht: Node 24 bringt node:sqlite, WebSocket, fetch und
die Ausfuehrung von TypeScript mit. Das Projekt hat damit null
Laufzeitabhaengigkeiten und keinen Build-Schritt; pnpm-Workspaces und die
Paketmanifeste sind entfallen, Importe laufen ueber die imports-Map der
Wurzel-package.json. erasableSyntaxOnly verhindert Syntax, die das Type
Stripping stillschweigend entfernen wuerde. Siehe Nachtrag 2 in ADR-0001.

Enthalten:
- packages/core: Config, Layout, Logger mit Secret-Maskierung, Retry, YAML
- packages/db: Migrationen, Repositories, alle SQL-Anweisungen an einem Ort
- packages/twitch: Secret-Speicher, OAuth, Helix mit Token-Bucket, IRC
- apps/ingest: Daemon, Einrichtungsseite, Spike-Erkennung
- 20 Tests (IRC-Parser, Spike-Detektor, Datenbankschicht)

Der Chat verbindet sich anonym, solange nichts hinterlegt ist - damit laesst
sich die Datenaufnahme pruefen, bevor die Twitch-Apps existieren.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VTGjd92LsDDWF3frd9FTB
2026-09-09 09:56:55 +02:00

112 lines
3.3 KiB
SQL

-- Grundschema. Siehe docs/03-data-model.md.
-- Zugangsdaten stehen bewusst NICHT hier drin (ADR-0005) - diese Datei
-- wird per VACUUM INTO auf die Windows-Seite gesichert.
CREATE TABLE events (
id TEXT PRIMARY KEY,
ts TEXT NOT NULL,
type TEXT NOT NULL,
stream_id TEXT,
user_id TEXT,
payload TEXT NOT NULL
);
CREATE INDEX idx_events_ts ON events(ts);
CREATE INDEX idx_events_type ON events(type, ts);
CREATE INDEX idx_events_strm ON events(stream_id, ts);
CREATE TABLE streams (
id TEXT PRIMARY KEY,
started_at TEXT NOT NULL,
ended_at TEXT,
vod_id TEXT,
peak_viewers INTEGER NOT NULL DEFAULT 0,
avg_viewers REAL,
unique_chatters INTEGER NOT NULL DEFAULT 0,
new_followers INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE stream_segments (
id INTEGER PRIMARY KEY,
stream_id TEXT NOT NULL REFERENCES streams(id),
started_at TEXT NOT NULL,
ended_at TEXT,
title TEXT,
category_id TEXT,
category TEXT,
obs_scene TEXT
);
CREATE INDEX idx_segments_stream ON stream_segments(stream_id, started_at);
CREATE TABLE viewer_samples (
stream_id TEXT NOT NULL REFERENCES streams(id),
ts TEXT NOT NULL,
viewers INTEGER NOT NULL,
PRIMARY KEY (stream_id, ts)
);
CREATE TABLE users (
id TEXT PRIMARY KEY,
login TEXT NOT NULL,
display_name TEXT NOT NULL,
first_seen_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
streams_present INTEGER NOT NULL DEFAULT 0,
streams_chatted INTEGER NOT NULL DEFAULT 0,
message_count INTEGER NOT NULL DEFAULT 0,
is_follower INTEGER NOT NULL DEFAULT 0,
followed_at TEXT,
is_sub INTEGER NOT NULL DEFAULT 0,
sub_tier TEXT,
points INTEGER NOT NULL DEFAULT 0,
watch_minutes INTEGER NOT NULL DEFAULT 0,
greeting TEXT,
notes TEXT,
pronouns TEXT,
muted_greeting INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX idx_users_login ON users(login);
CREATE TABLE user_streams (
user_id TEXT NOT NULL REFERENCES users(id),
stream_id TEXT NOT NULL REFERENCES streams(id),
first_ts TEXT NOT NULL,
last_ts TEXT NOT NULL,
messages INTEGER NOT NULL DEFAULT 0,
minutes INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (user_id, stream_id)
);
CREATE TABLE chat_messages (
id TEXT PRIMARY KEY,
stream_id TEXT REFERENCES streams(id),
ts TEXT NOT NULL,
user_id TEXT NOT NULL REFERENCES users(id),
text TEXT NOT NULL,
emotes TEXT,
is_first INTEGER NOT NULL DEFAULT 0,
is_command INTEGER NOT NULL DEFAULT 0,
bits INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX idx_chat_stream_ts ON chat_messages(stream_id, ts);
CREATE INDEX idx_chat_user ON chat_messages(user_id, ts);
CREATE TABLE clips (
id TEXT PRIMARY KEY,
twitch_clip_id TEXT,
stream_id TEXT REFERENCES streams(id),
created_at TEXT NOT NULL,
trigger TEXT NOT NULL,
trigger_score REAL,
source TEXT NOT NULL,
raw_path TEXT,
vertical_path TEXT,
subtitle_path TEXT,
duration_s REAL,
title TEXT,
transcript TEXT,
status TEXT NOT NULL,
score REAL,
uploaded_to TEXT
);
CREATE INDEX idx_clips_stream ON clips(stream_id, created_at);