sistema-de-chamados/middleware.ts

96 lines
2.9 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server"
import { getCookieCache } from "better-auth/cookies"
// Rotas públicas explícitas (não autenticadas)
// Permite handshake de máquina sem sessão prévia para criar a sessão de máquina.
const PUBLIC_PATHS = [/^\/login$/, /^\/machines\/handshake$/]
// Rotas somente admin
const ADMIN_ONLY_PATHS = [/^\/admin(?:$|\/)/]
const APP_HOME = "/dashboard"
export async function middleware(request: NextRequest) {
const { pathname, search } = request.nextUrl
if (PUBLIC_PATHS.some((pattern) => pattern.test(pathname))) return NextResponse.next()
const session = await getCookieCache(request)
if (!session?.user) {
const hasSessionCookie = Boolean(request.cookies.get("better-auth.session-token"))
const hasRefreshCookie =
Boolean(request.cookies.get("better-auth.refresh-token")) ||
Boolean(request.cookies.get("better-auth.refresh-token-v2"))
if (hasSessionCookie || hasRefreshCookie) {
const refreshed = await attemptSessionRefresh(request)
if (refreshed) {
return refreshed
}
}
const redirectUrl = new URL("/login", request.url)
redirectUrl.searchParams.set("callbackUrl", pathname + search)
return NextResponse.redirect(redirectUrl)
}
const role = (session.user as { role?: string })?.role?.toLowerCase() ?? "agent"
const isAdmin = role === "admin"
// Em desenvolvimento, evitamos bloquear rotas admin por possíveis diferenças
// de cache de cookie/sessão entre dev server e middleware. Em produção, aplica o gate.
if (
process.env.NODE_ENV === "production" &&
!isAdmin &&
ADMIN_ONLY_PATHS.some((pattern) => pattern.test(pathname))
) {
return NextResponse.redirect(new URL(APP_HOME, request.url))
}
return NextResponse.next()
}
export const config = {
runtime: "nodejs",
// Evita executar para assets e imagens estáticas
matcher: ["/((?!api|_next/static|_next/image|favicon.ico|icon.png).*)"],
}
async function attemptSessionRefresh(request: NextRequest): Promise<NextResponse | null> {
try {
const refreshUrl = new URL("/api/auth/get-session", request.url)
const response = await fetch(refreshUrl, {
method: "GET",
headers: {
cookie: request.headers.get("cookie") ?? "",
},
})
if (!response.ok) {
return null
}
const data = await response.json().catch(() => null)
if (!data?.user) {
return null
}
const redirect = NextResponse.redirect(request.nextUrl)
const setCookieHeaders =
typeof response.headers.raw === "function"
? response.headers.raw()["set-cookie"] ?? []
: []
if (setCookieHeaders.length === 0) {
const single = response.headers.get("set-cookie")
if (single) setCookieHeaders.push(single)
}
for (const cookie of setCookieHeaders) {
redirect.headers.append("set-cookie", cookie)
}
return redirect
} catch {
return null
}
}