mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-06-09 23:06:01 +00:00
149 lines
3.7 KiB
TypeScript
149 lines
3.7 KiB
TypeScript
import { type NextRequest, NextResponse } from "next/server";
|
|
import { auth } from "@/shared/lib/auth/config";
|
|
import { isSignupDisabled } from "@/shared/lib/auth/signup";
|
|
|
|
// Rotas protegidas que requerem autenticação
|
|
const PROTECTED_ROUTES = [
|
|
"/settings",
|
|
"/notes",
|
|
"/calendar",
|
|
"/cards",
|
|
"/categories",
|
|
"/accounts",
|
|
"/dashboard",
|
|
"/insights",
|
|
"/transactions",
|
|
"/budgets",
|
|
"/payers",
|
|
"/inbox",
|
|
"/reports",
|
|
];
|
|
|
|
// Rotas públicas (não requerem autenticação)
|
|
const PUBLIC_AUTH_ROUTES = ["/login", "/signup"];
|
|
|
|
function buildCsp(): string {
|
|
const isDev = process.env.NODE_ENV === "development";
|
|
|
|
const s3Origin = (() => {
|
|
try {
|
|
return process.env.S3_ENDPOINT
|
|
? new URL(process.env.S3_ENDPOINT).origin
|
|
: "";
|
|
} catch {
|
|
return "";
|
|
}
|
|
})();
|
|
|
|
const umamiOrigin = process.env.UMAMI_URL ?? "";
|
|
|
|
const connectExtras = [umamiOrigin, s3Origin].filter(Boolean).join(" ");
|
|
|
|
const imgExtras = [
|
|
"https://lh3.googleusercontent.com",
|
|
"https://img.logo.dev",
|
|
s3Origin,
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ");
|
|
|
|
return [
|
|
"default-src 'self'",
|
|
`script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ""}${umamiOrigin ? ` ${umamiOrigin}` : ""}`,
|
|
"style-src 'self' 'unsafe-inline'",
|
|
`img-src 'self' ${imgExtras} data: blob:`,
|
|
"font-src 'self'",
|
|
`connect-src 'self' ${connectExtras}`,
|
|
`frame-src 'self'${s3Origin ? ` ${s3Origin}` : ""}`,
|
|
"frame-ancestors 'none'",
|
|
].join("; ");
|
|
}
|
|
|
|
export default async function proxy(request: NextRequest) {
|
|
const { pathname } = request.nextUrl;
|
|
|
|
// Multi-domain: block all routes except landing on public domain
|
|
// Normalize PUBLIC_DOMAIN: strip protocol and port if provided
|
|
const publicDomain = process.env.PUBLIC_DOMAIN?.replace(
|
|
/^https?:\/\//,
|
|
"",
|
|
).replace(/:\d+$/, "");
|
|
const hostname = request.headers.get("host")?.replace(/:\d+$/, "");
|
|
|
|
if (publicDomain && hostname === publicDomain) {
|
|
if (pathname.startsWith("/api/")) {
|
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
}
|
|
if (pathname !== "/") {
|
|
return NextResponse.redirect(new URL("/", request.url));
|
|
}
|
|
return NextResponse.next();
|
|
}
|
|
|
|
// Validate actual session, not just cookie existence
|
|
const session = await auth.api.getSession({
|
|
headers: request.headers,
|
|
});
|
|
|
|
const isAuthenticated = !!session?.user;
|
|
const signupDisabled = isSignupDisabled();
|
|
|
|
if (signupDisabled) {
|
|
if (pathname === "/signup" || pathname.startsWith("/signup/")) {
|
|
return NextResponse.redirect(
|
|
new URL(isAuthenticated ? "/dashboard" : "/login", request.url),
|
|
);
|
|
}
|
|
|
|
if (pathname.startsWith("/api/auth/sign-up")) {
|
|
return NextResponse.json(
|
|
{ error: "Novos cadastros estão desativados." },
|
|
{ status: 403 },
|
|
);
|
|
}
|
|
}
|
|
|
|
// Redirect authenticated users away from login/signup pages
|
|
if (isAuthenticated && PUBLIC_AUTH_ROUTES.includes(pathname)) {
|
|
return NextResponse.redirect(new URL("/dashboard", request.url));
|
|
}
|
|
|
|
// Redirect unauthenticated users trying to access protected routes
|
|
const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
|
|
pathname.startsWith(route),
|
|
);
|
|
|
|
if (!isAuthenticated && isProtectedRoute) {
|
|
return NextResponse.redirect(new URL("/login", request.url));
|
|
}
|
|
|
|
const response = NextResponse.next();
|
|
if (!pathname.startsWith("/api/")) {
|
|
response.headers.set("Content-Security-Policy", buildCsp());
|
|
}
|
|
return response;
|
|
}
|
|
|
|
export const config = {
|
|
// Apply middleware to protected and auth routes
|
|
matcher: [
|
|
"/",
|
|
"/api/:path*",
|
|
"/settings/:path*",
|
|
"/notes/:path*",
|
|
"/calendar/:path*",
|
|
"/cards/:path*",
|
|
"/categories/:path*",
|
|
"/accounts/:path*",
|
|
"/dashboard/:path*",
|
|
"/insights/:path*",
|
|
"/transactions/:path*",
|
|
"/budgets/:path*",
|
|
"/payers/:path*",
|
|
"/inbox/:path*",
|
|
"/reports/:path*",
|
|
"/login",
|
|
"/signup",
|
|
],
|
|
};
|