@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,357 @@
1
+ const Organization = require('../models/Organization');
2
+ const OrganizationMember = require('../models/OrganizationMember');
3
+ const User = require('../models/User');
4
+ const emailService = require('../services/email.service');
5
+ const { isValidOrgRole, getAllowedOrgRoles, getDefaultOrgRole } = require('../utils/orgRoles');
6
+
7
+ const generateSlug = (name) => {
8
+ return name
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9]+/g, '-')
11
+ .replace(/^-|-$/g, '')
12
+ .substring(0, 50) + '-' + Date.now().toString(36);
13
+ };
14
+
15
+ exports.listOrgs = async (req, res) => {
16
+ try {
17
+ const memberships = await OrganizationMember.find({
18
+ userId: req.user._id,
19
+ status: 'active'
20
+ }).populate('orgId');
21
+
22
+ const orgs = memberships
23
+ .filter(m => m.orgId && m.orgId.status === 'active')
24
+ .map(m => ({
25
+ ...m.orgId.toJSON(),
26
+ myRole: m.role
27
+ }));
28
+
29
+ res.json({ orgs });
30
+ } catch (error) {
31
+ console.error('Error listing orgs:', error);
32
+ res.status(500).json({ error: 'Failed to list organizations' });
33
+ }
34
+ };
35
+
36
+ exports.listPublicOrgs = async (req, res) => {
37
+ try {
38
+ const orgs = await Organization.find({ status: 'active' })
39
+ .select('name slug description allowPublicJoin createdAt')
40
+ .sort({ createdAt: -1 })
41
+ .lean();
42
+
43
+ res.json({
44
+ orgs: orgs.map((o) => ({
45
+ _id: o._id,
46
+ name: o.name,
47
+ slug: o.slug,
48
+ description: o.description,
49
+ allowPublicJoin: o.allowPublicJoin,
50
+ createdAt: o.createdAt
51
+ }))
52
+ });
53
+ } catch (error) {
54
+ console.error('Error listing public orgs:', error);
55
+ res.status(500).json({ error: 'Failed to list organizations' });
56
+ }
57
+ };
58
+
59
+ exports.createOrg = async (req, res) => {
60
+ try {
61
+ const { name, description, slug: customSlug, allowPublicJoin } = req.body;
62
+
63
+ if (!name || name.trim().length === 0) {
64
+ return res.status(400).json({ error: 'Name is required' });
65
+ }
66
+
67
+ const slug = customSlug || generateSlug(name);
68
+
69
+ const existingOrg = await Organization.findOne({ slug });
70
+ if (existingOrg) {
71
+ return res.status(409).json({ error: 'An organization with this slug already exists' });
72
+ }
73
+
74
+ const org = await Organization.create({
75
+ name: name.trim(),
76
+ slug,
77
+ description: description?.trim(),
78
+ ownerUserId: req.user._id,
79
+ allowPublicJoin: allowPublicJoin || false
80
+ });
81
+
82
+ await OrganizationMember.create({
83
+ orgId: org._id,
84
+ userId: req.user._id,
85
+ role: 'owner',
86
+ addedByUserId: req.user._id
87
+ });
88
+
89
+ res.status(201).json({
90
+ message: 'Organization created successfully',
91
+ org: { ...org.toJSON(), myRole: 'owner' }
92
+ });
93
+ } catch (error) {
94
+ console.error('Error creating org:', error);
95
+ res.status(500).json({ error: 'Failed to create organization' });
96
+ }
97
+ };
98
+
99
+ exports.getOrg = async (req, res) => {
100
+ try {
101
+ res.json({ org: { ...req.org.toJSON(), myRole: req.orgMember?.role } });
102
+ } catch (error) {
103
+ console.error('Error getting org:', error);
104
+ res.status(500).json({ error: 'Failed to get organization' });
105
+ }
106
+ };
107
+
108
+ exports.updateOrg = async (req, res) => {
109
+ try {
110
+ const { name, description, allowPublicJoin } = req.body;
111
+ const updates = {};
112
+
113
+ if (name !== undefined) updates.name = name.trim();
114
+ if (description !== undefined) updates.description = description?.trim();
115
+ if (allowPublicJoin !== undefined) updates.allowPublicJoin = allowPublicJoin;
116
+
117
+ const org = await Organization.findByIdAndUpdate(
118
+ req.org._id,
119
+ updates,
120
+ { new: true, runValidators: true }
121
+ );
122
+
123
+ res.json({
124
+ message: 'Organization updated successfully',
125
+ org: { ...org.toJSON(), myRole: req.orgMember.role }
126
+ });
127
+ } catch (error) {
128
+ console.error('Error updating org:', error);
129
+ res.status(500).json({ error: 'Failed to update organization' });
130
+ }
131
+ };
132
+
133
+ exports.deleteOrg = async (req, res) => {
134
+ try {
135
+ await Organization.findByIdAndUpdate(req.org._id, { status: 'disabled' });
136
+ res.json({ message: 'Organization disabled successfully' });
137
+ } catch (error) {
138
+ console.error('Error deleting org:', error);
139
+ res.status(500).json({ error: 'Failed to disable organization' });
140
+ }
141
+ };
142
+
143
+ exports.listMembers = async (req, res) => {
144
+ try {
145
+ const members = await OrganizationMember.find({
146
+ orgId: req.org._id,
147
+ status: 'active'
148
+ }).populate('userId', 'email name');
149
+
150
+ res.json({
151
+ members: members.map(m => ({
152
+ _id: m._id,
153
+ userId: m.userId._id,
154
+ email: m.userId.email,
155
+ name: m.userId.name,
156
+ role: m.role,
157
+ createdAt: m.createdAt
158
+ }))
159
+ });
160
+ } catch (error) {
161
+ console.error('Error listing members:', error);
162
+ res.status(500).json({ error: 'Failed to list members' });
163
+ }
164
+ };
165
+
166
+ exports.addMember = async (req, res) => {
167
+ try {
168
+ const defaultRole = await getDefaultOrgRole();
169
+ const { email, role = defaultRole, sendNotification = false } = req.body;
170
+
171
+ if (!email) {
172
+ return res.status(400).json({ error: 'Email is required' });
173
+ }
174
+
175
+ if (!(await isValidOrgRole(role))) {
176
+ const allowed = await getAllowedOrgRoles();
177
+ return res.status(400).json({ error: 'Invalid role', allowedRoles: allowed });
178
+ }
179
+
180
+ const user = await User.findOne({ email: email.toLowerCase() });
181
+ if (!user) {
182
+ return res.status(404).json({ error: 'User not found. Use invite instead.' });
183
+ }
184
+
185
+ const existingMember = await OrganizationMember.findOne({
186
+ orgId: req.org._id,
187
+ userId: user._id
188
+ });
189
+
190
+ if (existingMember) {
191
+ if (existingMember.status === 'active') {
192
+ return res.status(409).json({ error: 'User is already a member' });
193
+ }
194
+ existingMember.status = 'active';
195
+ existingMember.role = role;
196
+ existingMember.addedByUserId = req.user._id;
197
+ await existingMember.save();
198
+ } else {
199
+ await OrganizationMember.create({
200
+ orgId: req.org._id,
201
+ userId: user._id,
202
+ role,
203
+ addedByUserId: req.user._id
204
+ });
205
+ }
206
+
207
+ if (sendNotification) {
208
+ try {
209
+ await emailService.sendEmail({
210
+ to: user.email,
211
+ subject: `You've been added to ${req.org.name}`,
212
+ html: `<p>Hello${user.name ? ' ' + user.name : ''},</p>
213
+ <p>You have been added to <strong>${req.org.name}</strong> as a ${role}.</p>
214
+ <p>Log in to access the organization.</p>`
215
+ });
216
+ } catch (emailError) {
217
+ console.error('Failed to send notification email:', emailError);
218
+ }
219
+ }
220
+
221
+ res.status(201).json({
222
+ message: 'Member added successfully',
223
+ member: { userId: user._id, email: user.email, name: user.name, role }
224
+ });
225
+ } catch (error) {
226
+ console.error('Error adding member:', error);
227
+ res.status(500).json({ error: 'Failed to add member' });
228
+ }
229
+ };
230
+
231
+ exports.updateMemberRole = async (req, res) => {
232
+ try {
233
+ const { userId } = req.params;
234
+ const { role } = req.body;
235
+
236
+ if (!(await isValidOrgRole(role))) {
237
+ const allowed = await getAllowedOrgRoles();
238
+ return res.status(400).json({ error: 'Invalid role', allowedRoles: allowed });
239
+ }
240
+
241
+ const member = await OrganizationMember.findOne({
242
+ orgId: req.org._id,
243
+ userId,
244
+ status: 'active'
245
+ });
246
+
247
+ if (!member) {
248
+ return res.status(404).json({ error: 'Member not found' });
249
+ }
250
+
251
+ if (member.role === 'owner') {
252
+ return res.status(403).json({ error: 'Cannot change owner role' });
253
+ }
254
+
255
+ member.role = role;
256
+ await member.save();
257
+
258
+ res.json({ message: 'Role updated successfully', member });
259
+ } catch (error) {
260
+ console.error('Error updating member role:', error);
261
+ res.status(500).json({ error: 'Failed to update role' });
262
+ }
263
+ };
264
+
265
+ exports.removeMember = async (req, res) => {
266
+ try {
267
+ const { userId } = req.params;
268
+
269
+ const member = await OrganizationMember.findOne({
270
+ orgId: req.org._id,
271
+ userId,
272
+ status: 'active'
273
+ });
274
+
275
+ if (!member) {
276
+ return res.status(404).json({ error: 'Member not found' });
277
+ }
278
+
279
+ if (member.role === 'owner') {
280
+ return res.status(403).json({ error: 'Cannot remove owner' });
281
+ }
282
+
283
+ if (userId === req.user._id.toString()) {
284
+ return res.status(400).json({ error: 'Cannot remove yourself' });
285
+ }
286
+
287
+ member.status = 'removed';
288
+ await member.save();
289
+
290
+ res.json({ message: 'Member removed successfully' });
291
+ } catch (error) {
292
+ console.error('Error removing member:', error);
293
+ res.status(500).json({ error: 'Failed to remove member' });
294
+ }
295
+ };
296
+
297
+ exports.joinOrg = async (req, res) => {
298
+ try {
299
+ if (!req.org.allowPublicJoin) {
300
+ return res.status(403).json({ error: 'This organization does not allow public join' });
301
+ }
302
+
303
+ const defaultRole = await getDefaultOrgRole();
304
+
305
+ const existingMember = await OrganizationMember.findOne({
306
+ orgId: req.org._id,
307
+ userId: req.user._id
308
+ });
309
+
310
+ if (existingMember) {
311
+ if (existingMember.status === 'active') {
312
+ return res.status(409).json({ error: 'You are already a member' });
313
+ }
314
+ existingMember.status = 'active';
315
+ existingMember.role = defaultRole;
316
+ await existingMember.save();
317
+ } else {
318
+ await OrganizationMember.create({
319
+ orgId: req.org._id,
320
+ userId: req.user._id,
321
+ role: defaultRole
322
+ });
323
+ }
324
+
325
+ res.status(201).json({
326
+ message: 'Successfully joined organization',
327
+ org: { ...req.org.toJSON(), myRole: defaultRole }
328
+ });
329
+ } catch (error) {
330
+ console.error('Error joining org:', error);
331
+ res.status(500).json({ error: 'Failed to join organization' });
332
+ }
333
+ };
334
+
335
+ exports.getOrgPublic = async (req, res) => {
336
+ try {
337
+ const { orgId } = req.params;
338
+ const org = await Organization.findOne({ _id: orgId, status: 'active' });
339
+
340
+ if (!org) {
341
+ return res.status(404).json({ error: 'Organization not found' });
342
+ }
343
+
344
+ res.json({
345
+ org: {
346
+ _id: org._id,
347
+ name: org.name,
348
+ slug: org.slug,
349
+ description: org.description,
350
+ allowPublicJoin: org.allowPublicJoin
351
+ }
352
+ });
353
+ } catch (error) {
354
+ console.error('Error getting public org:', error);
355
+ res.status(500).json({ error: 'Failed to get organization' });
356
+ }
357
+ };