feat(auth): add /login page, session middleware, role-based navigation and logout
This commit is contained in:
@@ -1,11 +1,71 @@
|
||||
import { createServerClient } from '@supabase/ssr';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export function middleware(req: NextRequest) {
|
||||
const url = req.nextUrl.clone();
|
||||
const hostname = req.headers.get('host') || '';
|
||||
export async function middleware(req: NextRequest) {
|
||||
let res = NextResponse.next({
|
||||
request: {
|
||||
headers: req.headers,
|
||||
},
|
||||
});
|
||||
|
||||
// 1. Extraer slug desde subdominio (ej: acme.4ventures.es)
|
||||
let tenantSlug = '4ventures-hub';
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
getAll() {
|
||||
return req.cookies.getAll();
|
||||
},
|
||||
setAll(cookiesToSet) {
|
||||
cookiesToSet.forEach(({ name, value }) => req.cookies.set(name, value));
|
||||
res = NextResponse.next({
|
||||
request: {
|
||||
headers: req.headers,
|
||||
},
|
||||
});
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
res.cookies.set(name, value, options)
|
||||
);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
const url = req.nextUrl.clone();
|
||||
const path = url.pathname;
|
||||
|
||||
// 1. Redirigir usuarios no autenticados que intenten acceder al dashboard
|
||||
if (!user && (path === '/' || path.startsWith('/dashboard'))) {
|
||||
const loginUrl = req.nextUrl.clone();
|
||||
loginUrl.pathname = '/login';
|
||||
if (path.startsWith('/dashboard')) {
|
||||
loginUrl.searchParams.set('next', path);
|
||||
}
|
||||
return NextResponse.redirect(loginUrl);
|
||||
}
|
||||
|
||||
// 2. Redirigir usuarios autenticados que intenten acceder a /login
|
||||
if (user && path === '/login') {
|
||||
const dashboardUrl = req.nextUrl.clone();
|
||||
dashboardUrl.pathname = '/dashboard/kpis';
|
||||
return NextResponse.redirect(dashboardUrl);
|
||||
}
|
||||
|
||||
// 3. Proteger la vista directiva de agencia contra clientes
|
||||
const role = user?.app_metadata?.role || user?.user_metadata?.role || 'client';
|
||||
if (user && role === 'client' && path.startsWith('/dashboard/agencia')) {
|
||||
const forbiddenUrl = req.nextUrl.clone();
|
||||
forbiddenUrl.pathname = '/dashboard/kpis';
|
||||
return NextResponse.redirect(forbiddenUrl);
|
||||
}
|
||||
|
||||
// 4. Resolver Tenant Slug (desde subdominio, metadata de usuario o cookie)
|
||||
let tenantSlug = user?.user_metadata?.slug || user?.app_metadata?.slug || '4ventures-hub';
|
||||
const hostname = req.headers.get('host') || '';
|
||||
const parts = hostname.split('.');
|
||||
|
||||
if (parts.length > 2) {
|
||||
@@ -15,29 +75,26 @@ export function middleware(req: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Override opcional vía query param ?tenant=slug
|
||||
const queryTenant = url.searchParams.get('tenant');
|
||||
if (queryTenant) {
|
||||
tenantSlug = queryTenant.toLowerCase();
|
||||
} else {
|
||||
const cookieTenant = req.cookies.get('4v_tenant')?.value;
|
||||
if (cookieTenant) {
|
||||
if (cookieTenant && !user?.user_metadata?.slug) {
|
||||
tenantSlug = cookieTenant;
|
||||
}
|
||||
}
|
||||
|
||||
const response = NextResponse.next({
|
||||
request: {
|
||||
headers: new Headers(req.headers)
|
||||
}
|
||||
});
|
||||
res.headers.set('x-tenant-slug', tenantSlug);
|
||||
res.headers.set('x-user-role', role);
|
||||
if (user?.email) {
|
||||
res.headers.set('x-user-email', user.email);
|
||||
}
|
||||
res.cookies.set('4v_tenant', tenantSlug, { path: '/', maxAge: 60 * 60 * 24 * 30 });
|
||||
|
||||
response.headers.set('x-tenant-slug', tenantSlug);
|
||||
response.cookies.set('4v_tenant', tenantSlug, { path: '/', maxAge: 60 * 60 * 24 * 30 });
|
||||
|
||||
return response;
|
||||
return res;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ['/((?!_next/static|_next/image|favicon.ico|_4v_attr.js).*)']
|
||||
matcher: ['/((?!_next/static|_next/image|favicon.ico|_4v_attr.js|api/chat|api/simulate).*)']
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user