feat: LinkedIn editorial, accesos agencia estables y deshacer en historial

Integra estrategias LinkedIn en calendarios (backlog publicado vs pendiente), unifica roles/cookies de vista agencia y permite deshacer cambios de brandbook dejando constancia en el historial.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-10-16 04:15:51 +02:00
parent 3b57cdce53
commit 4a1abd3318
54 changed files with 6400 additions and 527 deletions

View File

@@ -1,5 +1,13 @@
import { createServerClient } from '@supabase/ssr';
import { NextRequest, NextResponse } from 'next/server';
import {
VIEW_TENANT_COOKIE,
VIEW_TENANT_NAME_COOKIE,
VIEW_TENANT_SLUG_COOKIE,
clearedViewTenantCookieOpts,
isAgencyRole,
userRole,
} from '@/lib/agency';
function withSecurityHeaders(res: NextResponse, path?: string) {
res.headers.set('X-Frame-Options', 'DENY');
@@ -97,24 +105,22 @@ export async function middleware(req: NextRequest) {
return withSecurityHeaders(NextResponse.redirect(loginUrl));
}
const role = userRole(user);
const agency = isAgencyRole(role);
if (user && path === '/login') {
const role = user?.app_metadata?.role || user?.user_metadata?.role || 'client';
const dashboardUrl = req.nextUrl.clone();
dashboardUrl.pathname =
role === 'admin' || role === 'team'
? '/dashboard/agencia'
: '/dashboard/kpis';
dashboardUrl.pathname = agency ? '/dashboard/agencia' : '/dashboard/kpis';
return withSecurityHeaders(NextResponse.redirect(dashboardUrl));
}
const role = user?.app_metadata?.role || user?.user_metadata?.role || 'client';
if (user && role === 'client' && path.startsWith('/dashboard/agencia')) {
if (user && !agency && path.startsWith('/dashboard/agencia')) {
const forbiddenUrl = req.nextUrl.clone();
forbiddenUrl.pathname = '/dashboard/kpis';
return withSecurityHeaders(NextResponse.redirect(forbiddenUrl));
}
if (user && role === 'client' && path.startsWith('/api/admin/')) {
if (user && !agency && path.startsWith('/api/admin/')) {
return withSecurityHeaders(
NextResponse.json(
{
@@ -167,6 +173,21 @@ export async function middleware(req: NextRequest) {
secure: process.env.NODE_ENV === 'production',
});
// Clientes: no deben conservar cookies de "vista cliente" de una sesión agencia.
if (user && !agency) {
const hasView =
req.cookies.get(VIEW_TENANT_COOKIE)?.value ||
req.cookies.get(VIEW_TENANT_NAME_COOKIE)?.value;
if (hasView) {
const opts = clearedViewTenantCookieOpts(
process.env.NODE_ENV === 'production'
);
res.cookies.set(VIEW_TENANT_COOKIE, '', opts);
res.cookies.set(VIEW_TENANT_SLUG_COOKIE, '', opts);
res.cookies.set(VIEW_TENANT_NAME_COOKIE, '', opts);
}
}
return withSecurityHeaders(res);
}