BEGIN; CREATE TABLE IF NOT EXISTS rhombus_challenge ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, description TEXT NOT NULL, flag TEXT NOT NULL, category_id INTEGER NOT NULL, author_id INTEGER NOT NULL, ticket_template TEXT, healthscript TEXT, healthy BOOLEAN, last_healthcheck INTEGER, score_type INTEGER NOT NULL, static_points INTEGER, FOREIGN KEY (category_id) REFERENCES rhombus_category(id), FOREIGN KEY (author_id) REFERENCES rhombus_author(id) ); CREATE TABLE IF NOT EXISTS rhombus_file_attachment ( challenge_id INTEGER NOT NULL, name TEXT NOT NULL, url TEXT NOT NULL, hash TEXT, PRIMARY KEY (challenge_id, url), FOREIGN KEY (challenge_id) REFERENCES rhombus_challenge(id) ); CREATE TABLE IF NOT EXISTS rhombus_file ( hash TEXT NOT NULL UNIQUE, filename TEXT NOT NULL, contents BLOB NOT NULL, PRIMARY KEY (hash) ); CREATE TABLE IF NOT EXISTS rhombus_division ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS rhombus_author ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, avatar TEXT NOT NULL, discord_id INTEGER NOT NULL UNIQUE ); CREATE TABLE IF NOT EXISTS rhombus_points_snapshot ( team_id INTEGER NOT NULL, division_id INTEGER NOT NULL, at INTEGER NOT NULL DEFAULT(strftime('%s', 'now')), points INTEGER NOT NULL, PRIMARY KEY (team_id, division_id, at), FOREIGN KEY (team_id) REFERENCES rhombus_team(id), FOREIGN KEY (division_id) REFERENCES rhombus_division(id) ); CREATE TABLE IF NOT EXISTS rhombus_category ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, color TEXT NOT NULL, sequence INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS rhombus_solve ( challenge_id INTEGER NOT NULL, user_id INTEGER NOT NULL, team_id INTEGER NOT NULL, solved_at INTEGER NOT NULL DEFAULT(strftime('%s', 'now')), PRIMARY KEY (challenge_id, team_id), FOREIGN KEY (challenge_id) REFERENCES rhombus_challenge(id), FOREIGN KEY (user_id) REFERENCES rhombus_user(id), FOREIGN KEY (team_id) REFERENCES rhombus_team(id) ); CREATE TABLE IF NOT EXISTS rhombus_user ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, avatar TEXT NOT NULL, discord_id INTEGER UNIQUE, ctftime_id INTEGER UNIQUE, password TEXT, team_id INTEGER NOT NULL, owner_team_id INTEGER NOT NULL, disabled BOOLEAN NOT NULL DEFAULT(FALSE), is_admin BOOLEAN NOT NULL DEFAULT(FALSE), FOREIGN KEY (team_id) REFERENCES rhombus_team(id), FOREIGN KEY (owner_team_id) REFERENCES rhombus_team(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS rhombus_user_historical_names ( user_id INTEGER NOT NULL, name TEXT NOT NULL, at INTEGER NOT NULL DEFAULT(strftime('%s', 'now')), PRIMARY KEY (user_id, name, at), FOREIGN KEY (user_id) REFERENCES rhombus_user(id) ); CREATE TABLE IF NOT EXISTS rhombus_user_division ( user_id INTEGER NOT NULL, division_id INTEGER NOT NULL, PRIMARY KEY (user_id, division_id), FOREIGN KEY (user_id) REFERENCES rhombus_user(id), FOREIGN KEY (division_id) REFERENCES rhombus_division(id) ); CREATE TABLE IF NOT EXISTS rhombus_email ( email TEXT NOT NULL UNIQUE, user_id INTEGER NOT NULL, code TEXT UNIQUE, PRIMARY KEY (email, user_id), FOREIGN KEY (user_id) REFERENCES rhombus_user(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS rhombus_email_signin ( email TEXT NOT NULL, code TEXT UNIQUE, expires INTEGER NOT NULL DEFAULT(strftime('%s', 'now', '+10 minutes')), PRIMARY KEY (email) ); CREATE TRIGGER IF NOT EXISTS rhombus_email_signin_autodelete BEFORE INSERT ON rhombus_email_signin BEGIN DELETE FROM rhombus_email_signin WHERE expires < strftime('%s', 'now'); END; CREATE TABLE IF NOT EXISTS rhombus_writeup ( user_id INTEGER NOT NULL, challenge_id INTEGER NOT NULL, url TEXT NOT NULL, PRIMARY KEY (user_id, challenge_id), FOREIGN KEY (user_id) REFERENCES rhombus_user(id), FOREIGN KEY (challenge_id) REFERENCES rhombus_challenge(id) ); CREATE TABLE IF NOT EXISTS rhombus_team ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL UNIQUE, invite_token TEXT NOT NULL, ctftime_id INTEGER UNIQUE ); CREATE TABLE IF NOT EXISTS rhombus_team_historical_names ( team_id INTEGER NOT NULL, name TEXT NOT NULL, at INTEGER NOT NULL DEFAULT(strftime('%s', 'now')), PRIMARY KEY (team_id, name, at), FOREIGN KEY (team_id) REFERENCES rhombus_team(id) ); CREATE TABLE IF NOT EXISTS rhombus_team_division ( team_id INTEGER NOT NULL, division_id INTEGER NOT NULL, PRIMARY KEY (team_id, division_id), FOREIGN KEY (team_id) REFERENCES rhombus_team(id), FOREIGN KEY (division_id) REFERENCES rhombus_division(id) ); CREATE TABLE IF NOT EXISTS rhombus_team_division_last_edit ( team_id INTEGER NOT NULL, division_id INTEGER NOT NULL, last_edit_at INTEGER NOT NULL DEFAULT(strftime('%s', 'now')), PRIMARY KEY (team_id, division_id), FOREIGN KEY (team_id) REFERENCES rhombus_team(id), FOREIGN KEY (division_id) REFERENCES rhombus_division(id) ); CREATE TABLE IF NOT EXISTS rhombus_ticket ( ticket_number INTEGER NOT NULL UNIQUE, user_id INTEGER NOT NULL, challenge_id INTEGER NOT NULL, opened_at INTEGER NOT NULL DEFAULT(strftime('%s', 'now')), closed_at INTEGER, discord_channel_id INTEGER NOT NULL UNIQUE, discord_last_message_id INTEGER, PRIMARY KEY (ticket_number), FOREIGN KEY (user_id) REFERENCES rhombus_user(id), FOREIGN KEY (challenge_id) REFERENCES rhombus_challenge(id) ); CREATE TABLE IF NOT EXISTS rhombus_ticket_email_message_id_reference ( message_id TEXT NOT NULL, ticket_number INTEGER NOT NULL, user_sent BOOLEAN NOT NULL DEFAULT(FALSE), PRIMARY KEY (message_id), FOREIGN KEY (ticket_number) REFERENCES rhombus_ticket(ticket_number) ); CREATE TABLE IF NOT EXISTS rhombus_ticket_number_counter ( ticket_number INTEGER NOT NULL ); INSERT OR IGNORE INTO rhombus_ticket_number_counter (ticket_number) VALUES (0); CREATE TABLE IF NOT EXISTS rhombus_config ( id INTEGER PRIMARY KEY NOT NULL, config TEXT ); CREATE VIEW IF NOT EXISTS rhombus_challenge_division_points AS SELECT rhombus_challenge.id AS challenge_id, rhombus_division.id AS division_id, CASE WHEN rhombus_challenge.score_type = 0 THEN MAX(ROUND((((100 - 500) / (50. * 50)) * (COUNT(DISTINCT rhombus_team.id) * COUNT(DISTINCT rhombus_team.id))) + 500), 100) ELSE rhombus_challenge.static_points END AS points, COUNT(DISTINCT rhombus_team.id) AS solves FROM rhombus_challenge CROSS JOIN rhombus_division LEFT JOIN rhombus_solve ON rhombus_challenge.id = rhombus_solve.challenge_id AND rhombus_solve.team_id IN ( SELECT rhombus_team.id FROM rhombus_team JOIN rhombus_team_division ON rhombus_team.id = rhombus_team_division.team_id WHERE rhombus_team_division.division_id = rhombus_division.id ) LEFT JOIN rhombus_team ON rhombus_solve.team_id = rhombus_team.id GROUP BY rhombus_challenge.id, rhombus_division.id; CREATE VIEW IF NOT EXISTS rhombus_team_division_points AS SELECT tdp.team_id, division_id, points, last_solved_at FROM ( SELECT rhombus_team_division.team_id, rhombus_team_division.division_id, SUM(points) as points FROM rhombus_team_division JOIN ( SELECT DISTINCT rhombus_solve.team_id, rhombus_challenge_division_points.challenge_id, rhombus_challenge_division_points.division_id, rhombus_challenge_division_points.points FROM rhombus_challenge_division_points JOIN rhombus_solve ON rhombus_challenge_division_points.challenge_id = rhombus_solve.challenge_id ) AS unique_solves ON rhombus_team_division.team_id = unique_solves.team_id AND rhombus_team_division.division_id = unique_solves.division_id GROUP BY rhombus_team_division.team_id, rhombus_team_division.division_id ) as tdp JOIN ( SELECT rhombus_team.id as team_id, MAX(rhombus_solve.solved_at) as last_solved_at FROM rhombus_team JOIN rhombus_solve ON rhombus_solve.team_id = rhombus_team.id GROUP BY rhombus_team.id ) as ls ON tdp.team_id = ls.team_id; CREATE TABLE IF NOT EXISTS rhombus_track ( id INTEGER PRIMARY KEY NOT NULL, ip BLOB NOT NULL, user_agent TEXT NOT NULL, last_seen_at INTEGER NOT NULL DEFAULT(strftime('%s', 'now')), requests INTEGER NOT NULL, UNIQUE(ip, user_agent) ); CREATE UNIQUE INDEX IF NOT EXISTS rhombus_track_ip_user_agent ON rhombus_track(ip, user_agent); CREATE TABLE IF NOT EXISTS rhombus_track_ip ( user_id INTEGER NOT NULL, track_id INTEGER NOT NULL, PRIMARY KEY (user_id, track_id), FOREIGN KEY (track_id) REFERENCES rhombus_track(id) ON DELETE CASCADE ); -- Enforce a maximum number of `user_agent`s per ip, to make it less trivial for someone -- to fill up our db by simply spamming us with requests from different user agents CREATE TRIGGER IF NOT EXISTS rhombus_track_autodelete AFTER INSERT ON rhombus_track BEGIN DELETE FROM rhombus_track WHERE rowid = ( SELECT rowid FROM rhombus_track WHERE ip = NEW.ip AND rowid != NEW.rowid ORDER BY requests DESC LIMIT 1 OFFSET 31 ); END; COMMIT;