wulf-pulse/middleware.ts
lorentz 1112a06afe feat: RMM Overshell, IT Glue audit/write-back, LogLift, link-aware bundles, dashboard overhaul
- RMM Overshell (migration 077): admin page, dispatch UI, executor/worker, target
  resolver, script registry (AD/DHCP/DNS/event-log/services/software/network/loglift)
- LogLift evidence pipeline (migration 078): upload webhook, B2 storage client,
  receiver/matcher, EventLogCollector PowerShell script
- IT Glue audit + write-back (migrations 075, 076): asset-audit runner, ticket
  xrefs, applications/configurations browse pages + apply/revert/audit endpoints
- Link-aware analyzer bundles (migration 073) + provider toggle (migration 074):
  link-discovery service, OpenRouter LLM provider, related-tickets/itglue-suggestion
  panels, analyze-bundle endpoint
- Endpoint data model + device-link reconciliation (migrations 079, 080): conflicts
  admin page, reconciler service, resolve endpoints
- Dashboard overhaul: integration-health service + alerts, overview/health endpoints
- Permissions: add itglue + rmm scopes; middleware: public /api/rmm/loglift route

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 07:13:18 -04:00

97 lines
2.7 KiB
TypeScript

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { getSessionCookie } from "better-auth/cookies";
// Routes that don't require authentication
const publicRoutes = [
"/auth/sign-in",
"/auth/verify",
"/auth/2fa",
"/auth/setup",
"/api/auth",
// External service callbacks and webhooks
"/api/webhooks",
"/api/kiosk",
"/api/qbo/auth",
"/api/qbo/disconnect",
"/api/zabbix/webhook",
// Health and status checks
"/api/health",
"/api/integrations/status",
// Legal pages required by Intuit
"/legal",
// Mobile app API endpoints
"/api/mobile",
// OpenClaw external agent API (auth via x-openclaw-key header)
"/api/openclaw",
// LogLift evidence webhook (auth via x-openclaw-key header)
"/api/rmm/loglift",
// Sync endpoints called by scheduler
"/api/sync",
"/api/datto-rmm/sync",
"/api/itglue/sync",
"/api/veeam/sync",
"/api/veeam/rpo-check",
"/api/sentinelone/sync",
"/api/engagement/sync",
"/api/zoom/sync",
"/api/qbo/sync",
"/api/reports/ticket-digest",
"/api/notifications/morning-summary/send",
// Duo Security sync and data endpoints
"/api/duo",
];
// Routes that require admin or super-admin role
const adminRoutes = ["/admin"];
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Allow public routes
if (publicRoutes.some((route) => pathname.startsWith(route))) {
return NextResponse.next();
}
// Allow static files
if (
pathname.startsWith("/_next") ||
pathname.startsWith("/favicon") ||
pathname.includes(".")
) {
return NextResponse.next();
}
// Check for session cookie
const sessionCookie = getSessionCookie(request);
if (!sessionCookie) {
// Redirect to sign-in if no session
const signInUrl = new URL("/auth/sign-in", request.url);
signInUrl.searchParams.set("callbackUrl", pathname);
return NextResponse.redirect(signInUrl);
}
// For admin routes, we need to verify the role
// This is a basic check - the actual role verification happens in the API routes
if (adminRoutes.some((route) => pathname.startsWith(route))) {
// The session cookie exists, but we can't decode it here without the secret
// Role-based access control is enforced at the API level
// This middleware just ensures there's a session
return NextResponse.next();
}
return NextResponse.next();
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
"/((?!_next/static|_next/image|favicon.ico).*)",
],
};