Revert "Fixed the Manager deployment database dependency."

This reverts commit a8cdc2671e.
This commit is contained in:
Stefan Lazic committed 2026-09-05 09:32:28 +01:00
1 parent d063918aae
commit 7e70fa0583
3 files changed
+10 -92

No files matched your search

-59
View File
@@ -1,59 +0,0 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { NextRequest } from "next/server";
const isInstalledForRequest = vi.fn();
// If proxy.ts or its Manager request path statically imports Prisma, this
// mock throws while the test module is loading. Manager mode must not reach it.
vi.mock("@/lib/db/prisma", () => {
throw new Error("Prisma must not be imported by the Manager proxy path.");
});
vi.mock("@/lib/installation/installation-state", () => ({ isInstalledForRequest }));
import { handleProxyRequest } from "@/proxy";
function request(pathname: string) {
return new NextRequest(`https://manager.example.com${pathname}`);
}
describe("proxy deployment separation", () => {
const originalManagerMode = process.env.MANAGER_MODE;
afterEach(() => {
if (originalManagerMode === undefined) delete process.env.MANAGER_MODE;
else process.env.MANAGER_MODE = originalManagerMode;
isInstalledForRequest.mockReset();
});
it("serves the Manager root without loading or checking business installation state", async () => {
process.env.MANAGER_MODE = "true";
const response = await handleProxyRequest(request("/"));
expect(response.status).toBe(200);
expect(response.headers.get("content-security-policy")).toContain("default-src 'self'");
expect(isInstalledForRequest).not.toHaveBeenCalled();
});
it("checks installation state for a normal business request", async () => {
process.env.MANAGER_MODE = "false";
isInstalledForRequest.mockResolvedValue(false);
const response = await handleProxyRequest(request("/"));
expect(response.status).toBe(307);
expect(response.headers.get("location")).toBe("https://manager.example.com/install");
expect(isInstalledForRequest).toHaveBeenCalledOnce();
});
it("preserves protected business route redirects after installation", async () => {
process.env.MANAGER_MODE = "false";
isInstalledForRequest.mockResolvedValue(true);
const response = await handleProxyRequest(request("/owner"));
expect(response.status).toBe(307);
expect(response.headers.get("location")).toContain("/sign-in?callbackUrl=%2Fowner");
});
});
+1 -18
View File
@@ -1,6 +1,7 @@
import type { Metadata, Viewport } from "next"; import type { Metadata, Viewport } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import { Providers } from "@/components/providers"; import { Providers } from "@/components/providers";
import { getBranding } from "@/lib/business/branding";
import "./globals.css"; import "./globals.css";
const inter = Inter({ const inter = Inter({
@@ -17,13 +18,6 @@ const inter = Inter({
* than "NextChair" (see that file's own fallback-order comment). * than "NextChair" (see that file's own fallback-order comment).
*/ */
export async function generateMetadata(): Promise<Metadata> { export async function generateMetadata(): Promise<Metadata> {
if (process.env.MANAGER_MODE === "true") {
return {
title: { default: "NextChair Manager", template: "%s | NextChair Manager" },
description: "Manage NextChair business deployments.",
};
}
const { getBranding } = await import("@/lib/business/branding");
const branding = await getBranding(); const branding = await getBranding();
return { return {
@@ -47,17 +41,6 @@ export async function generateMetadata(): Promise<Metadata> {
} }
export async function generateViewport(): Promise<Viewport> { export async function generateViewport(): Promise<Viewport> {
if (process.env.MANAGER_MODE === "true") {
return {
width: "device-width",
initialScale: 1,
maximumScale: 1,
userScalable: false,
viewportFit: "cover",
themeColor: "#111b15",
};
}
const { getBranding } = await import("@/lib/business/branding");
const branding = await getBranding(); const branding = await getBranding();
return { return {
width: "device-width", width: "device-width",
+9 -15
View File
@@ -1,6 +1,7 @@
import NextAuth from "next-auth"; import NextAuth from "next-auth";
import { NextResponse, type NextRequest } from "next/server"; import { NextResponse } from "next/server";
import { authConfig } from "@/auth.config"; import { authConfig } from "@/auth.config";
import { isInstalledForRequest } from "@/lib/installation/installation-state";
// /home, /appointments and /settings are intentionally NOT hard-gated here // /home, /appointments and /settings are intentionally NOT hard-gated here
// as of Phase 4 — Home must work for guests (public business browsing + // as of Phase 4 — Home must work for guests (public business browsing +
@@ -20,6 +21,7 @@ const PROTECTED_PREFIXES = ["/staff", "/owner"];
// business data guard themselves (see /api/health and // business data guard themselves (see /api/health and
// /api/cron/process-notifications). // /api/cron/process-notifications).
const INSTALL_EXEMPT_PREFIXES = [ const INSTALL_EXEMPT_PREFIXES = [
"/manager",
"/install", "/install",
"/offline", "/offline",
"/manifest.webmanifest", "/manifest.webmanifest",
@@ -82,23 +84,17 @@ function buildCsp(): string {
].join("; "); ].join("; ");
} }
export async function handleProxyRequest(req: NextRequest) { export default auth(async (req) => {
const { pathname } = req.nextUrl; const { pathname } = req.nextUrl;
const csp = buildCsp(); const csp = buildCsp();
// Manager is a separate deployment and hostname, so its root pathname is
// normally `/`, not `/manager`. Branch before loading any business module.
if (process.env.MANAGER_MODE === "true") {
const response = NextResponse.next();
response.headers.set("Content-Security-Policy", csp);
return response;
}
const isInstallExempt = INSTALL_EXEMPT_PREFIXES.some( const isInstallExempt = INSTALL_EXEMPT_PREFIXES.some(
(prefix) => pathname === prefix || pathname.startsWith(prefix.endsWith("/") ? prefix : `${prefix}/`), (prefix) => pathname === prefix || pathname.startsWith(prefix.endsWith("/") ? prefix : `${prefix}/`),
); );
const { isInstalledForRequest } = await import("@/lib/installation/installation-state"); // The platform Manager is a separate deployment and deliberately has no
const installed = await isInstalledForRequest(); // business database. Its own session guard runs inside /manager pages/API.
const isManagerPath = pathname === "/manager" || pathname.startsWith("/manager/");
const installed = isManagerPath ? true : await isInstalledForRequest();
if (!installed && !isInstallExempt) { if (!installed && !isInstallExempt) {
const response = NextResponse.redirect(new URL("/install", req.nextUrl.origin)); const response = NextResponse.redirect(new URL("/install", req.nextUrl.origin));
@@ -130,9 +126,7 @@ export async function handleProxyRequest(req: NextRequest) {
const response = NextResponse.next(); const response = NextResponse.next();
response.headers.set("Content-Security-Policy", csp); response.headers.set("Content-Security-Policy", csp);
return response; return response;
} });
export default auth(handleProxyRequest);
export const config = { export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"], matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],