@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
@@ -1,4 +1,4 @@
1
- const WaitingList = require('../models/WaitingList');
1
+ const waitingListService = require('../services/waitingListJson.service');
2
2
  const { validateEmail, sanitizeString } = require('../utils/validation');
3
3
 
4
4
  // Subscribe to waiting list
@@ -31,36 +31,51 @@ exports.subscribe = async (req, res) => {
31
31
  });
32
32
  }
33
33
 
34
- // Check if email already exists
35
- const existingEntry = await WaitingList.findOne({ email: sanitizedEmail.toLowerCase() });
36
- if (existingEntry) {
37
- return res.status(409).json({
38
- error: 'This email is already on our waiting list',
39
- field: 'email'
34
+ // Check if email already exists and create new entry using JSON Configs service
35
+ try {
36
+ const waitingListEntry = await waitingListService.addWaitingListEntry({
37
+ email: sanitizedEmail.toLowerCase(),
38
+ type: sanitizedType.trim(),
39
+ referralSource: sanitizeString(referralSource) || 'website'
40
40
  });
41
- }
42
-
43
- // Create new waiting list entry
44
- const waitingListEntry = new WaitingList({
45
- email: sanitizedEmail.toLowerCase(),
46
- type: sanitizedType.trim(),
47
- referralSource: sanitizeString(referralSource) || 'website'
48
- });
49
41
 
50
- await waitingListEntry.save();
42
+ // Return success response without sensitive data
43
+ const response = { ...waitingListEntry };
44
+ delete response.email; // Don't return email in response for privacy
51
45
 
52
- // Return success response without sensitive data
53
- const response = waitingListEntry.toJSON();
54
- delete response.email; // Don't return email in response for privacy
55
-
56
- res.status(201).json({
57
- message: 'Successfully joined the waiting list!',
58
- data: response
59
- });
46
+ res.status(201).json({
47
+ message: 'Successfully joined the waiting list!',
48
+ data: response
49
+ });
50
+ } catch (serviceError) {
51
+ // Handle validation and duplicate errors from service
52
+ if (serviceError.code === 'VALIDATION') {
53
+ return res.status(400).json({
54
+ error: serviceError.message,
55
+ field: 'general'
56
+ });
57
+ }
58
+
59
+ if (serviceError.code === 'DUPLICATE_EMAIL' || serviceError.code === 'DUPLICATE' || serviceError.message.includes('already exists')) {
60
+ return res.status(409).json({
61
+ error: 'This email is already on our waiting list',
62
+ field: 'email'
63
+ });
64
+ }
65
+
66
+ if (serviceError.code === 'INITIALIZATION_FAILED') {
67
+ return res.status(500).json({
68
+ error: 'Service temporarily unavailable - please try again',
69
+ field: 'general'
70
+ });
71
+ }
72
+
73
+ throw serviceError; // Re-throw for general error handling
74
+ }
60
75
  } catch (error) {
61
76
  console.error('Waiting list subscription error:', error);
62
77
 
63
- // Handle specific MongoDB errors
78
+ // Handle specific errors
64
79
  if (error.code === 11000) {
65
80
  return res.status(409).json({
66
81
  error: 'This email is already on our waiting list',
@@ -86,35 +101,10 @@ exports.subscribe = async (req, res) => {
86
101
  // Get waiting list stats (public)
87
102
  exports.getStats = async (req, res) => {
88
103
  try {
89
- const totalSubscribers = await WaitingList.countDocuments({ status: 'active' });
90
-
91
- const typeAgg = await WaitingList.aggregate([
92
- { $match: { status: 'active' } },
93
- { $group: { _id: '$type', count: { $sum: 1 } } },
94
- { $sort: { count: -1, _id: 1 } },
95
- ]);
96
-
97
- const typeCounts = (typeAgg || []).reduce((acc, row) => {
98
- if (!row?._id) return acc;
99
- acc[String(row._id)] = row.count || 0;
100
- return acc;
101
- }, {});
102
-
103
- // Backward compatibility fields (legacy UI/tests)
104
- const buyerCount = (typeCounts.buyer || 0) + (typeCounts.both || 0);
105
- const sellerCount = (typeCounts.seller || 0) + (typeCounts.both || 0);
106
-
107
- // Add some mock growth data for demonstration
108
- const growthThisWeek = Math.floor(totalSubscribers * 0.05); // 5% growth
104
+ // Use JSON Configs service for cached statistics
105
+ const stats = await waitingListService.getWaitingListStats();
109
106
 
110
- res.json({
111
- totalSubscribers,
112
- buyerCount,
113
- sellerCount,
114
- typeCounts,
115
- growthThisWeek,
116
- lastUpdated: new Date().toISOString()
117
- });
107
+ res.json(stats);
118
108
  } catch (error) {
119
109
  console.error('Waiting list stats error:', error);
120
110
  res.status(500).json({
@@ -135,31 +125,19 @@ exports.adminList = async (req, res) => {
135
125
  offset = 0,
136
126
  } = req.query;
137
127
 
138
- const query = {};
139
- if (status) query.status = String(status);
140
- if (type) query.type = String(type);
141
- if (email) query.email = String(email).trim().toLowerCase();
142
-
143
128
  const parsedLimit = Math.min(500, Math.max(1, parseInt(limit, 10) || 50));
144
129
  const parsedOffset = Math.max(0, parseInt(offset, 10) || 0);
145
130
 
146
- const entries = await WaitingList.find(query)
147
- .sort({ createdAt: -1 })
148
- .limit(parsedLimit)
149
- .skip(parsedOffset)
150
- .select('email type status referralSource createdAt updatedAt')
151
- .lean();
152
-
153
- const total = await WaitingList.countDocuments(query);
154
-
155
- return res.json({
156
- entries,
157
- pagination: {
158
- total,
159
- limit: parsedLimit,
160
- offset: parsedOffset,
161
- },
131
+ // Use JSON Configs service for admin data with filtering and pagination
132
+ const result = await waitingListService.getWaitingListEntriesAdmin({
133
+ status,
134
+ type,
135
+ email,
136
+ limit: parsedLimit,
137
+ offset: parsedOffset
162
138
  });
139
+
140
+ return res.json(result);
163
141
  } catch (error) {
164
142
  console.error('Waiting list admin list error:', error);
165
143
  return res.status(500).json({ error: 'Failed to list entries' });
@@ -83,7 +83,7 @@ class MongooseHelper {
83
83
  const uri = this.getMongoUri();
84
84
  const options = this.getConnectionOptions();
85
85
 
86
- console.log(`[MongooseHelper] Connecting to MongoDB...`);
86
+ if (!process.env.TUI_MODE) console.log(`[MongooseHelper] Connecting to MongoDB...`);
87
87
 
88
88
  // Clear any existing connection
89
89
  if (mongoose.connection.readyState !== 0) {
@@ -94,7 +94,7 @@ class MongooseHelper {
94
94
 
95
95
  this.isConnected = true;
96
96
 
97
- console.log(`[MongooseHelper] ✅ Connected to MongoDB`);
97
+ if (!process.env.TUI_MODE) console.log(`[MongooseHelper] ✅ Connected to MongoDB`);
98
98
 
99
99
  // Setup connection error handling
100
100
  mongoose.connection.on('error', (error) => {
@@ -104,13 +104,13 @@ class MongooseHelper {
104
104
  });
105
105
 
106
106
  mongoose.connection.on('disconnected', () => {
107
- console.log('[MongooseHelper] Disconnected from MongoDB');
107
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] Disconnected from MongoDB');
108
108
  this.isConnected = false;
109
109
  this.connectionPromise = null;
110
110
  });
111
111
 
112
112
  mongoose.connection.on('reconnected', () => {
113
- console.log('[MongooseHelper] Reconnected to MongoDB');
113
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] Reconnected to MongoDB');
114
114
  this.isConnected = true;
115
115
  });
116
116
 
@@ -136,7 +136,7 @@ class MongooseHelper {
136
136
  if (this.connectionCount <= 0) {
137
137
  try {
138
138
  await mongoose.disconnect();
139
- console.log('[MongooseHelper] ✅ Disconnected from MongoDB');
139
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] ✅ Disconnected from MongoDB');
140
140
  } catch (error) {
141
141
  console.error('[MongooseHelper] Disconnect error:', error);
142
142
  } finally {
@@ -155,7 +155,7 @@ class MongooseHelper {
155
155
  try {
156
156
  if (mongoose.connection.readyState !== 0) {
157
157
  await mongoose.disconnect();
158
- console.log('[MongooseHelper] ✅ Force disconnected from MongoDB');
158
+ if (!process.env.TUI_MODE) console.log('[MongooseHelper] ✅ Force disconnected from MongoDB');
159
159
  }
160
160
  } catch (error) {
161
161
  console.error('[MongooseHelper] Force disconnect error:', error);
@@ -55,13 +55,13 @@ class ScriptBase {
55
55
  });
56
56
 
57
57
  try {
58
- console.log(`[${this.name}] Starting script execution...`);
58
+ if (!process.env.TUI_MODE) console.log(`[${this.name}] Starting script execution...`);
59
59
 
60
60
  const executionPromise = this._executeWithConnection();
61
61
  const result = await Promise.race([executionPromise, timeoutPromise]);
62
62
 
63
63
  const duration = Date.now() - this.startTime;
64
- console.log(`[${this.name}] ✅ Completed in ${duration}ms`);
64
+ if (!process.env.TUI_MODE) console.log(`[${this.name}] ✅ Completed in ${duration}ms`);
65
65
 
66
66
  return result;
67
67
  } catch (error) {
@@ -70,4 +70,74 @@ const requireAdmin = (req, res, next) => {
70
70
  next();
71
71
  };
72
72
 
73
- module.exports = { authenticate, basicAuth, requireAdmin };
73
+ // Admin session authentication middleware - checks session for authenticated admin user
74
+ const adminSessionAuth = (req, res, next) => {
75
+ // Check if session exists and user is authenticated
76
+ if (!req.session || !req.session.authenticated) {
77
+ // Store the originally requested URL for redirect after login
78
+ req.session = req.session || {};
79
+ req.session.returnTo = req.originalUrl;
80
+
81
+ // For API routes, return JSON error
82
+ if (req.xhr || req.headers.accept?.includes('application/json')) {
83
+ return res.status(401).json({
84
+ error: "Authentication required",
85
+ redirectTo: `${req.adminPath || '/admin'}/login`
86
+ });
87
+ }
88
+
89
+ // For web routes, redirect to login page
90
+ return res.redirect(`${req.adminPath || '/admin'}/login`);
91
+ }
92
+
93
+ // Verify session is still valid (check login time)
94
+ const loginTime = new Date(req.session.loginTime);
95
+ const now = new Date();
96
+ const sessionAge = (now - loginTime) / (1000 * 60 * 60); // hours
97
+
98
+ // Session expires after 24 hours
99
+ if (sessionAge > 24) {
100
+ req.session.destroy((err) => {
101
+ if (err) console.error('Error destroying expired session:', err);
102
+ });
103
+
104
+ if (req.xhr || req.headers.accept?.includes('application/json')) {
105
+ return res.status(401).json({
106
+ error: "Session expired",
107
+ redirectTo: `${req.adminPath || '/admin'}/login`
108
+ });
109
+ }
110
+
111
+ return res.redirect(`${req.adminPath || '/admin'}/login?error=Session expired`);
112
+ }
113
+
114
+ // Attach user info to request for consistency with other auth middleware
115
+ req.user = {
116
+ authenticated: true,
117
+ authType: req.session.authType,
118
+ role: req.session.role
119
+ };
120
+
121
+ if (req.session.authType === 'iam') {
122
+ req.user.id = req.session.userId;
123
+ req.user.email = req.session.email;
124
+ req.user.name = req.session.name;
125
+ } else {
126
+ req.user.username = req.session.username;
127
+ }
128
+
129
+ next();
130
+ };
131
+
132
+ // Admin authentication middleware that supports both session and basic auth
133
+ const adminAuth = (req, res, next) => {
134
+ // First try session authentication
135
+ if (req.session && req.session.authenticated) {
136
+ return adminSessionAuth(req, res, next);
137
+ }
138
+
139
+ // Fallback to basic auth for backward compatibility
140
+ return basicAuth(req, res, next);
141
+ };
142
+
143
+ module.exports = { authenticate, basicAuth, requireAdmin, adminSessionAuth, adminAuth };
@@ -56,7 +56,69 @@ function requireRight(requiredRight, options = {}) {
56
56
  };
57
57
  }
58
58
 
59
+ /**
60
+ * Middleware for module-level access control in admin panel
61
+ * Checks specific permissions for admin modules like audit, users, etc.
62
+ */
63
+ function requireModuleAccess(moduleId, action = 'read') {
64
+ return async (req, res, next) => {
65
+ try {
66
+ // Check for basic auth superadmin bypass
67
+ if (isBasicAuthSuperAdmin(req)) {
68
+ return next();
69
+ }
70
+
71
+ // Get user ID from session
72
+ const userId = req.session?.authData?.userId;
73
+ if (!userId) {
74
+ return res.redirect(`${req.adminPath || '/admin'}/login`);
75
+ }
76
+
77
+ // Check RBAC permission for specific module
78
+ const hasAccess = await rbacService.checkRight({
79
+ userId,
80
+ orgId: null, // Global admin permissions
81
+ right: `admin_panel__${moduleId}:${action}`
82
+ });
83
+
84
+ if (!hasAccess.allowed) {
85
+ // For API routes, return JSON error
86
+ if (req.path.startsWith('/api/')) {
87
+ return res.status(403).json({
88
+ error: 'Access denied',
89
+ reason: hasAccess.reason,
90
+ required: `admin_panel__${moduleId}:${action}`,
91
+ moduleId,
92
+ action
93
+ });
94
+ }
95
+
96
+ // For page routes, render 403 page
97
+ return res.status(403).render('admin-403', {
98
+ moduleId,
99
+ action,
100
+ required: `admin_panel__${moduleId}:${action}`,
101
+ reason: hasAccess.reason,
102
+ user: req.session.authData,
103
+ adminPath: req.adminPath || '/admin'
104
+ });
105
+ }
106
+
107
+ next();
108
+ } catch (error) {
109
+ console.error('Module access check error:', error);
110
+
111
+ if (req.path.startsWith('/api/')) {
112
+ return res.status(500).json({ error: 'Access check failed' });
113
+ } else {
114
+ return res.status(500).send('Access check failed');
115
+ }
116
+ }
117
+ };
118
+ }
119
+
59
120
  module.exports = {
60
121
  requireRight,
122
+ requireModuleAccess,
61
123
  isBasicAuthSuperAdmin,
62
124
  };