refactor(browser): share common server middleware

This commit is contained in:
Peter Steinberger
2026-02-15 04:46:05 +00:00
parent 909b5411bb
commit 28014de974
3 changed files with 49 additions and 51 deletions

View File

@@ -5,14 +5,16 @@ import type { ResolvedBrowserConfig } from "./config.js";
import type { BrowserRouteRegistrar } from "./routes/types.js";
import { isLoopbackHost } from "../gateway/net.js";
import { deleteBridgeAuthForPort, setBridgeAuthForPort } from "./bridge-auth-registry.js";
import { browserMutationGuardMiddleware } from "./csrf.js";
import { isAuthorizedBrowserRequest } from "./http-auth.js";
import { registerBrowserRoutes } from "./routes/index.js";
import {
type BrowserServerState,
createBrowserRouteContext,
type ProfileContext,
} from "./server-context.js";
import {
installBrowserAuthMiddleware,
installBrowserCommonMiddleware,
} from "./server-middleware.js";
export type BrowserBridge = {
server: Server;
@@ -36,35 +38,14 @@ export async function startBrowserBridgeServer(params: {
const port = params.port ?? 0;
const app = express();
app.use((req, res, next) => {
const ctrl = new AbortController();
const abort = () => ctrl.abort(new Error("request aborted"));
req.once("aborted", abort);
res.once("close", () => {
if (!res.writableEnded) {
abort();
}
});
// Make the signal available to browser route handlers (best-effort).
(req as unknown as { signal?: AbortSignal }).signal = ctrl.signal;
next();
});
app.use(express.json({ limit: "1mb" }));
app.use(browserMutationGuardMiddleware());
installBrowserCommonMiddleware(app);
const authToken = params.authToken?.trim() || undefined;
const authPassword = params.authPassword?.trim() || undefined;
if (!authToken && !authPassword) {
throw new Error("bridge server requires auth (authToken/authPassword missing)");
}
if (authToken || authPassword) {
app.use((req, res, next) => {
if (isAuthorizedBrowserRequest(req, { token: authToken, password: authPassword })) {
return next();
}
res.status(401).send("Unauthorized");
});
}
installBrowserAuthMiddleware(app, { token: authToken, password: authPassword });
const state: BrowserServerState = {
server: null as unknown as Server,

View File

@@ -0,0 +1,37 @@
import type { Express } from "express";
import express from "express";
import { browserMutationGuardMiddleware } from "./csrf.js";
import { isAuthorizedBrowserRequest } from "./http-auth.js";
export function installBrowserCommonMiddleware(app: Express) {
app.use((req, res, next) => {
const ctrl = new AbortController();
const abort = () => ctrl.abort(new Error("request aborted"));
req.once("aborted", abort);
res.once("close", () => {
if (!res.writableEnded) {
abort();
}
});
// Make the signal available to browser route handlers (best-effort).
(req as unknown as { signal?: AbortSignal }).signal = ctrl.signal;
next();
});
app.use(express.json({ limit: "1mb" }));
app.use(browserMutationGuardMiddleware());
}
export function installBrowserAuthMiddleware(
app: Express,
auth: { token?: string; password?: string },
) {
if (!auth.token && !auth.password) {
return;
}
app.use((req, res, next) => {
if (isAuthorizedBrowserRequest(req, auth)) {
return next();
}
res.status(401).send("Unauthorized");
});
}

View File

@@ -5,9 +5,7 @@ import { loadConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveBrowserConfig, resolveProfile } from "./config.js";
import { ensureBrowserControlAuth, resolveBrowserControlAuth } from "./control-auth.js";
import { browserMutationGuardMiddleware } from "./csrf.js";
import { ensureChromeExtensionRelayServer } from "./extension-relay.js";
import { isAuthorizedBrowserRequest } from "./http-auth.js";
import { isPwAiLoaded } from "./pw-ai-state.js";
import { registerBrowserRoutes } from "./routes/index.js";
import {
@@ -15,6 +13,10 @@ import {
createBrowserRouteContext,
listKnownProfileNames,
} from "./server-context.js";
import {
installBrowserAuthMiddleware,
installBrowserCommonMiddleware,
} from "./server-middleware.js";
let state: BrowserServerState | null = null;
const log = createSubsystemLogger("browser");
@@ -43,30 +45,8 @@ export async function startBrowserControlServerFromConfig(): Promise<BrowserServ
}
const app = express();
app.use((req, res, next) => {
const ctrl = new AbortController();
const abort = () => ctrl.abort(new Error("request aborted"));
req.once("aborted", abort);
res.once("close", () => {
if (!res.writableEnded) {
abort();
}
});
// Make the signal available to browser route handlers (best-effort).
(req as unknown as { signal?: AbortSignal }).signal = ctrl.signal;
next();
});
app.use(express.json({ limit: "1mb" }));
app.use(browserMutationGuardMiddleware());
if (browserAuth.token || browserAuth.password) {
app.use((req, res, next) => {
if (isAuthorizedBrowserRequest(req, browserAuth)) {
return next();
}
res.status(401).send("Unauthorized");
});
}
installBrowserCommonMiddleware(app);
installBrowserAuthMiddleware(app, browserAuth);
const ctx = createBrowserRouteContext({
getState: () => state,