insert into storage.buckets (id, name, public) values ('sites', 'sites', true) on conflict (id) do nothing; insert into storage.buckets (id, name, public) values ('md', 'md', true) on conflict (id) do nothing; insert into storage.buckets (id, name, public) values ('projects', 'projects', true) on conflict (id) do nothing; insert into storage.buckets (id, name, public) values ('admin', 'admin', false) on conflict (id) do nothing;
创建4个桶,前三个负责储存,第四个是放email-map.json的
继续执行
create table if notexists public.gh_site ( id uuid primary keydefault gen_random_uuid(), name text not nullunique, type text not nullcheck (type in ('html', 'md', 'project')), owner_id uuid not null, ip_address text, visit_count bigintdefault0, macos_submit_id text, created_at timestamptz default now(), updated_at timestamptz default now() );
create index if notexists idx_gh_site_owner_id on public.gh_site(owner_id); create index if notexists idx_gh_site_name on public.gh_site(name);
image
点绿色按钮,以后都点绿色的
站点主表,名称gh_site,可改,但是后面的话就得小心了。
create table if notexists public.gh_announcement ( id uuid primary keydefault gen_random_uuid(), content text not null, is_active booleandefaultfalse, created_at timestamptz default now() );
create index if notexists idx_gh_announcement_active on public.gh_announcement(is_active);
gh_announcement--全局公告
create table if notexists public.system_status ( id intprimary keydefault1check (id =1), maintenance_mode booleandefaultfalse, maintenance_message text, services jsonb default'{}'::jsonb, updated_at timestamptz default now(), updated_by text );
系统状态表
get_sites_paginated
createor replace function public.get_sites_paginated( p_limit int, p_offset int, p_order_by text default'updated_at', p_order_dir text default'DESC' ) returnstable( id uuid, name text, owner_id uuid, ip_address text, created_at timestamptz, updated_at timestamptz, total_count bigint ) language plpgsql as $$ declare order_sql text; begin order_sql := format('order by %I %s', p_order_by, p_order_dir); return query execute format(' select s.id, s.name, s.owner_id, s.ip_address, s.created_at, s.updated_at, count(*) over() as total_count from public.gh_site s %s limit $1 offset $2 ', order_sql) using p_limit, p_offset; end; $$;
get_users_paginated
createor replace function public.get_users_paginated( p_limit int, p_offset int ) returnstable( owner_id uuid, site_count bigint, created_at timestamptz, sites jsonb, total_count bigint ) language plpgsql as $$ begin return query with agg as ( select s.owner_id, count(*) as site_count, min(s.created_at) as created_at, jsonb_agg(jsonb_build_object('name', s.name, 'type', s.type)) as sites from public.gh_site s groupby s.owner_id ), total as (selectcount(*) from agg) select agg.owner_id, agg.site_count, agg.created_at, agg.sites, (select count from total) as total_count from agg orderby agg.created_at desc limit p_limit offset p_offset; end; $$;
get_site_stats
createor replace function public.get_site_stats() returnstable( total_sites bigint, total_users bigint, sites_today bigint ) language plpgsql as $$ begin return query select count(*) as total_sites, count(distinct owner_id) as total_users, count(*) filter (where created_at::date=current_date) as sites_today from public.gh_site; end; $$;
increment_visit
createor replace function public.increment_visit(p_name text) returns void language plpgsql as $$ begin update public.gh_site set visit_count = visit_count +1 where name = p_name; end; $$;
rls策略
alter table public.gh_site disable row level security; alter table public.gh_announcement disable row level security; alter table public.system_status disable row level security;
updated_at 自动更新触发器
createor replace function public.update_updated_at() returnstriggerlanguage plpgsql as $$ begin new.updated_at = now(); returnnew; end; $$;
createtrigger trigger_gh_site_updated_at before updateon public.gh_site foreachrowexecutefunction public.update_updated_at();