Files
project-gifuu/backend/Database.sql
T
2026-05-23 17:17:56 -07:00

184 lines
8.2 KiB
PL/PgSQL

DO $MAIN$
DECLARE _VERSION INTEGER;
BEGIN
/*
* FETCH SCHEMA VERSION
* Migration information is stored in the database using this nifty bit of
* logic. Please note that there is no rollback procedure so make sure you
* test as much as possible on your dev machine, thanks. @_@
*/
IF NOT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'kvs' AND table_schema = 'public') THEN
CREATE TABLE kvs (
key TEXT NOT NULL UNIQUE,
value TEXT NOT NULL
);
END IF;
SELECT value::INTEGER INTO _VERSION FROM kvs WHERE key = 'gifuu_version';
IF (SELECT _VERSION IS NULL) THEN
INSERT INTO kvs VALUES ('gifuu_updated', CURRENT_TIMESTAMP::TEXT);
INSERT INTO kvs VALUES ('gifuu_version', 0);
_VERSION := 0;
END IF;
/*
* Version: 1.0.0
* Name: Initial Release
* Description: Initialize Database for Initial Release
*/
IF (SELECT _VERSION < 1) THEN
_VERSION := 1;
RAISE NOTICE 'Upgrading to Version %', _VERSION;
-- INITIALIZATION --
CREATE EXTENSION IF NOT EXISTS pg_trgm;
IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'gifuu_backend') THEN
DROP OWNED BY gifuu_backend CASCADE;
DROP ROLE gifuu_backend;
END IF;
DROP SCHEMA IF EXISTS gifuu CASCADE;
CREATE SCHEMA gifuu;
-- TABLES --
CREATE TABLE gifuu.upload (
id BIGINT NOT NULL PRIMARY KEY, -- Upload ID
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Created At
upload_address_hash TEXT NOT NULL, -- Relevant IP Address
upload_token_hash TEXT NOT NULL, -- Relevant Edit Token
flag_sticker BOOLEAN NOT NULL, -- Is Sticker?
flag_audio BOOLEAN NOT NULL, -- Has Audio?
flag_bypass BOOLEAN NOT NULL DEFAULT FALSE, -- Ignore Reports?
encode_fps INT NOT NULL, -- Output FPS
encode_width INT NOT NULL, -- Output Width
encode_height INT NOT NULL, -- Output Height
meta_rating REAL NOT NULL, -- Model Safety Rating
meta_title TEXT NOT NULL -- User Provided Title
);
CREATE TABLE gifuu.tag (
id BIGSERIAL NOT NULL PRIMARY KEY, -- Tag ID
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Created At
label TEXT NOT NULL UNIQUE, -- Tag Name
usage INT NOT NULL DEFAULT 0 -- Tag Usage
);
CREATE TABLE gifuu.upload_tag (
gif_id BIGINT REFERENCES gifuu.upload (id) ON DELETE CASCADE, -- Relevant GIF ID
tag_id BIGINT REFERENCES gifuu.tag (id) ON DELETE CASCADE, -- Relevant Tag ID
PRIMARY KEY(gif_id, tag_id)
);
CREATE TABLE gifuu.mod_key (
token_hash TEXT NOT NULL PRIMARY KEY, -- Moderator Token
label TEXT NOT NULL -- Moderator Name / Label
);
CREATE TABLE gifuu.mod_banned (
address_hash TEXT NOT NULL PRIMARY KEY, -- Relevant IP Address
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Created At
reason TEXT -- Moderator Note
);
CREATE TABLE gifuu.mod_report (
id BIGSERIAL NOT NULL PRIMARY KEY, -- Report ID
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Created At
upload_id BIGINT NOT NULL, -- Relevant GIF ID
report_address_hash TEXT NOT NULL, -- Relevant IP Address
reason_type INT NOT NULL, -- Report Type
reason_text TEXT NOT NULL, -- Report Text
FOREIGN KEY (upload_id) REFERENCES gifuu.upload (id) ON DELETE CASCADE
);
-- INDEXES --
CREATE INDEX idx_tag_usage_popular ON gifuu.tag (usage DESC);
CREATE INDEX gin_tag_metadata ON gifuu.tag USING GIN (label gin_trgm_ops);
CREATE INDEX idx_upload_tag_gif ON gifuu.upload_tag (gif_id);
CREATE INDEX idx_upload_tag_tag ON gifuu.upload_tag (tag_id);
CREATE INDEX idx_upload_created ON gifuu.upload (created DESC);
-- TRIGGERS --
CREATE OR REPLACE FUNCTION gifuu.update_tag_usage() RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
UPDATE gifuu.tag SET usage = usage + 1 WHERE id = NEW.tag_id;
ELSIF TG_OP = 'DELETE' THEN
UPDATE gifuu.tag SET usage = usage - 1 WHERE id = OLD.tag_id;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER gifuu_tag_usage_insert
AFTER INSERT ON gifuu.upload_tag
FOR EACH ROW EXECUTE FUNCTION gifuu.update_tag_usage();
CREATE TRIGGER gifuu_tag_usage_delete
AFTER DELETE ON gifuu.upload_tag
FOR EACH ROW EXECUTE FUNCTION gifuu.update_tag_usage();
-- USERS --
CREATE ROLE gifuu_backend LOGIN NOINHERIT;
GRANT USAGE ON SCHEMA gifuu TO gifuu_backend;
GRANT ALL ON ALL TABLES IN SCHEMA gifuu TO gifuu_backend;
GRANT ALL ON ALL SEQUENCES IN SCHEMA gifuu TO gifuu_backend;
END IF;
/*
* HOUSEKEEPING
* Uses the "pg_cron" extension to enable automated maintenance without
* requiring the use of an external service, see installation guide here:
* https://github.com/citusdata/pg_cron#installing-pg_cron
*/
CREATE OR REPLACE PROCEDURE gifuu_reschedule (
_SCHEDULE TEXT,
_NAME TEXT,
_COMMAND TEXT
)
LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
-- Task Scheduling --
IF NOT EXISTS (SELECT FROM pg_available_extensions WHERE name = 'pg_cron') THEN
RAISE WARNING 'Extension "pg_cron" is unavailable, command "%" unscheduled.', _NAME;
ELSE
CREATE EXTENSION IF NOT EXISTS pg_cron;
IF EXISTS (SELECT FROM cron.job WHERE jobname = _NAME) THEN
PERFORM cron.unschedule(_NAME);
END IF;
IF _COMMAND IS NOT NULL THEN
PERFORM cron.schedule(_NAME, _SCHEDULE, _COMMAND);
RAISE NOTICE 'Scheduled "%" (%)', _NAME, _SCHEDULE;
END IF;
END IF;
-- Test Command --
IF _COMMAND IS NOT NULL THEN
RAISE NOTICE 'Testing command for task "%"', _NAME;
EXECUTE _COMMAND;
END IF;
END;
$$;
CALL gifuu_reschedule('0 0 * * *', 'gifuu:delete_unused_tags', $$ DELETE FROM gifuu.tag WHERE usage = 0; $$);
/*
* UPDATE SCHEMA VERSION
* Disabled in development to make iterative changes less annoying. Use the
* following query to enter production mode and make changes permanent:
*
* INSERT INTO kvs VALUES ('gifuu_production', 'true');
*/
IF EXISTS (SELECT FROM kvs WHERE key = 'gifuu_production') THEN
RAISE NOTICE 'Mode: Production';
UPDATE kvs SET value = _VERSION WHERE key = 'gifuu_version';
UPDATE kvs SET value = CURRENT_TIMESTAMP::TEXT WHERE key = 'gifuu_updated';
ELSE
RAISE NOTICE 'Mode: Development';
END IF;
END $MAIN$;