feat: upload any validated file to the asset library

Activos now supports multi-file/folder upload to private Storage
(PDF, Excel, CSV, images, ZIP, etc.) with per-tenant folders.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-22 15:26:33 +02:00
parent 8182402d96
commit ce9498e63c
9 changed files with 931 additions and 63 deletions

64
sql/asset_files.sql Normal file
View File

@@ -0,0 +1,64 @@
-- File assets library (validated uploads per tenant)
-- Idempotent.
create table if not exists public.asset_files (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
title text not null,
folder text not null default '',
file_name text not null,
mime_type text,
bytes bigint,
storage_path text not null,
sha256 text,
status text not null default 'approved'
check (status in ('approved', 'archived')),
created_by uuid references auth.users(id),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create index if not exists asset_files_tenant_folder_idx
on public.asset_files (tenant_id, folder, created_at desc);
alter table public.asset_files enable row level security;
drop policy if exists asset_files_tenant_select on public.asset_files;
drop policy if exists asset_files_tenant_insert on public.asset_files;
drop policy if exists asset_files_tenant_update on public.asset_files;
drop policy if exists asset_files_tenant_delete on public.asset_files;
create policy asset_files_tenant_select on public.asset_files
for select using (
tenant_id::text = coalesce(
auth.jwt() -> 'app_metadata' ->> 'tenant_id',
auth.jwt() -> 'user_metadata' ->> 'tenant_id'
)
);
create policy asset_files_tenant_insert on public.asset_files
for insert with check (
tenant_id::text = coalesce(
auth.jwt() -> 'app_metadata' ->> 'tenant_id',
auth.jwt() -> 'user_metadata' ->> 'tenant_id'
)
);
create policy asset_files_tenant_update on public.asset_files
for update using (
tenant_id::text = coalesce(
auth.jwt() -> 'app_metadata' ->> 'tenant_id',
auth.jwt() -> 'user_metadata' ->> 'tenant_id'
)
);
create policy asset_files_tenant_delete on public.asset_files
for delete using (
tenant_id::text = coalesce(
auth.jwt() -> 'app_metadata' ->> 'tenant_id',
auth.jwt() -> 'user_metadata' ->> 'tenant_id'
)
);
-- Private bucket (created via API if missing). Policies for storage.objects:
-- path layout: {tenant_id}/{optional/folder/}{uuid}_{filename}