diff --git a/migrations/1658909585601-hasura-storage.ts b/migrations/1658909585601-hasura-storage.ts index a8b49c96..4e2ddbea 100644 --- a/migrations/1658909585601-hasura-storage.ts +++ b/migrations/1658909585601-hasura-storage.ts @@ -31,150 +31,168 @@ export const up = async () => { log('up'); log('sql'); - await api.sql(sql` - CREATE SCHEMA IF NOT EXISTS storage; +await api.sql(sql` +CREATE SCHEMA IF NOT EXISTS storage; - CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public; - CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public; +CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; - BEGIN; +BEGIN; -- functions - CREATE OR REPLACE FUNCTION storage.set_current_timestamp_updated_at () - RETURNS TRIGGER - LANGUAGE plpgsql - AS $a$ - DECLARE - _new record; - BEGIN - _new := new; - _new. "updated_at" = now(); - RETURN _new; - END; - $a$; - - CREATE OR REPLACE FUNCTION storage.protect_default_bucket_delete () - RETURNS TRIGGER - LANGUAGE plpgsql - AS $a$ - BEGIN - IF OLD.ID = 'default' THEN - RAISE EXCEPTION 'Can not delete default bucket'; - END IF; - RETURN OLD; - END; - $a$; - - CREATE OR REPLACE FUNCTION storage.protect_default_bucket_update () - RETURNS TRIGGER - LANGUAGE plpgsql - AS $a$ - BEGIN - IF OLD.ID = 'default' AND NEW.ID <> 'default' THEN - RAISE EXCEPTION 'Can not rename default bucket'; - END IF; - RETURN NEW; - END; - $a$; - - -- tables - CREATE TABLE IF NOT EXISTS storage.buckets ( - id text NOT NULL PRIMARY KEY, - created_at timestamp with time zone DEFAULT now() NOT NULL, - updated_at timestamp with time zone DEFAULT now() NOT NULL, - download_expiration int NOT NULL DEFAULT 30, -- 30 seconds - min_upload_file_size int NOT NULL DEFAULT 1, - max_upload_file_size int NOT NULL DEFAULT 50000000, - cache_control text DEFAULT 'max-age=3600', - presigned_urls_enabled boolean NOT NULL DEFAULT TRUE - ); - - CREATE TABLE IF NOT EXISTS storage.files ( - id uuid DEFAULT public.gen_random_uuid () NOT NULL PRIMARY KEY, - link_id bigint unique, - created_at timestamp with time zone DEFAULT now() NOT NULL, - updated_at timestamp with time zone DEFAULT now() NOT NULL, - bucket_id text NOT NULL DEFAULT 'default', - name text, - size int, - mime_type text, - etag text, - is_uploaded boolean DEFAULT FALSE, - uploaded_by_user_id uuid, - uploaded_by_link_id bigint - ); - - -- constraints - DO $$ - BEGIN - IF NOT EXISTS(SELECT table_name - FROM information_schema.table_constraints - WHERE table_schema = 'storage' - AND table_name = 'files' - AND constraint_name = 'fk_bucket') - THEN - ALTER TABLE storage.files - ADD CONSTRAINT fk_bucket FOREIGN KEY (bucket_id) REFERENCES storage.buckets (id) ON UPDATE CASCADE ON DELETE CASCADE; - END IF; - END $$; - - DO $$ - BEGIN - IF NOT EXISTS(SELECT table_name - FROM information_schema.table_constraints - WHERE table_schema = 'storage' - AND table_name = 'files' - AND constraint_name = 'fk_link') - THEN - ALTER TABLE storage.files - ADD CONSTRAINT fk_link FOREIGN KEY (link_id) REFERENCES public.links (id) ON UPDATE CASCADE ON DELETE CASCADE; - END IF; - END $$; - - -- triggers - DROP TRIGGER IF EXISTS set_storage_buckets_updated_at ON storage.buckets; - CREATE TRIGGER set_storage_buckets_updated_at - BEFORE UPDATE ON storage.buckets - FOR EACH ROW - EXECUTE FUNCTION storage.set_current_timestamp_updated_at (); - - DROP TRIGGER IF EXISTS set_storage_files_updated_at ON storage.files; - CREATE TRIGGER set_storage_files_updated_at - BEFORE UPDATE ON storage.files - FOR EACH ROW - EXECUTE FUNCTION storage.set_current_timestamp_updated_at (); - - DROP TRIGGER IF EXISTS check_default_bucket_delete ON storage.buckets; - CREATE TRIGGER check_default_bucket_delete - BEFORE DELETE ON storage.buckets - FOR EACH ROW - EXECUTE PROCEDURE storage.protect_default_bucket_delete (); - - DROP TRIGGER IF EXISTS check_default_bucket_update ON storage.buckets; - CREATE TRIGGER check_default_bucket_update - BEFORE UPDATE ON storage.buckets - FOR EACH ROW - EXECUTE PROCEDURE storage.protect_default_bucket_update (); - - -- data - DO $$ - BEGIN - IF NOT EXISTS(SELECT id - FROM storage.buckets - WHERE id = 'default') - THEN - INSERT INTO storage.buckets (id) - VALUES ('default'); - END IF; - END $$; - - COMMIT; - - ALTER TABLE storage.buckets - ADD CONSTRAINT download_expiration_valid_range - CHECK (download_expiration >= 1 AND download_expiration <= 604800); - - ALTER TABLE storage.files DROP CONSTRAINT IF EXISTS fk_user; - `); +CREATE OR REPLACE FUNCTION storage.set_current_timestamp_updated_at () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $a$ +DECLARE + _new record; +BEGIN + _new := new; + _new. "updated_at" = now(); + RETURN _new; +END; +$a$; + +CREATE OR REPLACE FUNCTION storage.protect_default_bucket_delete () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $a$ +BEGIN + IF OLD.ID = 'default' THEN + RAISE EXCEPTION 'Can not delete default bucket'; + END IF; + RETURN OLD; +END; +$a$; + +CREATE OR REPLACE FUNCTION storage.protect_default_bucket_update () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $a$ +BEGIN + IF OLD.ID = 'default' AND NEW.ID <> 'default' THEN + RAISE EXCEPTION 'Can not rename default bucket'; + END IF; + RETURN NEW; +END; +$a$; + +-- tables +CREATE TABLE IF NOT EXISTS storage.buckets ( + id text NOT NULL PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + download_expiration int NOT NULL DEFAULT 30, -- 30 seconds + min_upload_file_size int NOT NULL DEFAULT 1, + max_upload_file_size int NOT NULL DEFAULT 50000000, + cache_control text DEFAULT 'max-age=3600', + presigned_urls_enabled boolean NOT NULL DEFAULT TRUE +); + +CREATE TABLE IF NOT EXISTS storage.files ( + id uuid DEFAULT public.gen_random_uuid () NOT NULL PRIMARY KEY, + link_id bigint unique, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + bucket_id text NOT NULL DEFAULT 'default', + name text, + size int, + mime_type text, + etag text, + is_uploaded boolean DEFAULT FALSE, + uploaded_by_user_id uuid, + uploaded_by_link_id bigint +); + +-- constraints +DO $$ +BEGIN + IF NOT EXISTS(SELECT table_name + FROM information_schema.table_constraints + WHERE table_schema = 'storage' + AND table_name = 'files' + AND constraint_name = 'fk_bucket') + THEN + ALTER TABLE storage.files + ADD CONSTRAINT fk_bucket FOREIGN KEY (bucket_id) REFERENCES storage.buckets (id) ON UPDATE CASCADE ON DELETE CASCADE; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS(SELECT table_name + FROM information_schema.table_constraints + WHERE table_schema = 'storage' + AND table_name = 'files' + AND constraint_name = 'fk_link') + THEN + ALTER TABLE storage.files + ADD CONSTRAINT fk_link FOREIGN KEY (link_id) REFERENCES public.links (id) ON UPDATE CASCADE ON DELETE CASCADE; + END IF; +END $$; + +-- triggers +DROP TRIGGER IF EXISTS set_storage_buckets_updated_at ON storage.buckets; +CREATE TRIGGER set_storage_buckets_updated_at + BEFORE UPDATE ON storage.buckets + FOR EACH ROW + EXECUTE FUNCTION storage.set_current_timestamp_updated_at (); + +DROP TRIGGER IF EXISTS set_storage_files_updated_at ON storage.files; +CREATE TRIGGER set_storage_files_updated_at + BEFORE UPDATE ON storage.files + FOR EACH ROW + EXECUTE FUNCTION storage.set_current_timestamp_updated_at (); + +DROP TRIGGER IF EXISTS check_default_bucket_delete ON storage.buckets; +CREATE TRIGGER check_default_bucket_delete + BEFORE DELETE ON storage.buckets + FOR EACH ROW + EXECUTE PROCEDURE storage.protect_default_bucket_delete (); + +DROP TRIGGER IF EXISTS check_default_bucket_update ON storage.buckets; +CREATE TRIGGER check_default_bucket_update + BEFORE UPDATE ON storage.buckets + FOR EACH ROW + EXECUTE PROCEDURE storage.protect_default_bucket_update (); + +-- data +DO $$ +BEGIN + IF NOT EXISTS(SELECT id + FROM storage.buckets + WHERE id = 'default') + THEN + INSERT INTO storage.buckets (id) + VALUES ('default'); + END IF; +END $$; + +COMMIT; + +ALTER TABLE storage.buckets + ADD CONSTRAINT download_expiration_valid_range + CHECK (download_expiration >= 1 AND download_expiration <= 604800); + +ALTER TABLE storage.files DROP CONSTRAINT IF EXISTS fk_user; + +ALTER TABLE "storage"."files" ADD COLUMN IF NOT EXISTS "metadata" JSONB; + +CREATE TABLE IF NOT EXISTS storage.virus ( + id uuid DEFAULT public.gen_random_uuid () NOT NULL PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + file_id UUID NOT NULL REFERENCES storage.files(id), + filename TEXT NOT NULL, + virus TEXT NOT NULL, + user_session JSONB NOT NULL +); + +DROP TRIGGER IF EXISTS set_storage_virus_updated_at ON storage.virus; +CREATE TRIGGER set_storage_virus_updated_at + BEFORE UPDATE ON storage.virus + FOR EACH ROW + EXECUTE FUNCTION storage.set_current_timestamp_updated_at (); +`); log('track'); @@ -494,6 +512,12 @@ export const down = async () => { log('sql'); await api.sql(sql` + DROP TRIGGER set_storage_virus_updated_at ON storage.virus; + + DROP TABLE IF EXISTS viruses; + + ALTER TABLE "storage"."files" DROP COLUMN IF EXISTS "metadata"; + DO $$ BEGIN IF EXISTS(SELECT table_name diff --git a/package.json b/package.json index 9fe6a110..cf0e74c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@deep-foundation/deeplinks", - "version": "0.0.574", + "version": "0.0.575", "license": "Unlicense", "type": "module", "main": "import.js",