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
This commit is contained in:
1 parent
4ceb61f095
commit
d07c18441b
82 files changed
+2775
-838
No files matched your search
@@ -0,0 +1,111 @@
|
||||
-- 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);
|
||||
Reference in new issue
Block a user