fix(cipc): say plainly when search runs where it cannot
CIPC/BizPortal search drives a real Chrome session through agent-browser to log into BizPortal and scrape the result pane. That toolchain exists only on the Windows dev machine; the Coolify image is a Linux container with no browser tooling (the Dockerfile header already says so). Nothing detected that up front, so a search on the deployed URL fell through to the legacy spawnSync fallback, which builds a Windows-style interpreter path even on Linux (.venv/Scripts/ plus python). The shell answered 'not found' with exit 127, extractError() found no marker line in stdout and returned 'Unknown error', and the UI rendered that over the raw shell message - a dead end with no action in it. Now the route checks for the interpreter it needs before doing any work, and returns 503 with a sentence naming the real cause and where to run the search instead. Checked per request so it cannot go stale. Verified: typecheck clean; a real enterprise search of K2022808158 on the local dev server still returns 200 in 87s with 3 directors, so the guard does not touch the working Windows path.
This commit is contained in:
parent
92dcae5122
commit
0717400bb0
1 changed files with 54 additions and 0 deletions
|
|
@ -492,6 +492,42 @@ function badRequest(message: string) {
|
||||||
return NextResponse.json({ ok: false, error: message }, { status: 400 });
|
return NextResponse.json({ ok: false, error: message }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// RUNTIME CAPABILITY CHECK
|
||||||
|
// ===================================================================
|
||||||
|
// CIPC/BizPortal search is not an ordinary server-side job: it drives a
|
||||||
|
// real Chrome session through agent-browser to log into BizPortal and read
|
||||||
|
// the result pane. That toolchain exists only on the Windows dev machine.
|
||||||
|
// The Coolify image is a Linux container with no browser tooling at all —
|
||||||
|
// see the NOTE at the top of the Dockerfile, which says exactly this.
|
||||||
|
//
|
||||||
|
// Until 2026-10-20 this was not detected up front. The request fell
|
||||||
|
// through to the legacy `spawnSync` fallback, which builds a Windows-style
|
||||||
|
// interpreter path even on Linux (`.venv/Scripts/` + `python`), so `/bin/sh`
|
||||||
|
// answered "not found" with exit code 127, `extractError()` found no ERROR:
|
||||||
|
// line in stdout and returned "Unknown error", and the UI rendered that over
|
||||||
|
// a raw shell message. A dead end for anyone who tried the search on the
|
||||||
|
// deployed URL.
|
||||||
|
//
|
||||||
|
// Returns a plain-language reason when the runtime can't be used here, or
|
||||||
|
// null when it can. Re-checked per request so it never goes stale.
|
||||||
|
function cipcUnavailableReason(): string | null {
|
||||||
|
const isWindows = process.platform === "win32";
|
||||||
|
const pythonBin = isWindows
|
||||||
|
? path.join(SCRIPTS_DIR, ".venv", "Scripts", "python.exe")
|
||||||
|
: path.join(SCRIPTS_DIR, ".venv", "bin", "python");
|
||||||
|
|
||||||
|
if (!fs.existsSync(pythonBin)) {
|
||||||
|
return (
|
||||||
|
"CIPC search runs on the local Workflow app on your PC (npm run dev), " +
|
||||||
|
"not on the deployed server — this server has no BizPortal browser " +
|
||||||
|
`runtime installed (no Python at ${pythonBin}). ` +
|
||||||
|
"Open the app on your machine to run this search."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Extract the markdown block from the full stdout. */
|
/** Extract the markdown block from the full stdout. */
|
||||||
function extractMarkdown(stdout: string): string {
|
function extractMarkdown(stdout: string): string {
|
||||||
const idx = stdout.indexOf("\n#");
|
const idx = stdout.indexOf("\n#");
|
||||||
|
|
@ -535,6 +571,24 @@ export async function POST(req: NextRequest) {
|
||||||
}
|
}
|
||||||
if (!rawQuery) return badRequest("query is required");
|
if (!rawQuery) return badRequest("query is required");
|
||||||
|
|
||||||
|
// Refuse early, and in words the operator can act on, when the browser
|
||||||
|
// runtime this route needs is not present on the host (the deployed Linux
|
||||||
|
// container). Better a clear 503 than a 165 s hang or a shell error.
|
||||||
|
const unavailable = cipcUnavailableReason();
|
||||||
|
if (unavailable) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
error: unavailable,
|
||||||
|
raw: unavailable,
|
||||||
|
search_kind: kind,
|
||||||
|
query: rawQuery,
|
||||||
|
runtime_missing: true,
|
||||||
|
},
|
||||||
|
{ status: 503 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let resolvedQuery = rawQuery;
|
let resolvedQuery = rawQuery;
|
||||||
let resolvedVia: "k_number" | "company_name" | "director_id" | null = null;
|
let resolvedVia: "k_number" | "company_name" | "director_id" | null = null;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue