Files
dataroom/middleware.ts
zeekay c7b24906d5 refactor: the client is @hanzo/kv
ioredis is replaced by @hanzo/kv, which is a fork of it — the module specifier
changes and the exported names follow: Redis is KV, RedisOptions is KVOptions.
Local identifiers and comments follow too, so nothing in the tree calls this
client by another product's name.

Third-party packages keep their own names, because they name themselves:
@socket.io/redis-adapter, connect-redis, rate-limit-redis. They take a client of
this shape and @hanzo/kv is that shape.

⚠️ Mechanical and unverified: no build or typecheck was run in this repo. Run
one before relying on it.
2026-07-26 16:34:55 -07:00

94 lines
2.5 KiB
TypeScript

import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import AppMiddleware from "@/lib/middleware/app";
import { BLOCKED_PATHNAMES } from "./lib/constants";
import IncomingWebhookMiddleware, {
isWebhookPath,
} from "./lib/middleware/incoming-webhooks";
import InsightsMiddleware from "./lib/middleware/insights";
function isAnalyticsPath(path: string) {
const pattern = /^\/ingest\/.*/;
return pattern.test(path);
}
// App domains that should NOT be treated as custom domains
const APP_DOMAINS = [
"localhost",
"dataroom.hanzo.ai",
"hanzo.ai",
".vercel.app",
];
function isCustomDomain(host: string) {
if (process.env.NODE_ENV === "development") {
return host?.includes(".local") || host?.includes("dataroom.hanzo.dev");
}
return !APP_DOMAINS.some((d) =>
d.startsWith(".") ? host?.endsWith(d) : host?.includes(d),
);
}
export const config = {
matcher: [
/*
* Match all paths except for:
* 1. /api/ routes
* 2. /_next/ (Next.js internals)
* 3. /_static (inside /public)
* 4. /_vercel (Vercel internals)
* 5. /favicon.ico, /sitemap.xml, /robots.txt (static files)
*/
"/((?!api/|_next/|_static|vendor|_icons|_vercel|favicon.ico|sitemap.xml|robots.txt).*)",
],
};
export default async function middleware(req: NextRequest, ev: NextFetchEvent) {
const path = req.nextUrl.pathname;
const host = req.headers.get("host");
if (isAnalyticsPath(path)) {
return InsightsMiddleware(req);
}
// Handle incoming webhooks
if (isWebhookPath(host)) {
return IncomingWebhookMiddleware(req);
}
// For custom domains, rewrite to domain viewer routes
// NOTE: DomainMiddleware is dynamically imported to avoid pulling @hanzo/kv
// into the Edge Runtime bundle (Edge Runtime has no TCP/net APIs)
if (isCustomDomain(host || "")) {
const { default: DomainMiddleware } = await import(
"@/lib/middleware/domain"
);
return DomainMiddleware(req);
}
// Handle standard app paths
if (
!path.startsWith("/view/") &&
!path.startsWith("/verify") &&
!path.startsWith("/unsubscribe") &&
!path.startsWith("/notification-preferences") &&
!path.startsWith("/auth/email")
) {
return AppMiddleware(req);
}
// Check for blocked pathnames in view routes
if (
path.startsWith("/view/") &&
(BLOCKED_PATHNAMES.some((blockedPath) => path.includes(blockedPath)) ||
path.includes("."))
) {
const url = req.nextUrl.clone();
url.pathname = "/404";
return NextResponse.rewrite(url, { status: 404 });
}
return NextResponse.next();
}