@intranefr/superbackend 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/.env.example +5 -0
  2. package/README.md +11 -0
  3. package/index.js +23 -0
  4. package/package.json +7 -2
  5. package/src/admin/endpointRegistry.js +120 -0
  6. package/src/controllers/admin.controller.js +22 -5
  7. package/src/controllers/adminBlockDefinitions.controller.js +127 -0
  8. package/src/controllers/adminBlockDefinitionsAi.controller.js +54 -0
  9. package/src/controllers/adminCache.controller.js +342 -0
  10. package/src/controllers/adminContextBlockDefinitions.controller.js +141 -0
  11. package/src/controllers/adminCrons.controller.js +388 -0
  12. package/src/controllers/adminDbBrowser.controller.js +124 -0
  13. package/src/controllers/adminEjsVirtual.controller.js +13 -3
  14. package/src/controllers/adminHeadless.controller.js +9 -2
  15. package/src/controllers/adminHealthChecks.controller.js +570 -0
  16. package/src/controllers/adminI18n.controller.js +51 -29
  17. package/src/controllers/adminLlm.controller.js +126 -2
  18. package/src/controllers/adminPages.controller.js +720 -0
  19. package/src/controllers/adminPagesContextBlocksAi.controller.js +54 -0
  20. package/src/controllers/adminProxy.controller.js +113 -0
  21. package/src/controllers/adminRateLimits.controller.js +138 -0
  22. package/src/controllers/adminRbac.controller.js +803 -0
  23. package/src/controllers/adminScripts.controller.js +93 -2
  24. package/src/controllers/adminSeoConfig.controller.js +71 -48
  25. package/src/controllers/blogAdmin.controller.js +279 -0
  26. package/src/controllers/blogAiAdmin.controller.js +224 -0
  27. package/src/controllers/blogAutomationAdmin.controller.js +141 -0
  28. package/src/controllers/blogInternal.controller.js +26 -0
  29. package/src/controllers/blogPublic.controller.js +89 -0
  30. package/src/controllers/fileManager.controller.js +190 -0
  31. package/src/controllers/fileManagerStoragePolicy.controller.js +23 -0
  32. package/src/controllers/healthChecksPublic.controller.js +196 -0
  33. package/src/controllers/metrics.controller.js +64 -4
  34. package/src/controllers/orgAdmin.controller.js +80 -0
  35. package/src/middleware/internalCronAuth.js +29 -0
  36. package/src/middleware/rbac.js +62 -0
  37. package/src/middleware.js +756 -48
  38. package/src/models/BlockDefinition.js +27 -0
  39. package/src/models/BlogAutomationLock.js +14 -0
  40. package/src/models/BlogAutomationRun.js +39 -0
  41. package/src/models/BlogPost.js +42 -0
  42. package/src/models/CacheEntry.js +26 -0
  43. package/src/models/ConsoleEntry.js +32 -0
  44. package/src/models/ConsoleLog.js +23 -0
  45. package/src/models/ContextBlockDefinition.js +33 -0
  46. package/src/models/CronExecution.js +47 -0
  47. package/src/models/CronJob.js +70 -0
  48. package/src/models/ExternalDbConnection.js +49 -0
  49. package/src/models/FileEntry.js +22 -0
  50. package/src/models/HealthAutoHealAttempt.js +57 -0
  51. package/src/models/HealthCheck.js +132 -0
  52. package/src/models/HealthCheckRun.js +51 -0
  53. package/src/models/HealthIncident.js +49 -0
  54. package/src/models/Page.js +95 -0
  55. package/src/models/PageCollection.js +42 -0
  56. package/src/models/ProxyEntry.js +66 -0
  57. package/src/models/RateLimitCounter.js +19 -0
  58. package/src/models/RateLimitMetricBucket.js +20 -0
  59. package/src/models/RbacGrant.js +25 -0
  60. package/src/models/RbacGroup.js +16 -0
  61. package/src/models/RbacGroupMember.js +13 -0
  62. package/src/models/RbacGroupRole.js +13 -0
  63. package/src/models/RbacRole.js +25 -0
  64. package/src/models/RbacUserRole.js +13 -0
  65. package/src/routes/adminBlog.routes.js +21 -0
  66. package/src/routes/adminBlogAi.routes.js +16 -0
  67. package/src/routes/adminBlogAutomation.routes.js +27 -0
  68. package/src/routes/adminCache.routes.js +20 -0
  69. package/src/routes/adminConsoleManager.routes.js +302 -0
  70. package/src/routes/adminCrons.routes.js +25 -0
  71. package/src/routes/adminDbBrowser.routes.js +65 -0
  72. package/src/routes/adminEjsVirtual.routes.js +2 -1
  73. package/src/routes/adminHeadless.routes.js +2 -1
  74. package/src/routes/adminHealthChecks.routes.js +28 -0
  75. package/src/routes/adminI18n.routes.js +4 -3
  76. package/src/routes/adminLlm.routes.js +4 -2
  77. package/src/routes/adminPages.routes.js +55 -0
  78. package/src/routes/adminProxy.routes.js +15 -0
  79. package/src/routes/adminRateLimits.routes.js +17 -0
  80. package/src/routes/adminRbac.routes.js +38 -0
  81. package/src/routes/adminSeoConfig.routes.js +5 -4
  82. package/src/routes/adminUiComponents.routes.js +2 -1
  83. package/src/routes/blogInternal.routes.js +14 -0
  84. package/src/routes/blogPublic.routes.js +9 -0
  85. package/src/routes/fileManager.routes.js +62 -0
  86. package/src/routes/fileManagerStoragePolicy.routes.js +9 -0
  87. package/src/routes/healthChecksPublic.routes.js +9 -0
  88. package/src/routes/log.routes.js +43 -60
  89. package/src/routes/metrics.routes.js +4 -2
  90. package/src/routes/orgAdmin.routes.js +1 -0
  91. package/src/routes/pages.routes.js +123 -0
  92. package/src/routes/proxy.routes.js +46 -0
  93. package/src/routes/rbac.routes.js +47 -0
  94. package/src/routes/webhook.routes.js +2 -1
  95. package/src/routes/workflows.routes.js +4 -0
  96. package/src/services/blockDefinitionsAi.service.js +247 -0
  97. package/src/services/blog.service.js +99 -0
  98. package/src/services/blogAutomation.service.js +978 -0
  99. package/src/services/blogCronsBootstrap.service.js +184 -0
  100. package/src/services/blogPublishing.service.js +58 -0
  101. package/src/services/cacheLayer.service.js +696 -0
  102. package/src/services/consoleManager.service.js +700 -0
  103. package/src/services/consoleOverride.service.js +6 -1
  104. package/src/services/cronScheduler.service.js +350 -0
  105. package/src/services/dbBrowser.service.js +536 -0
  106. package/src/services/ejsVirtual.service.js +102 -32
  107. package/src/services/fileManager.service.js +475 -0
  108. package/src/services/fileManagerStoragePolicy.service.js +285 -0
  109. package/src/services/healthChecks.service.js +650 -0
  110. package/src/services/healthChecksBootstrap.service.js +109 -0
  111. package/src/services/healthChecksScheduler.service.js +106 -0
  112. package/src/services/llmDefaults.service.js +190 -0
  113. package/src/services/migrationAssets/s3.js +2 -2
  114. package/src/services/pages.service.js +602 -0
  115. package/src/services/pagesContext.service.js +331 -0
  116. package/src/services/pagesContextBlocksAi.service.js +349 -0
  117. package/src/services/proxy.service.js +535 -0
  118. package/src/services/rateLimiter.service.js +623 -0
  119. package/src/services/rbac.service.js +212 -0
  120. package/src/services/scriptsRunner.service.js +1 -1
  121. package/src/services/uiComponentsAi.service.js +6 -19
  122. package/src/services/workflow.service.js +23 -8
  123. package/src/utils/orgRoles.js +14 -0
  124. package/src/utils/rbac/engine.js +60 -0
  125. package/src/utils/rbac/rightsRegistry.js +29 -0
  126. package/views/admin-blog-automation.ejs +877 -0
  127. package/views/admin-blog-edit.ejs +542 -0
  128. package/views/admin-blog.ejs +399 -0
  129. package/views/admin-cache.ejs +681 -0
  130. package/views/admin-console-manager.ejs +680 -0
  131. package/views/admin-crons.ejs +645 -0
  132. package/views/admin-db-browser.ejs +445 -0
  133. package/views/admin-ejs-virtual.ejs +16 -10
  134. package/views/admin-file-manager.ejs +942 -0
  135. package/views/admin-health-checks.ejs +725 -0
  136. package/views/admin-i18n.ejs +59 -5
  137. package/views/admin-llm.ejs +99 -1
  138. package/views/admin-organizations.ejs +163 -1
  139. package/views/admin-pages.ejs +2424 -0
  140. package/views/admin-proxy.ejs +491 -0
  141. package/views/admin-rate-limiter.ejs +625 -0
  142. package/views/admin-rbac.ejs +1331 -0
  143. package/views/admin-scripts.ejs +1 -1
  144. package/views/admin-seo-config.ejs +61 -7
  145. package/views/admin-ui-components.ejs +57 -25
  146. package/views/admin-workflows.ejs +7 -7
  147. package/views/file-manager.ejs +866 -0
  148. package/views/pages/blocks/contact.ejs +27 -0
  149. package/views/pages/blocks/cta.ejs +18 -0
  150. package/views/pages/blocks/faq.ejs +20 -0
  151. package/views/pages/blocks/features.ejs +19 -0
  152. package/views/pages/blocks/hero.ejs +13 -0
  153. package/views/pages/blocks/html.ejs +5 -0
  154. package/views/pages/blocks/image.ejs +14 -0
  155. package/views/pages/blocks/testimonials.ejs +26 -0
  156. package/views/pages/blocks/text.ejs +10 -0
  157. package/views/pages/layouts/default.ejs +51 -0
  158. package/views/pages/layouts/minimal.ejs +42 -0
  159. package/views/pages/layouts/sidebar.ejs +54 -0
  160. package/views/pages/partials/footer.ejs +13 -0
  161. package/views/pages/partials/header.ejs +12 -0
  162. package/views/pages/partials/sidebar.ejs +8 -0
  163. package/views/pages/runtime/page.ejs +10 -0
  164. package/views/pages/templates/article.ejs +20 -0
  165. package/views/pages/templates/default.ejs +12 -0
  166. package/views/pages/templates/landing.ejs +14 -0
  167. package/views/pages/templates/listing.ejs +15 -0
  168. package/views/partials/admin-image-upload-modal.ejs +221 -0
  169. package/views/partials/dashboard/nav-items.ejs +11 -0
  170. package/views/partials/llm-provider-model-picker.ejs +183 -0
  171. package/src/routes/llmUi.routes.js +0 -26
package/src/middleware.js CHANGED
@@ -1,3 +1,12 @@
1
+ const consoleOverride = require("./services/consoleOverride.service");
2
+ const consoleManager = require("./services/consoleManager.service");
3
+
4
+ // Initialize console override service early to capture all logs
5
+ // Avoid keeping timers/streams alive during Jest runs.
6
+ if (process.env.NODE_ENV !== "test" && !process.env.JEST_WORKER_ID) {
7
+ consoleOverride.init()
8
+ }
9
+
1
10
  const express = require("express");
2
11
  const path = require("path");
3
12
  const mongoose = require("mongoose");
@@ -6,17 +15,54 @@ const fs = require("fs");
6
15
  const ejs = require("ejs");
7
16
  const { basicAuth } = require("./middleware/auth");
8
17
  const endpointRegistry = require("./admin/endpointRegistry");
9
- const { createFeatureFlagsEjsMiddleware } = require("./services/featureFlags.service");
10
- const consoleOverride = require("./services/consoleOverride.service");
18
+ const {
19
+ createFeatureFlagsEjsMiddleware,
20
+ } = require("./services/featureFlags.service");
21
+ const globalSettingsService = require("./services/globalSettings.service");
22
+ const cronScheduler = require("./services/cronScheduler.service");
23
+ const healthChecksScheduler = require("./services/healthChecksScheduler.service");
24
+ const healthChecksBootstrap = require("./services/healthChecksBootstrap.service");
25
+ const blogCronsBootstrap = require("./services/blogCronsBootstrap.service");
11
26
  const {
12
27
  hookConsoleError,
13
28
  setupProcessHandlers,
14
29
  expressErrorMiddleware,
15
30
  requestIdMiddleware,
16
31
  } = require("./middleware/errorCapture");
32
+ const rateLimiter = require("./services/rateLimiter.service");
17
33
 
18
34
  let errorCaptureInitialized = false;
19
35
 
36
+ /**
37
+ * Check if console manager should be enabled based on environment variable and global settings
38
+ * Priority: Environment Variable > Global Settings > Default (true)
39
+ * @returns {Promise<boolean>} Whether console manager should be enabled
40
+ */
41
+ async function isConsoleManagerEnabled() {
42
+ // Environment variable takes highest priority
43
+ const envEnabled = process.env.CONSOLE_MANAGER_ENABLED;
44
+ if (envEnabled !== undefined) {
45
+ const enabled = String(envEnabled).toLowerCase() !== 'false';
46
+ console.log(`[Console Manager] Environment variable CONSOLE_MANAGER_ENABLED=${envEnabled}, ${enabled ? 'enabled' : 'disabled'}`);
47
+ return enabled;
48
+ }
49
+
50
+ // Check global settings if environment variable not set
51
+ try {
52
+ const enabledRaw = await globalSettingsService.getSettingValue(
53
+ "CONSOLE_MANAGER_ENABLED",
54
+ "true"
55
+ );
56
+ const enabled = String(enabledRaw) === "true";
57
+ console.log(`[Console Manager] Global setting CONSOLE_MANAGER_ENABLED=${enabledRaw}, ${enabled ? 'enabled' : 'disabled'}`);
58
+ return enabled;
59
+ } catch (error) {
60
+ console.error("[Console Manager] Error loading global setting:", error);
61
+ console.log("[Console Manager] Fallback to enabled due to error");
62
+ return true; // Fallback to enabled on error
63
+ }
64
+ }
65
+
20
66
  /**
21
67
  * Creates and configures the SaaS backend middleware
22
68
  * @param {Object} options - Configuration options
@@ -30,26 +76,65 @@ let errorCaptureInitialized = false;
30
76
  function createMiddleware(options = {}) {
31
77
  const router = express.Router();
32
78
  const adminPath = options.adminPath || "/admin";
79
+ const pagesPrefix = options.pagesPrefix || "/";
33
80
 
34
- // Expose adminPath and WS attachment helper
81
+ const normalizeBasePath = (value) => {
82
+ const v = String(value || "").trim();
83
+ if (!v) return "/files";
84
+ return v.startsWith("/") ? v : `/${v}`;
85
+ };
86
+
87
+ const fileManagerPublicConfig = {
88
+ enabled: false,
89
+ basePath: "/files",
90
+ loaded: false,
91
+ };
92
+
93
+ // Restart-required behavior: we load settings once and keep the values in memory.
94
+ (async () => {
95
+ try {
96
+ const enabledRaw = await globalSettingsService.getSettingValue(
97
+ "FILE_MANAGER_ENABLED",
98
+ "false",
99
+ );
100
+ const basePathRaw = await globalSettingsService.getSettingValue(
101
+ "FILE_MANAGER_BASE_PATH",
102
+ "/files",
103
+ );
104
+
105
+ fileManagerPublicConfig.enabled = String(enabledRaw) === "true";
106
+ fileManagerPublicConfig.basePath = normalizeBasePath(basePathRaw);
107
+ fileManagerPublicConfig.loaded = true;
108
+ } catch (error) {
109
+ console.error("Error loading File Manager public config:", error);
110
+ fileManagerPublicConfig.loaded = true;
111
+ }
112
+ })();
113
+
114
+ // Expose adminPath, pagesPrefix and WS attachment helper
35
115
  router.adminPath = adminPath;
116
+ router.pagesPrefix = pagesPrefix;
36
117
  router.attachWs = (server) => {
37
- const { attachTerminalWebsocketServer } = require('./services/terminalsWs.service');
118
+ const {
119
+ attachTerminalWebsocketServer,
120
+ } = require("./services/terminalsWs.service");
38
121
  attachTerminalWebsocketServer(server, { basePathPrefix: adminPath });
39
122
  };
40
123
 
41
- // Initialize console override service early to capture all logs
42
- consoleOverride.init();
43
-
44
124
  if (!errorCaptureInitialized) {
45
125
  errorCaptureInitialized = true;
46
126
  hookConsoleError();
47
127
  setupProcessHandlers();
48
128
  }
49
129
 
130
+ // Console manager will be initialized after database connection
131
+
50
132
  // Database connection
51
133
  const mongoUri =
52
- options.mongodbUri || options.dbConnection || process.env.MONGODB_URI || process.env.MONGO_URI;
134
+ options.mongodbUri ||
135
+ options.dbConnection ||
136
+ process.env.MONGODB_URI ||
137
+ process.env.MONGO_URI;
53
138
 
54
139
  if (!mongoUri && mongoose.connection.readyState !== 1) {
55
140
  console.warn(
@@ -60,23 +145,132 @@ function createMiddleware(options = {}) {
60
145
  serverSelectionTimeoutMS: 5000,
61
146
  maxPoolSize: 10,
62
147
  };
63
-
148
+
64
149
  // Return a promise that resolves when connection is established
65
150
  const connectionPromise = mongoose
66
151
  .connect(mongoUri, connectionOptions)
67
- .then(() => {
152
+ .then(async () => {
68
153
  console.log("✅ Middleware: Connected to MongoDB");
154
+ // Start cron scheduler after DB connection
155
+ await cronScheduler.start();
156
+ await healthChecksScheduler.start();
157
+ await healthChecksBootstrap.bootstrap();
158
+ await blogCronsBootstrap.bootstrap();
159
+
160
+ // Initialize console manager AFTER database is connected
161
+ if (process.env.NODE_ENV !== "test" && !process.env.JEST_WORKER_ID) {
162
+ const consoleManagerEnabled = await isConsoleManagerEnabled();
163
+ if (consoleManagerEnabled) {
164
+ consoleManager.init();
165
+ console.log("[Console Manager] Initialized");
166
+ } else {
167
+ console.log("[Console Manager] Disabled - console methods not overridden");
168
+ }
169
+ }
170
+
69
171
  return true;
70
172
  })
71
173
  .catch((err) => {
72
174
  console.error("❌ Middleware: MongoDB connection error:", err);
73
175
  return false;
74
176
  });
75
-
177
+
178
+ router.get(`${adminPath}/health-checks`, basicAuth, (req, res) => {
179
+ const templatePath = path.join(
180
+ __dirname,
181
+ "..",
182
+ "views",
183
+ "admin-health-checks.ejs",
184
+ );
185
+ fs.readFile(templatePath, "utf8", (err, template) => {
186
+ if (err) {
187
+ console.error("Error reading template:", err);
188
+ return res.status(500).send("Error loading page");
189
+ }
190
+ try {
191
+ const html = ejs.render(
192
+ template,
193
+ {
194
+ baseUrl: req.baseUrl,
195
+ adminPath,
196
+ },
197
+ {
198
+ filename: templatePath,
199
+ },
200
+ );
201
+ res.send(html);
202
+ } catch (renderErr) {
203
+ console.error("Error rendering template:", renderErr);
204
+ res.status(500).send("Error rendering page");
205
+ }
206
+ });
207
+ });
208
+
209
+ router.get(`${adminPath}/console-manager`, basicAuth, (req, res) => {
210
+ const templatePath = path.join(
211
+ __dirname,
212
+ "..",
213
+ "views",
214
+ "admin-console-manager.ejs",
215
+ );
216
+ fs.readFile(templatePath, "utf8", (err, template) => {
217
+ if (err) {
218
+ console.error("Error reading template:", err);
219
+ return res.status(500).send("Error loading page");
220
+ }
221
+ try {
222
+ const html = ejs.render(
223
+ template,
224
+ {
225
+ baseUrl: req.baseUrl,
226
+ adminPath,
227
+ },
228
+ {
229
+ filename: templatePath,
230
+ },
231
+ );
232
+ res.send(html);
233
+ } catch (renderErr) {
234
+ console.error("Error rendering template:", renderErr);
235
+ res.status(500).send("Error rendering page");
236
+ }
237
+ });
238
+ });
239
+
76
240
  // Store the promise so it can be awaited if needed
77
241
  router.connectionPromise = connectionPromise;
78
242
  } else if (mongoose.connection.readyState === 1) {
79
243
  console.log("✅ Middleware: Using existing MongoDB connection");
244
+ // Start cron scheduler for existing connection
245
+ cronScheduler.start().catch((err) => {
246
+ console.error("Failed to start cron scheduler:", err);
247
+ });
248
+ healthChecksScheduler.start().catch((err) => {
249
+ console.error("Failed to start health checks scheduler:", err);
250
+ });
251
+ healthChecksBootstrap.bootstrap().catch((err) => {
252
+ console.error("Failed to bootstrap health checks:", err);
253
+ });
254
+ blogCronsBootstrap.bootstrap().catch((err) => {
255
+ console.error("Failed to bootstrap blog crons:", err);
256
+ });
257
+
258
+ // Initialize console manager AFTER database is already connected
259
+ if (process.env.NODE_ENV !== "test" && !process.env.JEST_WORKER_ID) {
260
+ isConsoleManagerEnabled().then(consoleManagerEnabled => {
261
+ if (consoleManagerEnabled) {
262
+ consoleManager.init();
263
+ console.log("[Console Manager] Initialized");
264
+ } else {
265
+ console.log("[Console Manager] Disabled - console methods not overridden");
266
+ }
267
+ }).catch(error => {
268
+ console.error("[Console Manager] Error checking enabled status:", error);
269
+ console.log("[Console Manager] Fallback to enabled due to error");
270
+ consoleManager.init();
271
+ console.log("[Console Manager] Initialized (fallback)");
272
+ });
273
+ }
80
274
  }
81
275
 
82
276
  // CORS configuration
@@ -138,6 +332,12 @@ function createMiddleware(options = {}) {
138
332
  webhookHandler,
139
333
  );
140
334
 
335
+ router.use(
336
+ "/proxy",
337
+ express.raw({ type: "*/*", limit: "10mb" }),
338
+ require("./routes/proxy.routes"),
339
+ );
340
+
141
341
  // Regular JSON parsing for other routes (skip if parent app already handles it)
142
342
  if (!options.skipBodyParser) {
143
343
  router.use(express.json());
@@ -146,6 +346,8 @@ function createMiddleware(options = {}) {
146
346
 
147
347
  router.use(requestIdMiddleware);
148
348
 
349
+ router.use("/api", rateLimiter.limit("globalApiLimiter"));
350
+
149
351
  // Serve public static files (e.g. /og/og-default.png)
150
352
  router.use(express.static(path.join(__dirname, "..", "public")));
151
353
 
@@ -164,6 +366,53 @@ function createMiddleware(options = {}) {
164
366
  // EJS locals: feature flags for server-rendered pages
165
367
  router.use(createFeatureFlagsEjsMiddleware());
166
368
 
369
+ // Public File Manager SPA (gated by global settings; restart required)
370
+ router.get("*", (req, res, next) => {
371
+ try {
372
+ if (!fileManagerPublicConfig.enabled) return next();
373
+
374
+ const basePath = fileManagerPublicConfig.basePath || "/files";
375
+ const reqPath = req.path;
376
+ const matches =
377
+ reqPath === basePath ||
378
+ reqPath === `${basePath}/` ||
379
+ reqPath.startsWith(`${basePath}/`);
380
+
381
+ if (!matches) return next();
382
+ if (req.method !== "GET") return next();
383
+
384
+ const templatePath = path.join(
385
+ __dirname,
386
+ "..",
387
+ "views",
388
+ "file-manager.ejs",
389
+ );
390
+ fs.readFile(templatePath, "utf8", (err, template) => {
391
+ if (err) {
392
+ console.error("Error reading template:", err);
393
+ return res.status(500).send("Error loading page");
394
+ }
395
+ try {
396
+ const html = ejs.render(
397
+ template,
398
+ {
399
+ baseUrl: req.baseUrl,
400
+ fileManagerBasePath: basePath,
401
+ },
402
+ { filename: templatePath },
403
+ );
404
+ res.send(html);
405
+ } catch (renderErr) {
406
+ console.error("Error rendering template:", renderErr);
407
+ res.status(500).send("Error rendering page");
408
+ }
409
+ });
410
+ } catch (error) {
411
+ console.error("Error serving File Manager SPA:", error);
412
+ next();
413
+ }
414
+ });
415
+
167
416
  // API Routes
168
417
  router.use("/api/auth", require("./routes/auth.routes"));
169
418
  router.use("/api/billing", require("./routes/billing.routes"));
@@ -177,22 +426,65 @@ function createMiddleware(options = {}) {
177
426
  );
178
427
  router.use("/api/admin/orgs", require("./routes/orgAdmin.routes"));
179
428
  router.use("/api/admin/users", require("./routes/userAdmin.routes"));
180
- router.use("/api/admin/notifications", require("./routes/notificationAdmin.routes"));
429
+ router.use("/api/admin/rbac", require("./routes/adminRbac.routes"));
430
+ router.use(
431
+ "/api/admin/notifications",
432
+ require("./routes/notificationAdmin.routes"),
433
+ );
181
434
  router.use("/api/admin/stripe", require("./routes/stripeAdmin.routes"));
182
-
435
+
183
436
  // Stats Routes
184
437
  const adminStatsController = require("./controllers/adminStats.controller");
185
- router.get("/api/admin/stats/overview", basicAuth, adminStatsController.getOverviewStats);
186
-
438
+ router.get(
439
+ "/api/admin/stats/overview",
440
+ basicAuth,
441
+ adminStatsController.getOverviewStats,
442
+ );
443
+
187
444
  router.get(`${adminPath}/stats/dashboard-home`, basicAuth, (req, res) => {
188
- const templatePath = path.join(__dirname, "..", "views", "admin-dashboard-home.ejs");
445
+ const templatePath = path.join(
446
+ __dirname,
447
+ "..",
448
+ "views",
449
+ "admin-dashboard-home.ejs",
450
+ );
189
451
  fs.readFile(templatePath, "utf8", (err, template) => {
190
452
  if (err) {
191
453
  console.error("Error reading template:", err);
192
454
  return res.status(500).send("Error loading page");
193
455
  }
194
456
  try {
195
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
457
+ const html = ejs.render(
458
+ template,
459
+ { baseUrl: req.baseUrl, adminPath },
460
+ { filename: templatePath },
461
+ );
462
+ res.send(html);
463
+ } catch (renderErr) {
464
+ console.error("Error rendering template:", renderErr);
465
+ res.status(500).send("Error rendering page");
466
+ }
467
+ });
468
+ });
469
+
470
+ router.get(`${adminPath}/rbac`, basicAuth, (req, res) => {
471
+ const templatePath = path.join(__dirname, "..", "views", "admin-rbac.ejs");
472
+ fs.readFile(templatePath, "utf8", (err, template) => {
473
+ if (err) {
474
+ console.error("Error reading template:", err);
475
+ return res.status(500).send("Error loading page");
476
+ }
477
+ try {
478
+ const html = ejs.render(
479
+ template,
480
+ {
481
+ baseUrl: req.baseUrl,
482
+ adminPath,
483
+ },
484
+ {
485
+ filename: templatePath,
486
+ },
487
+ );
196
488
  res.send(html);
197
489
  } catch (renderErr) {
198
490
  console.error("Error rendering template:", renderErr);
@@ -265,6 +557,89 @@ function createMiddleware(options = {}) {
265
557
  });
266
558
  });
267
559
 
560
+ router.get(`${adminPath}/crons`, basicAuth, (req, res) => {
561
+ const templatePath = path.join(__dirname, "..", "views", "admin-crons.ejs");
562
+ fs.readFile(templatePath, "utf8", (err, template) => {
563
+ if (err) {
564
+ console.error("Error reading template:", err);
565
+ return res.status(500).send("Error loading page");
566
+ }
567
+ try {
568
+ const html = ejs.render(
569
+ template,
570
+ {
571
+ baseUrl: req.baseUrl,
572
+ adminPath,
573
+ },
574
+ {
575
+ filename: templatePath,
576
+ },
577
+ );
578
+ res.send(html);
579
+ } catch (renderErr) {
580
+ console.error("Error rendering template:", renderErr);
581
+ res.status(500).send("Error rendering page");
582
+ }
583
+ });
584
+ });
585
+
586
+ router.get(`${adminPath}/cache`, basicAuth, (req, res) => {
587
+ const templatePath = path.join(__dirname, "..", "views", "admin-cache.ejs");
588
+ fs.readFile(templatePath, "utf8", (err, template) => {
589
+ if (err) {
590
+ console.error("Error reading template:", err);
591
+ return res.status(500).send("Error loading page");
592
+ }
593
+ try {
594
+ const html = ejs.render(
595
+ template,
596
+ {
597
+ baseUrl: req.baseUrl,
598
+ adminPath,
599
+ },
600
+ {
601
+ filename: templatePath,
602
+ },
603
+ );
604
+ res.send(html);
605
+ } catch (renderErr) {
606
+ console.error("Error rendering template:", renderErr);
607
+ res.status(500).send("Error rendering page");
608
+ }
609
+ });
610
+ });
611
+
612
+ router.get(`${adminPath}/db-browser`, basicAuth, (req, res) => {
613
+ const templatePath = path.join(
614
+ __dirname,
615
+ "..",
616
+ "views",
617
+ "admin-db-browser.ejs",
618
+ );
619
+ fs.readFile(templatePath, "utf8", (err, template) => {
620
+ if (err) {
621
+ console.error("Error reading template:", err);
622
+ return res.status(500).send("Error loading page");
623
+ }
624
+ try {
625
+ const html = ejs.render(
626
+ template,
627
+ {
628
+ baseUrl: req.baseUrl,
629
+ adminPath,
630
+ },
631
+ {
632
+ filename: templatePath,
633
+ },
634
+ );
635
+ res.send(html);
636
+ } catch (renderErr) {
637
+ console.error("Error rendering template:", renderErr);
638
+ res.status(500).send("Error rendering page");
639
+ }
640
+ });
641
+ });
642
+
268
643
  router.use("/api/admin", require("./routes/admin.routes"));
269
644
  router.use("/api/admin/settings", require("./routes/globalSettings.routes"));
270
645
  router.use(
@@ -275,6 +650,11 @@ function createMiddleware(options = {}) {
275
650
  "/api/admin/json-configs",
276
651
  require("./routes/adminJsonConfigs.routes"),
277
652
  );
653
+ router.use(
654
+ "/api/admin/rate-limits",
655
+ require("./routes/adminRateLimits.routes"),
656
+ );
657
+ router.use("/api/admin/proxy", require("./routes/adminProxy.routes"));
278
658
  router.use(
279
659
  "/api/admin/seo-config",
280
660
  require("./routes/adminSeoConfig.routes"),
@@ -282,19 +662,51 @@ function createMiddleware(options = {}) {
282
662
  router.use("/api/admin/i18n", require("./routes/adminI18n.routes"));
283
663
  router.use("/api/admin/headless", require("./routes/adminHeadless.routes"));
284
664
  router.use("/api/admin/scripts", require("./routes/adminScripts.routes"));
665
+ router.use("/api/admin/crons", require("./routes/adminCrons.routes"));
666
+ router.use(
667
+ "/api/admin/health-checks",
668
+ require("./routes/adminHealthChecks.routes"),
669
+ );
670
+ router.use("/api/admin/cache", require("./routes/adminCache.routes"));
671
+ router.use(
672
+ "/api/admin/console-manager",
673
+ require("./routes/adminConsoleManager.routes"),
674
+ );
675
+ router.use(
676
+ "/api/admin/db-browser",
677
+ require("./routes/adminDbBrowser.routes"),
678
+ );
285
679
  router.use("/api/admin/terminals", require("./routes/adminTerminals.routes"));
286
680
  router.use("/api/admin/assets", require("./routes/adminAssets.routes"));
287
681
  router.use(
288
682
  "/api/admin/upload-namespaces",
289
683
  require("./routes/adminUploadNamespaces.routes"),
290
684
  );
291
- router.use("/api/admin/ui-components", require("./routes/adminUiComponents.routes"));
685
+ router.use(
686
+ "/api/admin/ui-components",
687
+ require("./routes/adminUiComponents.routes"),
688
+ );
292
689
  router.use("/api/admin/migration", require("./routes/adminMigration.routes"));
293
- router.use("/api/admin/errors", basicAuth, require("./routes/adminErrors.routes"));
294
- router.use("/api/admin/audit", basicAuth, require("./routes/adminAudit.routes"));
690
+ router.use(
691
+ "/api/admin/errors",
692
+ basicAuth,
693
+ require("./routes/adminErrors.routes"),
694
+ );
695
+ router.use(
696
+ "/api/admin/audit",
697
+ basicAuth,
698
+ require("./routes/adminAudit.routes"),
699
+ );
295
700
  router.use("/api/admin/llm", require("./routes/adminLlm.routes"));
296
- router.use("/api/admin/ejs-virtual", require("./routes/adminEjsVirtual.routes"));
297
- router.use("/api/workflows", basicAuth, require("./routes/workflows.routes"));
701
+ router.use(
702
+ "/api/admin/ejs-virtual",
703
+ require("./routes/adminEjsVirtual.routes"),
704
+ );
705
+ router.use("/api/admin/pages", require("./routes/adminPages.routes"));
706
+ router.use("/api/admin", require("./routes/adminBlog.routes"));
707
+ router.use("/api/admin", require("./routes/adminBlogAi.routes"));
708
+ router.use("/api/admin", require("./routes/adminBlogAutomation.routes"));
709
+ router.use("/api/admin/workflows", basicAuth, require("./routes/workflows.routes"));
298
710
  router.use("/w", require("./routes/workflowWebhook.routes"));
299
711
  router.use("/api/webhooks", require("./routes/webhook.routes"));
300
712
  router.use("/api/settings", require("./routes/globalSettings.routes"));
@@ -310,21 +722,43 @@ function createMiddleware(options = {}) {
310
722
  router.use("/api/log", require("./routes/log.routes"));
311
723
  router.use("/api/error-tracking", require("./routes/errorTracking.routes"));
312
724
  router.use("/api/ui-components", require("./routes/uiComponentsPublic.routes"));
313
- router.use("/api/llm/ui", require("./routes/llmUi.routes"));
725
+ router.use("/api/rbac", require("./routes/rbac.routes"));
726
+ router.use("/api/file-manager", require("./routes/fileManager.routes"));
727
+
728
+ // Public blog APIs (headless)
729
+ router.use("/api", require("./routes/blogPublic.routes"));
730
+
731
+ // Internal blog endpoints (used by HTTP CronJobs)
732
+ router.use("/api/internal", require("./routes/blogInternal.routes"));
733
+
734
+ // Public health checks status (gated by global setting)
735
+ router.use(
736
+ "/api/health-checks",
737
+ require("./routes/healthChecksPublic.routes"),
738
+ );
314
739
 
315
740
  // Public assets proxy
316
741
  router.use("/public/assets", require("./routes/publicAssets.routes"));
317
742
 
318
743
  // Admin dashboard (polished view)
319
744
  router.get(adminPath, basicAuth, (req, res) => {
320
- const templatePath = path.join(__dirname, "..", "views", "admin-dashboard.ejs");
745
+ const templatePath = path.join(
746
+ __dirname,
747
+ "..",
748
+ "views",
749
+ "admin-dashboard.ejs",
750
+ );
321
751
  fs.readFile(templatePath, "utf8", (err, template) => {
322
752
  if (err) {
323
753
  console.error("Error reading template:", err);
324
754
  return res.status(500).send("Error loading page");
325
755
  }
326
756
  try {
327
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
757
+ const html = ejs.render(
758
+ template,
759
+ { baseUrl: req.baseUrl, adminPath },
760
+ { filename: templatePath },
761
+ );
328
762
  res.send(html);
329
763
  } catch (renderErr) {
330
764
  console.error("Error rendering template:", renderErr);
@@ -362,7 +796,12 @@ function createMiddleware(options = {}) {
362
796
  });
363
797
 
364
798
  router.get(`${adminPath}/migration`, basicAuth, (req, res) => {
365
- const templatePath = path.join(__dirname, "..", "views", "admin-migration.ejs");
799
+ const templatePath = path.join(
800
+ __dirname,
801
+ "..",
802
+ "views",
803
+ "admin-migration.ejs",
804
+ );
366
805
  fs.readFile(templatePath, "utf8", (err, template) => {
367
806
  if (err) {
368
807
  console.error("Error reading template:", err);
@@ -390,12 +829,59 @@ function createMiddleware(options = {}) {
390
829
 
391
830
  // Admin LLM/AI page (protected by basic auth)
392
831
  router.get(`${adminPath}/admin-llm`, basicAuth, (req, res) => {
832
+ const templatePath = path.join(__dirname, "..", "views", "admin-llm.ejs");
833
+ fs.readFile(templatePath, "utf8", (err, template) => {
834
+ if (err) {
835
+ console.error("Error reading template:", err);
836
+ return res.status(500).send("Error loading page");
837
+ }
838
+ try {
839
+ const html = ejs.render(
840
+ template,
841
+ {
842
+ baseUrl: req.baseUrl,
843
+ adminPath,
844
+ },
845
+ {
846
+ filename: templatePath,
847
+ },
848
+ );
849
+ res.send(html);
850
+ } catch (renderErr) {
851
+ console.error("Error rendering template:", renderErr);
852
+ res.status(500).send("Error rendering page");
853
+ }
854
+ });
855
+ });
856
+
857
+ router.get(`${adminPath}/workflows/:id`, basicAuth, (req, res) => {
393
858
  const templatePath = path.join(
394
859
  __dirname,
395
860
  "..",
396
861
  "views",
397
- "admin-llm.ejs",
862
+ "admin-workflows.ejs",
398
863
  );
864
+ fs.readFile(templatePath, "utf8", (err, template) => {
865
+ if (err) {
866
+ console.error("Error reading template:", err);
867
+ return res.status(500).send("Error loading page");
868
+ }
869
+ try {
870
+ const html = ejs.render(
871
+ template,
872
+ { baseUrl: req.baseUrl, adminPath },
873
+ { filename: templatePath },
874
+ );
875
+ res.send(html);
876
+ } catch (renderErr) {
877
+ console.error("Error rendering template:", renderErr);
878
+ res.status(500).send("Error rendering page");
879
+ }
880
+ });
881
+ });
882
+
883
+ router.get(`${adminPath}/pages`, basicAuth, (req, res) => {
884
+ const templatePath = path.join(__dirname, "..", "views", "admin-pages.ejs");
399
885
  fs.readFile(templatePath, "utf8", (err, template) => {
400
886
  if (err) {
401
887
  console.error("Error reading template:", err);
@@ -420,15 +906,133 @@ function createMiddleware(options = {}) {
420
906
  });
421
907
  });
422
908
 
423
- router.get(`${adminPath}/workflows/:id`, basicAuth, (req, res) => {
424
- const templatePath = path.join(__dirname, "..", "views", "admin-workflows.ejs");
909
+ router.get(`${adminPath}/blog`, basicAuth, (req, res) => {
910
+ const templatePath = path.join(__dirname, "..", "views", "admin-blog.ejs");
911
+ fs.readFile(templatePath, "utf8", (err, template) => {
912
+ if (err) {
913
+ console.error("Error reading template:", err);
914
+ return res.status(500).send("Error loading page");
915
+ }
916
+ try {
917
+ const html = ejs.render(
918
+ template,
919
+ { baseUrl: req.baseUrl, adminPath },
920
+ { filename: templatePath },
921
+ );
922
+ res.send(html);
923
+ } catch (renderErr) {
924
+ console.error("Error rendering template:", renderErr);
925
+ res.status(500).send("Error rendering page");
926
+ }
927
+ });
928
+ });
929
+
930
+ router.get(`${adminPath}/blog-automation`, basicAuth, (req, res) => {
931
+ const templatePath = path.join(
932
+ __dirname,
933
+ "..",
934
+ "views",
935
+ "admin-blog-automation.ejs",
936
+ );
937
+ fs.readFile(templatePath, "utf8", (err, template) => {
938
+ if (err) {
939
+ console.error("Error reading template:", err);
940
+ return res.status(500).send("Error loading page");
941
+ }
942
+ try {
943
+ const html = ejs.render(
944
+ template,
945
+ { baseUrl: req.baseUrl, adminPath },
946
+ { filename: templatePath },
947
+ );
948
+ res.send(html);
949
+ } catch (renderErr) {
950
+ console.error("Error rendering template:", renderErr);
951
+ res.status(500).send("Error rendering page");
952
+ }
953
+ });
954
+ });
955
+
956
+ router.get(`${adminPath}/blog/new`, basicAuth, (req, res) => {
957
+ const templatePath = path.join(
958
+ __dirname,
959
+ "..",
960
+ "views",
961
+ "admin-blog-edit.ejs",
962
+ );
963
+ fs.readFile(templatePath, "utf8", (err, template) => {
964
+ if (err) {
965
+ console.error("Error reading template:", err);
966
+ return res.status(500).send("Error loading page");
967
+ }
968
+ try {
969
+ const html = ejs.render(
970
+ template,
971
+ { baseUrl: req.baseUrl, adminPath, postId: "", mode: "new" },
972
+ { filename: templatePath },
973
+ );
974
+ res.send(html);
975
+ } catch (renderErr) {
976
+ console.error("Error rendering template:", renderErr);
977
+ res.status(500).send("Error rendering page");
978
+ }
979
+ });
980
+ });
981
+
982
+ router.get(`${adminPath}/blog/edit/:id`, basicAuth, (req, res) => {
983
+ const templatePath = path.join(
984
+ __dirname,
985
+ "..",
986
+ "views",
987
+ "admin-blog-edit.ejs",
988
+ );
989
+ fs.readFile(templatePath, "utf8", (err, template) => {
990
+ if (err) {
991
+ console.error("Error reading template:", err);
992
+ return res.status(500).send("Error loading page");
993
+ }
994
+ try {
995
+ const html = ejs.render(
996
+ template,
997
+ {
998
+ baseUrl: req.baseUrl,
999
+ adminPath,
1000
+ postId: String(req.params.id || ""),
1001
+ mode: "edit",
1002
+ },
1003
+ { filename: templatePath },
1004
+ );
1005
+ res.send(html);
1006
+ } catch (renderErr) {
1007
+ console.error("Error rendering template:", renderErr);
1008
+ res.status(500).send("Error rendering page");
1009
+ }
1010
+ });
1011
+ });
1012
+
1013
+ router.get(`${adminPath}/file-manager`, basicAuth, (req, res) => {
1014
+ const templatePath = path.join(
1015
+ __dirname,
1016
+ "..",
1017
+ "views",
1018
+ "admin-file-manager.ejs",
1019
+ );
425
1020
  fs.readFile(templatePath, "utf8", (err, template) => {
426
1021
  if (err) {
427
1022
  console.error("Error reading template:", err);
428
1023
  return res.status(500).send("Error loading page");
429
1024
  }
430
1025
  try {
431
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
1026
+ const html = ejs.render(
1027
+ template,
1028
+ {
1029
+ baseUrl: req.baseUrl,
1030
+ adminPath,
1031
+ },
1032
+ {
1033
+ filename: templatePath,
1034
+ },
1035
+ );
432
1036
  res.send(html);
433
1037
  } catch (renderErr) {
434
1038
  console.error("Error rendering template:", renderErr);
@@ -438,7 +1042,12 @@ function createMiddleware(options = {}) {
438
1042
  });
439
1043
 
440
1044
  router.get(`${adminPath}/ejs-virtual`, basicAuth, (req, res) => {
441
- const templatePath = path.join(__dirname, "..", "views", "admin-ejs-virtual.ejs");
1045
+ const templatePath = path.join(
1046
+ __dirname,
1047
+ "..",
1048
+ "views",
1049
+ "admin-ejs-virtual.ejs",
1050
+ );
442
1051
  fs.readFile(templatePath, "utf8", (err, template) => {
443
1052
  if (err) {
444
1053
  console.error("Error reading template:", err);
@@ -464,7 +1073,12 @@ function createMiddleware(options = {}) {
464
1073
  });
465
1074
 
466
1075
  router.get(`${adminPath}/seo-config`, basicAuth, (req, res) => {
467
- const templatePath = path.join(__dirname, "..", "views", "admin-seo-config.ejs");
1076
+ const templatePath = path.join(
1077
+ __dirname,
1078
+ "..",
1079
+ "views",
1080
+ "admin-seo-config.ejs",
1081
+ );
468
1082
  fs.readFile(templatePath, "utf8", (err, template) => {
469
1083
  if (err) {
470
1084
  console.error("Error reading template:", err);
@@ -498,7 +1112,11 @@ function createMiddleware(options = {}) {
498
1112
  return res.status(500).send("Error loading page");
499
1113
  }
500
1114
  try {
501
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
1115
+ const html = ejs.render(
1116
+ template,
1117
+ { baseUrl: req.baseUrl, adminPath },
1118
+ { filename: templatePath },
1119
+ );
502
1120
  res.send(html);
503
1121
  } catch (renderErr) {
504
1122
  console.error("Error rendering template:", renderErr);
@@ -520,7 +1138,11 @@ function createMiddleware(options = {}) {
520
1138
  return res.status(500).send("Error loading page");
521
1139
  }
522
1140
  try {
523
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
1141
+ const html = ejs.render(
1142
+ template,
1143
+ { baseUrl: req.baseUrl, adminPath },
1144
+ { filename: templatePath },
1145
+ );
524
1146
  res.send(html);
525
1147
  } catch (renderErr) {
526
1148
  console.error("Error rendering template:", renderErr);
@@ -784,12 +1406,7 @@ function createMiddleware(options = {}) {
784
1406
 
785
1407
  // Admin users page (protected by basic auth)
786
1408
  router.get(`${adminPath}/users`, basicAuth, (req, res) => {
787
- const templatePath = path.join(
788
- __dirname,
789
- "..",
790
- "views",
791
- "admin-users.ejs",
792
- );
1409
+ const templatePath = path.join(__dirname, "..", "views", "admin-users.ejs");
793
1410
  fs.readFile(templatePath, "utf8", (err, template) => {
794
1411
  if (err) {
795
1412
  console.error("Error reading template:", err);
@@ -908,6 +1525,31 @@ function createMiddleware(options = {}) {
908
1525
  });
909
1526
  });
910
1527
 
1528
+ router.get(`${adminPath}/rate-limiter`, basicAuth, (req, res) => {
1529
+ const templatePath = path.join(
1530
+ __dirname,
1531
+ "..",
1532
+ "views",
1533
+ "admin-rate-limiter.ejs",
1534
+ );
1535
+ fs.readFile(templatePath, "utf8", (err, template) => {
1536
+ if (err) {
1537
+ console.error("Error reading template:", err);
1538
+ return res.status(500).send("Error loading page");
1539
+ }
1540
+ try {
1541
+ const html = ejs.render(template, {
1542
+ baseUrl: req.baseUrl,
1543
+ adminPath,
1544
+ });
1545
+ res.send(html);
1546
+ } catch (renderErr) {
1547
+ console.error("Error rendering template:", renderErr);
1548
+ res.status(500).send("Error rendering page");
1549
+ }
1550
+ });
1551
+ });
1552
+
911
1553
  // Admin global settings page (protected by basic auth) - render manually
912
1554
  router.get(`${adminPath}/global-settings`, basicAuth, (req, res) => {
913
1555
  const templatePath = path.join(
@@ -932,14 +1574,23 @@ function createMiddleware(options = {}) {
932
1574
  });
933
1575
 
934
1576
  router.get(`${adminPath}/errors`, basicAuth, (req, res) => {
935
- const templatePath = path.join(__dirname, "..", "views", "admin-errors.ejs");
1577
+ const templatePath = path.join(
1578
+ __dirname,
1579
+ "..",
1580
+ "views",
1581
+ "admin-errors.ejs",
1582
+ );
936
1583
  fs.readFile(templatePath, "utf8", (err, template) => {
937
1584
  if (err) {
938
1585
  console.error("Error reading template:", err);
939
1586
  return res.status(500).send("Error loading page");
940
1587
  }
941
1588
  try {
942
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
1589
+ const html = ejs.render(
1590
+ template,
1591
+ { baseUrl: req.baseUrl, adminPath },
1592
+ { filename: templatePath },
1593
+ );
943
1594
  res.send(html);
944
1595
  } catch (renderErr) {
945
1596
  console.error("Error rendering template:", renderErr);
@@ -956,7 +1607,11 @@ function createMiddleware(options = {}) {
956
1607
  return res.status(500).send("Error loading page");
957
1608
  }
958
1609
  try {
959
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
1610
+ const html = ejs.render(
1611
+ template,
1612
+ { baseUrl: req.baseUrl, adminPath },
1613
+ { filename: templatePath },
1614
+ );
960
1615
  res.send(html);
961
1616
  } catch (renderErr) {
962
1617
  console.error("Error rendering template:", renderErr);
@@ -966,14 +1621,44 @@ function createMiddleware(options = {}) {
966
1621
  });
967
1622
 
968
1623
  router.get(`${adminPath}/coolify-deploy`, basicAuth, (req, res) => {
969
- const templatePath = path.join(__dirname, "..", "views", "admin-coolify-deploy.ejs");
1624
+ const templatePath = path.join(
1625
+ __dirname,
1626
+ "..",
1627
+ "views",
1628
+ "admin-coolify-deploy.ejs",
1629
+ );
1630
+ fs.readFile(templatePath, "utf8", (err, template) => {
1631
+ if (err) {
1632
+ console.error("Error reading template:", err);
1633
+ return res.status(500).send("Error loading page");
1634
+ }
1635
+ try {
1636
+ const html = ejs.render(
1637
+ template,
1638
+ { baseUrl: req.baseUrl, adminPath },
1639
+ { filename: templatePath },
1640
+ );
1641
+ res.send(html);
1642
+ } catch (renderErr) {
1643
+ console.error("Error rendering template:", renderErr);
1644
+ res.status(500).send("Error rendering page");
1645
+ }
1646
+ });
1647
+ });
1648
+
1649
+ router.get(`${adminPath}/proxy`, basicAuth, (req, res) => {
1650
+ const templatePath = path.join(__dirname, "..", "views", "admin-proxy.ejs");
970
1651
  fs.readFile(templatePath, "utf8", (err, template) => {
971
1652
  if (err) {
972
1653
  console.error("Error reading template:", err);
973
1654
  return res.status(500).send("Error loading page");
974
1655
  }
975
1656
  try {
976
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
1657
+ const html = ejs.render(
1658
+ template,
1659
+ { baseUrl: req.baseUrl, adminPath },
1660
+ { filename: templatePath },
1661
+ );
977
1662
  res.send(html);
978
1663
  } catch (renderErr) {
979
1664
  console.error("Error rendering template:", renderErr);
@@ -983,14 +1668,23 @@ function createMiddleware(options = {}) {
983
1668
  });
984
1669
 
985
1670
  router.get(`${adminPath}/webhooks`, basicAuth, (req, res) => {
986
- const templatePath = path.join(__dirname, "..", "views", "admin-webhooks.ejs");
1671
+ const templatePath = path.join(
1672
+ __dirname,
1673
+ "..",
1674
+ "views",
1675
+ "admin-webhooks.ejs",
1676
+ );
987
1677
  fs.readFile(templatePath, "utf8", (err, template) => {
988
1678
  if (err) {
989
1679
  console.error("Error reading template:", err);
990
1680
  return res.status(500).send("Error loading page");
991
1681
  }
992
1682
  try {
993
- const html = ejs.render(template, { baseUrl: req.baseUrl, adminPath }, { filename: templatePath });
1683
+ const html = ejs.render(
1684
+ template,
1685
+ { baseUrl: req.baseUrl, adminPath },
1686
+ { filename: templatePath },
1687
+ );
994
1688
  res.send(html);
995
1689
  } catch (renderErr) {
996
1690
  console.error("Error rendering template:", renderErr);
@@ -999,7 +1693,7 @@ function createMiddleware(options = {}) {
999
1693
  });
1000
1694
  });
1001
1695
 
1002
- router.get("/health", (req, res) => {
1696
+ router.get("/health", rateLimiter.limit("healthRateLimiter"), (req, res) => {
1003
1697
  res.json({
1004
1698
  status: "ok",
1005
1699
  mode: "middleware",
@@ -1011,6 +1705,20 @@ function createMiddleware(options = {}) {
1011
1705
  router.use("/api/ejs-virtual", require("./routes/adminEjsVirtual.routes"));
1012
1706
  router.use("/api/webhooks", require("./routes/webhook.routes"));
1013
1707
 
1708
+ // Store pagesPrefix and adminPath on app for pages router
1709
+ router.use((req, res, next) => {
1710
+ if (!req.app.get("pagesPrefix")) {
1711
+ req.app.set("pagesPrefix", pagesPrefix);
1712
+ }
1713
+ if (!req.app.get("adminPath")) {
1714
+ req.app.set("adminPath", adminPath);
1715
+ }
1716
+ next();
1717
+ });
1718
+
1719
+ // Public pages router (catch-all, must be last before error handler)
1720
+ router.use(require("./routes/pages.routes"));
1721
+
1014
1722
  // Error handling middleware
1015
1723
  router.use(expressErrorMiddleware);
1016
1724