@intranefr/superbackend 1.4.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 (188) hide show
  1. package/.commiat +4 -0
  2. package/.env.example +47 -0
  3. package/README.md +110 -0
  4. package/index.js +94 -0
  5. package/package.json +67 -0
  6. package/public/css/styles.css +139 -0
  7. package/public/js/animations.js +41 -0
  8. package/sdk/error-tracking/browser/package.json +16 -0
  9. package/sdk/error-tracking/browser/src/core.js +270 -0
  10. package/sdk/error-tracking/browser/src/embed.js +18 -0
  11. package/sdk/error-tracking/browser/src/index.js +1 -0
  12. package/server.js +5 -0
  13. package/src/admin/endpointRegistry.js +300 -0
  14. package/src/controllers/admin.controller.js +321 -0
  15. package/src/controllers/adminAssets.controller.js +530 -0
  16. package/src/controllers/adminAssetsStorage.controller.js +260 -0
  17. package/src/controllers/adminEjsVirtual.controller.js +354 -0
  18. package/src/controllers/adminFeatureFlags.controller.js +155 -0
  19. package/src/controllers/adminHeadless.controller.js +1071 -0
  20. package/src/controllers/adminI18n.controller.js +604 -0
  21. package/src/controllers/adminJsonConfigs.controller.js +97 -0
  22. package/src/controllers/adminLlm.controller.js +273 -0
  23. package/src/controllers/adminMigration.controller.js +257 -0
  24. package/src/controllers/adminSeoConfig.controller.js +515 -0
  25. package/src/controllers/adminStats.controller.js +121 -0
  26. package/src/controllers/adminUploadNamespaces.controller.js +208 -0
  27. package/src/controllers/assets.controller.js +248 -0
  28. package/src/controllers/auth.controller.js +93 -0
  29. package/src/controllers/billing.controller.js +223 -0
  30. package/src/controllers/featureFlags.controller.js +35 -0
  31. package/src/controllers/forms.controller.js +217 -0
  32. package/src/controllers/globalSettings.controller.js +252 -0
  33. package/src/controllers/headlessCrud.controller.js +126 -0
  34. package/src/controllers/i18n.controller.js +12 -0
  35. package/src/controllers/invite.controller.js +249 -0
  36. package/src/controllers/jsonConfigs.controller.js +19 -0
  37. package/src/controllers/metrics.controller.js +149 -0
  38. package/src/controllers/notificationAdmin.controller.js +264 -0
  39. package/src/controllers/notifications.controller.js +131 -0
  40. package/src/controllers/org.controller.js +357 -0
  41. package/src/controllers/orgAdmin.controller.js +491 -0
  42. package/src/controllers/stripeAdmin.controller.js +410 -0
  43. package/src/controllers/user.controller.js +361 -0
  44. package/src/controllers/userAdmin.controller.js +277 -0
  45. package/src/controllers/waitingList.controller.js +167 -0
  46. package/src/controllers/webhook.controller.js +200 -0
  47. package/src/middleware/auth.js +66 -0
  48. package/src/middleware/errorCapture.js +170 -0
  49. package/src/middleware/headlessApiTokenAuth.js +57 -0
  50. package/src/middleware/org.js +108 -0
  51. package/src/middleware.js +901 -0
  52. package/src/models/ActionEvent.js +31 -0
  53. package/src/models/ActivityLog.js +41 -0
  54. package/src/models/Asset.js +84 -0
  55. package/src/models/AuditEvent.js +93 -0
  56. package/src/models/EmailLog.js +28 -0
  57. package/src/models/ErrorAggregate.js +72 -0
  58. package/src/models/FormSubmission.js +41 -0
  59. package/src/models/GlobalSetting.js +38 -0
  60. package/src/models/HeadlessApiToken.js +24 -0
  61. package/src/models/HeadlessModelDefinition.js +41 -0
  62. package/src/models/I18nEntry.js +77 -0
  63. package/src/models/I18nLocale.js +33 -0
  64. package/src/models/Invite.js +70 -0
  65. package/src/models/JsonConfig.js +46 -0
  66. package/src/models/Notification.js +60 -0
  67. package/src/models/Organization.js +57 -0
  68. package/src/models/OrganizationMember.js +43 -0
  69. package/src/models/StripeCatalogItem.js +77 -0
  70. package/src/models/StripeWebhookEvent.js +57 -0
  71. package/src/models/User.js +89 -0
  72. package/src/models/VirtualEjsFile.js +60 -0
  73. package/src/models/VirtualEjsFileVersion.js +43 -0
  74. package/src/models/VirtualEjsGroupChange.js +32 -0
  75. package/src/models/WaitingList.js +41 -0
  76. package/src/models/Webhook.js +63 -0
  77. package/src/models/Workflow.js +29 -0
  78. package/src/models/WorkflowExecution.js +12 -0
  79. package/src/routes/admin.routes.js +26 -0
  80. package/src/routes/adminAssets.routes.js +28 -0
  81. package/src/routes/adminAssetsStorage.routes.js +13 -0
  82. package/src/routes/adminAudit.routes.js +196 -0
  83. package/src/routes/adminEjsVirtual.routes.js +17 -0
  84. package/src/routes/adminErrors.routes.js +164 -0
  85. package/src/routes/adminFeatureFlags.routes.js +12 -0
  86. package/src/routes/adminHeadless.routes.js +38 -0
  87. package/src/routes/adminI18n.routes.js +22 -0
  88. package/src/routes/adminJsonConfigs.routes.js +15 -0
  89. package/src/routes/adminLlm.routes.js +12 -0
  90. package/src/routes/adminMigration.routes.js +81 -0
  91. package/src/routes/adminSeoConfig.routes.js +20 -0
  92. package/src/routes/adminUploadNamespaces.routes.js +13 -0
  93. package/src/routes/assets.routes.js +21 -0
  94. package/src/routes/auth.routes.js +12 -0
  95. package/src/routes/billing.routes.js +11 -0
  96. package/src/routes/errorTracking.routes.js +31 -0
  97. package/src/routes/featureFlags.routes.js +9 -0
  98. package/src/routes/forms.routes.js +9 -0
  99. package/src/routes/formsAdmin.routes.js +13 -0
  100. package/src/routes/globalSettings.routes.js +18 -0
  101. package/src/routes/headless.routes.js +15 -0
  102. package/src/routes/i18n.routes.js +8 -0
  103. package/src/routes/invite.routes.js +9 -0
  104. package/src/routes/jsonConfigs.routes.js +8 -0
  105. package/src/routes/log.routes.js +111 -0
  106. package/src/routes/metrics.routes.js +9 -0
  107. package/src/routes/notificationAdmin.routes.js +15 -0
  108. package/src/routes/notifications.routes.js +12 -0
  109. package/src/routes/org.routes.js +31 -0
  110. package/src/routes/orgAdmin.routes.js +20 -0
  111. package/src/routes/publicAssets.routes.js +7 -0
  112. package/src/routes/stripeAdmin.routes.js +20 -0
  113. package/src/routes/user.routes.js +22 -0
  114. package/src/routes/userAdmin.routes.js +15 -0
  115. package/src/routes/waitingList.routes.js +13 -0
  116. package/src/routes/waitingListAdmin.routes.js +9 -0
  117. package/src/routes/webhook.routes.js +32 -0
  118. package/src/routes/workflowWebhook.routes.js +54 -0
  119. package/src/routes/workflows.routes.js +110 -0
  120. package/src/services/assets.service.js +110 -0
  121. package/src/services/audit.service.js +62 -0
  122. package/src/services/auditLogger.js +165 -0
  123. package/src/services/ejsVirtual.service.js +614 -0
  124. package/src/services/email.service.js +351 -0
  125. package/src/services/errorLogger.js +221 -0
  126. package/src/services/featureFlags.service.js +202 -0
  127. package/src/services/forms.service.js +214 -0
  128. package/src/services/globalSettings.service.js +49 -0
  129. package/src/services/headlessApiTokens.service.js +158 -0
  130. package/src/services/headlessCrypto.service.js +31 -0
  131. package/src/services/headlessModels.service.js +356 -0
  132. package/src/services/i18n.service.js +314 -0
  133. package/src/services/i18nInferredKeys.service.js +337 -0
  134. package/src/services/jsonConfigs.service.js +392 -0
  135. package/src/services/llm.service.js +749 -0
  136. package/src/services/migration.service.js +581 -0
  137. package/src/services/migrationAssets/fsLocal.js +58 -0
  138. package/src/services/migrationAssets/index.js +134 -0
  139. package/src/services/migrationAssets/s3.js +75 -0
  140. package/src/services/migrationAssets/sftp.js +92 -0
  141. package/src/services/notification.service.js +212 -0
  142. package/src/services/objectStorage.service.js +514 -0
  143. package/src/services/seoConfig.service.js +402 -0
  144. package/src/services/storage.js +150 -0
  145. package/src/services/stripe.service.js +185 -0
  146. package/src/services/stripeHelper.service.js +264 -0
  147. package/src/services/uploadNamespaces.service.js +326 -0
  148. package/src/services/webhook.service.js +157 -0
  149. package/src/services/workflow.service.js +271 -0
  150. package/src/utils/asyncHandler.js +5 -0
  151. package/src/utils/encryption.js +80 -0
  152. package/src/utils/jwt.js +40 -0
  153. package/src/utils/orgRoles.js +156 -0
  154. package/src/utils/validation.js +26 -0
  155. package/src/utils/webhookRetry.js +93 -0
  156. package/views/admin-assets.ejs +444 -0
  157. package/views/admin-audit.ejs +283 -0
  158. package/views/admin-coolify-deploy.ejs +207 -0
  159. package/views/admin-dashboard-home.ejs +291 -0
  160. package/views/admin-dashboard.ejs +397 -0
  161. package/views/admin-ejs-virtual.ejs +280 -0
  162. package/views/admin-errors.ejs +368 -0
  163. package/views/admin-feature-flags.ejs +390 -0
  164. package/views/admin-forms.ejs +526 -0
  165. package/views/admin-global-settings.ejs +436 -0
  166. package/views/admin-headless.ejs +2020 -0
  167. package/views/admin-i18n-locales.ejs +221 -0
  168. package/views/admin-i18n.ejs +728 -0
  169. package/views/admin-json-configs.ejs +410 -0
  170. package/views/admin-llm.ejs +884 -0
  171. package/views/admin-metrics.ejs +274 -0
  172. package/views/admin-migration.ejs +814 -0
  173. package/views/admin-notifications.ejs +430 -0
  174. package/views/admin-organizations.ejs +984 -0
  175. package/views/admin-seo-config.ejs +673 -0
  176. package/views/admin-stripe-pricing.ejs +558 -0
  177. package/views/admin-test.ejs +342 -0
  178. package/views/admin-users.ejs +452 -0
  179. package/views/admin-waiting-list.ejs +547 -0
  180. package/views/admin-webhooks.ejs +329 -0
  181. package/views/admin-workflows.ejs +310 -0
  182. package/views/partials/admin-assets-script.ejs +2022 -0
  183. package/views/partials/admin-test-sidebar.ejs +14 -0
  184. package/views/partials/dashboard/nav-items.ejs +66 -0
  185. package/views/partials/dashboard/palette.ejs +63 -0
  186. package/views/partials/dashboard/sidebar.ejs +21 -0
  187. package/views/partials/dashboard/tab-bar.ejs +26 -0
  188. package/views/partials/footer.ejs +3 -0
@@ -0,0 +1,321 @@
1
+ const User = require('../models/User');
2
+ const StripeWebhookEvent = require('../models/StripeWebhookEvent');
3
+ const asyncHandler = require('../utils/asyncHandler');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
7
+ const { generateAccessToken, generateRefreshToken } = require('../utils/jwt');
8
+ const { retryFailedWebhooks, processWebhookEvent } = require('../utils/webhookRetry');
9
+
10
+ // Get all users
11
+ const getUsers = asyncHandler(async (req, res) => {
12
+ const users = await User.find()
13
+ .select('-passwordHash')
14
+ .sort({ createdAt: -1 })
15
+ .limit(100);
16
+
17
+ res.json(users.map(u => u.toJSON()));
18
+ });
19
+
20
+ // Get single user
21
+ const getUser = asyncHandler(async (req, res) => {
22
+ const user = await User.findById(req.params.id);
23
+
24
+ if (!user) {
25
+ return res.status(404).json({ error: 'User not found' });
26
+ }
27
+
28
+ res.json({ user: user.toJSON() });
29
+ });
30
+
31
+ // Update user subscription
32
+ const updateUserSubscription = asyncHandler(async (req, res) => {
33
+ const { subscriptionStatus } = req.body;
34
+ const user = await User.findById(req.params.id);
35
+
36
+ if (!user) {
37
+ return res.status(404).json({ error: 'User not found' });
38
+ }
39
+
40
+ if (subscriptionStatus) {
41
+ user.subscriptionStatus = subscriptionStatus;
42
+ }
43
+
44
+ await user.save();
45
+
46
+ res.json({ user: user.toJSON() });
47
+ });
48
+
49
+ // Update user password
50
+ const updateUserPassword = asyncHandler(async (req, res) => {
51
+ const { passwordHash } = req.body;
52
+ const bcrypt = require('bcryptjs');
53
+ const user = await User.findById(req.params.id);
54
+
55
+ if (!user) {
56
+ return res.status(404).json({ error: 'User not found' });
57
+ }
58
+
59
+ if (!passwordHash) {
60
+ return res.status(400).json({ error: 'Password is required' });
61
+ }
62
+
63
+ // Detect if the provided password is already a bcrypt hash
64
+ const isBcryptHash = /^\$2[aby]\$\d{2}\$/.test(passwordHash);
65
+ if (isBcryptHash) {
66
+ return res.status(400).json({
67
+ error: 'Invalid password format',
68
+ message: 'The password appears to be already hashed. Please provide a plaintext password instead.',
69
+ hint: 'Bcrypt hashes start with $2a$, $2b$, or $2y$ followed by a cost parameter (e.g., $2a$10$...)'
70
+ });
71
+ }
72
+
73
+ try {
74
+ // Set plaintext password and let pre-save hook hash it
75
+ user.passwordHash = passwordHash;
76
+ await user.save();
77
+
78
+ } catch (error) {
79
+ console.error('Save error:', error);
80
+ throw error;
81
+ }
82
+
83
+ res.json({ user: user.toJSON() });
84
+ });
85
+
86
+ // Reconcile user subscription
87
+ const reconcileUser = asyncHandler(async (req, res) => {
88
+ const user = await User.findById(req.params.id);
89
+
90
+ if (!user) {
91
+ return res.status(404).json({ error: 'User not found' });
92
+ }
93
+
94
+ if (!user.stripeCustomerId) {
95
+ return res.json({ status: 'success', message: 'No Stripe customer found' });
96
+ }
97
+
98
+ const subscriptions = await stripe.subscriptions.list({
99
+ customer: user.stripeCustomerId,
100
+ limit: 1
101
+ });
102
+
103
+ if (subscriptions.data.length > 0) {
104
+ const subscription = subscriptions.data[0];
105
+ user.stripeSubscriptionId = subscription.id;
106
+ user.subscriptionStatus = subscription.status === 'active' ? 'active' : subscription.status;
107
+ await user.save();
108
+ } else {
109
+ // No active subscription found. Check for successful one-off (payment) checkouts
110
+ const sessions = await stripe.checkout.sessions.list({
111
+ customer: user.stripeCustomerId,
112
+ limit: 10
113
+ });
114
+
115
+ const lifetimeSession = sessions.data.find((session) => {
116
+ const mode = session.metadata?.billingMode || session.mode;
117
+ return (
118
+ mode === 'payment' &&
119
+ session.payment_status === 'paid'
120
+ );
121
+ });
122
+
123
+ if (lifetimeSession) {
124
+ user.subscriptionStatus = 'active';
125
+ } else {
126
+ user.subscriptionStatus = 'none';
127
+ }
128
+
129
+ await user.save();
130
+ }
131
+
132
+ res.json({ status: 'success', user: user.toJSON() });
133
+ });
134
+
135
+ // Generate JWT for testing
136
+ const generateToken = asyncHandler(async (req, res) => {
137
+ const { userId } = req.body;
138
+
139
+ if (!userId) {
140
+ return res.status(400).json({ error: 'userId is required' });
141
+ }
142
+
143
+ const user = await User.findById(userId);
144
+ if (!user) {
145
+ return res.status(404).json({ error: 'User not found' });
146
+ }
147
+
148
+ const token = generateAccessToken(user._id);
149
+ const refreshToken = generateRefreshToken(user._id);
150
+
151
+ res.json({ token, refreshToken, userId: user._id });
152
+ });
153
+
154
+ // Get all webhook events
155
+ const getWebhookEvents = asyncHandler(async (req, res) => {
156
+ const { limit = 50, offset = 0, eventType, status } = req.query;
157
+
158
+ const filter = {};
159
+ if (eventType) filter.eventType = eventType;
160
+ if (status) filter.status = status;
161
+
162
+ const events = await StripeWebhookEvent.find(filter)
163
+ .sort({ receivedAt: -1 })
164
+ .skip(parseInt(offset))
165
+ .limit(parseInt(limit));
166
+
167
+ const total = await StripeWebhookEvent.countDocuments(filter);
168
+
169
+ res.json({
170
+ events,
171
+ pagination: {
172
+ total,
173
+ limit: parseInt(limit),
174
+ offset: parseInt(offset)
175
+ }
176
+ });
177
+ });
178
+
179
+ // Get single webhook event
180
+ const getWebhookEvent = asyncHandler(async (req, res) => {
181
+ const event = await StripeWebhookEvent.findOne({ stripeEventId: req.params.id });
182
+
183
+ if (!event) {
184
+ return res.status(404).json({ error: 'Webhook event not found' });
185
+ }
186
+
187
+ res.json({ event });
188
+ });
189
+
190
+ // Retry failed webhook events
191
+ const retryFailedWebhookEvents = asyncHandler(async (req, res) => {
192
+ const { limit = 10, maxRetries = 3 } = req.body;
193
+
194
+ const results = await retryFailedWebhooks({ limit, maxRetries });
195
+
196
+ res.json({
197
+ status: 'success',
198
+ results
199
+ });
200
+ });
201
+
202
+ // Retry single webhook event
203
+ const retrySingleWebhookEvent = asyncHandler(async (req, res) => {
204
+ const event = await StripeWebhookEvent.findOne({ stripeEventId: req.params.id });
205
+
206
+ if (!event) {
207
+ return res.status(404).json({ error: 'Webhook event not found' });
208
+ }
209
+
210
+ if (event.status === 'processed') {
211
+ return res.status(400).json({ error: 'Event already processed' });
212
+ }
213
+
214
+ try {
215
+ await processWebhookEvent(event);
216
+
217
+ event.status = 'processed';
218
+ event.processedAt = new Date();
219
+ await event.save();
220
+
221
+ res.json({
222
+ status: 'success',
223
+ message: 'Event processed successfully',
224
+ event
225
+ });
226
+ } catch (err) {
227
+ event.retryCount++;
228
+ event.processingErrors.push({
229
+ message: err.message,
230
+ timestamp: new Date()
231
+ });
232
+ await event.save();
233
+
234
+ res.status(500).json({
235
+ status: 'error',
236
+ message: err.message,
237
+ event
238
+ });
239
+ }
240
+ });
241
+
242
+ // Get webhook statistics
243
+ const getWebhookStats = asyncHandler(async (req, res) => {
244
+ const stats = await StripeWebhookEvent.aggregate([
245
+ {
246
+ $group: {
247
+ _id: '$status',
248
+ count: { $sum: 1 }
249
+ }
250
+ }
251
+ ]);
252
+
253
+ const eventTypeStats = await StripeWebhookEvent.aggregate([
254
+ {
255
+ $group: {
256
+ _id: '$eventType',
257
+ count: { $sum: 1 },
258
+ failedCount: {
259
+ $sum: { $cond: [{ $eq: ['$status', 'failed'] }, 1, 0] }
260
+ }
261
+ }
262
+ },
263
+ { $sort: { count: -1 } }
264
+ ]);
265
+
266
+ const recentFailures = await StripeWebhookEvent.find({ status: 'failed' })
267
+ .sort({ receivedAt: -1 })
268
+ .limit(10)
269
+ .select('stripeEventId eventType receivedAt retryCount processingErrors');
270
+
271
+ res.json({
272
+ statusStats: stats,
273
+ eventTypeStats,
274
+ recentFailures
275
+ });
276
+ });
277
+
278
+ // Coolify Headless Deploy provisioning
279
+ const provisionCoolifyDeploy = asyncHandler(async (req, res) => {
280
+ try {
281
+ const { overwrite } = req.body;
282
+ const managePath = path.join(process.cwd(), "manage.sh");
283
+ const exists = fs.existsSync(managePath);
284
+
285
+ if (exists && !overwrite) {
286
+ return res.json({
287
+ success: false,
288
+ requiresConfirmation: true,
289
+ message: "Script already exists. Do you want to overwrite it?",
290
+ });
291
+ }
292
+
293
+ // In ref-saasbackend, manage.sh already exists in the root of the repository
294
+ // If it didn't, we would write it here. For this case, we'll just success.
295
+ res.json({
296
+ success: true,
297
+ message: exists
298
+ ? "Coolify Headless Deploy script (manage.sh) was already there."
299
+ : "Coolify Headless Deploy script (manage.sh) is ready in the root directory.",
300
+ path: managePath,
301
+ });
302
+ } catch (error) {
303
+ console.error("❌ Error provisioning script:", error);
304
+ res.status(500).json({ error: error.message });
305
+ }
306
+ });
307
+
308
+ module.exports = {
309
+ getUsers,
310
+ getUser,
311
+ updateUserSubscription,
312
+ updateUserPassword,
313
+ reconcileUser,
314
+ generateToken,
315
+ getWebhookEvents,
316
+ getWebhookEvent,
317
+ retryFailedWebhookEvents,
318
+ retrySingleWebhookEvent,
319
+ getWebhookStats,
320
+ provisionCoolifyDeploy
321
+ };