* Start the HTTP/WS API server. * * @throws Error if a server is already running (check lockfile first)
(options: StartServerOptions)
| 346 | * @throws Error if a server is already running (check lockfile first) |
| 347 | */ |
| 348 | async startServer(options: StartServerOptions): Promise<ServerInfo> { |
| 349 | if (this.server) { |
| 350 | throw new Error("Server already running in this process"); |
| 351 | } |
| 352 | |
| 353 | // Create lockfile instance for checking - don't store yet |
| 354 | const lockfile = new ServerLockfile(options.muxHome); |
| 355 | |
| 356 | // Check for existing server (another process) |
| 357 | const existing = await lockfile.read(); |
| 358 | if (existing) { |
| 359 | throw new Error( |
| 360 | `Another mux server is already running at ${existing.baseUrl} (PID: ${existing.pid})` |
| 361 | ); |
| 362 | } |
| 363 | |
| 364 | const bindHost = |
| 365 | typeof options.host === "string" && options.host.trim() ? options.host.trim() : "127.0.0.1"; |
| 366 | |
| 367 | this.apiAuthToken = options.authToken; |
| 368 | |
| 369 | // Resolve the static assets directory (dist/) that contains index.html. |
| 370 | // Non-bundled (Electron): __dirname is dist/node/services/, so ../.. reaches dist/. |
| 371 | // Bundled (Docker): __dirname is dist/runtime/, so .. reaches dist/. |
| 372 | const staticDirCandidates = [path.join(__dirname, "../.."), path.join(__dirname, "..")]; |
| 373 | |
| 374 | let staticDir: string | undefined; |
| 375 | if (options.serveStatic) { |
| 376 | for (const candidate of staticDirCandidates) { |
| 377 | try { |
| 378 | await fs.access(path.join(candidate, "index.html")); |
| 379 | staticDir = candidate; |
| 380 | break; |
| 381 | } catch { |
| 382 | // Try the next candidate. |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | if (!staticDir) { |
| 387 | log.warn( |
| 388 | `API server static UI requested, but index.html is missing near ${__dirname}. Disabling.` |
| 389 | ); |
| 390 | } |
| 391 | } |
| 392 | const serveStatic = options.serveStatic === true && staticDir !== undefined; |
| 393 | |
| 394 | // Non-CLI starts (desktop/browser mode) do not parse CLI flags, so allow an |
| 395 | // explicit env override for TLS-terminating proxies that rewrite forwarded proto. |
| 396 | const allowHttpOrigin = options.allowHttpOrigin ?? resolveAllowHttpOriginEnvFlag(); |
| 397 | |
| 398 | const serverOptions: OrpcServerOptions = { |
| 399 | host: bindHost, |
| 400 | port: options.port ?? 0, |
| 401 | context: options.context, |
| 402 | authToken: options.authToken, |
| 403 | router: options.router, |
| 404 | desktopBridgeServer: options.context.desktopBridgeServer, |
| 405 | serveStatic, |
no test coverage detected