mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-29 01:31:18 +00:00
refactor(browser): share common server middleware
This commit is contained in:
@@ -5,14 +5,16 @@ import type { ResolvedBrowserConfig } from "./config.js";
|
|||||||
import type { BrowserRouteRegistrar } from "./routes/types.js";
|
import type { BrowserRouteRegistrar } from "./routes/types.js";
|
||||||
import { isLoopbackHost } from "../gateway/net.js";
|
import { isLoopbackHost } from "../gateway/net.js";
|
||||||
import { deleteBridgeAuthForPort, setBridgeAuthForPort } from "./bridge-auth-registry.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 { registerBrowserRoutes } from "./routes/index.js";
|
||||||
import {
|
import {
|
||||||
type BrowserServerState,
|
type BrowserServerState,
|
||||||
createBrowserRouteContext,
|
createBrowserRouteContext,
|
||||||
type ProfileContext,
|
type ProfileContext,
|
||||||
} from "./server-context.js";
|
} from "./server-context.js";
|
||||||
|
import {
|
||||||
|
installBrowserAuthMiddleware,
|
||||||
|
installBrowserCommonMiddleware,
|
||||||
|
} from "./server-middleware.js";
|
||||||
|
|
||||||
export type BrowserBridge = {
|
export type BrowserBridge = {
|
||||||
server: Server;
|
server: Server;
|
||||||
@@ -36,35 +38,14 @@ export async function startBrowserBridgeServer(params: {
|
|||||||
const port = params.port ?? 0;
|
const port = params.port ?? 0;
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use((req, res, next) => {
|
installBrowserCommonMiddleware(app);
|
||||||
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());
|
|
||||||
|
|
||||||
const authToken = params.authToken?.trim() || undefined;
|
const authToken = params.authToken?.trim() || undefined;
|
||||||
const authPassword = params.authPassword?.trim() || undefined;
|
const authPassword = params.authPassword?.trim() || undefined;
|
||||||
if (!authToken && !authPassword) {
|
if (!authToken && !authPassword) {
|
||||||
throw new Error("bridge server requires auth (authToken/authPassword missing)");
|
throw new Error("bridge server requires auth (authToken/authPassword missing)");
|
||||||
}
|
}
|
||||||
if (authToken || authPassword) {
|
installBrowserAuthMiddleware(app, { token: authToken, password: authPassword });
|
||||||
app.use((req, res, next) => {
|
|
||||||
if (isAuthorizedBrowserRequest(req, { token: authToken, password: authPassword })) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
res.status(401).send("Unauthorized");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const state: BrowserServerState = {
|
const state: BrowserServerState = {
|
||||||
server: null as unknown as Server,
|
server: null as unknown as Server,
|
||||||
|
|||||||
37
src/browser/server-middleware.ts
Normal file
37
src/browser/server-middleware.ts
Normal 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");
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,9 +5,7 @@ import { loadConfig } from "../config/config.js";
|
|||||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||||
import { resolveBrowserConfig, resolveProfile } from "./config.js";
|
import { resolveBrowserConfig, resolveProfile } from "./config.js";
|
||||||
import { ensureBrowserControlAuth, resolveBrowserControlAuth } from "./control-auth.js";
|
import { ensureBrowserControlAuth, resolveBrowserControlAuth } from "./control-auth.js";
|
||||||
import { browserMutationGuardMiddleware } from "./csrf.js";
|
|
||||||
import { ensureChromeExtensionRelayServer } from "./extension-relay.js";
|
import { ensureChromeExtensionRelayServer } from "./extension-relay.js";
|
||||||
import { isAuthorizedBrowserRequest } from "./http-auth.js";
|
|
||||||
import { isPwAiLoaded } from "./pw-ai-state.js";
|
import { isPwAiLoaded } from "./pw-ai-state.js";
|
||||||
import { registerBrowserRoutes } from "./routes/index.js";
|
import { registerBrowserRoutes } from "./routes/index.js";
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +13,10 @@ import {
|
|||||||
createBrowserRouteContext,
|
createBrowserRouteContext,
|
||||||
listKnownProfileNames,
|
listKnownProfileNames,
|
||||||
} from "./server-context.js";
|
} from "./server-context.js";
|
||||||
|
import {
|
||||||
|
installBrowserAuthMiddleware,
|
||||||
|
installBrowserCommonMiddleware,
|
||||||
|
} from "./server-middleware.js";
|
||||||
|
|
||||||
let state: BrowserServerState | null = null;
|
let state: BrowserServerState | null = null;
|
||||||
const log = createSubsystemLogger("browser");
|
const log = createSubsystemLogger("browser");
|
||||||
@@ -43,30 +45,8 @@ export async function startBrowserControlServerFromConfig(): Promise<BrowserServ
|
|||||||
}
|
}
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use((req, res, next) => {
|
installBrowserCommonMiddleware(app);
|
||||||
const ctrl = new AbortController();
|
installBrowserAuthMiddleware(app, browserAuth);
|
||||||
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");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const ctx = createBrowserRouteContext({
|
const ctx = createBrowserRouteContext({
|
||||||
getState: () => state,
|
getState: () => state,
|
||||||
|
|||||||
Reference in New Issue
Block a user