github-actions[bot] commited on
Commit
f6dd1f9
·
1 Parent(s): baeebee

deploy: 7ae99ac — 更新 index.js

Browse files
Files changed (1) hide show
  1. backend/src/index.js +14 -33
backend/src/index.js CHANGED
@@ -15,7 +15,12 @@ const litellm = require("./litellm");
15
  const app = express();
16
  const PORT = parseInt(process.env.PORT || "3001", 10);
17
 
18
- // ─── Middleware ────────────────────────────────────────────────────────────
 
 
 
 
 
19
 
20
  app.use(helmet({ crossOriginResourcePolicy: false }));
21
  app.use(
@@ -33,7 +38,6 @@ app.use(
33
  })
34
  );
35
 
36
- // Rate limiting
37
  app.use(
38
  "/api/",
39
  rateLimit({
@@ -45,44 +49,28 @@ app.use(
45
  })
46
  );
47
 
48
- // ─── Routes ───────────────────────────────────────────────────────────────
49
-
50
- // BUG FIX: The simple stub was removed. statsRouter's /health checks LiteLLM + DB health.
51
  app.use("/api/models", modelsRouter);
52
  app.use("/api", statsRouter);
53
 
54
- // 404
55
  app.use((req, res) => {
56
  res.status(404).json({ success: false, error: "Not found" });
57
  });
58
 
59
- // Error handler
60
  app.use((err, req, res, _next) => {
61
  logger.error("Unhandled error", { error: err.message, stack: err.stack });
62
  res.status(500).json({ success: false, error: "Internal server error" });
63
  });
64
 
65
- // ─── Startup ──────────────────────────────────────────────────────────────
66
-
67
  async function start() {
68
- // Initialize DB
69
  db.getDb();
70
  logger.info("Database initialized");
71
-
72
- // BUG FIX: 5s hardcoded delay was unreliable — LiteLLM can take 30-60s to start.
73
- // Use retry loop with backoff instead.
74
  syncModelsToLitellmWithRetry();
75
-
76
  app.listen(PORT, "0.0.0.0", () => {
77
- logger.info(`AI Gateway Backend running on port ${PORT}`);
78
- logger.info(`Gateway public URL: ${process.env.GATEWAY_PUBLIC_URL || "http://localhost"}`);
79
  });
80
  }
81
 
82
- /**
83
- * Retry wrapper: attempts sync with exponential backoff for up to ~5 minutes.
84
- * Handles the case where LiteLLM container starts slower than the backend.
85
- */
86
  async function syncModelsToLitellmWithRetry() {
87
  const MAX_ATTEMPTS = 10;
88
  const BASE_DELAY_MS = 5000;
@@ -93,33 +81,26 @@ async function syncModelsToLitellmWithRetry() {
93
  return;
94
  } catch (err) {
95
  const delay = Math.min(BASE_DELAY_MS * attempt, 30000);
96
- logger.warn(`LiteLLM not ready (attempt ${attempt}/${MAX_ATTEMPTS}), retrying in ${delay}ms...`);
97
  await new Promise((r) => setTimeout(r, delay));
98
  }
99
  }
100
- logger.error("LiteLLM did not become ready after all retry attempts. Models not synced.");
101
  }
102
 
103
- /**
104
- * On startup, re-register all persisted models with LiteLLM.
105
- * This handles the case where LiteLLM was restarted and lost its in-memory state.
106
- */
107
  async function syncModelsToLitellm() {
108
  try {
109
  const models = db.listModels({ enabledOnly: true });
110
- logger.info(`Syncing ${models.length} models to LiteLLM...`);
111
-
112
  for (const model of models) {
113
  try {
114
- // _apiKey is the raw (unmasked) API key set by deserializeModel
115
  const litellmId = await litellm.registerModel({ ...model, _apiKey: model._apiKey });
116
  db.updateModel(model.id, { litellmId });
117
- logger.info(`Synced: ${model.name}`);
118
  } catch (err) {
119
- logger.warn(`Failed to sync model ${model.name}: ${err.message}`);
120
  }
121
  }
122
-
123
  logger.info("Model sync complete");
124
  } catch (err) {
125
  logger.error("Model sync failed", { error: err.message });
@@ -129,4 +110,4 @@ async function syncModelsToLitellm() {
129
  start().catch((err) => {
130
  logger.error("Fatal startup error", { error: err.message });
131
  process.exit(1);
132
- });
 
15
  const app = express();
16
  const PORT = parseInt(process.env.PORT || "3001", 10);
17
 
18
+ // BUG FIX: Set trust proxy BEFORE rate limiter.
19
+ // nginx runs in front of backend and sets X-Forwarded-For header.
20
+ // Without trust proxy, express-rate-limit throws ERR_ERL_UNEXPECTED_X_FORWARDED_FOR
21
+ // and logs a ValidationError on every single request.
22
+ // Value 1 = trust first proxy hop (nginx on localhost).
23
+ app.set("trust proxy", 1);
24
 
25
  app.use(helmet({ crossOriginResourcePolicy: false }));
26
  app.use(
 
38
  })
39
  );
40
 
 
41
  app.use(
42
  "/api/",
43
  rateLimit({
 
49
  })
50
  );
51
 
 
 
 
52
  app.use("/api/models", modelsRouter);
53
  app.use("/api", statsRouter);
54
 
 
55
  app.use((req, res) => {
56
  res.status(404).json({ success: false, error: "Not found" });
57
  });
58
 
 
59
  app.use((err, req, res, _next) => {
60
  logger.error("Unhandled error", { error: err.message, stack: err.stack });
61
  res.status(500).json({ success: false, error: "Internal server error" });
62
  });
63
 
 
 
64
  async function start() {
 
65
  db.getDb();
66
  logger.info("Database initialized");
 
 
 
67
  syncModelsToLitellmWithRetry();
 
68
  app.listen(PORT, "0.0.0.0", () => {
69
+ logger.info("AI Gateway Backend running on port " + PORT);
70
+ logger.info("Gateway public URL: " + (process.env.GATEWAY_PUBLIC_URL || "http://localhost"));
71
  });
72
  }
73
 
 
 
 
 
74
  async function syncModelsToLitellmWithRetry() {
75
  const MAX_ATTEMPTS = 10;
76
  const BASE_DELAY_MS = 5000;
 
81
  return;
82
  } catch (err) {
83
  const delay = Math.min(BASE_DELAY_MS * attempt, 30000);
84
+ logger.warn("LiteLLM not ready (attempt " + attempt + "/" + MAX_ATTEMPTS + "), retrying in " + delay + "ms...");
85
  await new Promise((r) => setTimeout(r, delay));
86
  }
87
  }
88
+ logger.error("LiteLLM did not become ready after all retry attempts.");
89
  }
90
 
 
 
 
 
91
  async function syncModelsToLitellm() {
92
  try {
93
  const models = db.listModels({ enabledOnly: true });
94
+ logger.info("Syncing " + models.length + " models to LiteLLM...");
 
95
  for (const model of models) {
96
  try {
 
97
  const litellmId = await litellm.registerModel({ ...model, _apiKey: model._apiKey });
98
  db.updateModel(model.id, { litellmId });
99
+ logger.info("Synced: " + model.name);
100
  } catch (err) {
101
+ logger.warn("Failed to sync model " + model.name + ": " + err.message);
102
  }
103
  }
 
104
  logger.info("Model sync complete");
105
  } catch (err) {
106
  logger.error("Model sync failed", { error: err.message });
 
110
  start().catch((err) => {
111
  logger.error("Fatal startup error", { error: err.message });
112
  process.exit(1);
113
+ });