Limit client asset library to app-generated folders only.
Agency still sees the full Drive tree and portal uploads; clients only see Pipeline-creatividades / calendarios / brandbook-style paths. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,6 +15,10 @@ import {
|
||||
sanitizeFolder,
|
||||
serviceSupabase,
|
||||
} from '@/lib/assets';
|
||||
import {
|
||||
filterPortalAssetsForClient,
|
||||
isAppGeneratedPortalFolder,
|
||||
} from '@/lib/assets-client-scope';
|
||||
import {
|
||||
ensureChildFolder,
|
||||
getTenantDriveFolderId,
|
||||
@@ -64,12 +68,18 @@ export async function GET() {
|
||||
const msg = await r.text();
|
||||
return NextResponse.json({
|
||||
assets: [],
|
||||
agency: true,
|
||||
clientScope: 'full',
|
||||
warning: msg.includes('does not exist')
|
||||
? 'Aplicar sql/asset_files.sql'
|
||||
: msg.slice(0, 200),
|
||||
});
|
||||
}
|
||||
return NextResponse.json({ assets: await r.json() });
|
||||
return NextResponse.json({
|
||||
assets: await r.json(),
|
||||
agency: true,
|
||||
clientScope: 'full',
|
||||
});
|
||||
}
|
||||
|
||||
const supabase = await createClient();
|
||||
@@ -81,19 +91,27 @@ export async function GET() {
|
||||
if (error) {
|
||||
return NextResponse.json({
|
||||
assets: [],
|
||||
agency: false,
|
||||
clientScope: 'app_generated',
|
||||
warning: error.message,
|
||||
});
|
||||
}
|
||||
return NextResponse.json({ assets: data ?? [] });
|
||||
// Client JWT path: still filter to app-generated folders only
|
||||
return NextResponse.json({
|
||||
assets: filterPortalAssetsForClient(data ?? []),
|
||||
agency: false,
|
||||
clientScope: 'app_generated',
|
||||
});
|
||||
}
|
||||
|
||||
/** POST multipart: files[] + optional folder */
|
||||
export async function POST(req: NextRequest) {
|
||||
const g = await gate();
|
||||
if ('error' in g && g.error) return g.error;
|
||||
const { user, ctx } = g as Awaited<ReturnType<typeof gate>> & {
|
||||
const { user, ctx, agency } = g as Awaited<ReturnType<typeof gate>> & {
|
||||
user: { id: string };
|
||||
ctx: { tenantId: string };
|
||||
agency: boolean;
|
||||
};
|
||||
|
||||
const bucketOk = await ensureAssetsBucket();
|
||||
@@ -115,6 +133,15 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
const form = await req.formData();
|
||||
const folder = sanitizeFolder(String(form.get('folder') || ''));
|
||||
if (!agency && !isAppGeneratedPortalFolder(folder)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
'Como cliente solo puedes usar carpetas generadas por la app (creatividades, calendarios, brandbook…).',
|
||||
},
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
const files = form.getAll('files').filter((f): f is File => f instanceof File);
|
||||
|
||||
if (!files.length) {
|
||||
@@ -234,10 +261,18 @@ export async function POST(req: NextRequest) {
|
||||
export async function DELETE(req: NextRequest) {
|
||||
const g = await gate();
|
||||
if ('error' in g && g.error) return g.error;
|
||||
const { ctx } = g as Awaited<ReturnType<typeof gate>> & {
|
||||
const { ctx, agency } = g as Awaited<ReturnType<typeof gate>> & {
|
||||
ctx: { tenantId: string };
|
||||
agency: boolean;
|
||||
};
|
||||
|
||||
if (!agency) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Solo la agencia puede eliminar archivos de la biblioteca' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
const id = req.nextUrl.searchParams.get('id')?.trim();
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: 'id requerido' }, { status: 400 });
|
||||
|
||||
Reference in New Issue
Block a user