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
112 lines
3.3 KiB
SQL
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);
|