chore: init monorepo snapshot
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
alter table public.documents
|
||||
add column if not exists wide_layout boolean not null default false;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists use_small_text boolean not null default false;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists show_heading_numbers boolean not null default true;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists show_toc boolean not null default false;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists show_structure boolean not null default false;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists protect_editing boolean not null default false;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists show_word_count boolean not null default true;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists word_count integer not null default 0;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists character_count integer not null default 0;
|
||||
|
||||
alter table public.documents
|
||||
add column if not exists block_count integer not null default 0;
|
||||
@@ -0,0 +1,83 @@
|
||||
create table if not exists public.media_assets (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
workspace_id uuid not null references public.workspaces(id) on delete cascade,
|
||||
document_id uuid not null references public.documents(id) on delete cascade,
|
||||
asset_type text not null default 'image',
|
||||
file_url text,
|
||||
thumbnail_url text,
|
||||
ocr_text text,
|
||||
ocr_status text default 'pending',
|
||||
created_by uuid references public.profiles(id) on delete set null,
|
||||
created_at timestamptz not null default timezone('utc', now()),
|
||||
updated_at timestamptz not null default timezone('utc', now())
|
||||
);
|
||||
|
||||
create index if not exists media_assets_workspace_idx on public.media_assets (workspace_id, created_at desc);
|
||||
create index if not exists media_assets_document_idx on public.media_assets (document_id);
|
||||
create index if not exists media_assets_ocr_text_idx on public.media_assets using gin (to_tsvector('simple', coalesce(ocr_text, '')));
|
||||
|
||||
create or replace function public.set_media_assets_updated_at()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security invoker
|
||||
as $$
|
||||
begin
|
||||
new.updated_at := timezone('utc', now());
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists set_media_assets_updated_at on public.media_assets;
|
||||
create trigger set_media_assets_updated_at
|
||||
before update on public.media_assets
|
||||
for each row
|
||||
execute procedure public.set_media_assets_updated_at();
|
||||
|
||||
alter table public.media_assets enable row level security;
|
||||
|
||||
drop policy if exists "Select assets within workspace" on public.media_assets;
|
||||
create policy "Select assets within workspace"
|
||||
on public.media_assets
|
||||
for select
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = media_assets.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Insert assets within workspace" on public.media_assets;
|
||||
create policy "Insert assets within workspace"
|
||||
on public.media_assets
|
||||
for insert
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = media_assets.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Update assets within workspace" on public.media_assets;
|
||||
create policy "Update assets within workspace"
|
||||
on public.media_assets
|
||||
for update
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = media_assets.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
)
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = media_assets.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,195 @@
|
||||
create table if not exists public.page_refs (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
workspace_id uuid not null references public.workspaces(id) on delete cascade,
|
||||
source_page_id uuid not null references public.documents(id) on delete cascade,
|
||||
source_block_id text,
|
||||
target_page_id uuid not null references public.documents(id) on delete cascade,
|
||||
alias text,
|
||||
display_mode text not null default 'inline' check (display_mode in ('inline','embed')),
|
||||
is_previewable boolean not null default true,
|
||||
created_by uuid not null references public.profiles(id) on delete cascade,
|
||||
created_at timestamptz not null default timezone('utc', now()),
|
||||
updated_at timestamptz not null default timezone('utc', now())
|
||||
);
|
||||
|
||||
create index if not exists page_refs_target_idx on public.page_refs (workspace_id, target_page_id, display_mode);
|
||||
create index if not exists page_refs_source_idx on public.page_refs (workspace_id, source_page_id);
|
||||
create index if not exists page_refs_recent_idx on public.page_refs (workspace_id, updated_at desc);
|
||||
|
||||
create unique index if not exists page_refs_unique_block_ref
|
||||
on public.page_refs (workspace_id, source_page_id, coalesce(source_block_id, ''), target_page_id, display_mode);
|
||||
|
||||
create or replace function public.set_page_refs_updated_at()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security invoker
|
||||
as $$
|
||||
begin
|
||||
new.updated_at := timezone('utc', now());
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists set_page_refs_updated_at on public.page_refs;
|
||||
create trigger set_page_refs_updated_at
|
||||
before update on public.page_refs
|
||||
for each row
|
||||
execute procedure public.set_page_refs_updated_at();
|
||||
|
||||
alter table public.page_refs enable row level security;
|
||||
|
||||
drop policy if exists "Select refs within workspace" on public.page_refs;
|
||||
create policy "Select refs within workspace"
|
||||
on public.page_refs
|
||||
for select
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = page_refs.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Insert refs within workspace" on public.page_refs;
|
||||
create policy "Insert refs within workspace"
|
||||
on public.page_refs
|
||||
for insert
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = page_refs.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Update refs ownership" on public.page_refs;
|
||||
create policy "Update refs ownership"
|
||||
on public.page_refs
|
||||
for update
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = page_refs.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
)
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = page_refs.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
create or replace function public.record_page_ref(
|
||||
p_workspace_id uuid,
|
||||
p_source_page_id uuid,
|
||||
p_source_block_id text,
|
||||
p_target_page_id uuid,
|
||||
p_alias text,
|
||||
p_display_mode text default 'inline',
|
||||
p_is_previewable boolean default true,
|
||||
p_created_by uuid default auth.uid()
|
||||
)
|
||||
returns public.page_refs
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
declare
|
||||
v_existing public.page_refs%rowtype;
|
||||
v_mode text := coalesce(nullif(p_display_mode, ''), 'inline');
|
||||
begin
|
||||
select *
|
||||
into v_existing
|
||||
from public.page_refs
|
||||
where workspace_id = p_workspace_id
|
||||
and source_page_id = p_source_page_id
|
||||
and target_page_id = p_target_page_id
|
||||
and coalesce(source_block_id, '') = coalesce(p_source_block_id, '')
|
||||
and display_mode = v_mode
|
||||
limit 1;
|
||||
|
||||
if found then
|
||||
update public.page_refs
|
||||
set alias = coalesce(p_alias, public.page_refs.alias),
|
||||
is_previewable = coalesce(p_is_previewable, public.page_refs.is_previewable),
|
||||
updated_at = timezone('utc', now())
|
||||
where id = v_existing.id
|
||||
returning * into v_existing;
|
||||
|
||||
return v_existing;
|
||||
end if;
|
||||
|
||||
insert into public.page_refs (
|
||||
workspace_id,
|
||||
source_page_id,
|
||||
source_block_id,
|
||||
target_page_id,
|
||||
alias,
|
||||
display_mode,
|
||||
is_previewable,
|
||||
created_by
|
||||
)
|
||||
values (
|
||||
p_workspace_id,
|
||||
p_source_page_id,
|
||||
p_source_block_id,
|
||||
p_target_page_id,
|
||||
p_alias,
|
||||
v_mode,
|
||||
coalesce(p_is_previewable, true),
|
||||
coalesce(p_created_by, auth.uid())
|
||||
)
|
||||
returning * into v_existing;
|
||||
|
||||
return v_existing;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace function public.list_backlinks(
|
||||
p_workspace_id uuid,
|
||||
p_target_page_id uuid,
|
||||
p_limit integer default 50,
|
||||
p_offset integer default 0
|
||||
)
|
||||
returns table (
|
||||
id uuid,
|
||||
source_page_id uuid,
|
||||
source_block_id text,
|
||||
alias text,
|
||||
display_mode text,
|
||||
is_previewable boolean,
|
||||
created_at timestamptz,
|
||||
updated_at timestamptz,
|
||||
source_title text,
|
||||
workspace_id uuid
|
||||
)
|
||||
language sql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
select
|
||||
r.id,
|
||||
r.source_page_id,
|
||||
r.source_block_id,
|
||||
r.alias,
|
||||
r.display_mode,
|
||||
r.is_previewable,
|
||||
r.created_at,
|
||||
r.updated_at,
|
||||
d.title as source_title,
|
||||
r.workspace_id
|
||||
from public.page_refs r
|
||||
join public.documents d on d.id = r.source_page_id
|
||||
where r.workspace_id = p_workspace_id
|
||||
and r.target_page_id = p_target_page_id
|
||||
and d.workspace_id = p_workspace_id
|
||||
order by r.updated_at desc
|
||||
limit greatest(p_limit, 1)
|
||||
offset greatest(p_offset, 0);
|
||||
$$;
|
||||
@@ -0,0 +1,28 @@
|
||||
create table if not exists public.user_recent_pages (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
user_id uuid not null references public.profiles(id) on delete cascade,
|
||||
workspace_id uuid not null references public.workspaces(id) on delete cascade,
|
||||
document_id uuid not null references public.documents(id) on delete cascade,
|
||||
last_accessed_at timestamptz not null default timezone('utc', now())
|
||||
);
|
||||
|
||||
create unique index if not exists user_recent_pages_user_document_idx
|
||||
on public.user_recent_pages (user_id, document_id);
|
||||
|
||||
create index if not exists user_recent_pages_workspace_idx
|
||||
on public.user_recent_pages (workspace_id, last_accessed_at desc);
|
||||
|
||||
alter table public.user_recent_pages enable row level security;
|
||||
|
||||
drop policy if exists "Recent pages are visible to owner" on public.user_recent_pages;
|
||||
create policy "Recent pages are visible to owner"
|
||||
on public.user_recent_pages
|
||||
for select
|
||||
using (user_id = auth.uid());
|
||||
|
||||
drop policy if exists "Upsert recent pages for owner" on public.user_recent_pages;
|
||||
create policy "Upsert recent pages for owner"
|
||||
on public.user_recent_pages
|
||||
for all
|
||||
using (user_id = auth.uid())
|
||||
with check (user_id = auth.uid());
|
||||
@@ -0,0 +1,269 @@
|
||||
create table if not exists public.document_tables (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
workspace_id uuid not null references public.workspaces(id) on delete cascade,
|
||||
document_id uuid not null references public.documents(id) on delete cascade,
|
||||
title text not null default '未命名表格',
|
||||
schema jsonb not null default '{}'::jsonb,
|
||||
view_preferences jsonb not null default '{}'::jsonb,
|
||||
is_archived boolean not null default false,
|
||||
snapshot jsonb,
|
||||
last_synced_at timestamptz,
|
||||
created_by uuid references public.profiles(id) on delete set null,
|
||||
updated_by uuid references public.profiles(id) on delete set null,
|
||||
created_at timestamptz not null default timezone('utc', now()),
|
||||
updated_at timestamptz not null default timezone('utc', now())
|
||||
);
|
||||
|
||||
create index if not exists document_tables_workspace_idx
|
||||
on public.document_tables (workspace_id, updated_at desc);
|
||||
|
||||
create index if not exists document_tables_document_idx
|
||||
on public.document_tables (document_id, updated_at desc);
|
||||
|
||||
create index if not exists document_tables_archived_idx
|
||||
on public.document_tables (workspace_id, is_archived, updated_at desc);
|
||||
|
||||
create or replace function public.set_document_tables_updated_at()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security invoker
|
||||
as $$
|
||||
begin
|
||||
new.updated_at := timezone('utc', now());
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists set_document_tables_updated_at on public.document_tables;
|
||||
create trigger set_document_tables_updated_at
|
||||
before update on public.document_tables
|
||||
for each row
|
||||
execute procedure public.set_document_tables_updated_at();
|
||||
|
||||
create or replace function public.ensure_document_table_workspace()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security invoker
|
||||
as $$
|
||||
declare
|
||||
v_workspace uuid;
|
||||
begin
|
||||
select workspace_id
|
||||
into v_workspace
|
||||
from public.documents
|
||||
where id = new.document_id;
|
||||
|
||||
if not found then
|
||||
raise exception 'Document % not found when creating document table', new.document_id;
|
||||
end if;
|
||||
|
||||
if new.workspace_id is null then
|
||||
new.workspace_id := v_workspace;
|
||||
elsif new.workspace_id <> v_workspace then
|
||||
raise exception 'Workspace mismatch between document table and document %', new.document_id;
|
||||
end if;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists ensure_document_table_workspace on public.document_tables;
|
||||
create trigger ensure_document_table_workspace
|
||||
before insert or update on public.document_tables
|
||||
for each row
|
||||
execute procedure public.ensure_document_table_workspace();
|
||||
|
||||
alter table public.document_tables enable row level security;
|
||||
|
||||
drop policy if exists "Select tables within workspace" on public.document_tables;
|
||||
create policy "Select tables within workspace"
|
||||
on public.document_tables
|
||||
for select
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_tables.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Insert tables within workspace" on public.document_tables;
|
||||
create policy "Insert tables within workspace"
|
||||
on public.document_tables
|
||||
for insert
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_tables.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Update tables within workspace" on public.document_tables;
|
||||
create policy "Update tables within workspace"
|
||||
on public.document_tables
|
||||
for update
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_tables.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
)
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_tables.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Delete tables within workspace" on public.document_tables;
|
||||
create policy "Delete tables within workspace"
|
||||
on public.document_tables
|
||||
for delete
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_tables.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
create table if not exists public.document_table_rows (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
workspace_id uuid not null references public.workspaces(id) on delete cascade,
|
||||
document_id uuid not null references public.documents(id) on delete cascade,
|
||||
table_id uuid not null references public.document_tables(id) on delete cascade,
|
||||
row_index integer not null,
|
||||
row_data jsonb not null default '{}'::jsonb,
|
||||
row_hash text,
|
||||
is_deleted boolean not null default false,
|
||||
updated_by uuid references public.profiles(id) on delete set null,
|
||||
created_at timestamptz not null default timezone('utc', now()),
|
||||
updated_at timestamptz not null default timezone('utc', now())
|
||||
);
|
||||
|
||||
create unique index if not exists document_table_rows_unique_row
|
||||
on public.document_table_rows (table_id, row_index)
|
||||
where is_deleted = false;
|
||||
|
||||
create index if not exists document_table_rows_workspace_idx
|
||||
on public.document_table_rows (workspace_id, updated_at desc);
|
||||
|
||||
create index if not exists document_table_rows_table_idx
|
||||
on public.document_table_rows (table_id, row_index);
|
||||
|
||||
create or replace function public.set_document_table_rows_updated_at()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security invoker
|
||||
as $$
|
||||
begin
|
||||
new.updated_at := timezone('utc', now());
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists set_document_table_rows_updated_at on public.document_table_rows;
|
||||
create trigger set_document_table_rows_updated_at
|
||||
before update on public.document_table_rows
|
||||
for each row
|
||||
execute procedure public.set_document_table_rows_updated_at();
|
||||
|
||||
create or replace function public.sync_document_table_row_meta()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security invoker
|
||||
as $$
|
||||
declare
|
||||
v_table record;
|
||||
begin
|
||||
select workspace_id, document_id
|
||||
into v_table
|
||||
from public.document_tables
|
||||
where id = new.table_id;
|
||||
|
||||
if not found then
|
||||
raise exception 'Document table % not found when creating row', new.table_id;
|
||||
end if;
|
||||
|
||||
new.workspace_id := v_table.workspace_id;
|
||||
new.document_id := v_table.document_id;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
drop trigger if exists sync_document_table_row_meta on public.document_table_rows;
|
||||
create trigger sync_document_table_row_meta
|
||||
before insert or update on public.document_table_rows
|
||||
for each row
|
||||
execute procedure public.sync_document_table_row_meta();
|
||||
|
||||
alter table public.document_table_rows enable row level security;
|
||||
|
||||
drop policy if exists "Select table rows within workspace" on public.document_table_rows;
|
||||
create policy "Select table rows within workspace"
|
||||
on public.document_table_rows
|
||||
for select
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_table_rows.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Insert table rows within workspace" on public.document_table_rows;
|
||||
create policy "Insert table rows within workspace"
|
||||
on public.document_table_rows
|
||||
for insert
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_table_rows.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Update table rows within workspace" on public.document_table_rows;
|
||||
create policy "Update table rows within workspace"
|
||||
on public.document_table_rows
|
||||
for update
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_table_rows.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
)
|
||||
with check (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_table_rows.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
drop policy if exists "Delete table rows within workspace" on public.document_table_rows;
|
||||
create policy "Delete table rows within workspace"
|
||||
on public.document_table_rows
|
||||
for delete
|
||||
using (
|
||||
exists (
|
||||
select 1
|
||||
from public.workspace_members wm
|
||||
where wm.workspace_id = document_table_rows.workspace_id
|
||||
and wm.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
insert into storage.buckets (id, name, public)
|
||||
values ('media', 'media', true)
|
||||
on conflict (id) do nothing;
|
||||
|
||||
alter table if exists storage.objects enable row level security;
|
||||
|
||||
drop policy if exists "Public read access for media bucket" on storage.objects;
|
||||
create policy "Public read access for media bucket"
|
||||
on storage.objects
|
||||
for select
|
||||
using (bucket_id = 'media');
|
||||
|
||||
drop policy if exists "Authenticated upload to media bucket" on storage.objects;
|
||||
create policy "Authenticated upload to media bucket"
|
||||
on storage.objects
|
||||
for insert
|
||||
with check (
|
||||
bucket_id = 'media'
|
||||
and auth.role() = 'authenticated'
|
||||
);
|
||||
|
||||
drop policy if exists "Authenticated delete media bucket" on storage.objects;
|
||||
create policy "Authenticated delete media bucket"
|
||||
on storage.objects
|
||||
for delete
|
||||
using (
|
||||
bucket_id = 'media'
|
||||
and auth.role() = 'authenticated'
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
alter table if exists public.media_assets
|
||||
add column if not exists file_name text,
|
||||
add column if not exists file_size bigint,
|
||||
add column if not exists mime_type text;
|
||||
Reference in New Issue
Block a user