import { createClient } from '@supabase/supabase-js'; import { serviceHeaders, supabaseUrl, } from '@/lib/agency'; export const ASSETS_BUCKET = 'tenant-assets'; export const MAX_ASSET_BYTES = 80 * 1024 * 1024; // 80 MB export function serviceSupabase() { const key = process.env.SUPABASE_SERVICE_ROLE_KEY?.trim(); if (!key) return null; return createClient(supabaseUrl(), key, { auth: { persistSession: false, autoRefreshToken: false }, }); } export async function ensureAssetsBucket(): Promise<{ ok: boolean; error?: string }> { const headers = serviceHeaders(); if (!headers) return { ok: false, error: 'no service role' }; const list = await fetch(`${supabaseUrl()}/storage/v1/bucket`, { headers, cache: 'no-store', }); if (list.ok) { const buckets = (await list.json()) as { id: string }[]; if (buckets.some((b) => b.id === ASSETS_BUCKET)) { return { ok: true }; } } const create = await fetch(`${supabaseUrl()}/storage/v1/bucket`, { method: 'POST', headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify({ id: ASSETS_BUCKET, name: ASSETS_BUCKET, public: false, file_size_limit: MAX_ASSET_BYTES, allowed_mime_types: null, }), }); if (!create.ok) { const text = await create.text(); // race: already exists if (text.toLowerCase().includes('already') || create.status === 409) { return { ok: true }; } return { ok: false, error: text.slice(0, 300) }; } return { ok: true }; } export function sanitizeFolder(raw: string): string { return raw .trim() .replace(/\\/g, '/') .replace(/\.\./g, '') .replace(/^\/+|\/+$/g, '') .replace(/[^a-zA-Z0-9/_ .-]/g, '-') .slice(0, 180); } export function sanitizeFileName(name: string): string { const base = name.split(/[/\\]/).pop() || 'file'; return base.replace(/[^\w.\- ()áéíóúÁÉÍÓÚñÑ]+/gi, '_').slice(0, 180); }