184 lines
8.2 KiB
PL/PgSQL
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$;
|