CoolFace
Apppublic

chwellofficial/nt360Slides

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
start.js344 linesDownload Raw Back to root
1/* This script starts the FastAPI and Next.js servers, setting up user configuration if necessary. It reads environment variables to configure API keys and other settings, ensuring that the user configuration file is created if it doesn't exist. The script also handles the starting of both servers and keeps the Node.js process alive until one of the servers exits. */2 3import { join, dirname } from "path";4import { fileURLToPath } from "url";5import { spawn } from "child_process";6import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";7 8const __filename = fileURLToPath(import.meta.url);9const __dirname = dirname(__filename);10 11const fastapiDir = join(__dirname, "servers/fastapi");12const nextjsDir = join(__dirname, "servers/nextjs");13const nextjsStandaloneServer = join(nextjsDir, "server.js");14const exportSyncScript = join(__dirname, "scripts/sync-presentation-export.cjs");15 16const args = process.argv.slice(2);17const hasDevArg = args.includes("--dev") || args.includes("-d");18const isDev = hasDevArg;19const canChangeKeys = process.env.CAN_CHANGE_KEYS !== "false";20 21const fastapiPort = 8000;22const nextjsPort = 3000;23const appmcpPort = 8001;24 25const userConfigPath = join(process.env.APP_DATA_DIRECTORY, "userConfig.json");26const userDataDir = dirname(userConfigPath);27 28// Create user_data directory if it doesn't exist29if (!existsSync(userDataDir)) {30  mkdirSync(userDataDir, { recursive: true });31}32 33// Setup node_modules for development34const setupNodeModules = () => {35  return new Promise((resolve, reject) => {36    console.log("Setting up node_modules for Next.js...");37    const npmProcess = spawn("npm", ["install"], {38      cwd: nextjsDir,39      stdio: "inherit",40      env: process.env,41    });42 43    npmProcess.on("error", (err) => {44      console.error("npm install failed:", err);45      reject(err);46    });47 48    npmProcess.on("exit", (code) => {49      if (code === 0) {50        console.log("npm install completed successfully");51        resolve();52      } else {53        console.error(`npm install failed with exit code: ${code}`);54        reject(new Error(`npm install failed with exit code: ${code}`));55      }56    });57  });58};59 60const runCommand = (command, commandArgs, options = {}) => {61  return new Promise((resolve, reject) => {62    const child = spawn(command, commandArgs, {63      cwd: options.cwd || __dirname,64      stdio: options.stdio || "inherit",65      env: options.env || process.env,66    });67 68    child.on("error", (err) => {69      reject(err);70    });71 72    child.on("exit", (code) => {73      if (code === 0) {74        resolve();75      } else {76        reject(new Error(`${command} exited with code: ${code}`));77      }78    });79  });80};81 82const runNodeScript = (scriptPath, scriptArgs) => {83  return runCommand(process.execPath, [scriptPath, ...scriptArgs], {84    cwd: __dirname,85  });86};87 88const isTruthyEnv = (value) => {89  if (value == null) {90    return false;91  }92 93  return !["", "0", "false", "no", "off"].includes(94    String(value).trim().toLowerCase()95  );96};97 98const isOllamaInstalled = () =>99  existsSync("/usr/bin/ollama") || existsSync("/usr/local/bin/ollama");100 101const shouldStartOllama = () => isTruthyEnv(process.env.START_OLLAMA);102 103const ensureOllamaRuntime = async () => {104  if (!shouldStartOllama() || isOllamaInstalled()) {105    return;106  }107 108  console.log("START_OLLAMA=true; installing Ollama runtime...");109  await runCommand("sh", ["-c", "curl -fsSL https://ollama.com/install.sh | sh"], {110    cwd: "/",111  });112};113 114const ensurePresentationExportRuntime = async () => {115  if (process.env.ENSURE_PRESENTATION_EXPORT_RUNTIME === "false") {116    return;117  }118 119  if (!existsSync(exportSyncScript)) {120    console.warn("presentation-export sync script not found; skipping runtime check");121    return;122  }123 124  try {125    await runNodeScript(exportSyncScript, ["--check-only"]);126  } catch (err) {127    if (!isDev) {128      throw new Error(129        "presentation-export runtime is missing in this container image. Rebuild the image so the runtime package is installed."130      );131    }132 133    console.warn("presentation-export runtime missing in dev mount. Syncing runtime package...");134    await runNodeScript(exportSyncScript, ["--force"]);135  }136};137 138process.env.USER_CONFIG_PATH = userConfigPath;139// Let Next.js middleware reach FastAPI over the loopback interface inside the140// container without having to bounce through nginx (the host-facing port is141// not reachable from inside the Next.js process).142if (!process.env.FAST_API_INTERNAL_URL) {143  process.env.FAST_API_INTERNAL_URL = `http://127.0.0.1:${fastapiPort}`;144}145 146//? UserConfig is only setup if API Keys can be changed147const setupUserConfigFromEnv = () => {148  let existingConfig = {};149 150  if (existsSync(userConfigPath)) {151    existingConfig = JSON.parse(readFileSync(userConfigPath, "utf8"));152  }153 154  if (!["ollama", "openai", "google", "anthropic", "custom", "codex"].includes(existingConfig.LLM)) {155    existingConfig.LLM = undefined;156  }157 158  const userConfig = {159    LLM: process.env.LLM || existingConfig.LLM,160    OPENAI_API_KEY: process.env.OPENAI_API_KEY || existingConfig.OPENAI_API_KEY,161    OPENAI_MODEL: process.env.OPENAI_MODEL || existingConfig.OPENAI_MODEL,162    GOOGLE_API_KEY: process.env.GOOGLE_API_KEY || existingConfig.GOOGLE_API_KEY,163    GOOGLE_MODEL: process.env.GOOGLE_MODEL || existingConfig.GOOGLE_MODEL,164    OLLAMA_URL: process.env.OLLAMA_URL || existingConfig.OLLAMA_URL,165    OLLAMA_MODEL: process.env.OLLAMA_MODEL || existingConfig.OLLAMA_MODEL,166    ANTHROPIC_API_KEY:167      process.env.ANTHROPIC_API_KEY || existingConfig.ANTHROPIC_API_KEY,168    ANTHROPIC_MODEL:169      process.env.ANTHROPIC_MODEL || existingConfig.ANTHROPIC_MODEL,170    CUSTOM_LLM_URL: process.env.CUSTOM_LLM_URL || existingConfig.CUSTOM_LLM_URL,171    CUSTOM_LLM_API_KEY:172      process.env.CUSTOM_LLM_API_KEY || existingConfig.CUSTOM_LLM_API_KEY,173    CUSTOM_MODEL: process.env.CUSTOM_MODEL || existingConfig.CUSTOM_MODEL,174    PEXELS_API_KEY: process.env.PEXELS_API_KEY || existingConfig.PEXELS_API_KEY,175    PIXABAY_API_KEY:176      process.env.PIXABAY_API_KEY || existingConfig.PIXABAY_API_KEY,177    IMAGE_PROVIDER: process.env.IMAGE_PROVIDER || existingConfig.IMAGE_PROVIDER,178    DISABLE_THINKING:179      process.env.DISABLE_THINKING || existingConfig.DISABLE_THINKING,180    EXTENDED_REASONING:181      process.env.EXTENDED_REASONING || existingConfig.EXTENDED_REASONING,182    WEB_GROUNDING: process.env.WEB_GROUNDING || existingConfig.WEB_GROUNDING,183    USE_CUSTOM_URL: process.env.USE_CUSTOM_URL || existingConfig.USE_CUSTOM_URL,184    COMFYUI_URL: process.env.COMFYUI_URL || existingConfig.COMFYUI_URL,185    COMFYUI_WORKFLOW:186      process.env.COMFYUI_WORKFLOW || existingConfig.COMFYUI_WORKFLOW,187    DALL_E_3_QUALITY:188      process.env.DALL_E_3_QUALITY || existingConfig.DALL_E_3_QUALITY,189    GPT_IMAGE_1_5_QUALITY:190      process.env.GPT_IMAGE_1_5_QUALITY || existingConfig.GPT_IMAGE_1_5_QUALITY,191    CODEX_MODEL: process.env.CODEX_MODEL || existingConfig.CODEX_MODEL,192    CODEX_ACCESS_TOKEN: existingConfig.CODEX_ACCESS_TOKEN,193    CODEX_REFRESH_TOKEN: existingConfig.CODEX_REFRESH_TOKEN,194    CODEX_TOKEN_EXPIRES: existingConfig.CODEX_TOKEN_EXPIRES,195    CODEX_ACCOUNT_ID: existingConfig.CODEX_ACCOUNT_ID,196    AUTH_USERNAME: existingConfig.AUTH_USERNAME,197    AUTH_PASSWORD_HASH: existingConfig.AUTH_PASSWORD_HASH,198    AUTH_SECRET_KEY: existingConfig.AUTH_SECRET_KEY,199  };200 201  writeFileSync(userConfigPath, JSON.stringify(userConfig));202};203 204const startServers = async () => {205  const fastApiProcess = spawn(206    "python",207    [208      "server.py",209      "--port",210      fastapiPort.toString(),211      "--reload",212      isDev ? "true" : "false",213    ],214    {215      cwd: fastapiDir,216      stdio: "inherit",217      env: { ...process.env, PYTHONPATH: fastapiDir },218    }219  );220 221  fastApiProcess.on("error", (err) => {222    console.error("FastAPI process failed to start:", err);223  });224 225  const appmcpProcess = spawn(226    "python",227    ["mcp_server.py", "--port", appmcpPort.toString()],228    {229      cwd: fastapiDir,230      stdio: "ignore",231      env: process.env,232    }233  );234 235  appmcpProcess.on("error", (err) => {236    console.error("App MCP process failed to start:", err);237  });238 239  const useStandaloneNextjs = !isDev && existsSync(nextjsStandaloneServer);240 241  const nextjsProcess = spawn(242    useStandaloneNextjs ? process.execPath : "npm",243    useStandaloneNextjs244      ? [nextjsStandaloneServer]245      : [246          "run",247          isDev ? "dev" : "start",248          "--",249          "-H",250          "127.0.0.1",251          "-p",252          nextjsPort.toString(),253        ],254    {255      cwd: nextjsDir,256      stdio: "inherit",257      env:258        useStandaloneNextjs259          ? {260              ...process.env,261              HOSTNAME: "127.0.0.1",262              PORT: nextjsPort.toString(),263            }264          : process.env,265    }266  );267 268  nextjsProcess.on("error", (err) => {269    console.error("Next.js process failed to start:", err);270  });271 272  const shouldStartOllamaRuntime = shouldStartOllama();273  const ollamaInstalled = isOllamaInstalled();274 275  const exitPromises = [276    new Promise((resolve) => fastApiProcess.on("exit", resolve)),277    new Promise((resolve) => nextjsProcess.on("exit", resolve)),278  ];279 280  if (shouldStartOllamaRuntime && ollamaInstalled) {281    const ollamaProcess = spawn("ollama", ["serve"], {282      cwd: "/",283      stdio: "inherit",284      env: process.env,285    });286    ollamaProcess.on("error", (err) => {287      console.error("Ollama process failed to start:", err);288    });289    exitPromises.push(new Promise((resolve) => ollamaProcess.on("exit", resolve)));290  } else if (shouldStartOllamaRuntime) {291    console.log(292      "Ollama requested, but the binary is not installed. Set START_OLLAMA=true to install it at startup, or set OLLAMA_URL to a remote daemon."293    );294  } else {295    console.log(296      "Ollama disabled (START_OLLAMA=false); use OLLAMA_URL for a remote daemon if needed."297    );298  }299 300  // Keep the Node process alive until one of the servers exits301  const exitCode = await Promise.race(exitPromises);302 303  console.log(`One of the processes exited. Exit code: ${exitCode}`);304  process.exit(exitCode);305};306 307// Start nginx service308const startNginx = () => {309  const nginxProcess = spawn("service", ["nginx", "start"], {310    stdio: "inherit",311    env: process.env,312  });313 314  nginxProcess.on("error", (err) => {315    console.error("Nginx process failed to start:", err);316  });317 318  nginxProcess.on("exit", (code) => {319    if (code === 0) {320      console.log("Nginx started successfully");321    } else {322      console.error(`Nginx failed to start with exit code: ${code}`);323    }324  });325};326 327const main = async () => {328  await ensurePresentationExportRuntime();329  await ensureOllamaRuntime();330 331  if (isDev) {332    await setupNodeModules();333  }334 335  if (canChangeKeys) {336    setupUserConfigFromEnv();337  }338 339  startServers();340  startNginx();341};342 343main();344