first commit

This commit is contained in:
luoyangwei
2026-06-06 00:49:07 +08:00
commit 2b860d201e
134 changed files with 22773 additions and 0 deletions
@@ -0,0 +1,73 @@
create table if not exists public.wg_gallery_photos (
id uuid primary key default gen_random_uuid(),
unsplash_id text not null unique,
slug text,
description text,
alt_description text,
width integer,
height integer,
color text,
blur_hash text,
likes integer,
photo_created_at timestamptz,
photo_updated_at timestamptz,
urls jsonb not null default '{}'::jsonb,
links jsonb not null default '{}'::jsonb,
user_info jsonb not null default '{}'::jsonb,
raw jsonb not null,
source text not null default 'unsplash-wallpapers',
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create table if not exists public.wg_gallery_sync_state (
source text primary key,
next_page integer not null default 1,
per_page integer not null default 20,
last_synced_at timestamptz,
last_inserted_count integer not null default 0,
last_seen_count integer not null default 0,
last_error text,
updated_at timestamptz not null default now()
);
create index if not exists wg_gallery_photos_source_created_at_idx
on public.wg_gallery_photos (source, created_at desc);
create index if not exists wg_gallery_photos_photo_created_at_idx
on public.wg_gallery_photos (photo_created_at desc);
alter table public.wg_gallery_photos enable row level security;
alter table public.wg_gallery_sync_state enable row level security;
drop policy if exists "Public can read gallery photos" on public.wg_gallery_photos;
create policy "Public can read gallery photos"
on public.wg_gallery_photos
for select
to anon, authenticated
using (true);
drop trigger if exists set_wg_gallery_photos_updated_at on public.wg_gallery_photos;
drop trigger if exists set_wg_gallery_sync_state_updated_at on public.wg_gallery_sync_state;
drop function if exists public.set_wg_updated_at();
create function public.set_wg_updated_at()
returns trigger
language plpgsql
set search_path = public
as $$
begin
new.updated_at = now();
return new;
end;
$$;
create trigger set_wg_gallery_photos_updated_at
before update on public.wg_gallery_photos
for each row
execute function public.set_wg_updated_at();
create trigger set_wg_gallery_sync_state_updated_at
before update on public.wg_gallery_sync_state
for each row
execute function public.set_wg_updated_at();
@@ -0,0 +1,209 @@
alter table if exists public.wg_gallery_photos
add column if not exists color_family text,
add column if not exists is_picked boolean not null default false,
add column if not exists picked_at timestamptz,
add column if not exists picked_by text,
add column if not exists pick_note text,
add column if not exists oss_bucket text,
add column if not exists oss_object_key text,
add column if not exists oss_url text,
add column if not exists oss_synced_at timestamptz,
add column if not exists oss_sync_error text;
create table if not exists public.wg_photo_collections (
id uuid primary key default gen_random_uuid(),
title text not null,
slug text not null unique,
description text,
cover_photo_id uuid references public.wg_gallery_photos(id) on delete set null,
is_published boolean not null default false,
published_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create table if not exists public.wg_photo_collection_items (
id uuid primary key default gen_random_uuid(),
collection_id uuid not null references public.wg_photo_collections(id) on delete cascade,
photo_id uuid not null references public.wg_gallery_photos(id) on delete cascade,
sort_order integer not null default 0,
created_at timestamptz not null default now(),
unique (collection_id, photo_id)
);
create table if not exists public.wg_app_users (
id uuid primary key default gen_random_uuid(),
auth_user_id uuid unique references auth.users(id) on delete set null,
email text unique,
password_hash text,
display_name text,
avatar_url text,
auth_provider text not null default 'email',
status text not null default 'active',
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create table if not exists public.wg_user_favorites (
user_id uuid not null references public.wg_app_users(id) on delete cascade,
photo_id uuid not null references public.wg_gallery_photos(id) on delete cascade,
created_at timestamptz not null default now(),
primary key (user_id, photo_id)
);
create table if not exists public.wg_user_settings (
user_id uuid primary key references public.wg_app_users(id) on delete cascade,
preferences jsonb not null default '{}'::jsonb,
download_preferences jsonb not null default '{}'::jsonb,
updated_at timestamptz not null default now()
);
create index if not exists wg_gallery_photos_pick_idx
on public.wg_gallery_photos (is_picked, picked_at desc, photo_created_at desc);
create index if not exists wg_gallery_photos_color_family_idx
on public.wg_gallery_photos (color_family);
create index if not exists wg_gallery_photos_color_idx
on public.wg_gallery_photos (color);
create index if not exists wg_photo_collections_published_idx
on public.wg_photo_collections (is_published, published_at desc, created_at desc);
create index if not exists wg_photo_collection_items_collection_sort_idx
on public.wg_photo_collection_items (collection_id, sort_order, created_at);
create index if not exists wg_photo_collection_items_photo_idx
on public.wg_photo_collection_items (photo_id);
create index if not exists wg_app_users_auth_user_idx
on public.wg_app_users (auth_user_id);
create index if not exists wg_app_users_email_idx
on public.wg_app_users (email);
alter table public.wg_gallery_photos enable row level security;
alter table public.wg_photo_collections enable row level security;
alter table public.wg_photo_collection_items enable row level security;
alter table public.wg_app_users enable row level security;
alter table public.wg_user_favorites enable row level security;
alter table public.wg_user_settings enable row level security;
drop policy if exists "Public can read gallery photos" on public.wg_gallery_photos;
drop policy if exists "Public can read picked gallery photos" on public.wg_gallery_photos;
create policy "Public can read picked gallery photos"
on public.wg_gallery_photos
for select
to anon, authenticated
using (is_picked = true);
drop policy if exists "Public can read published collections" on public.wg_photo_collections;
create policy "Public can read published collections"
on public.wg_photo_collections
for select
to anon, authenticated
using (is_published = true);
drop policy if exists "Public can read published collection items" on public.wg_photo_collection_items;
create policy "Public can read published collection items"
on public.wg_photo_collection_items
for select
to anon, authenticated
using (
exists (
select 1
from public.wg_photo_collections collections
where collections.id = collection_id
and collections.is_published = true
)
and exists (
select 1
from public.wg_gallery_photos photos
where photos.id = photo_id
and photos.is_picked = true
)
);
drop policy if exists "Users can read own profile" on public.wg_app_users;
create policy "Users can read own profile"
on public.wg_app_users
for select
to authenticated
using (auth_user_id = auth.uid());
drop policy if exists "Users can update own profile" on public.wg_app_users;
create policy "Users can update own profile"
on public.wg_app_users
for update
to authenticated
using (auth_user_id = auth.uid())
with check (auth_user_id = auth.uid());
drop policy if exists "Users can manage own favorites" on public.wg_user_favorites;
create policy "Users can manage own favorites"
on public.wg_user_favorites
for all
to authenticated
using (
exists (
select 1
from public.wg_app_users users
where users.id = user_id
and users.auth_user_id = auth.uid()
)
)
with check (
exists (
select 1
from public.wg_app_users users
where users.id = user_id
and users.auth_user_id = auth.uid()
)
);
drop policy if exists "Users can manage own settings" on public.wg_user_settings;
create policy "Users can manage own settings"
on public.wg_user_settings
for all
to authenticated
using (
exists (
select 1
from public.wg_app_users users
where users.id = user_id
and users.auth_user_id = auth.uid()
)
)
with check (
exists (
select 1
from public.wg_app_users users
where users.id = user_id
and users.auth_user_id = auth.uid()
)
);
grant select on table public.wg_gallery_photos to anon, authenticated;
grant select on table public.wg_photo_collections to anon, authenticated;
grant select on table public.wg_photo_collection_items to anon, authenticated;
grant select, update on table public.wg_app_users to authenticated;
grant select, insert, update, delete on table public.wg_user_favorites to authenticated;
grant select, insert, update, delete on table public.wg_user_settings to authenticated;
drop trigger if exists set_wg_photo_collections_updated_at on public.wg_photo_collections;
create trigger set_wg_photo_collections_updated_at
before update on public.wg_photo_collections
for each row
execute function public.set_wg_updated_at();
drop trigger if exists set_wg_app_users_updated_at on public.wg_app_users;
create trigger set_wg_app_users_updated_at
before update on public.wg_app_users
for each row
execute function public.set_wg_updated_at();
drop trigger if exists set_wg_user_settings_updated_at on public.wg_user_settings;
create trigger set_wg_user_settings_updated_at
before update on public.wg_user_settings
for each row
execute function public.set_wg_updated_at();
@@ -0,0 +1,15 @@
alter table if exists public.wg_app_users
add column if not exists phone text unique,
add column if not exists wechat_openid text unique,
add column if not exists wechat_unionid text,
add column if not exists last_login_at timestamptz,
add column if not exists disabled_at timestamptz;
create index if not exists wg_app_users_phone_idx
on public.wg_app_users (phone);
create index if not exists wg_app_users_wechat_openid_idx
on public.wg_app_users (wechat_openid);
create index if not exists wg_app_users_status_idx
on public.wg_app_users (status, created_at desc);
@@ -0,0 +1,72 @@
update public.wg_app_users
set status = 'active'
where status is null
or status not in ('active', 'disabled');
alter table if exists public.wg_app_users
alter column status set default 'active',
alter column status set not null;
do $$
begin
if not exists (
select 1
from pg_constraint
where conname = 'wg_app_users_status_check'
and conrelid = 'public.wg_app_users'::regclass
) then
alter table public.wg_app_users
add constraint wg_app_users_status_check
check (status in ('active', 'disabled'));
end if;
end $$;
create table if not exists public.wg_app_user_login_logs (
id uuid primary key default gen_random_uuid(),
user_id uuid references public.wg_app_users(id) on delete set null,
email text,
auth_provider text not null default 'email',
login_status text not null,
failure_reason text,
ip_address text,
user_agent text,
accept_language text,
referer text,
origin text,
device_info jsonb not null default '{}'::jsonb,
jwt_token text,
jwt_token_hash text,
jwt_expires_at timestamptz,
created_at timestamptz not null default now()
);
do $$
begin
if not exists (
select 1
from pg_constraint
where conname = 'wg_app_user_login_logs_status_check'
and conrelid = 'public.wg_app_user_login_logs'::regclass
) then
alter table public.wg_app_user_login_logs
add constraint wg_app_user_login_logs_status_check
check (login_status in ('success', 'failed', 'blocked'));
end if;
end $$;
create index if not exists wg_app_user_login_logs_user_created_idx
on public.wg_app_user_login_logs (user_id, created_at desc);
create index if not exists wg_app_user_login_logs_email_created_idx
on public.wg_app_user_login_logs (email, created_at desc);
create index if not exists wg_app_user_login_logs_status_created_idx
on public.wg_app_user_login_logs (login_status, created_at desc);
create index if not exists wg_app_user_login_logs_token_hash_idx
on public.wg_app_user_login_logs (jwt_token_hash);
alter table public.wg_app_user_login_logs enable row level security;
comment on table public.wg_app_user_login_logs is
'App user login audit logs. Written with service role and intentionally not granted to anon/authenticated.';