Files
Levyraati26_go/migrations/001_init.sql
T
Esa Kataja 0f15ae0bfc Release 2026.08.02-1
SQLite replaces Postgres, and two fixes from using the thing.

- The database is a file under ./storage instead of a second container. Ten
  members never needed a database server, and the driver is pure Go, so the
  build stays CGO_ENABLED=0 and the dependency count is unchanged. One bind
  mount is now the whole backup: no pgdata, no healthcheck-gated depends_on,
  no startup retry loop. Timestamps are UTC text, idle_ttl is seconds, the
  divisive/unified boards carry their own stddev, and foreign keys are on by
  pragma. Tests get a database file each and run without any setup
- Invites are copied, not clicked. An invite is something to send, and the
  anchor opened the join form in the admin's own browser
- Feedback asks for more than faults: the footer reads "Ongelmia? Ideoita?
  Palautetta?" and the page behind it invites ideas rather than only bugs
- Kuuntele YouTubessa opens in a new tab, so a half-typed review survives it
2026-08-02 20:58:52 +03:00

96 lines
3.5 KiB
SQL

-- Timestamps are declared `timestamp` and hold UTC 'YYYY-MM-DD HH:MM:SS': the declared type is what
-- makes the driver hand them back as time.Time, and a fixed-width UTC string is what makes
-- `order by created_at` and `expires_at > datetime('now')` mean what they say.
create table users (
id integer primary key autoincrement,
name text not null,
email text not null unique,
password_hash text not null,
avatar text,
banned integer not null default 0,
created_at timestamp not null default (datetime('now'))
);
create table sessions (
token text primary key,
user_id integer not null references users (id) on delete cascade,
idle_ttl integer not null, -- seconds; SQLite has no interval type
expires_at timestamp not null,
created_at timestamp not null default (datetime('now'))
);
create index sessions_user on sessions (user_id);
create table invites (
id integer primary key autoincrement,
code text not null unique,
is_valid integer not null default 1,
created_at timestamp not null default (datetime('now'))
);
create table songs (
id integer primary key autoincrement,
title text not null,
artist text not null,
genre text not null,
description text,
-- LRC or plain text, told apart by whether the first line starts with '['. Not covered by the
-- lock: nobody reviewed the lyrics.
lyrics text,
audio_file text not null,
duration_seconds integer not null,
source_url text,
submitted_by integer not null references users (id),
created_at timestamp not null default (datetime('now'))
);
create index songs_created_at on songs (created_at desc);
create table submissions (
id integer primary key autoincrement,
user_id integer not null references users (id) on delete cascade,
status text not null default 'queued',
status_msg text,
source_url text,
tmp_path text,
title text,
artist text,
genre text,
description text,
lyrics text,
created_at timestamp not null default (datetime('now')),
constraint submissions_status check (
status in ('queued', 'downloading', 'converting', 'ready', 'failed')
)
);
-- The submission quota (5 per rolling 24h, failures excluded) reads this.
create index submissions_user_created on submissions (user_id, created_at desc);
create table reviews (
id integer primary key autoincrement,
song_id integer not null references songs (id) on delete cascade,
reviewer_id integer not null references users (id),
score integer not null check (score between 1 and 100),
text text not null,
created_at timestamp not null default (datetime('now')),
updated_at timestamp not null default (datetime('now')),
unique (song_id, reviewer_id)
);
create index reviews_song on reviews (song_id);
-- The queue asks "songs this member has not reviewed" — that lookup is by reviewer.
create index reviews_reviewer_song on reviews (reviewer_id, song_id);
create table reports (
id integer primary key autoincrement,
user_id integer not null references users (id) on delete cascade,
body text not null,
page text,
user_agent text,
resolved_at timestamp,
created_at timestamp not null default (datetime('now'))
);