first commit
This commit is contained in:
@@ -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.';
|
||||
Reference in New Issue
Block a user