This commit is contained in:
parent
f055fe609c
commit
30e1cde3b2
|
|
@ -1,183 +1,223 @@
|
||||||
|
|
||||||
DO $$
|
DO $$
|
||||||
BEGIN
|
BEGIN
|
||||||
IF NOT EXISTS(SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'storage') THEN
|
IF NOT EXISTS (
|
||||||
|
SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'storage'
|
||||||
|
) THEN
|
||||||
CREATE SCHEMA storage;
|
CREATE SCHEMA storage;
|
||||||
END IF;
|
END IF;
|
||||||
END$$;
|
END$$;
|
||||||
|
|
||||||
DO $$
|
DO $$
|
||||||
DECLARE
|
DECLARE
|
||||||
install_roles text = COALESCE(current_setting('storage.install_roles', true), 'true');
|
install_roles text := COALESCE(current_setting('storage.install_roles', true), 'true');
|
||||||
anon_role text = COALESCE(current_setting('storage.anon_role', true), 'anon');
|
anon_role text := COALESCE(current_setting('storage.anon_role', true), 'anon');
|
||||||
authenticated_role text = COALESCE(current_setting('storage.authenticated_role', true), 'authenticated');
|
authenticated_role text := COALESCE(current_setting('storage.authenticated_role', true), 'authenticated');
|
||||||
service_role text = COALESCE(current_setting('storage.service_role', true), 'service_role');
|
service_role text := COALESCE(current_setting('storage.service_role', true), 'service_role');
|
||||||
BEGIN
|
BEGIN
|
||||||
IF install_roles != 'true' THEN
|
IF install_roles != 'true' THEN RETURN; END IF;
|
||||||
RETURN;
|
|
||||||
|
-- 角色存在判断后再建
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = anon_role) THEN
|
||||||
|
EXECUTE 'CREATE ROLE ' || quote_ident(anon_role) || ' NOLOGIN NOINHERIT';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Install ROLES
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = authenticated_role) THEN
|
||||||
EXECUTE 'CREATE ROLE IF NOT EXISTS ' || anon_role || ' NOLOGIN NOINHERIT';
|
EXECUTE 'CREATE ROLE ' || quote_ident(authenticated_role) || ' NOLOGIN NOINHERIT';
|
||||||
EXECUTE 'CREATE ROLE IF NOT EXISTS ' || authenticated_role || ' NOLOGIN NOINHERIT';
|
END IF;
|
||||||
EXECUTE 'CREATE ROLE IF NOT EXISTS ' || service_role || ' NOLOGIN NOINHERIT bypassrls';
|
|
||||||
|
|
||||||
-- create user authenticator noinherit;
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = service_role) THEN
|
||||||
IF NOT EXISTS (
|
EXECUTE 'CREATE ROLE ' || quote_ident(service_role) || ' NOLOGIN NOINHERIT BYPASSRLS';
|
||||||
SELECT 1
|
END IF;
|
||||||
FROM pg_roles
|
|
||||||
WHERE rolname = 'authenticator'
|
|
||||||
) THEN
|
|
||||||
EXECUTE 'create user authenticator noinherit;';
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
EXECUTE 'grant ' || anon_role || ' to authenticator';
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticator') THEN
|
||||||
EXECUTE 'grant ' || authenticated_role || ' to authenticator';
|
CREATE USER authenticator NOINHERIT;
|
||||||
EXECUTE 'grant ' || service_role || ' to authenticator';
|
END IF;
|
||||||
grant postgres to authenticator;
|
|
||||||
|
|
||||||
EXECUTE 'grant usage on schema storage to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
-- 授权角色
|
||||||
|
EXECUTE 'GRANT ' || quote_ident(anon_role) || ' TO authenticator';
|
||||||
|
EXECUTE 'GRANT ' || quote_ident(authenticated_role) || ' TO authenticator';
|
||||||
|
EXECUTE 'GRANT ' || quote_ident(service_role) || ' TO authenticator';
|
||||||
|
|
||||||
EXECUTE 'alter default privileges in schema storage grant all on tables to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
-- 不再尝试 GRANT postgres TO authenticator(禁止行为)
|
||||||
EXECUTE 'alter default privileges in schema storage grant all on functions to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
|
||||||
EXECUTE 'alter default privileges in schema storage grant all on sequences to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
-- schema usage 与默认权限
|
||||||
|
EXECUTE 'GRANT USAGE ON SCHEMA storage TO ' ||
|
||||||
|
quote_ident(anon_role) || ',' ||
|
||||||
|
quote_ident(authenticated_role) || ',' ||
|
||||||
|
quote_ident(service_role);
|
||||||
|
|
||||||
|
EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA storage GRANT ALL ON TABLES TO ' ||
|
||||||
|
quote_ident(anon_role) || ',' || quote_ident(authenticated_role) || ',' || quote_ident(service_role);
|
||||||
|
|
||||||
|
EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA storage GRANT ALL ON FUNCTIONS TO ' ||
|
||||||
|
quote_ident(anon_role) || ',' || quote_ident(authenticated_role) || ',' || quote_ident(service_role);
|
||||||
|
|
||||||
|
EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA storage GRANT ALL ON SEQUENCES TO ' ||
|
||||||
|
quote_ident(anon_role) || ',' || quote_ident(authenticated_role) || ',' || quote_ident(service_role);
|
||||||
END$$;
|
END$$;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS storage.migrations (
|
||||||
CREATE TABLE IF NOT EXISTS "storage"."migrations" (
|
|
||||||
id integer PRIMARY KEY,
|
id integer PRIMARY KEY,
|
||||||
name varchar(100) UNIQUE NOT NULL,
|
name varchar(100) UNIQUE NOT NULL,
|
||||||
hash varchar(40) NOT NULL, -- sha1 hex encoded hash of the file name and contents, to ensure it hasn't been altered since applying the migration
|
hash varchar(40) NOT NULL,
|
||||||
executed_at timestamp DEFAULT current_timestamp
|
executed_at timestamp DEFAULT current_timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "storage"."buckets" (
|
CREATE TABLE IF NOT EXISTS storage.buckets (
|
||||||
"id" text not NULL,
|
id text NOT NULL,
|
||||||
"name" text NOT NULL,
|
name text NOT NULL,
|
||||||
"owner" uuid,
|
owner uuid,
|
||||||
"created_at" timestamptz DEFAULT now(),
|
created_at timestamptz DEFAULT now(),
|
||||||
"updated_at" timestamptz DEFAULT now(),
|
updated_at timestamptz DEFAULT now(),
|
||||||
PRIMARY KEY ("id")
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "bname" ON "storage"."buckets" USING BTREE ("name");
|
CREATE UNIQUE INDEX IF NOT EXISTS bname ON storage.buckets (name);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "storage"."objects" (
|
CREATE TABLE IF NOT EXISTS storage.objects (
|
||||||
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
id uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||||
"bucket_id" text,
|
bucket_id text,
|
||||||
"name" text,
|
name text,
|
||||||
"owner" uuid,
|
owner uuid,
|
||||||
"created_at" timestamptz DEFAULT now(),
|
created_at timestamptz DEFAULT now(),
|
||||||
"updated_at" timestamptz DEFAULT now(),
|
updated_at timestamptz DEFAULT now(),
|
||||||
"last_accessed_at" timestamptz DEFAULT now(),
|
last_accessed_at timestamptz DEFAULT now(),
|
||||||
"metadata" jsonb,
|
metadata jsonb,
|
||||||
CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY ("bucket_id") REFERENCES "storage"."buckets"("id"),
|
CONSTRAINT objects_bucketId_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id),
|
||||||
PRIMARY KEY ("id")
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "bucketid_objname" ON "storage"."objects" USING BTREE ("bucket_id","name");
|
CREATE UNIQUE INDEX IF NOT EXISTS bucketid_objname ON storage.objects (bucket_id, name);
|
||||||
CREATE INDEX IF NOT EXISTS name_prefix_search ON storage.objects(name text_pattern_ops);
|
CREATE INDEX IF NOT EXISTS name_prefix_search ON storage.objects (name text_pattern_ops);
|
||||||
|
|
||||||
ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
drop function if exists storage.foldername;
|
-- 安全函数定义
|
||||||
CREATE OR REPLACE FUNCTION storage.foldername(name text)
|
CREATE OR REPLACE FUNCTION storage.foldername(name text)
|
||||||
RETURNS text[]
|
RETURNS text[]
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $function$
|
AS $$
|
||||||
DECLARE
|
DECLARE _parts text[];
|
||||||
_parts text[];
|
|
||||||
BEGIN
|
BEGIN
|
||||||
select string_to_array(name, '/') into _parts;
|
SELECT string_to_array(name, '/') INTO _parts;
|
||||||
return _parts[1:array_length(_parts,1)-1];
|
RETURN _parts[1:array_length(_parts,1)-1];
|
||||||
END
|
END
|
||||||
$function$;
|
$$;
|
||||||
|
|
||||||
drop function if exists storage.filename;
|
|
||||||
CREATE OR REPLACE FUNCTION storage.filename(name text)
|
CREATE OR REPLACE FUNCTION storage.filename(name text)
|
||||||
RETURNS text
|
RETURNS text
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $function$
|
AS $$
|
||||||
DECLARE
|
DECLARE _parts text[];
|
||||||
_parts text[];
|
|
||||||
BEGIN
|
BEGIN
|
||||||
select string_to_array(name, '/') into _parts;
|
SELECT string_to_array(name, '/') INTO _parts;
|
||||||
return _parts[array_length(_parts,1)];
|
RETURN _parts[array_length(_parts,1)];
|
||||||
END
|
END
|
||||||
$function$;
|
$$;
|
||||||
|
|
||||||
drop function if exists storage.extension;
|
|
||||||
CREATE OR REPLACE FUNCTION storage.extension(name text)
|
CREATE OR REPLACE FUNCTION storage.extension(name text)
|
||||||
RETURNS text
|
RETURNS text
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $function$
|
AS $$
|
||||||
DECLARE
|
DECLARE _parts text[]; _filename text;
|
||||||
_parts text[];
|
|
||||||
_filename text;
|
|
||||||
BEGIN
|
BEGIN
|
||||||
select string_to_array(name, '/') into _parts;
|
SELECT string_to_array(name, '/') INTO _parts;
|
||||||
select _parts[array_length(_parts,1)] into _filename;
|
SELECT _parts[array_length(_parts,1)] INTO _filename;
|
||||||
-- @todo return the last part instead of 2
|
RETURN reverse(split_part(reverse(_filename), '.', 1));
|
||||||
return reverse(split_part(reverse(_filename), '.', 1));
|
|
||||||
END
|
END
|
||||||
$function$;
|
$$;
|
||||||
|
|
||||||
-- @todo can this query be optimised further?
|
CREATE OR REPLACE FUNCTION storage.search(
|
||||||
drop function if exists storage.search;
|
prefix text, bucketname text, limits int DEFAULT 100, levels int DEFAULT 1, offsets int DEFAULT 0
|
||||||
CREATE OR REPLACE FUNCTION storage.search(prefix text, bucketname text, limits int DEFAULT 100, levels int DEFAULT 1, offsets int DEFAULT 0)
|
)
|
||||||
RETURNS TABLE (
|
RETURNS TABLE (
|
||||||
name text,
|
name text,
|
||||||
id uuid,
|
id uuid,
|
||||||
updated_at TIMESTAMPTZ,
|
updated_at timestamptz,
|
||||||
created_at TIMESTAMPTZ,
|
created_at timestamptz,
|
||||||
last_accessed_at TIMESTAMPTZ,
|
last_accessed_at timestamptz,
|
||||||
metadata jsonb
|
metadata jsonb
|
||||||
)
|
)
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $function$
|
AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
return query
|
RETURN QUERY
|
||||||
with files_folders as (
|
WITH files_folders AS (
|
||||||
select ((string_to_array(objects.name, '/'))[levels]) as folder
|
SELECT (string_to_array(objects.name, '/'))[levels] AS folder
|
||||||
from objects
|
FROM storage.objects
|
||||||
where objects.name ilike prefix || '%'
|
WHERE objects.name ILIKE prefix || '%'
|
||||||
and bucket_id = bucketname
|
AND bucket_id = bucketname
|
||||||
GROUP by folder
|
GROUP BY folder
|
||||||
limit limits
|
LIMIT limits OFFSET offsets
|
||||||
offset offsets
|
)
|
||||||
)
|
SELECT
|
||||||
select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders
|
files_folders.folder AS name,
|
||||||
left join objects
|
objects.id,
|
||||||
on prefix || files_folders.folder = objects.name and objects.bucket_id=bucketname;
|
objects.updated_at,
|
||||||
|
objects.created_at,
|
||||||
|
objects.last_accessed_at,
|
||||||
|
objects.metadata
|
||||||
|
FROM files_folders
|
||||||
|
LEFT JOIN storage.objects
|
||||||
|
ON prefix || files_folders.folder = objects.name
|
||||||
|
AND objects.bucket_id = bucketname;
|
||||||
END
|
END
|
||||||
$function$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
DO $$
|
DO $$
|
||||||
DECLARE
|
DECLARE
|
||||||
install_roles text = COALESCE(current_setting('storage.install_roles', true), 'true');
|
install_roles text := COALESCE(current_setting('storage.install_roles', true), 'true');
|
||||||
super_user text = COALESCE(current_setting('storage.super_user', true), 'supabase_storage_admin');
|
super_user text := COALESCE(current_setting('storage.super_user', true), 'supabase_storage_admin');
|
||||||
BEGIN
|
BEGIN
|
||||||
IF install_roles != 'true' THEN
|
IF install_roles != 'true' THEN RETURN; END IF;
|
||||||
RETURN;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = super_user) THEN
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = super_user) THEN
|
||||||
EXECUTE 'CREATE USER ' || super_user || ' NOINHERIT CREATEROLE LOGIN NOREPLICATION';
|
EXECUTE 'CREATE ROLE ' || quote_ident(super_user) || ' NOINHERIT CREATEROLE LOGIN NOREPLICATION';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Grant privileges to Super User
|
EXECUTE 'GRANT ALL PRIVILEGES ON SCHEMA storage TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'GRANT ALL PRIVILEGES ON SCHEMA storage TO ' || super_user;
|
EXECUTE 'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO ' || super_user;
|
EXECUTE 'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO ' || super_user;
|
|
||||||
|
|
||||||
IF super_user != 'postgres' THEN
|
IF super_user != 'postgres' THEN
|
||||||
EXECUTE 'ALTER USER ' || super_user || ' SET search_path = "storage"';
|
EXECUTE 'ALTER ROLE ' || quote_ident(super_user) || ' SET search_path = "storage"';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
EXECUTE 'ALTER table "storage".objects owner to ' || super_user;
|
EXECUTE 'ALTER TABLE storage.objects OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER table "storage".buckets owner to ' || super_user;
|
EXECUTE 'ALTER TABLE storage.buckets OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER table "storage".migrations OWNER TO ' || super_user;
|
EXECUTE 'ALTER TABLE storage.migrations OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER function "storage".foldername(text) owner to ' || super_user;
|
|
||||||
EXECUTE 'ALTER function "storage".filename(text) owner to ' || super_user;
|
EXECUTE 'ALTER FUNCTION storage.foldername(text) OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER function "storage".extension(text) owner to ' || super_user;
|
EXECUTE 'ALTER FUNCTION storage.filename(text) OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER function "storage".search(text,text,int,int,int) owner to ' || super_user;
|
EXECUTE 'ALTER FUNCTION storage.extension(text) OWNER TO ' || quote_ident(super_user);
|
||||||
|
EXECUTE 'ALTER FUNCTION storage.search(text,text,int,int,int) OWNER TO ' || quote_ident(super_user);
|
||||||
|
|
||||||
|
END$$;
|
||||||
|
|
||||||
|
|
||||||
|
-- ✅ 单独插入 bucket(允许重复执行)
|
||||||
|
INSERT INTO storage.buckets (id, name, owner)
|
||||||
|
VALUES ('profile_images', 'profile_images', NULL)
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
|
|
||||||
|
-- ✅ 单独定义权限策略(重复执行不会报错)
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_policies WHERE policyname = 'anon upload profile_images'
|
||||||
|
) THEN
|
||||||
|
EXECUTE $$CREATE POLICY "anon upload profile_images"
|
||||||
|
ON storage.objects
|
||||||
|
FOR INSERT
|
||||||
|
TO anon
|
||||||
|
WITH CHECK (bucket_id = 'profile_images')$$;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_policies WHERE policyname = 'anon read profile_images'
|
||||||
|
) THEN
|
||||||
|
EXECUTE $$CREATE POLICY "anon read profile_images"
|
||||||
|
ON storage.objects
|
||||||
|
FOR SELECT
|
||||||
|
TO anon
|
||||||
|
USING (bucket_id = 'profile_images')$$;
|
||||||
|
END IF;
|
||||||
END$$;
|
END$$;
|
||||||
Loading…
Reference in New Issue