@intranefr/superbackend 1.5.2 → 1.6.3

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 (134) hide show
  1. package/cookies.txt +6 -0
  2. package/cookies1.txt +6 -0
  3. package/cookies2.txt +6 -0
  4. package/cookies3.txt +6 -0
  5. package/cookies4.txt +5 -0
  6. package/cookies_old.txt +5 -0
  7. package/cookies_old_test.txt +6 -0
  8. package/cookies_super.txt +5 -0
  9. package/cookies_super_test.txt +6 -0
  10. package/cookies_test.txt +6 -0
  11. package/index.js +9 -0
  12. package/manage.js +745 -0
  13. package/package.json +6 -2
  14. package/plugins/core-waiting-list-migration/README.md +118 -0
  15. package/plugins/core-waiting-list-migration/index.js +438 -0
  16. package/plugins/global-settings-presets/index.js +20 -0
  17. package/plugins/hello-cli/index.js +17 -0
  18. package/plugins/ui-components-seeder/components/suiAlert.js +212 -0
  19. package/plugins/ui-components-seeder/components/suiToast.js +186 -0
  20. package/plugins/ui-components-seeder/index.js +31 -0
  21. package/public/js/admin-ui-components-preview.js +281 -0
  22. package/public/js/admin-ui-components.js +408 -0
  23. package/public/js/llm-provider-model-picker.js +193 -0
  24. package/public/test-iframe-fix.html +63 -0
  25. package/public/test-iframe.html +14 -0
  26. package/src/admin/endpointRegistry.js +68 -0
  27. package/src/controllers/admin.controller.js +36 -10
  28. package/src/controllers/adminAgents.controller.js +37 -0
  29. package/src/controllers/adminDataCleanup.controller.js +45 -0
  30. package/src/controllers/adminLlm.controller.js +19 -8
  31. package/src/controllers/adminLogin.controller.js +269 -0
  32. package/src/controllers/adminMarkdowns.controller.js +157 -0
  33. package/src/controllers/adminPlugins.controller.js +55 -0
  34. package/src/controllers/adminRegistry.controller.js +106 -0
  35. package/src/controllers/adminScripts.controller.js +138 -0
  36. package/src/controllers/adminStats.controller.js +4 -4
  37. package/src/controllers/adminTelegram.controller.js +72 -0
  38. package/src/controllers/markdowns.controller.js +42 -0
  39. package/src/controllers/registry.controller.js +32 -0
  40. package/src/controllers/waitingList.controller.js +52 -74
  41. package/src/helpers/mongooseHelper.js +6 -6
  42. package/src/helpers/scriptBase.js +2 -2
  43. package/src/middleware/auth.js +71 -1
  44. package/src/middleware/rbac.js +62 -0
  45. package/src/middleware.js +584 -176
  46. package/src/models/Agent.js +105 -0
  47. package/src/models/AgentMessage.js +82 -0
  48. package/src/models/GlobalSetting.js +11 -1
  49. package/src/models/Markdown.js +75 -0
  50. package/src/models/ScriptRun.js +8 -0
  51. package/src/models/TelegramBot.js +42 -0
  52. package/src/models/UiComponent.js +2 -0
  53. package/src/models/User.js +1 -1
  54. package/src/routes/admin.routes.js +3 -3
  55. package/src/routes/adminAgents.routes.js +13 -0
  56. package/src/routes/adminAssets.routes.js +11 -11
  57. package/src/routes/adminBlog.routes.js +2 -2
  58. package/src/routes/adminBlogAi.routes.js +2 -2
  59. package/src/routes/adminBlogAutomation.routes.js +2 -2
  60. package/src/routes/adminCache.routes.js +2 -2
  61. package/src/routes/adminConsoleManager.routes.js +2 -2
  62. package/src/routes/adminCrons.routes.js +2 -2
  63. package/src/routes/adminDataCleanup.routes.js +26 -0
  64. package/src/routes/adminDbBrowser.routes.js +2 -2
  65. package/src/routes/adminEjsVirtual.routes.js +2 -2
  66. package/src/routes/adminFeatureFlags.routes.js +6 -6
  67. package/src/routes/adminHeadless.routes.js +2 -2
  68. package/src/routes/adminHealthChecks.routes.js +2 -2
  69. package/src/routes/adminI18n.routes.js +2 -2
  70. package/src/routes/adminJsonConfigs.routes.js +8 -8
  71. package/src/routes/adminLlm.routes.js +8 -7
  72. package/src/routes/adminLogin.routes.js +23 -0
  73. package/src/routes/adminMarkdowns.routes.js +10 -0
  74. package/src/routes/adminMigration.routes.js +12 -12
  75. package/src/routes/adminPages.routes.js +2 -2
  76. package/src/routes/adminPlugins.routes.js +15 -0
  77. package/src/routes/adminProxy.routes.js +2 -2
  78. package/src/routes/adminRateLimits.routes.js +8 -8
  79. package/src/routes/adminRbac.routes.js +2 -2
  80. package/src/routes/adminRegistry.routes.js +24 -0
  81. package/src/routes/adminScripts.routes.js +6 -3
  82. package/src/routes/adminSeoConfig.routes.js +10 -10
  83. package/src/routes/adminTelegram.routes.js +14 -0
  84. package/src/routes/adminTerminals.routes.js +2 -2
  85. package/src/routes/adminUiComponents.routes.js +2 -2
  86. package/src/routes/adminUploadNamespaces.routes.js +7 -7
  87. package/src/routes/blogInternal.routes.js +2 -2
  88. package/src/routes/experiments.routes.js +2 -2
  89. package/src/routes/formsAdmin.routes.js +6 -6
  90. package/src/routes/globalSettings.routes.js +8 -8
  91. package/src/routes/internalExperiments.routes.js +2 -2
  92. package/src/routes/markdowns.routes.js +16 -0
  93. package/src/routes/notificationAdmin.routes.js +7 -7
  94. package/src/routes/orgAdmin.routes.js +16 -16
  95. package/src/routes/pages.routes.js +3 -3
  96. package/src/routes/registry.routes.js +11 -0
  97. package/src/routes/stripeAdmin.routes.js +12 -12
  98. package/src/routes/userAdmin.routes.js +7 -7
  99. package/src/routes/waitingListAdmin.routes.js +2 -2
  100. package/src/routes/workflows.routes.js +3 -3
  101. package/src/services/agent.service.js +546 -0
  102. package/src/services/agentHistory.service.js +345 -0
  103. package/src/services/agentTools.service.js +578 -0
  104. package/src/services/dataCleanup.service.js +286 -0
  105. package/src/services/jsonConfigs.service.js +284 -10
  106. package/src/services/llm.service.js +219 -6
  107. package/src/services/markdowns.service.js +522 -0
  108. package/src/services/plugins.service.js +348 -0
  109. package/src/services/registry.service.js +452 -0
  110. package/src/services/scriptsRunner.service.js +328 -37
  111. package/src/services/telegram.service.js +130 -0
  112. package/src/services/uiComponents.service.js +180 -0
  113. package/src/services/waitingListJson.service.js +401 -0
  114. package/src/utils/rbac/rightsRegistry.js +118 -0
  115. package/test-access.js +63 -0
  116. package/test-iframe-fix.html +63 -0
  117. package/test-iframe.html +14 -0
  118. package/views/admin-403.ejs +92 -0
  119. package/views/admin-agents.ejs +273 -0
  120. package/views/admin-coolify-deploy.ejs +8 -8
  121. package/views/admin-dashboard-home.ejs +52 -2
  122. package/views/admin-dashboard.ejs +179 -7
  123. package/views/admin-data-cleanup.ejs +357 -0
  124. package/views/admin-experiments.ejs +1 -1
  125. package/views/admin-login.ejs +286 -0
  126. package/views/admin-markdowns.ejs +905 -0
  127. package/views/admin-plugins-system.ejs +223 -0
  128. package/views/admin-scripts.ejs +221 -4
  129. package/views/admin-telegram.ejs +269 -0
  130. package/views/admin-ui-components.ejs +82 -402
  131. package/views/admin-users.ejs +207 -11
  132. package/views/partials/dashboard/nav-items.ejs +5 -0
  133. package/views/partials/llm-provider-model-picker.ejs +0 -161
  134. package/analysis-only.skill +0 -0
package/src/middleware.js CHANGED
@@ -23,7 +23,10 @@ const mongoose = require("mongoose");
23
23
  const cors = require("cors");
24
24
  const fs = require("fs");
25
25
  const ejs = require("ejs");
26
- const { basicAuth } = require("./middleware/auth");
26
+ const session = require("express-session");
27
+ const MongoStore = require("connect-mongo");
28
+ const { adminSessionAuth } = require("./middleware/auth");
29
+ const { requireModuleAccess } = require("./middleware/rbac");
27
30
  const endpointRegistry = require("./admin/endpointRegistry");
28
31
  const {
29
32
  createFeatureFlagsEjsMiddleware,
@@ -40,6 +43,7 @@ const {
40
43
  requestIdMiddleware,
41
44
  } = require("./middleware/errorCapture");
42
45
  const rateLimiter = require("./services/rateLimiter.service");
46
+ const pluginsService = require("./services/plugins.service");
43
47
 
44
48
  let errorCaptureInitialized = false;
45
49
 
@@ -81,6 +85,10 @@ async function isConsoleManagerEnabled() {
81
85
  * @param {string} options.jwtSecret - JWT secret for authentication
82
86
  * @param {Object} options.dbConnection - Existing Mongoose connection
83
87
  * @param {boolean} options.skipBodyParser - Skip adding body parser middleware (default: false)
88
+ * @param {Object} options.telegram - Telegram configuration
89
+ * @param {boolean} options.telegram.enabled - Whether to enable Telegram bots (default: true)
90
+ * @param {Object} options.cron - Cron scheduler configuration
91
+ * @param {boolean} options.cron.enabled - Whether to enable cron scheduler (default: true)
84
92
  * @returns {express.Router} Configured Express router
85
93
  */
86
94
  function createMiddleware(options = {}) {
@@ -88,6 +96,66 @@ function createMiddleware(options = {}) {
88
96
  const adminPath = options.adminPath || "/admin";
89
97
  const pagesPrefix = options.pagesPrefix || "/";
90
98
 
99
+ const bootstrapPluginsRuntime = async () => {
100
+ try {
101
+ const superbackend = globalThis.superbackend || globalThis.saasbackend || {};
102
+ await pluginsService.bootstrap({
103
+ context: {
104
+ services: superbackend.services || {},
105
+ helpers: superbackend.helpers || {},
106
+ },
107
+ });
108
+ const pluginServices = pluginsService.getExposedServices();
109
+ const pluginHelpers = pluginsService.getExposedHelpers();
110
+ if (superbackend.services && typeof superbackend.services === "object") {
111
+ superbackend.services.pluginsRuntime = pluginServices;
112
+ }
113
+ if (superbackend.helpers && typeof superbackend.helpers === "object") {
114
+ superbackend.helpers.pluginsRuntime = pluginHelpers;
115
+ }
116
+ } catch (error) {
117
+ console.error("Failed to bootstrap plugins runtime:", error);
118
+ }
119
+ };
120
+
121
+ // Debug: Log received options
122
+ console.log("[Middleware Debug] Received options:", {
123
+ telegramEnabled: options.telegram?.enabled,
124
+ cronEnabled: options.cron?.enabled
125
+ });
126
+
127
+ router.get(`${adminPath}/plugins-system`, requireModuleAccessWithIframe('plugins', 'read'), (req, res) => {
128
+ const templatePath = path.join(
129
+ __dirname,
130
+ "..",
131
+ "views",
132
+ "admin-plugins-system.ejs",
133
+ );
134
+ fs.readFile(templatePath, "utf8", (err, template) => {
135
+ if (err) {
136
+ console.error("Error reading template:", err);
137
+ return res.status(500).send("Error loading page");
138
+ }
139
+ try {
140
+ const html = ejs.render(
141
+ template,
142
+ {
143
+ baseUrl: req.baseUrl,
144
+ adminPath,
145
+ isIframe: req.isIframe || false
146
+ },
147
+ {
148
+ filename: templatePath,
149
+ },
150
+ );
151
+ res.send(html);
152
+ } catch (renderErr) {
153
+ console.error("Error rendering template:", renderErr);
154
+ res.status(500).send("Error rendering page");
155
+ }
156
+ });
157
+ });
158
+
91
159
  const normalizeBasePath = (value) => {
92
160
  const v = String(value || "").trim();
93
161
  if (!v) return "/files";
@@ -161,18 +229,35 @@ function createMiddleware(options = {}) {
161
229
  maxPoolSize: 10,
162
230
  };
163
231
 
232
+ const telegramService = require("./services/telegram.service");
233
+
164
234
  // Return a promise that resolves when connection is established
165
235
  const connectionPromise = mongoose
166
236
  .connect(mongoUri, connectionOptions)
167
237
  .then(async () => {
168
238
  console.log("✅ Middleware: Connected to MongoDB");
169
- // Start cron scheduler after DB connection
170
- await cronScheduler.start();
171
- await healthChecksScheduler.start();
172
- await healthChecksBootstrap.bootstrap();
173
- await blogCronsBootstrap.bootstrap();
174
- await require("./services/experimentsCronsBootstrap.service").bootstrap();
175
239
 
240
+ // Start cron scheduler after DB connection (only if enabled)
241
+ if (options.cron?.enabled !== false) {
242
+ await cronScheduler.start();
243
+ await healthChecksScheduler.start();
244
+ await healthChecksBootstrap.bootstrap();
245
+ await blogCronsBootstrap.bootstrap();
246
+ await require("./services/experimentsCronsBootstrap.service").bootstrap();
247
+ } else {
248
+ console.log("🔍 Cron scheduler disabled - cron.enabled:", options.cron?.enabled);
249
+ }
250
+
251
+ // Initialize Telegram bots (check telegram config)
252
+ const telegramEnabled = options.telegram?.enabled !== false;
253
+ if (telegramEnabled) {
254
+ await telegramService.init();
255
+ } else {
256
+ console.log("🔍 Telegram bots disabled - telegram.enabled:", options.telegram?.enabled);
257
+ }
258
+
259
+ await bootstrapPluginsRuntime();
260
+
176
261
  // Console manager is already initialized early in the middleware
177
262
  console.log("[Console Manager] MongoDB connection established");
178
263
 
@@ -183,7 +268,7 @@ function createMiddleware(options = {}) {
183
268
  return false;
184
269
  });
185
270
 
186
- router.get(`${adminPath}/health-checks`, basicAuth, (req, res) => {
271
+ router.get(`${adminPath}/health-checks`, requireModuleAccessWithIframe('health-checks', 'read'), (req, res) => {
187
272
  const templatePath = path.join(
188
273
  __dirname,
189
274
  "..",
@@ -201,6 +286,7 @@ function createMiddleware(options = {}) {
201
286
  {
202
287
  baseUrl: req.baseUrl,
203
288
  adminPath,
289
+ isIframe: req.isIframe || false
204
290
  },
205
291
  {
206
292
  filename: templatePath,
@@ -214,7 +300,39 @@ function createMiddleware(options = {}) {
214
300
  });
215
301
  });
216
302
 
217
- router.get(`${adminPath}/console-manager`, basicAuth, (req, res) => {
303
+ router.get(`${adminPath}/data-cleanup`, requireModuleAccessWithIframe('data-cleanup', 'read'), (req, res) => {
304
+ const templatePath = path.join(
305
+ __dirname,
306
+ "..",
307
+ "views",
308
+ "admin-data-cleanup.ejs",
309
+ );
310
+ fs.readFile(templatePath, "utf8", (err, template) => {
311
+ if (err) {
312
+ console.error("Error reading template:", err);
313
+ return res.status(500).send("Error loading page");
314
+ }
315
+ try {
316
+ const html = ejs.render(
317
+ template,
318
+ {
319
+ baseUrl: req.baseUrl,
320
+ adminPath,
321
+ isIframe: req.isIframe || false
322
+ },
323
+ {
324
+ filename: templatePath,
325
+ },
326
+ );
327
+ res.send(html);
328
+ } catch (renderErr) {
329
+ console.error("Error rendering template:", renderErr);
330
+ res.status(500).send("Error rendering page");
331
+ }
332
+ });
333
+ });
334
+
335
+ router.get(`${adminPath}/console-manager`, requireModuleAccessWithIframe('console-manager', 'read'), (req, res) => {
218
336
  const templatePath = path.join(
219
337
  __dirname,
220
338
  "..",
@@ -232,6 +350,7 @@ function createMiddleware(options = {}) {
232
350
  {
233
351
  baseUrl: req.baseUrl,
234
352
  adminPath,
353
+ isIframe: req.isIframe || false
235
354
  },
236
355
  {
237
356
  filename: templatePath,
@@ -249,25 +368,44 @@ function createMiddleware(options = {}) {
249
368
  router.connectionPromise = connectionPromise;
250
369
  } else if (mongoose.connection.readyState === 1) {
251
370
  console.log("✅ Middleware: Using existing MongoDB connection");
252
- // Start cron scheduler for existing connection
253
- cronScheduler.start().catch((err) => {
254
- console.error("Failed to start cron scheduler:", err);
255
- });
256
- healthChecksScheduler.start().catch((err) => {
257
- console.error("Failed to start health checks scheduler:", err);
258
- });
259
- healthChecksBootstrap.bootstrap().catch((err) => {
260
- console.error("Failed to bootstrap health checks:", err);
261
- });
262
- blogCronsBootstrap.bootstrap().catch((err) => {
263
- console.error("Failed to bootstrap blog crons:", err);
264
- });
371
+
372
+ // Start cron scheduler for existing connection (only if enabled)
373
+ if (options.cron?.enabled !== false) {
374
+ cronScheduler.start().catch((err) => {
375
+ console.error("Failed to start cron scheduler:", err);
376
+ });
377
+ healthChecksScheduler.start().catch((err) => {
378
+ console.error("Failed to start health checks scheduler:", err);
379
+ });
380
+ healthChecksBootstrap.bootstrap().catch((err) => {
381
+ console.error("Failed to bootstrap health checks:", err);
382
+ });
383
+ blogCronsBootstrap.bootstrap().catch((err) => {
384
+ console.error("Failed to bootstrap blog crons:", err);
385
+ });
265
386
 
266
- require("./services/experimentsCronsBootstrap.service")
267
- .bootstrap()
268
- .catch((err) => {
269
- console.error("Failed to bootstrap experiments crons:", err);
387
+ require("./services/experimentsCronsBootstrap.service")
388
+ .bootstrap()
389
+ .catch((err) => {
390
+ console.error("Failed to bootstrap experiments crons:", err);
391
+ });
392
+ } else {
393
+ console.log("🔍 Cron scheduler disabled - cron.enabled:", options.cron?.enabled, "(existing connection)");
394
+ }
395
+
396
+ // Initialize Telegram bots for existing connection (check telegram config)
397
+ const telegramEnabled = options.telegram?.enabled !== false;
398
+ if (telegramEnabled) {
399
+ telegramService.init().catch(err => {
400
+ console.error("Failed to initialize Telegram service (existing connection):", err);
270
401
  });
402
+ } else {
403
+ console.log("🔍 Telegram bots disabled - telegram.enabled:", options.telegram?.enabled, "(existing connection)");
404
+ }
405
+
406
+ bootstrapPluginsRuntime().catch((err) => {
407
+ console.error("Failed to bootstrap plugins runtime (existing connection):", err);
408
+ });
271
409
 
272
410
  // Initialize console manager AFTER database is already connected
273
411
  if (process.env.NODE_ENV !== "test" && !process.env.JEST_WORKER_ID) {
@@ -362,10 +500,34 @@ function createMiddleware(options = {}) {
362
500
  router.use(express.urlencoded({ extended: true }));
363
501
  }
364
502
 
503
+ // Session middleware for admin authentication
504
+ const sessionMiddleware = session({
505
+ secret: process.env.SESSION_SECRET || 'superbackend-session-secret-fallback',
506
+ resave: false,
507
+ saveUninitialized: false,
508
+ cookie: {
509
+ secure: process.env.NODE_ENV === 'production',
510
+ httpOnly: true,
511
+ maxAge: 24 * 60 * 60 * 1000, // 24 hours
512
+ sameSite: 'lax'
513
+ },
514
+ store: MongoStore.create({
515
+ mongoUrl: options.mongodbUri || process.env.MONGODB_URI,
516
+ collectionName: 'admin_sessions',
517
+ ttl: 24 * 60 * 60 // 24 hours in seconds
518
+ }),
519
+ name: 'superbackend.admin.session'
520
+ });
521
+
522
+ router.use(sessionMiddleware);
523
+
365
524
  router.use(requestIdMiddleware);
366
525
 
367
526
  router.use("/api", rateLimiter.limit("globalApiLimiter"));
368
527
 
528
+ // Serve public static files with /public prefix (for admin UI components)
529
+ router.use("/public", express.static(path.join(__dirname, "..", "public")));
530
+
369
531
  // Serve public static files (e.g. /og/og-default.png)
370
532
  router.use(express.static(path.join(__dirname, "..", "public")));
371
533
 
@@ -443,7 +605,7 @@ function createMiddleware(options = {}) {
443
605
  require("./routes/waitingListAdmin.routes"),
444
606
  );
445
607
  router.use("/api/admin/orgs", require("./routes/orgAdmin.routes"));
446
- router.use("/api/admin/users", require("./routes/userAdmin.routes"));
608
+ router.use("/api/admin/users", requireModuleAccessWithIframe('users', 'read'), require("./routes/userAdmin.routes"));
447
609
  router.use("/api/admin/rbac", require("./routes/adminRbac.routes"));
448
610
  router.use(
449
611
  "/api/admin/notifications",
@@ -455,11 +617,111 @@ function createMiddleware(options = {}) {
455
617
  const adminStatsController = require("./controllers/adminStats.controller");
456
618
  router.get(
457
619
  "/api/admin/stats/overview",
458
- basicAuth,
620
+ adminSessionAuth,
459
621
  adminStatsController.getOverviewStats,
460
622
  );
461
623
 
462
- router.get(`${adminPath}/stats/dashboard-home`, basicAuth, (req, res) => {
624
+ // Middleware to check if request is from authenticated parent iframe
625
+ function checkIframeAuth(req, res, next) {
626
+ const referer = req.get('Referer');
627
+ const origin = req.get('Origin');
628
+ const adminPath = req.adminPath || '/admin';
629
+
630
+ // Check for iframe token parameter (more reliable than referer)
631
+ const iframeToken = req.query.iframe_token;
632
+ if (iframeToken && iframeToken === 'authenticated') {
633
+ req.isIframe = true;
634
+ return next();
635
+ }
636
+
637
+ // Fallback to referer/origin check
638
+ const isValidReferer = referer && referer.includes(adminPath);
639
+ const isValidOrigin = origin && origin.includes(req.hostname);
640
+
641
+ if (isValidReferer || isValidOrigin) {
642
+ req.isIframe = true;
643
+ return next();
644
+ }
645
+
646
+ // If not from iframe, require normal authentication
647
+ return adminSessionAuth(req, res, next);
648
+ }
649
+
650
+ // Combined middleware for iframe authentication + module access
651
+ function requireModuleAccessWithIframe(moduleId, action = 'read') {
652
+ return async (req, res, next) => {
653
+ try {
654
+ // Check for iframe authentication first
655
+ const referer = req.get('Referer');
656
+ const origin = req.get('Origin');
657
+ const adminPath = req.adminPath || '/admin';
658
+ const iframeToken = req.query.iframe_token;
659
+
660
+ const isValidIframe = (iframeToken && iframeToken === 'authenticated') ||
661
+ (referer && referer.includes(adminPath)) ||
662
+ (origin && origin.includes(req.hostname));
663
+
664
+ if (isValidIframe) {
665
+ req.isIframe = true;
666
+ // For iframe requests, we'll allow access but mark it as iframe context
667
+ return next();
668
+ }
669
+
670
+ // Check for basic auth superadmin bypass
671
+ if (isBasicAuthSuperAdmin(req)) {
672
+ return next();
673
+ }
674
+
675
+ // Get user ID from session
676
+ const userId = req.session?.authData?.userId;
677
+ if (!userId) {
678
+ return res.redirect(`${req.adminPath || '/admin'}/login`);
679
+ }
680
+
681
+ // Check RBAC permission for specific module
682
+ const hasAccess = await rbacService.checkRight({
683
+ userId,
684
+ orgId: null, // Global admin permissions
685
+ right: `admin_panel__${moduleId}:${action}`
686
+ });
687
+
688
+ if (!hasAccess.allowed) {
689
+ // For API routes, return JSON error
690
+ if (req.path.startsWith('/api/')) {
691
+ return res.status(403).json({
692
+ error: 'Access denied',
693
+ reason: hasAccess.reason,
694
+ required: `admin_panel__${moduleId}:${action}`,
695
+ moduleId,
696
+ action
697
+ });
698
+ }
699
+
700
+ // For page routes, render 403 page
701
+ return res.status(403).render('admin-403', {
702
+ moduleId,
703
+ action,
704
+ required: `admin_panel__${moduleId}:${action}`,
705
+ reason: hasAccess.reason,
706
+ user: req.session.authData,
707
+ adminPath: req.adminPath || '/admin'
708
+ });
709
+ }
710
+
711
+ next();
712
+ } catch (error) {
713
+ console.error('Module access check error:', error);
714
+
715
+ if (req.path.startsWith('/api/')) {
716
+ return res.status(500).json({ error: 'Access check failed' });
717
+ } else {
718
+ return res.status(500).send('Access check failed');
719
+ }
720
+ }
721
+ };
722
+ }
723
+
724
+ router.get(`${adminPath}/stats/dashboard-home`, checkIframeAuth, (req, res) => {
463
725
  const templatePath = path.join(
464
726
  __dirname,
465
727
  "..",
@@ -474,7 +736,11 @@ function createMiddleware(options = {}) {
474
736
  try {
475
737
  const html = ejs.render(
476
738
  template,
477
- { baseUrl: req.baseUrl, adminPath },
739
+ {
740
+ baseUrl: req.baseUrl,
741
+ adminPath,
742
+ isIframe: req.isIframe || false
743
+ },
478
744
  { filename: templatePath },
479
745
  );
480
746
  res.send(html);
@@ -485,7 +751,7 @@ function createMiddleware(options = {}) {
485
751
  });
486
752
  });
487
753
 
488
- router.get(`${adminPath}/experiments`, basicAuth, (req, res) => {
754
+ router.get(`${adminPath}/experiments`, requireModuleAccessWithIframe('experiments', 'read'), (req, res) => {
489
755
  const templatePath = path.join(
490
756
  __dirname,
491
757
  "..",
@@ -503,6 +769,7 @@ function createMiddleware(options = {}) {
503
769
  {
504
770
  baseUrl: req.baseUrl,
505
771
  adminPath,
772
+ isIframe: req.isIframe || false
506
773
  },
507
774
  {
508
775
  filename: templatePath,
@@ -516,7 +783,7 @@ function createMiddleware(options = {}) {
516
783
  });
517
784
  });
518
785
 
519
- router.get(`${adminPath}/rbac`, basicAuth, (req, res) => {
786
+ router.get(`${adminPath}/rbac`, requireModuleAccessWithIframe('rbac', 'read'), (req, res) => {
520
787
  const templatePath = path.join(__dirname, "..", "views", "admin-rbac.ejs");
521
788
  fs.readFile(templatePath, "utf8", (err, template) => {
522
789
  if (err) {
@@ -529,6 +796,7 @@ function createMiddleware(options = {}) {
529
796
  {
530
797
  baseUrl: req.baseUrl,
531
798
  adminPath,
799
+ isIframe: req.isIframe || false
532
800
  },
533
801
  {
534
802
  filename: templatePath,
@@ -542,7 +810,7 @@ function createMiddleware(options = {}) {
542
810
  });
543
811
  });
544
812
 
545
- router.get(`${adminPath}/terminals`, basicAuth, (req, res) => {
813
+ router.get(`${adminPath}/terminals`, requireModuleAccessWithIframe('terminals', 'read'), (req, res) => {
546
814
  const templatePath = path.join(
547
815
  __dirname,
548
816
  "..",
@@ -561,6 +829,7 @@ function createMiddleware(options = {}) {
561
829
  baseUrl: req.baseUrl,
562
830
  adminPath,
563
831
  endpointRegistry,
832
+ isIframe: req.isIframe || false
564
833
  },
565
834
  {
566
835
  filename: templatePath,
@@ -574,7 +843,7 @@ function createMiddleware(options = {}) {
574
843
  });
575
844
  });
576
845
 
577
- router.get(`${adminPath}/scripts`, basicAuth, (req, res) => {
846
+ router.get(`${adminPath}/scripts`, requireModuleAccessWithIframe('scripts', 'read'), (req, res) => {
578
847
  const templatePath = path.join(
579
848
  __dirname,
580
849
  "..",
@@ -593,6 +862,7 @@ function createMiddleware(options = {}) {
593
862
  baseUrl: req.baseUrl,
594
863
  adminPath,
595
864
  endpointRegistry,
865
+ isIframe: req.isIframe || false
596
866
  },
597
867
  {
598
868
  filename: templatePath,
@@ -606,7 +876,7 @@ function createMiddleware(options = {}) {
606
876
  });
607
877
  });
608
878
 
609
- router.get(`${adminPath}/crons`, basicAuth, (req, res) => {
879
+ router.get(`${adminPath}/crons`, requireModuleAccessWithIframe('crons', 'read'), (req, res) => {
610
880
  const templatePath = path.join(__dirname, "..", "views", "admin-crons.ejs");
611
881
  fs.readFile(templatePath, "utf8", (err, template) => {
612
882
  if (err) {
@@ -619,6 +889,7 @@ function createMiddleware(options = {}) {
619
889
  {
620
890
  baseUrl: req.baseUrl,
621
891
  adminPath,
892
+ isIframe: req.isIframe || false
622
893
  },
623
894
  {
624
895
  filename: templatePath,
@@ -632,7 +903,7 @@ function createMiddleware(options = {}) {
632
903
  });
633
904
  });
634
905
 
635
- router.get(`${adminPath}/cache`, basicAuth, (req, res) => {
906
+ router.get(`${adminPath}/cache`, requireModuleAccessWithIframe('cache', 'read'), (req, res) => {
636
907
  const templatePath = path.join(__dirname, "..", "views", "admin-cache.ejs");
637
908
  fs.readFile(templatePath, "utf8", (err, template) => {
638
909
  if (err) {
@@ -645,6 +916,7 @@ function createMiddleware(options = {}) {
645
916
  {
646
917
  baseUrl: req.baseUrl,
647
918
  adminPath,
919
+ isIframe: req.isIframe || false
648
920
  },
649
921
  {
650
922
  filename: templatePath,
@@ -658,36 +930,101 @@ function createMiddleware(options = {}) {
658
930
  });
659
931
  });
660
932
 
661
- router.get(`${adminPath}/db-browser`, basicAuth, (req, res) => {
662
- const templatePath = path.join(
663
- __dirname,
664
- "..",
665
- "views",
666
- "admin-db-browser.ejs",
667
- );
668
- fs.readFile(templatePath, "utf8", (err, template) => {
669
- if (err) {
670
- console.error("Error reading template:", err);
671
- return res.status(500).send("Error loading page");
672
- }
673
- try {
674
- const html = ejs.render(
675
- template,
676
- {
677
- baseUrl: req.baseUrl,
678
- adminPath,
679
- },
680
- {
681
- filename: templatePath,
682
- },
683
- );
684
- res.send(html);
685
- } catch (renderErr) {
686
- console.error("Error rendering template:", renderErr);
687
- res.status(500).send("Error rendering page");
688
- }
933
+ router.get(`${adminPath}/db-browser`, requireModuleAccessWithIframe('db-browser', 'read'), (req, res) => {
934
+ const templatePath = path.join(
935
+ __dirname,
936
+ "..",
937
+ "views",
938
+ "admin-db-browser.ejs",
939
+ );
940
+ fs.readFile(templatePath, "utf8", (err, template) => {
941
+ if (err) {
942
+ console.error("Error reading template:", err);
943
+ return res.status(500).send("Error loading page");
944
+ }
945
+ try {
946
+ const html = ejs.render(
947
+ template,
948
+ {
949
+ baseUrl: req.baseUrl,
950
+ adminPath,
951
+ isIframe: req.isIframe || false
952
+ },
953
+ {
954
+ filename: templatePath,
955
+ },
956
+ );
957
+ res.send(html);
958
+ } catch (renderErr) {
959
+ console.error("Error rendering template:", renderErr);
960
+ res.status(500).send("Error rendering page");
961
+ }
962
+ });
963
+ });
964
+
965
+ router.get(`${adminPath}/telegram`, requireModuleAccessWithIframe('telegram', 'read'), (req, res) => {
966
+ const templatePath = path.join(
967
+ __dirname,
968
+ "..",
969
+ "views",
970
+ "admin-telegram.ejs",
971
+ );
972
+ fs.readFile(templatePath, "utf8", (err, template) => {
973
+ if (err) {
974
+ console.error("Error reading template:", err);
975
+ return res.status(500).send("Error loading page");
976
+ }
977
+ try {
978
+ const html = ejs.render(
979
+ template,
980
+ {
981
+ baseUrl: req.baseUrl,
982
+ adminPath,
983
+ isIframe: req.isIframe || false
984
+ },
985
+ {
986
+ filename: templatePath,
987
+ },
988
+ );
989
+ res.send(html);
990
+ } catch (renderErr) {
991
+ console.error("Error rendering template:", renderErr);
992
+ res.status(500).send("Error rendering page");
993
+ }
994
+ });
995
+ });
996
+
997
+ router.get(`${adminPath}/agents`, requireModuleAccessWithIframe('agents', 'read'), (req, res) => {
998
+ const templatePath = path.join(
999
+ __dirname,
1000
+ "..",
1001
+ "views",
1002
+ "admin-agents.ejs",
1003
+ );
1004
+ fs.readFile(templatePath, "utf8", (err, template) => {
1005
+ if (err) {
1006
+ console.error("Error reading template:", err);
1007
+ return res.status(500).send("Error loading page");
1008
+ }
1009
+ try {
1010
+ const html = ejs.render(
1011
+ template,
1012
+ {
1013
+ baseUrl: req.baseUrl,
1014
+ adminPath,
1015
+ isIframe: req.isIframe || false
1016
+ },
1017
+ {
1018
+ filename: templatePath,
1019
+ },
1020
+ );
1021
+ res.send(html);
1022
+ } catch (renderErr) {
1023
+ console.error("Error rendering template:", renderErr);
1024
+ res.status(500).send("Error rendering page");
1025
+ }
1026
+ });
689
1027
  });
690
- });
691
1028
 
692
1029
  router.use("/api/admin", require("./routes/admin.routes"));
693
1030
  router.use("/api/admin/settings", require("./routes/globalSettings.routes"));
@@ -699,6 +1036,10 @@ function createMiddleware(options = {}) {
699
1036
  "/api/admin/json-configs",
700
1037
  require("./routes/adminJsonConfigs.routes"),
701
1038
  );
1039
+ router.use(
1040
+ "/api/admin/markdowns",
1041
+ require("./routes/adminMarkdowns.routes"),
1042
+ );
702
1043
  router.use(
703
1044
  "/api/admin/rate-limits",
704
1045
  require("./routes/adminRateLimits.routes"),
@@ -725,6 +1066,10 @@ function createMiddleware(options = {}) {
725
1066
  "/api/admin/db-browser",
726
1067
  require("./routes/adminDbBrowser.routes"),
727
1068
  );
1069
+ router.use(
1070
+ "/api/admin/data-cleanup",
1071
+ require("./routes/adminDataCleanup.routes"),
1072
+ );
728
1073
  router.use("/api/admin/terminals", require("./routes/adminTerminals.routes"));
729
1074
  router.use("/api/admin/experiments", require("./routes/adminExperiments.routes"));
730
1075
  router.use("/api/admin/assets", require("./routes/adminAssets.routes"));
@@ -739,15 +1084,19 @@ function createMiddleware(options = {}) {
739
1084
  router.use("/api/admin/migration", require("./routes/adminMigration.routes"));
740
1085
  router.use(
741
1086
  "/api/admin/errors",
742
- basicAuth,
1087
+ adminSessionAuth,
743
1088
  require("./routes/adminErrors.routes"),
744
1089
  );
745
1090
  router.use(
746
1091
  "/api/admin/audit",
747
- basicAuth,
1092
+ requireModuleAccessWithIframe('audit', 'read'),
748
1093
  require("./routes/adminAudit.routes"),
749
1094
  );
750
1095
  router.use("/api/admin/llm", require("./routes/adminLlm.routes"));
1096
+ router.use("/api/admin/telegram", require("./routes/adminTelegram.routes"));
1097
+ router.use("/api/admin/agents", require("./routes/adminAgents.routes"));
1098
+ router.use("/api/admin/registries", require("./routes/adminRegistry.routes"));
1099
+ router.use("/api/admin/plugins", require("./routes/adminPlugins.routes"));
751
1100
  router.use(
752
1101
  "/api/admin/ejs-virtual",
753
1102
  require("./routes/adminEjsVirtual.routes"),
@@ -756,12 +1105,13 @@ function createMiddleware(options = {}) {
756
1105
  router.use("/api/admin", require("./routes/adminBlog.routes"));
757
1106
  router.use("/api/admin", require("./routes/adminBlogAi.routes"));
758
1107
  router.use("/api/admin", require("./routes/adminBlogAutomation.routes"));
759
- router.use("/api/admin/workflows", basicAuth, require("./routes/workflows.routes"));
1108
+ router.use("/api/admin/workflows", adminSessionAuth, require("./routes/workflows.routes"));
760
1109
  router.use("/w", require("./routes/workflowWebhook.routes"));
761
1110
  router.use("/api/webhooks", require("./routes/webhook.routes"));
762
1111
  router.use("/api/settings", require("./routes/globalSettings.routes"));
763
1112
  router.use("/api/feature-flags", require("./routes/featureFlags.routes"));
764
1113
  router.use("/api/json-configs", require("./routes/jsonConfigs.routes"));
1114
+ router.use("/api/markdowns", require("./routes/markdowns.routes"));
765
1115
  router.use("/api/assets", require("./routes/assets.routes"));
766
1116
  router.use("/api/i18n", require("./routes/i18n.routes"));
767
1117
  router.use("/api/headless", require("./routes/headless.routes"));
@@ -773,6 +1123,7 @@ function createMiddleware(options = {}) {
773
1123
  router.use("/api/error-tracking", require("./routes/errorTracking.routes"));
774
1124
  router.use("/api/ui-components", require("./routes/uiComponentsPublic.routes"));
775
1125
  router.use("/api/rbac", require("./routes/rbac.routes"));
1126
+ router.use("/registry", require("./routes/registry.routes"));
776
1127
  router.use("/api/file-manager", require("./routes/fileManager.routes"));
777
1128
  router.use("/api/experiments", require("./routes/experiments.routes"));
778
1129
 
@@ -794,8 +1145,14 @@ function createMiddleware(options = {}) {
794
1145
  // Public assets proxy
795
1146
  router.use("/public/assets", require("./routes/publicAssets.routes"));
796
1147
 
797
- // Admin dashboard (polished view)
798
- router.get(adminPath, basicAuth, (req, res) => {
1148
+ // Admin login routes (no authentication required)
1149
+ router.use(`${adminPath}`, (req, res, next) => {
1150
+ req.adminPath = adminPath;
1151
+ next();
1152
+ }, require("./routes/adminLogin.routes"));
1153
+
1154
+ // Admin dashboard (redirect to login if not authenticated)
1155
+ router.get(adminPath, adminSessionAuth, (req, res) => {
799
1156
  const templatePath = path.join(
800
1157
  __dirname,
801
1158
  "..",
@@ -810,7 +1167,7 @@ function createMiddleware(options = {}) {
810
1167
  try {
811
1168
  const html = ejs.render(
812
1169
  template,
813
- { baseUrl: req.baseUrl, adminPath },
1170
+ { baseUrl: req.baseUrl, adminPath, isIframe: req.isIframe || false },
814
1171
  { filename: templatePath },
815
1172
  );
816
1173
  res.send(html);
@@ -821,8 +1178,8 @@ function createMiddleware(options = {}) {
821
1178
  });
822
1179
  });
823
1180
 
824
- // Admin technical API test page (protected by basic auth)
825
- router.get(`${adminPath}/api/test`, basicAuth, (req, res) => {
1181
+ // Admin technical API test page (protected by session auth)
1182
+ router.get(`${adminPath}/api/test`, adminSessionAuth, (req, res) => {
826
1183
  const templatePath = path.join(__dirname, "..", "views", "admin-test.ejs");
827
1184
  fs.readFile(templatePath, "utf8", (err, template) => {
828
1185
  if (err) {
@@ -836,6 +1193,7 @@ function createMiddleware(options = {}) {
836
1193
  baseUrl: req.baseUrl,
837
1194
  adminPath,
838
1195
  endpointRegistry,
1196
+ isIframe: req.isIframe || false,
839
1197
  },
840
1198
  {
841
1199
  filename: templatePath,
@@ -849,7 +1207,7 @@ function createMiddleware(options = {}) {
849
1207
  });
850
1208
  });
851
1209
 
852
- router.get(`${adminPath}/migration`, basicAuth, (req, res) => {
1210
+ router.get(`${adminPath}/migration`, requireModuleAccessWithIframe('migration', 'read'), (req, res) => {
853
1211
  const templatePath = path.join(
854
1212
  __dirname,
855
1213
  "..",
@@ -867,7 +1225,7 @@ function createMiddleware(options = {}) {
867
1225
  {
868
1226
  baseUrl: req.baseUrl,
869
1227
  adminPath,
870
- endpointRegistry,
1228
+ isIframe: req.isIframe || false
871
1229
  },
872
1230
  {
873
1231
  filename: templatePath,
@@ -882,7 +1240,7 @@ function createMiddleware(options = {}) {
882
1240
  });
883
1241
 
884
1242
  // Admin LLM/AI page (protected by basic auth)
885
- router.get(`${adminPath}/admin-llm`, basicAuth, (req, res) => {
1243
+ router.get(`${adminPath}/admin-llm`, requireModuleAccessWithIframe('admin-llm', 'read'), (req, res) => {
886
1244
  const templatePath = path.join(__dirname, "..", "views", "admin-llm.ejs");
887
1245
  fs.readFile(templatePath, "utf8", (err, template) => {
888
1246
  if (err) {
@@ -895,6 +1253,7 @@ function createMiddleware(options = {}) {
895
1253
  {
896
1254
  baseUrl: req.baseUrl,
897
1255
  adminPath,
1256
+ isIframe: req.isIframe || false
898
1257
  },
899
1258
  {
900
1259
  filename: templatePath,
@@ -908,7 +1267,7 @@ function createMiddleware(options = {}) {
908
1267
  });
909
1268
  });
910
1269
 
911
- router.get(`${adminPath}/workflows/:id`, basicAuth, (req, res) => {
1270
+ router.get(`${adminPath}/workflows/:id`, adminSessionAuth, (req, res) => {
912
1271
  const templatePath = path.join(
913
1272
  __dirname,
914
1273
  "..",
@@ -923,7 +1282,7 @@ function createMiddleware(options = {}) {
923
1282
  try {
924
1283
  const html = ejs.render(
925
1284
  template,
926
- { baseUrl: req.baseUrl, adminPath },
1285
+ { baseUrl: req.baseUrl, adminPath, isIframe: req.isIframe || false },
927
1286
  { filename: templatePath },
928
1287
  );
929
1288
  res.send(html);
@@ -934,7 +1293,7 @@ function createMiddleware(options = {}) {
934
1293
  });
935
1294
  });
936
1295
 
937
- router.get(`${adminPath}/pages`, basicAuth, (req, res) => {
1296
+ router.get(`${adminPath}/pages`, requireModuleAccessWithIframe('pages', 'read'), (req, res) => {
938
1297
  const templatePath = path.join(__dirname, "..", "views", "admin-pages.ejs");
939
1298
  fs.readFile(templatePath, "utf8", (err, template) => {
940
1299
  if (err) {
@@ -947,6 +1306,7 @@ function createMiddleware(options = {}) {
947
1306
  {
948
1307
  baseUrl: req.baseUrl,
949
1308
  adminPath,
1309
+ isIframe: req.isIframe || false
950
1310
  },
951
1311
  {
952
1312
  filename: templatePath,
@@ -960,7 +1320,7 @@ function createMiddleware(options = {}) {
960
1320
  });
961
1321
  });
962
1322
 
963
- router.get(`${adminPath}/blog`, basicAuth, (req, res) => {
1323
+ router.get(`${adminPath}/blog`, requireModuleAccessWithIframe('blog', 'read'), (req, res) => {
964
1324
  const templatePath = path.join(__dirname, "..", "views", "admin-blog.ejs");
965
1325
  fs.readFile(templatePath, "utf8", (err, template) => {
966
1326
  if (err) {
@@ -970,7 +1330,7 @@ function createMiddleware(options = {}) {
970
1330
  try {
971
1331
  const html = ejs.render(
972
1332
  template,
973
- { baseUrl: req.baseUrl, adminPath },
1333
+ { baseUrl: req.baseUrl, adminPath, isIframe: req.isIframe || false },
974
1334
  { filename: templatePath },
975
1335
  );
976
1336
  res.send(html);
@@ -981,7 +1341,7 @@ function createMiddleware(options = {}) {
981
1341
  });
982
1342
  });
983
1343
 
984
- router.get(`${adminPath}/blog-automation`, basicAuth, (req, res) => {
1344
+ router.get(`${adminPath}/blog-automation`, requireModuleAccessWithIframe('blog-automation', 'read'), (req, res) => {
985
1345
  const templatePath = path.join(
986
1346
  __dirname,
987
1347
  "..",
@@ -996,7 +1356,7 @@ function createMiddleware(options = {}) {
996
1356
  try {
997
1357
  const html = ejs.render(
998
1358
  template,
999
- { baseUrl: req.baseUrl, adminPath },
1359
+ { baseUrl: req.baseUrl, adminPath, isIframe: req.isIframe || false },
1000
1360
  { filename: templatePath },
1001
1361
  );
1002
1362
  res.send(html);
@@ -1007,7 +1367,7 @@ function createMiddleware(options = {}) {
1007
1367
  });
1008
1368
  });
1009
1369
 
1010
- router.get(`${adminPath}/blog/new`, basicAuth, (req, res) => {
1370
+ router.get(`${adminPath}/blog/new`, requireModuleAccessWithIframe('blog', 'read'), (req, res) => {
1011
1371
  const templatePath = path.join(
1012
1372
  __dirname,
1013
1373
  "..",
@@ -1022,7 +1382,7 @@ function createMiddleware(options = {}) {
1022
1382
  try {
1023
1383
  const html = ejs.render(
1024
1384
  template,
1025
- { baseUrl: req.baseUrl, adminPath, postId: "", mode: "new" },
1385
+ { baseUrl: req.baseUrl, adminPath, postId: "", mode: "new", isIframe: req.isIframe || false },
1026
1386
  { filename: templatePath },
1027
1387
  );
1028
1388
  res.send(html);
@@ -1033,7 +1393,7 @@ function createMiddleware(options = {}) {
1033
1393
  });
1034
1394
  });
1035
1395
 
1036
- router.get(`${adminPath}/blog/edit/:id`, basicAuth, (req, res) => {
1396
+ router.get(`${adminPath}/blog/edit/:id`, requireModuleAccessWithIframe('blog', 'read'), (req, res) => {
1037
1397
  const templatePath = path.join(
1038
1398
  __dirname,
1039
1399
  "..",
@@ -1053,6 +1413,7 @@ function createMiddleware(options = {}) {
1053
1413
  adminPath,
1054
1414
  postId: String(req.params.id || ""),
1055
1415
  mode: "edit",
1416
+ isIframe: req.isIframe || false,
1056
1417
  },
1057
1418
  { filename: templatePath },
1058
1419
  );
@@ -1064,38 +1425,39 @@ function createMiddleware(options = {}) {
1064
1425
  });
1065
1426
  });
1066
1427
 
1067
- router.get(`${adminPath}/file-manager`, basicAuth, (req, res) => {
1068
- const templatePath = path.join(
1069
- __dirname,
1070
- "..",
1071
- "views",
1072
- "admin-file-manager.ejs",
1073
- );
1074
- fs.readFile(templatePath, "utf8", (err, template) => {
1075
- if (err) {
1076
- console.error("Error reading template:", err);
1077
- return res.status(500).send("Error loading page");
1078
- }
1079
- try {
1080
- const html = ejs.render(
1081
- template,
1082
- {
1083
- baseUrl: req.baseUrl,
1084
- adminPath,
1085
- },
1086
- {
1087
- filename: templatePath,
1088
- },
1089
- );
1090
- res.send(html);
1091
- } catch (renderErr) {
1092
- console.error("Error rendering template:", renderErr);
1093
- res.status(500).send("Error rendering page");
1094
- }
1428
+ router.get(`${adminPath}/file-manager`, requireModuleAccessWithIframe('file-manager', 'read'), (req, res) => {
1429
+ const templatePath = path.join(
1430
+ __dirname,
1431
+ "..",
1432
+ "views",
1433
+ "admin-file-manager.ejs",
1434
+ );
1435
+ fs.readFile(templatePath, "utf8", (err, template) => {
1436
+ if (err) {
1437
+ console.error("Error reading template:", err);
1438
+ return res.status(500).send("Error loading page");
1439
+ }
1440
+ try {
1441
+ const html = ejs.render(
1442
+ template,
1443
+ {
1444
+ baseUrl: req.baseUrl,
1445
+ adminPath,
1446
+ isIframe: req.isIframe || false
1447
+ },
1448
+ {
1449
+ filename: templatePath,
1450
+ },
1451
+ );
1452
+ res.send(html);
1453
+ } catch (renderErr) {
1454
+ console.error("Error rendering template:", renderErr);
1455
+ res.status(500).send("Error rendering page");
1456
+ }
1095
1457
  });
1096
1458
  });
1097
1459
 
1098
- router.get(`${adminPath}/ejs-virtual`, basicAuth, (req, res) => {
1460
+ router.get(`${adminPath}/ejs-virtual`, requireModuleAccessWithIframe('ejs-virtual', 'read'), (req, res) => {
1099
1461
  const templatePath = path.join(
1100
1462
  __dirname,
1101
1463
  "..",
@@ -1113,6 +1475,7 @@ function createMiddleware(options = {}) {
1113
1475
  {
1114
1476
  baseUrl: req.baseUrl,
1115
1477
  adminPath,
1478
+ isIframe: req.isIframe || false
1116
1479
  },
1117
1480
  {
1118
1481
  filename: templatePath,
@@ -1126,7 +1489,7 @@ function createMiddleware(options = {}) {
1126
1489
  });
1127
1490
  });
1128
1491
 
1129
- router.get(`${adminPath}/seo-config`, basicAuth, (req, res) => {
1492
+ router.get(`${adminPath}/seo-config`, requireModuleAccessWithIframe('seo-config', 'read'), (req, res) => {
1130
1493
  const templatePath = path.join(
1131
1494
  __dirname,
1132
1495
  "..",
@@ -1145,6 +1508,7 @@ function createMiddleware(options = {}) {
1145
1508
  baseUrl: req.baseUrl,
1146
1509
  adminPath,
1147
1510
  endpointRegistry,
1511
+ isIframe: req.isIframe || false,
1148
1512
  },
1149
1513
  {
1150
1514
  filename: templatePath,
@@ -1158,7 +1522,7 @@ function createMiddleware(options = {}) {
1158
1522
  });
1159
1523
  });
1160
1524
 
1161
- router.get(`${adminPath}/i18n`, basicAuth, (req, res) => {
1525
+ router.get(`${adminPath}/i18n`, requireModuleAccessWithIframe('i18n', 'read'), (req, res) => {
1162
1526
  const templatePath = path.join(__dirname, "..", "views", "admin-i18n.ejs");
1163
1527
  fs.readFile(templatePath, "utf8", (err, template) => {
1164
1528
  if (err) {
@@ -1168,7 +1532,7 @@ function createMiddleware(options = {}) {
1168
1532
  try {
1169
1533
  const html = ejs.render(
1170
1534
  template,
1171
- { baseUrl: req.baseUrl, adminPath },
1535
+ { baseUrl: req.baseUrl, adminPath, isIframe: req.isIframe || false },
1172
1536
  { filename: templatePath },
1173
1537
  );
1174
1538
  res.send(html);
@@ -1179,7 +1543,7 @@ function createMiddleware(options = {}) {
1179
1543
  });
1180
1544
  });
1181
1545
 
1182
- router.get(`${adminPath}/i18n/locales`, basicAuth, (req, res) => {
1546
+ router.get(`${adminPath}/i18n/locales`, requireModuleAccessWithIframe('i18n', 'read'), (req, res) => {
1183
1547
  const templatePath = path.join(
1184
1548
  __dirname,
1185
1549
  "..",
@@ -1194,7 +1558,7 @@ function createMiddleware(options = {}) {
1194
1558
  try {
1195
1559
  const html = ejs.render(
1196
1560
  template,
1197
- { baseUrl: req.baseUrl, adminPath },
1561
+ { baseUrl: req.baseUrl, adminPath, isIframe: req.isIframe || false },
1198
1562
  { filename: templatePath },
1199
1563
  );
1200
1564
  res.send(html);
@@ -1206,7 +1570,7 @@ function createMiddleware(options = {}) {
1206
1570
  });
1207
1571
 
1208
1572
  // Admin forms page (protected by basic auth)
1209
- router.get(`${adminPath}/forms`, basicAuth, (req, res) => {
1573
+ router.get(`${adminPath}/forms`, requireModuleAccessWithIframe('forms', 'read'), (req, res) => {
1210
1574
  const templatePath = path.join(__dirname, "..", "views", "admin-forms.ejs");
1211
1575
  fs.readFile(templatePath, "utf8", (err, template) => {
1212
1576
  if (err) {
@@ -1220,6 +1584,7 @@ function createMiddleware(options = {}) {
1220
1584
  baseUrl: req.baseUrl,
1221
1585
  adminPath,
1222
1586
  endpointRegistry,
1587
+ isIframe: req.isIframe || false
1223
1588
  },
1224
1589
  {
1225
1590
  filename: templatePath,
@@ -1234,7 +1599,7 @@ function createMiddleware(options = {}) {
1234
1599
  });
1235
1600
 
1236
1601
  // Admin feature flags page (protected by basic auth)
1237
- router.get(`${adminPath}/feature-flags`, basicAuth, (req, res) => {
1602
+ router.get(`${adminPath}/feature-flags`, requireModuleAccessWithIframe('feature-flags', 'read'), (req, res) => {
1238
1603
  const templatePath = path.join(
1239
1604
  __dirname,
1240
1605
  "..",
@@ -1253,6 +1618,7 @@ function createMiddleware(options = {}) {
1253
1618
  baseUrl: req.baseUrl,
1254
1619
  adminPath,
1255
1620
  endpointRegistry,
1621
+ isIframe: req.isIframe || false
1256
1622
  },
1257
1623
  {
1258
1624
  filename: templatePath,
@@ -1267,7 +1633,7 @@ function createMiddleware(options = {}) {
1267
1633
  });
1268
1634
 
1269
1635
  // Admin headless CMS page (protected by basic auth)
1270
- router.get(`${adminPath}/headless`, basicAuth, (req, res) => {
1636
+ router.get(`${adminPath}/headless`, requireModuleAccessWithIframe('headless', 'read'), (req, res) => {
1271
1637
  const templatePath = path.join(
1272
1638
  __dirname,
1273
1639
  "..",
@@ -1286,6 +1652,7 @@ function createMiddleware(options = {}) {
1286
1652
  baseUrl: req.baseUrl,
1287
1653
  adminPath,
1288
1654
  endpointRegistry,
1655
+ isIframe: req.isIframe || false,
1289
1656
  },
1290
1657
  {
1291
1658
  filename: templatePath,
@@ -1300,7 +1667,7 @@ function createMiddleware(options = {}) {
1300
1667
  });
1301
1668
 
1302
1669
  // Admin UI Components page (protected by basic auth)
1303
- router.get(`${adminPath}/ui-components`, basicAuth, (req, res) => {
1670
+ router.get(`${adminPath}/ui-components`, requireModuleAccessWithIframe('ui-components', 'read'), (req, res) => {
1304
1671
  const templatePath = path.join(
1305
1672
  __dirname,
1306
1673
  "..",
@@ -1316,9 +1683,10 @@ function createMiddleware(options = {}) {
1316
1683
  const html = ejs.render(
1317
1684
  template,
1318
1685
  {
1319
- baseUrl: req.baseUrl,
1686
+ baseUrl: req.baseUrl || '',
1320
1687
  adminPath,
1321
1688
  endpointRegistry,
1689
+ isIframe: req.isIframe || false
1322
1690
  },
1323
1691
  {
1324
1692
  filename: templatePath,
@@ -1333,7 +1701,7 @@ function createMiddleware(options = {}) {
1333
1701
  });
1334
1702
 
1335
1703
  // Admin JSON configs page (protected by basic auth)
1336
- router.get(`${adminPath}/json-configs`, basicAuth, (req, res) => {
1704
+ router.get(`${adminPath}/json-configs`, requireModuleAccessWithIframe('json-configs', 'read'), (req, res) => {
1337
1705
  const templatePath = path.join(
1338
1706
  __dirname,
1339
1707
  "..",
@@ -1352,6 +1720,41 @@ function createMiddleware(options = {}) {
1352
1720
  baseUrl: req.baseUrl,
1353
1721
  adminPath,
1354
1722
  endpointRegistry,
1723
+ isIframe: req.isIframe || false,
1724
+ },
1725
+ {
1726
+ filename: templatePath,
1727
+ },
1728
+ );
1729
+ res.send(html);
1730
+ } catch (renderErr) {
1731
+ console.error("Error rendering template:", renderErr);
1732
+ res.status(500).send("Error rendering page");
1733
+ }
1734
+ });
1735
+ });
1736
+
1737
+ // Admin markdowns page (protected by basic auth)
1738
+ router.get(`${adminPath}/markdowns`, requireModuleAccessWithIframe('markdowns', 'read'), (req, res) => {
1739
+ const templatePath = path.join(
1740
+ __dirname,
1741
+ "..",
1742
+ "views",
1743
+ "admin-markdowns.ejs",
1744
+ );
1745
+ fs.readFile(templatePath, "utf8", (err, template) => {
1746
+ if (err) {
1747
+ console.error("Error reading template:", err);
1748
+ return res.status(500).send("Error loading page");
1749
+ }
1750
+ try {
1751
+ const html = ejs.render(
1752
+ template,
1753
+ {
1754
+ baseUrl: req.baseUrl,
1755
+ adminPath,
1756
+ endpointRegistry,
1757
+ isIframe: req.isIframe || false,
1355
1758
  },
1356
1759
  {
1357
1760
  filename: templatePath,
@@ -1366,7 +1769,7 @@ function createMiddleware(options = {}) {
1366
1769
  });
1367
1770
 
1368
1771
  // Admin assets page (protected by basic auth)
1369
- router.get(`${adminPath}/assets`, basicAuth, (req, res) => {
1772
+ router.get(`${adminPath}/assets`, requireModuleAccessWithIframe('assets', 'read'), (req, res) => {
1370
1773
  const templatePath = path.join(
1371
1774
  __dirname,
1372
1775
  "..",
@@ -1385,6 +1788,7 @@ function createMiddleware(options = {}) {
1385
1788
  baseUrl: req.baseUrl,
1386
1789
  adminPath,
1387
1790
  endpointRegistry,
1791
+ isIframe: req.isIframe || false,
1388
1792
  },
1389
1793
  {
1390
1794
  filename: templatePath,
@@ -1399,7 +1803,7 @@ function createMiddleware(options = {}) {
1399
1803
  });
1400
1804
 
1401
1805
  // Admin waiting list page (protected by basic auth)
1402
- router.get(`${adminPath}/waiting-list`, basicAuth, (req, res) => {
1806
+ router.get(`${adminPath}/waiting-list`, requireModuleAccessWithIframe('waiting-list', 'read'), (req, res) => {
1403
1807
  const templatePath = path.join(
1404
1808
  __dirname,
1405
1809
  "..",
@@ -1416,6 +1820,7 @@ function createMiddleware(options = {}) {
1416
1820
  baseUrl: req.baseUrl,
1417
1821
  adminPath,
1418
1822
  endpointRegistry,
1823
+ isIframe: req.isIframe || false
1419
1824
  });
1420
1825
  res.send(html);
1421
1826
  } catch (renderErr) {
@@ -1426,7 +1831,7 @@ function createMiddleware(options = {}) {
1426
1831
  });
1427
1832
 
1428
1833
  // Admin organizations page (protected by basic auth)
1429
- router.get(`${adminPath}/organizations`, basicAuth, (req, res) => {
1834
+ router.get(`${adminPath}/organizations`, requireModuleAccessWithIframe('organizations', 'read'), (req, res) => {
1430
1835
  const templatePath = path.join(
1431
1836
  __dirname,
1432
1837
  "..",
@@ -1445,6 +1850,7 @@ function createMiddleware(options = {}) {
1445
1850
  baseUrl: req.baseUrl,
1446
1851
  adminPath,
1447
1852
  endpointRegistry,
1853
+ isIframe: req.isIframe || false
1448
1854
  },
1449
1855
  {
1450
1856
  filename: templatePath,
@@ -1458,8 +1864,8 @@ function createMiddleware(options = {}) {
1458
1864
  });
1459
1865
  });
1460
1866
 
1461
- // Admin users page (protected by basic auth)
1462
- router.get(`${adminPath}/users`, basicAuth, (req, res) => {
1867
+ // Admin users page (protected by session auth)
1868
+ router.get(`${adminPath}/users`, requireModuleAccessWithIframe('users', 'read'), (req, res) => {
1463
1869
  const templatePath = path.join(__dirname, "..", "views", "admin-users.ejs");
1464
1870
  fs.readFile(templatePath, "utf8", (err, template) => {
1465
1871
  if (err) {
@@ -1470,13 +1876,11 @@ function createMiddleware(options = {}) {
1470
1876
  const html = ejs.render(
1471
1877
  template,
1472
1878
  {
1473
- baseUrl: req.baseUrl,
1879
+ baseUrl: req.baseUrl,
1474
1880
  adminPath,
1475
- endpointRegistry,
1476
- },
1477
- {
1478
- filename: templatePath,
1881
+ isIframe: req.isIframe || false
1479
1882
  },
1883
+ { filename: templatePath },
1480
1884
  );
1481
1885
  res.send(html);
1482
1886
  } catch (renderErr) {
@@ -1486,8 +1890,8 @@ function createMiddleware(options = {}) {
1486
1890
  });
1487
1891
  });
1488
1892
 
1489
- // Admin notifications page (protected by basic auth)
1490
- router.get(`${adminPath}/notifications`, basicAuth, (req, res) => {
1893
+ // Admin notifications page (protected by session auth)
1894
+ router.get(`${adminPath}/notifications`, requireModuleAccessWithIframe('notifications', 'read'), (req, res) => {
1491
1895
  const templatePath = path.join(
1492
1896
  __dirname,
1493
1897
  "..",
@@ -1506,6 +1910,7 @@ function createMiddleware(options = {}) {
1506
1910
  baseUrl: req.baseUrl,
1507
1911
  adminPath,
1508
1912
  endpointRegistry,
1913
+ isIframe: req.isIframe || false
1509
1914
  },
1510
1915
  {
1511
1916
  filename: templatePath,
@@ -1519,8 +1924,8 @@ function createMiddleware(options = {}) {
1519
1924
  });
1520
1925
  });
1521
1926
 
1522
- // Admin Stripe pricing page (protected by basic auth)
1523
- router.get(`${adminPath}/stripe-pricing`, basicAuth, (req, res) => {
1927
+ // Admin Stripe pricing page (protected by session auth)
1928
+ router.get(`${adminPath}/stripe-pricing`, adminSessionAuth, (req, res) => {
1524
1929
  const templatePath = path.join(
1525
1930
  __dirname,
1526
1931
  "..",
@@ -1547,39 +1952,38 @@ function createMiddleware(options = {}) {
1547
1952
  res.send(html);
1548
1953
  } catch (renderErr) {
1549
1954
  console.error("Error rendering template:", renderErr);
1550
- res.status(500).send("Error rendering page");
1551
- }
1552
- });
1553
- });
1554
1955
 
1555
- // Admin metrics page (protected by basic auth)
1556
- router.get(`${adminPath}/metrics`, basicAuth, (req, res) => {
1557
- const templatePath = path.join(
1558
- __dirname,
1559
- "..",
1560
- "views",
1561
- "admin-metrics.ejs",
1562
- );
1563
- fs.readFile(templatePath, "utf8", (err, template) => {
1564
- if (err) {
1565
- console.error("Error reading template:", err);
1566
- return res.status(500).send("Error loading page");
1567
- }
1568
- try {
1569
- const html = ejs.render(template, {
1570
- baseUrl: req.baseUrl,
1571
- adminPath,
1572
- endpointRegistry,
1573
- });
1574
- res.send(html);
1575
- } catch (renderErr) {
1576
- console.error("Error rendering template:", renderErr);
1577
- res.status(500).send("Error rendering page");
1956
+ // Admin metrics page (protected by session auth)
1957
+ router.get(`${adminPath}/metrics`, adminSessionAuth, (req, res) => {
1958
+ const templatePath = path.join(
1959
+ __dirname,
1960
+ "..",
1961
+ "views",
1962
+ "admin-metrics.ejs",
1963
+ );
1964
+ fs.readFile(templatePath, "utf8", (err, template) => {
1965
+ if (err) {
1966
+ console.error("Error reading template:", err);
1967
+ return res.status(500).send("Error loading page");
1968
+ }
1969
+ try {
1970
+ const html = ejs.render(template, {
1971
+ baseUrl: req.baseUrl,
1972
+ adminPath,
1973
+ endpointRegistry,
1974
+ });
1975
+ res.send(html);
1976
+ } catch (renderErr) {
1977
+ console.error("Error rendering template:", renderErr);
1978
+ res.status(500).send("Error rendering page");
1979
+ }
1980
+ });
1981
+ });
1578
1982
  }
1579
1983
  });
1580
1984
  });
1581
1985
 
1582
- router.get(`${adminPath}/rate-limiter`, basicAuth, (req, res) => {
1986
+ router.get(`${adminPath}/rate-limiter`, adminSessionAuth, (req, res) => {
1583
1987
  const templatePath = path.join(
1584
1988
  __dirname,
1585
1989
  "..",
@@ -1604,8 +2008,8 @@ function createMiddleware(options = {}) {
1604
2008
  });
1605
2009
  });
1606
2010
 
1607
- // Admin global settings page (protected by basic auth) - render manually
1608
- router.get(`${adminPath}/global-settings`, basicAuth, (req, res) => {
2011
+ // Admin global settings page (protected by session auth) - render manually
2012
+ router.get(`${adminPath}/global-settings`, adminSessionAuth, (req, res) => {
1609
2013
  const templatePath = path.join(
1610
2014
  __dirname,
1611
2015
  "..",
@@ -1627,7 +2031,7 @@ function createMiddleware(options = {}) {
1627
2031
  });
1628
2032
  });
1629
2033
 
1630
- router.get(`${adminPath}/errors`, basicAuth, (req, res) => {
2034
+ router.get(`${adminPath}/errors`, adminSessionAuth, (req, res) => {
1631
2035
  const templatePath = path.join(
1632
2036
  __dirname,
1633
2037
  "..",
@@ -1653,7 +2057,7 @@ function createMiddleware(options = {}) {
1653
2057
  });
1654
2058
  });
1655
2059
 
1656
- router.get(`${adminPath}/audit`, basicAuth, (req, res) => {
2060
+ router.get(`${adminPath}/audit`, requireModuleAccessWithIframe('audit', 'read'), (req, res) => {
1657
2061
  const templatePath = path.join(__dirname, "..", "views", "admin-audit.ejs");
1658
2062
  fs.readFile(templatePath, "utf8", (err, template) => {
1659
2063
  if (err) {
@@ -1663,7 +2067,11 @@ function createMiddleware(options = {}) {
1663
2067
  try {
1664
2068
  const html = ejs.render(
1665
2069
  template,
1666
- { baseUrl: req.baseUrl, adminPath },
2070
+ {
2071
+ baseUrl: req.baseUrl,
2072
+ adminPath,
2073
+ isIframe: req.isIframe || false
2074
+ },
1667
2075
  { filename: templatePath },
1668
2076
  );
1669
2077
  res.send(html);
@@ -1674,7 +2082,7 @@ function createMiddleware(options = {}) {
1674
2082
  });
1675
2083
  });
1676
2084
 
1677
- router.get(`${adminPath}/coolify-deploy`, basicAuth, (req, res) => {
2085
+ router.get(`${adminPath}/coolify-deploy`, adminSessionAuth, (req, res) => {
1678
2086
  const templatePath = path.join(
1679
2087
  __dirname,
1680
2088
  "..",
@@ -1700,7 +2108,7 @@ function createMiddleware(options = {}) {
1700
2108
  });
1701
2109
  });
1702
2110
 
1703
- router.get(`${adminPath}/proxy`, basicAuth, (req, res) => {
2111
+ router.get(`${adminPath}/proxy`, adminSessionAuth, (req, res) => {
1704
2112
  const templatePath = path.join(__dirname, "..", "views", "admin-proxy.ejs");
1705
2113
  fs.readFile(templatePath, "utf8", (err, template) => {
1706
2114
  if (err) {
@@ -1721,7 +2129,7 @@ function createMiddleware(options = {}) {
1721
2129
  });
1722
2130
  });
1723
2131
 
1724
- router.get(`${adminPath}/webhooks`, basicAuth, (req, res) => {
2132
+ router.get(`${adminPath}/webhooks`, adminSessionAuth, (req, res) => {
1725
2133
  const templatePath = path.join(
1726
2134
  __dirname,
1727
2135
  "..",