HEAVY: migrate accounts and logs to service/repo architecture
This commit is contained in:
parent
5c255fb34b
commit
49e14b5bc5
37 changed files with 1019 additions and 687 deletions
102
repository/postgres/migrator.go
Normal file
102
repository/postgres/migrator.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
const DB_VERSION int = 4
|
||||
|
||||
func CheckDBVersionAndMigrate(db *sqlx.DB) {
|
||||
db.MustExec("CREATE SCHEMA IF NOT EXISTS arimelody")
|
||||
db.MustExec("SET search_path TO arimelody, public")
|
||||
db.MustExec(
|
||||
"CREATE TABLE IF NOT EXISTS arimelody.schema_version (" +
|
||||
"version INTEGER PRIMARY KEY, " +
|
||||
"applied_at TIMESTAMP DEFAULT current_timestamp)",
|
||||
)
|
||||
|
||||
oldDBVersion := 0
|
||||
schemaVersionCount := 0
|
||||
err := db.Get(&schemaVersionCount, "SELECT COUNT(*) FROM schema_version")
|
||||
if err != nil { panic(err) }
|
||||
if schemaVersionCount > 0 {
|
||||
err := db.Get(&oldDBVersion, "SELECT MAX(version) FROM schema_version")
|
||||
if err != nil { panic(err) }
|
||||
}
|
||||
|
||||
for oldDBVersion < DB_VERSION {
|
||||
switch oldDBVersion {
|
||||
case 0:
|
||||
// default case; assume no database exists
|
||||
ApplyMigration(db, "000-init")
|
||||
oldDBVersion = DB_VERSION
|
||||
|
||||
case 1:
|
||||
// the irony is i actually have to awkwardly shove schema_version
|
||||
// into the old database in order for this to work LOL
|
||||
ApplyMigration(db, "001-pre-versioning")
|
||||
oldDBVersion = 2
|
||||
|
||||
case 2:
|
||||
ApplyMigration(db, "002-audit-logs")
|
||||
oldDBVersion = 3
|
||||
|
||||
case 3:
|
||||
ApplyMigration(db, "003-fail-lock")
|
||||
oldDBVersion = 4
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Database schema up to date.\n")
|
||||
}
|
||||
|
||||
//go:embed "schema-migration"
|
||||
var schemaFS embed.FS
|
||||
|
||||
func ApplyMigration(db *sqlx.DB, scriptFile string) {
|
||||
fmt.Printf("Applying schema migration %s...\n", scriptFile)
|
||||
|
||||
bytes, err := schemaFS.ReadFile("schema-migration/" + scriptFile + ".sql")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to open schema file \"%s\": %v\n", scriptFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
script := string(bytes)
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to begin migration: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(script)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to apply migration: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(
|
||||
"INSERT INTO schema_version (version, applied_at) " +
|
||||
"VALUES ($1, $2)",
|
||||
DB_VERSION,
|
||||
time.Now(),
|
||||
)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to update schema version: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to commit transaction: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
143
repository/postgres/schema-migration/000-init.sql
Normal file
143
repository/postgres/schema-migration/000-init.sql
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
--
|
||||
-- Tables
|
||||
--
|
||||
|
||||
-- Audit logs
|
||||
CREATE TABLE arimelody.auditlog (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
level int NOT NULL DEFAULT 0,
|
||||
type TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
-- Accounts
|
||||
CREATE TABLE arimelody.account (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT,
|
||||
avatar_url TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
fail_attempts INT NOT NULL DEFAULT 0,
|
||||
locked BOOLEAN DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.account ADD CONSTRAINT account_pk PRIMARY KEY (id);
|
||||
|
||||
-- Privilege
|
||||
CREATE TABLE arimelody.privilege (
|
||||
account UUID NOT NULL,
|
||||
privilege TEXT NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_pk PRIMARY KEY (account, privilege);
|
||||
|
||||
-- Invites
|
||||
CREATE TABLE arimelody.invite (
|
||||
code text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.invite ADD CONSTRAINT invite_pk PRIMARY KEY (code);
|
||||
|
||||
-- Sessions
|
||||
CREATE TABLE arimelody.session (
|
||||
token TEXT,
|
||||
user_agent TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP DEFAULT NULL,
|
||||
account UUID,
|
||||
attempt_account UUID,
|
||||
message TEXT,
|
||||
error TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_pk PRIMARY KEY (token);
|
||||
|
||||
-- TOTP methods
|
||||
CREATE TABLE arimelody.totp (
|
||||
name TEXT NOT NULL,
|
||||
account UUID NOT NULL,
|
||||
secret TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
confirmed BOOLEAN DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_pk PRIMARY KEY (account, name);
|
||||
|
||||
|
||||
|
||||
-- Artists (should be applicable to all art)
|
||||
CREATE TABLE arimelody.artist (
|
||||
id character varying(64),
|
||||
name text NOT NULL,
|
||||
website text,
|
||||
avatar text
|
||||
);
|
||||
ALTER TABLE arimelody.artist ADD CONSTRAINT artist_pk PRIMARY KEY (id);
|
||||
|
||||
-- Music releases
|
||||
CREATE TABLE arimelody.musicrelease (
|
||||
id character varying(64) NOT NULL,
|
||||
visible bool DEFAULT false,
|
||||
title text NOT NULL,
|
||||
description text,
|
||||
type text,
|
||||
release_date TIMESTAMP NOT NULL,
|
||||
artwork text,
|
||||
buyname text,
|
||||
buylink text,
|
||||
copyright text,
|
||||
copyrightURL text,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
ALTER TABLE arimelody.musicrelease ADD CONSTRAINT musicrelease_pk PRIMARY KEY (id);
|
||||
|
||||
-- Music links (external platform links under a release)
|
||||
CREATE TABLE arimelody.musiclink (
|
||||
release character varying(64) NOT NULL,
|
||||
name text NOT NULL,
|
||||
url text NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.musiclink ADD CONSTRAINT musiclink_pk PRIMARY KEY (release, name);
|
||||
|
||||
-- Music credits (artist credits under a release)
|
||||
CREATE TABLE arimelody.musiccredit (
|
||||
release character varying(64) NOT NULL,
|
||||
artist character varying(64) NOT NULL,
|
||||
role text NOT NULL,
|
||||
is_primary boolean DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_pk PRIMARY KEY (release, artist);
|
||||
|
||||
-- Music tracks (tracks under a release)
|
||||
CREATE TABLE arimelody.musictrack (
|
||||
id uuid DEFAULT gen_random_uuid(),
|
||||
title text NOT NULL,
|
||||
description text,
|
||||
lyrics text,
|
||||
preview_url text
|
||||
);
|
||||
ALTER TABLE arimelody.musictrack ADD CONSTRAINT musictrack_pk PRIMARY KEY (id);
|
||||
|
||||
-- Music release/track pairs
|
||||
CREATE TABLE arimelody.musicreleasetrack (
|
||||
release character varying(64) NOT NULL,
|
||||
track uuid NOT NULL,
|
||||
number integer NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT musicreleasetrack_pk PRIMARY KEY (release, track);
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Foreign keys
|
||||
--
|
||||
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_attempt_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_artist_fk FOREIGN KEY (artist) REFERENCES artist(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_release_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musiclink ADD CONSTRAINT musiclink_release_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON UPDATE CASCADE ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT music_pair_trackref_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT music_pair_releaseref_fk FOREIGN KEY (track) REFERENCES musictrack(id) ON DELETE CASCADE;
|
||||
58
repository/postgres/schema-migration/001-pre-versioning.sql
Normal file
58
repository/postgres/schema-migration/001-pre-versioning.sql
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
--
|
||||
-- New items
|
||||
--
|
||||
|
||||
-- Accounts
|
||||
CREATE TABLE arimelody.account (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT,
|
||||
avatar_url TEXT,
|
||||
created_at TIMESTAMP DEFAULT current_timestamp
|
||||
);
|
||||
ALTER TABLE arimelody.account ADD CONSTRAINT account_pk PRIMARY KEY (id);
|
||||
|
||||
-- Privilege
|
||||
CREATE TABLE arimelody.privilege (
|
||||
account UUID NOT NULL,
|
||||
privilege TEXT NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_pk PRIMARY KEY (account, privilege);
|
||||
|
||||
-- Invites
|
||||
CREATE TABLE arimelody.invite (
|
||||
code text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.invite ADD CONSTRAINT invite_pk PRIMARY KEY (code);
|
||||
|
||||
-- Sessions
|
||||
CREATE TABLE arimelody.session (
|
||||
token TEXT,
|
||||
user_agent TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP DEFAULT NULL,
|
||||
account UUID,
|
||||
attempt_account UUID,
|
||||
message TEXT,
|
||||
error TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_pk PRIMARY KEY (token);
|
||||
|
||||
-- TOTP methods
|
||||
CREATE TABLE arimelody.totp (
|
||||
name TEXT NOT NULL,
|
||||
account UUID NOT NULL,
|
||||
secret TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
confirmed BOOLEAN DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_pk PRIMARY KEY (account, name);
|
||||
|
||||
-- Foreign keys
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_attempt_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
12
repository/postgres/schema-migration/002-audit-logs.sql
Normal file
12
repository/postgres/schema-migration/002-audit-logs.sql
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
-- Audit logs
|
||||
CREATE TABLE arimelody.auditlog (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
level int NOT NULL DEFAULT 0,
|
||||
type TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
-- Need moar timestamps
|
||||
ALTER TABLE arimelody.musicrelease ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT current_timestamp;
|
||||
ALTER TABLE arimelody.account ALTER COLUMN created_at SET NOT NULL;
|
||||
3
repository/postgres/schema-migration/003-fail-lock.sql
Normal file
3
repository/postgres/schema-migration/003-fail-lock.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-- it would be nice to prevent brute-forcing
|
||||
ALTER TABLE arimelody.account ADD COLUMN fail_attempts INT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE arimelody.account ADD COLUMN locked BOOLEAN DEFAULT false;
|
||||
Loading…
Add table
Add a link
Reference in a new issue