Property Management Network — Next.js 16 (App Router), Better Auth, Drizzle ORM over PostgreSQL, Stripe, OpenAI, Resend. Includes: - Security hardening: access-control/IDOR fixes, TLS-by-default DB layer, constant-time cron auth, strict security headers, atomic AI quota gating, HTML/email output encoding, demo-backdoor disabled in production. - Superadmin dashboard at /admin (overview/MRR, server-paginated users with ban/impersonate/plan/delete, billing, platform activity + admin audit log, AI usage, system health) via the Better Auth admin plugin. - Seed/migration utility scripts under scripts/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
1.4 KiB
TypeScript
59 lines
1.4 KiB
TypeScript
import { NextResponse, type NextRequest } from "next/server"
|
|
import { getSessionCookie } from "better-auth/cookies"
|
|
|
|
const PROTECTED_PATHS = [
|
|
"/admin",
|
|
"/dashboard",
|
|
"/properties",
|
|
"/tenants",
|
|
"/rent",
|
|
"/maintenance",
|
|
"/leases",
|
|
"/expenses",
|
|
"/settings",
|
|
"/onboarding",
|
|
"/calendar",
|
|
"/inspections",
|
|
"/vendors",
|
|
"/reports",
|
|
"/activity",
|
|
"/ai",
|
|
"/ai-dashboard",
|
|
"/predictions",
|
|
"/recommendations",
|
|
"/impact",
|
|
"/follow-ups",
|
|
]
|
|
|
|
const AUTH_PATHS = ["/login", "/signup", "/forgot-password"]
|
|
|
|
export async function proxy(request: NextRequest) {
|
|
const pathname = request.nextUrl.pathname
|
|
|
|
// Optimistic check based on the presence of the session cookie. Real
|
|
// enforcement happens in routes / server components via getSessionUser().
|
|
const sessionCookie = getSessionCookie(request)
|
|
|
|
const isProtected = PROTECTED_PATHS.some((p) => pathname.startsWith(p))
|
|
if (isProtected && !sessionCookie) {
|
|
const url = request.nextUrl.clone()
|
|
url.pathname = "/login"
|
|
return NextResponse.redirect(url)
|
|
}
|
|
|
|
const isAuthPage = AUTH_PATHS.some((p) => pathname.startsWith(p))
|
|
if (isAuthPage && sessionCookie) {
|
|
const url = request.nextUrl.clone()
|
|
url.pathname = "/dashboard"
|
|
return NextResponse.redirect(url)
|
|
}
|
|
|
|
return NextResponse.next()
|
|
}
|
|
|
|
export const config = {
|
|
matcher: [
|
|
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
|
|
],
|
|
}
|