@intranefr/superbackend 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/.env.example +5 -0
  2. package/README.md +11 -0
  3. package/index.js +23 -0
  4. package/package.json +7 -2
  5. package/src/admin/endpointRegistry.js +120 -0
  6. package/src/controllers/admin.controller.js +22 -5
  7. package/src/controllers/adminBlockDefinitions.controller.js +127 -0
  8. package/src/controllers/adminBlockDefinitionsAi.controller.js +54 -0
  9. package/src/controllers/adminCache.controller.js +342 -0
  10. package/src/controllers/adminContextBlockDefinitions.controller.js +141 -0
  11. package/src/controllers/adminCrons.controller.js +388 -0
  12. package/src/controllers/adminDbBrowser.controller.js +124 -0
  13. package/src/controllers/adminEjsVirtual.controller.js +13 -3
  14. package/src/controllers/adminHeadless.controller.js +9 -2
  15. package/src/controllers/adminHealthChecks.controller.js +570 -0
  16. package/src/controllers/adminI18n.controller.js +51 -29
  17. package/src/controllers/adminLlm.controller.js +126 -2
  18. package/src/controllers/adminPages.controller.js +720 -0
  19. package/src/controllers/adminPagesContextBlocksAi.controller.js +54 -0
  20. package/src/controllers/adminProxy.controller.js +113 -0
  21. package/src/controllers/adminRateLimits.controller.js +138 -0
  22. package/src/controllers/adminRbac.controller.js +803 -0
  23. package/src/controllers/adminScripts.controller.js +93 -2
  24. package/src/controllers/adminSeoConfig.controller.js +71 -48
  25. package/src/controllers/blogAdmin.controller.js +279 -0
  26. package/src/controllers/blogAiAdmin.controller.js +224 -0
  27. package/src/controllers/blogAutomationAdmin.controller.js +141 -0
  28. package/src/controllers/blogInternal.controller.js +26 -0
  29. package/src/controllers/blogPublic.controller.js +89 -0
  30. package/src/controllers/fileManager.controller.js +190 -0
  31. package/src/controllers/fileManagerStoragePolicy.controller.js +23 -0
  32. package/src/controllers/healthChecksPublic.controller.js +196 -0
  33. package/src/controllers/metrics.controller.js +64 -4
  34. package/src/controllers/orgAdmin.controller.js +80 -0
  35. package/src/middleware/internalCronAuth.js +29 -0
  36. package/src/middleware/rbac.js +62 -0
  37. package/src/middleware.js +756 -48
  38. package/src/models/BlockDefinition.js +27 -0
  39. package/src/models/BlogAutomationLock.js +14 -0
  40. package/src/models/BlogAutomationRun.js +39 -0
  41. package/src/models/BlogPost.js +42 -0
  42. package/src/models/CacheEntry.js +26 -0
  43. package/src/models/ConsoleEntry.js +32 -0
  44. package/src/models/ConsoleLog.js +23 -0
  45. package/src/models/ContextBlockDefinition.js +33 -0
  46. package/src/models/CronExecution.js +47 -0
  47. package/src/models/CronJob.js +70 -0
  48. package/src/models/ExternalDbConnection.js +49 -0
  49. package/src/models/FileEntry.js +22 -0
  50. package/src/models/HealthAutoHealAttempt.js +57 -0
  51. package/src/models/HealthCheck.js +132 -0
  52. package/src/models/HealthCheckRun.js +51 -0
  53. package/src/models/HealthIncident.js +49 -0
  54. package/src/models/Page.js +95 -0
  55. package/src/models/PageCollection.js +42 -0
  56. package/src/models/ProxyEntry.js +66 -0
  57. package/src/models/RateLimitCounter.js +19 -0
  58. package/src/models/RateLimitMetricBucket.js +20 -0
  59. package/src/models/RbacGrant.js +25 -0
  60. package/src/models/RbacGroup.js +16 -0
  61. package/src/models/RbacGroupMember.js +13 -0
  62. package/src/models/RbacGroupRole.js +13 -0
  63. package/src/models/RbacRole.js +25 -0
  64. package/src/models/RbacUserRole.js +13 -0
  65. package/src/routes/adminBlog.routes.js +21 -0
  66. package/src/routes/adminBlogAi.routes.js +16 -0
  67. package/src/routes/adminBlogAutomation.routes.js +27 -0
  68. package/src/routes/adminCache.routes.js +20 -0
  69. package/src/routes/adminConsoleManager.routes.js +302 -0
  70. package/src/routes/adminCrons.routes.js +25 -0
  71. package/src/routes/adminDbBrowser.routes.js +65 -0
  72. package/src/routes/adminEjsVirtual.routes.js +2 -1
  73. package/src/routes/adminHeadless.routes.js +2 -1
  74. package/src/routes/adminHealthChecks.routes.js +28 -0
  75. package/src/routes/adminI18n.routes.js +4 -3
  76. package/src/routes/adminLlm.routes.js +4 -2
  77. package/src/routes/adminPages.routes.js +55 -0
  78. package/src/routes/adminProxy.routes.js +15 -0
  79. package/src/routes/adminRateLimits.routes.js +17 -0
  80. package/src/routes/adminRbac.routes.js +38 -0
  81. package/src/routes/adminSeoConfig.routes.js +5 -4
  82. package/src/routes/adminUiComponents.routes.js +2 -1
  83. package/src/routes/blogInternal.routes.js +14 -0
  84. package/src/routes/blogPublic.routes.js +9 -0
  85. package/src/routes/fileManager.routes.js +62 -0
  86. package/src/routes/fileManagerStoragePolicy.routes.js +9 -0
  87. package/src/routes/healthChecksPublic.routes.js +9 -0
  88. package/src/routes/log.routes.js +43 -60
  89. package/src/routes/metrics.routes.js +4 -2
  90. package/src/routes/orgAdmin.routes.js +1 -0
  91. package/src/routes/pages.routes.js +123 -0
  92. package/src/routes/proxy.routes.js +46 -0
  93. package/src/routes/rbac.routes.js +47 -0
  94. package/src/routes/webhook.routes.js +2 -1
  95. package/src/routes/workflows.routes.js +4 -0
  96. package/src/services/blockDefinitionsAi.service.js +247 -0
  97. package/src/services/blog.service.js +99 -0
  98. package/src/services/blogAutomation.service.js +978 -0
  99. package/src/services/blogCronsBootstrap.service.js +184 -0
  100. package/src/services/blogPublishing.service.js +58 -0
  101. package/src/services/cacheLayer.service.js +696 -0
  102. package/src/services/consoleManager.service.js +700 -0
  103. package/src/services/consoleOverride.service.js +6 -1
  104. package/src/services/cronScheduler.service.js +350 -0
  105. package/src/services/dbBrowser.service.js +536 -0
  106. package/src/services/ejsVirtual.service.js +102 -32
  107. package/src/services/fileManager.service.js +475 -0
  108. package/src/services/fileManagerStoragePolicy.service.js +285 -0
  109. package/src/services/healthChecks.service.js +650 -0
  110. package/src/services/healthChecksBootstrap.service.js +109 -0
  111. package/src/services/healthChecksScheduler.service.js +106 -0
  112. package/src/services/llmDefaults.service.js +190 -0
  113. package/src/services/migrationAssets/s3.js +2 -2
  114. package/src/services/pages.service.js +602 -0
  115. package/src/services/pagesContext.service.js +331 -0
  116. package/src/services/pagesContextBlocksAi.service.js +349 -0
  117. package/src/services/proxy.service.js +535 -0
  118. package/src/services/rateLimiter.service.js +623 -0
  119. package/src/services/rbac.service.js +212 -0
  120. package/src/services/scriptsRunner.service.js +1 -1
  121. package/src/services/uiComponentsAi.service.js +6 -19
  122. package/src/services/workflow.service.js +23 -8
  123. package/src/utils/orgRoles.js +14 -0
  124. package/src/utils/rbac/engine.js +60 -0
  125. package/src/utils/rbac/rightsRegistry.js +29 -0
  126. package/views/admin-blog-automation.ejs +877 -0
  127. package/views/admin-blog-edit.ejs +542 -0
  128. package/views/admin-blog.ejs +399 -0
  129. package/views/admin-cache.ejs +681 -0
  130. package/views/admin-console-manager.ejs +680 -0
  131. package/views/admin-crons.ejs +645 -0
  132. package/views/admin-db-browser.ejs +445 -0
  133. package/views/admin-ejs-virtual.ejs +16 -10
  134. package/views/admin-file-manager.ejs +942 -0
  135. package/views/admin-health-checks.ejs +725 -0
  136. package/views/admin-i18n.ejs +59 -5
  137. package/views/admin-llm.ejs +99 -1
  138. package/views/admin-organizations.ejs +163 -1
  139. package/views/admin-pages.ejs +2424 -0
  140. package/views/admin-proxy.ejs +491 -0
  141. package/views/admin-rate-limiter.ejs +625 -0
  142. package/views/admin-rbac.ejs +1331 -0
  143. package/views/admin-scripts.ejs +1 -1
  144. package/views/admin-seo-config.ejs +61 -7
  145. package/views/admin-ui-components.ejs +57 -25
  146. package/views/admin-workflows.ejs +7 -7
  147. package/views/file-manager.ejs +866 -0
  148. package/views/pages/blocks/contact.ejs +27 -0
  149. package/views/pages/blocks/cta.ejs +18 -0
  150. package/views/pages/blocks/faq.ejs +20 -0
  151. package/views/pages/blocks/features.ejs +19 -0
  152. package/views/pages/blocks/hero.ejs +13 -0
  153. package/views/pages/blocks/html.ejs +5 -0
  154. package/views/pages/blocks/image.ejs +14 -0
  155. package/views/pages/blocks/testimonials.ejs +26 -0
  156. package/views/pages/blocks/text.ejs +10 -0
  157. package/views/pages/layouts/default.ejs +51 -0
  158. package/views/pages/layouts/minimal.ejs +42 -0
  159. package/views/pages/layouts/sidebar.ejs +54 -0
  160. package/views/pages/partials/footer.ejs +13 -0
  161. package/views/pages/partials/header.ejs +12 -0
  162. package/views/pages/partials/sidebar.ejs +8 -0
  163. package/views/pages/runtime/page.ejs +10 -0
  164. package/views/pages/templates/article.ejs +20 -0
  165. package/views/pages/templates/default.ejs +12 -0
  166. package/views/pages/templates/landing.ejs +14 -0
  167. package/views/pages/templates/listing.ejs +15 -0
  168. package/views/partials/admin-image-upload-modal.ejs +221 -0
  169. package/views/partials/dashboard/nav-items.ejs +11 -0
  170. package/views/partials/llm-provider-model-picker.ejs +183 -0
  171. package/src/routes/llmUi.routes.js +0 -26
@@ -0,0 +1,49 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const healthIncidentSchema = new mongoose.Schema(
4
+ {
5
+ healthCheckId: {
6
+ type: mongoose.Schema.Types.ObjectId,
7
+ ref: 'HealthCheck',
8
+ required: true,
9
+ index: true,
10
+ },
11
+
12
+ status: {
13
+ type: String,
14
+ enum: ['open', 'acknowledged', 'resolved'],
15
+ default: 'open',
16
+ index: true,
17
+ },
18
+
19
+ severity: {
20
+ type: String,
21
+ enum: ['warning', 'critical'],
22
+ default: 'warning',
23
+ },
24
+
25
+ openedAt: { type: Date, default: Date.now, index: true },
26
+ acknowledgedAt: { type: Date },
27
+ resolvedAt: { type: Date },
28
+ lastSeenAt: { type: Date, default: Date.now },
29
+
30
+ consecutiveFailureCount: { type: Number, default: 0 },
31
+ consecutiveSuccessCount: { type: Number, default: 0 },
32
+
33
+ lastRunId: { type: mongoose.Schema.Types.ObjectId, ref: 'HealthCheckRun' },
34
+
35
+ summary: { type: String, default: '' },
36
+ lastError: { type: String, default: '' },
37
+
38
+ autoHealAttemptCount: { type: Number, default: 0 },
39
+ lastAutoHealAttemptAt: { type: Date },
40
+ },
41
+ { timestamps: true, collection: 'health_incidents' },
42
+ );
43
+
44
+ healthIncidentSchema.index({ healthCheckId: 1, openedAt: -1 });
45
+ healthIncidentSchema.index({ status: 1, openedAt: -1 });
46
+
47
+ module.exports =
48
+ mongoose.models.HealthIncident ||
49
+ mongoose.model('HealthIncident', healthIncidentSchema);
@@ -0,0 +1,95 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const seoMetaSchema = new mongoose.Schema(
4
+ {
5
+ title: { type: String, default: '' },
6
+ description: { type: String, default: '' },
7
+ keywords: { type: String, default: '' },
8
+ ogImage: { type: String, default: '' },
9
+ canonicalUrl: { type: String, default: '' },
10
+ },
11
+ { _id: false },
12
+ );
13
+
14
+ const pageSchema = new mongoose.Schema(
15
+ {
16
+ slug: {
17
+ type: String,
18
+ required: true,
19
+ index: true,
20
+ },
21
+ collectionId: {
22
+ type: mongoose.Schema.Types.ObjectId,
23
+ ref: 'PageCollection',
24
+ default: null,
25
+ index: true,
26
+ },
27
+ title: {
28
+ type: String,
29
+ required: true,
30
+ },
31
+ templateKey: {
32
+ type: String,
33
+ default: 'default',
34
+ },
35
+ layoutKey: {
36
+ type: String,
37
+ default: 'default',
38
+ },
39
+ blocks: {
40
+ type: mongoose.Schema.Types.Mixed,
41
+ default: [],
42
+ },
43
+ repeat: {
44
+ type: mongoose.Schema.Types.Mixed,
45
+ default: null,
46
+ },
47
+ customCss: {
48
+ type: String,
49
+ default: '',
50
+ },
51
+ customJs: {
52
+ type: String,
53
+ default: '',
54
+ },
55
+ seoMeta: {
56
+ type: seoMetaSchema,
57
+ default: () => ({}),
58
+ },
59
+ tenantId: {
60
+ type: mongoose.Schema.Types.ObjectId,
61
+ ref: 'Organization',
62
+ default: null,
63
+ index: true,
64
+ },
65
+ isGlobal: {
66
+ type: Boolean,
67
+ default: true,
68
+ index: true,
69
+ },
70
+ status: {
71
+ type: String,
72
+ enum: ['draft', 'published', 'archived'],
73
+ default: 'draft',
74
+ index: true,
75
+ },
76
+ publishedAt: {
77
+ type: Date,
78
+ default: null,
79
+ },
80
+ },
81
+ { timestamps: true },
82
+ );
83
+
84
+ pageSchema.index({ slug: 1, collectionId: 1, tenantId: 1 }, { unique: true });
85
+ pageSchema.index({ status: 1, isGlobal: 1 });
86
+ pageSchema.index({ collectionId: 1, status: 1 });
87
+
88
+ pageSchema.virtual('routePath').get(function () {
89
+ return this._routePath || null;
90
+ });
91
+
92
+ pageSchema.set('toJSON', { virtuals: true });
93
+ pageSchema.set('toObject', { virtuals: true });
94
+
95
+ module.exports = mongoose.model('Page', pageSchema);
@@ -0,0 +1,42 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const pageCollectionSchema = new mongoose.Schema(
4
+ {
5
+ slug: {
6
+ type: String,
7
+ required: true,
8
+ index: true,
9
+ },
10
+ name: {
11
+ type: String,
12
+ required: true,
13
+ },
14
+ description: {
15
+ type: String,
16
+ default: '',
17
+ },
18
+ tenantId: {
19
+ type: mongoose.Schema.Types.ObjectId,
20
+ ref: 'Organization',
21
+ default: null,
22
+ index: true,
23
+ },
24
+ isGlobal: {
25
+ type: Boolean,
26
+ default: true,
27
+ index: true,
28
+ },
29
+ status: {
30
+ type: String,
31
+ enum: ['active', 'archived'],
32
+ default: 'active',
33
+ index: true,
34
+ },
35
+ },
36
+ { timestamps: true },
37
+ );
38
+
39
+ pageCollectionSchema.index({ slug: 1, tenantId: 1 }, { unique: true });
40
+ pageCollectionSchema.index({ isGlobal: 1, status: 1 });
41
+
42
+ module.exports = mongoose.model('PageCollection', pageCollectionSchema);
@@ -0,0 +1,66 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const proxyRuleSchema = new mongoose.Schema(
4
+ {
5
+ enabled: { type: Boolean, default: true },
6
+ type: { type: String, enum: ['contains', 'regexp'], required: true },
7
+ value: { type: String, required: true },
8
+ applyTo: { type: String, enum: ['targetUrl', 'host', 'path'], default: 'targetUrl' },
9
+ flags: { type: String, default: 'i' },
10
+ },
11
+ { _id: false },
12
+ );
13
+
14
+ const proxyEntrySchema = new mongoose.Schema(
15
+ {
16
+ name: { type: String, default: '' },
17
+ enabled: { type: Boolean, default: false, index: true },
18
+ match: {
19
+ type: {
20
+ type: String,
21
+ enum: ['exact', 'contains', 'regexp'],
22
+ default: 'contains',
23
+ },
24
+ value: { type: String, required: true },
25
+ applyTo: { type: String, enum: ['targetUrl', 'host', 'path'], default: 'host' },
26
+ flags: { type: String, default: 'i' },
27
+ },
28
+ policy: {
29
+ mode: { type: String, enum: ['blacklist', 'whitelist', 'allowAll', 'denyAll'], default: 'whitelist' },
30
+ rules: { type: [proxyRuleSchema], default: [] },
31
+ },
32
+ rateLimit: {
33
+ enabled: { type: Boolean, default: false },
34
+ limiterId: { type: String, default: null },
35
+ },
36
+ cache: {
37
+ enabled: { type: Boolean, default: false },
38
+ ttlSeconds: { type: Number, default: 60 },
39
+ namespace: { type: String, default: 'proxy' },
40
+ methods: { type: [String], default: ['GET', 'HEAD'] },
41
+ keyParts: {
42
+ url: { type: Boolean, default: true },
43
+ query: { type: Boolean, default: true },
44
+ bodyHash: { type: Boolean, default: true },
45
+ headersHash: { type: Boolean, default: true },
46
+ },
47
+ keyHeaderAllowList: { type: [String], default: [] },
48
+ },
49
+ headers: {
50
+ forwardAuthorization: { type: Boolean, default: true },
51
+ forwardCookie: { type: Boolean, default: true },
52
+ allowList: { type: [String], default: [] },
53
+ denyList: { type: [String], default: [] },
54
+ },
55
+ transform: {
56
+ enabled: { type: Boolean, default: false },
57
+ timeoutMs: { type: Number, default: 200 },
58
+ code: { type: String, default: '' },
59
+ },
60
+ },
61
+ { timestamps: true, collection: 'proxy_entries' },
62
+ );
63
+
64
+ proxyEntrySchema.index({ 'match.type': 1, 'match.applyTo': 1 });
65
+
66
+ module.exports = mongoose.models.ProxyEntry || mongoose.model('ProxyEntry', proxyEntrySchema);
@@ -0,0 +1,19 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rateLimitCounterSchema = new mongoose.Schema(
4
+ {
5
+ limiterId: { type: String, required: true, index: true },
6
+ identityKey: { type: String, required: true, index: true },
7
+ windowStart: { type: Date, required: true, index: true },
8
+
9
+ count: { type: Number, default: 0 },
10
+
11
+ expiresAt: { type: Date, default: null, index: true },
12
+ },
13
+ { timestamps: true, collection: 'rate_limit_counters' },
14
+ );
15
+
16
+ rateLimitCounterSchema.index({ limiterId: 1, identityKey: 1, windowStart: 1 }, { unique: true });
17
+ rateLimitCounterSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
18
+
19
+ module.exports = mongoose.models.RateLimitCounter || mongoose.model('RateLimitCounter', rateLimitCounterSchema);
@@ -0,0 +1,20 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rateLimitMetricBucketSchema = new mongoose.Schema(
4
+ {
5
+ bucketStart: { type: Date, required: true, index: true },
6
+ limiterId: { type: String, required: true, index: true },
7
+
8
+ checked: { type: Number, default: 0 },
9
+ allowed: { type: Number, default: 0 },
10
+ blocked: { type: Number, default: 0 },
11
+
12
+ expiresAt: { type: Date, default: null, index: true },
13
+ },
14
+ { timestamps: true, collection: 'rate_limit_metric_buckets' },
15
+ );
16
+
17
+ rateLimitMetricBucketSchema.index({ limiterId: 1, bucketStart: 1 }, { unique: true });
18
+ rateLimitMetricBucketSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
19
+
20
+ module.exports = mongoose.models.RateLimitMetricBucket || mongoose.model('RateLimitMetricBucket', rateLimitMetricBucketSchema);
@@ -0,0 +1,25 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rbacGrantSchema = new mongoose.Schema(
4
+ {
5
+ subjectType: { type: String, enum: ['user', 'role', 'group', 'org'], required: true, index: true },
6
+ subjectId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
7
+
8
+ scopeType: { type: String, enum: ['global', 'org'], required: true, index: true },
9
+ scopeId: { type: mongoose.Schema.Types.ObjectId, ref: 'Organization', default: null, index: true },
10
+
11
+ right: { type: String, required: true, trim: true, index: true },
12
+ effect: { type: String, enum: ['allow', 'deny'], default: 'allow', index: true },
13
+
14
+ createdByActorType: { type: String, default: null },
15
+ createdByActorId: { type: String, default: null },
16
+ },
17
+ { timestamps: true, collection: 'rbac_grants' },
18
+ );
19
+
20
+ rbacGrantSchema.index(
21
+ { subjectType: 1, subjectId: 1, scopeType: 1, scopeId: 1, right: 1 },
22
+ { unique: true },
23
+ );
24
+
25
+ module.exports = mongoose.models.RbacGrant || mongoose.model('RbacGrant', rbacGrantSchema);
@@ -0,0 +1,16 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rbacGroupSchema = new mongoose.Schema(
4
+ {
5
+ name: { type: String, required: true, trim: true },
6
+ description: { type: String, default: '', trim: true },
7
+ status: { type: String, enum: ['active', 'disabled'], default: 'active', index: true },
8
+ isGlobal: { type: Boolean, default: true, index: true },
9
+ orgId: { type: mongoose.Schema.Types.ObjectId, ref: 'Organization', default: null, index: true },
10
+ },
11
+ { timestamps: true, collection: 'rbac_groups' },
12
+ );
13
+
14
+ rbacGroupSchema.index({ isGlobal: 1, orgId: 1, name: 1 });
15
+
16
+ module.exports = mongoose.models.RbacGroup || mongoose.model('RbacGroup', rbacGroupSchema);
@@ -0,0 +1,13 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rbacGroupMemberSchema = new mongoose.Schema(
4
+ {
5
+ groupId: { type: mongoose.Schema.Types.ObjectId, ref: 'RbacGroup', required: true, index: true },
6
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true, index: true },
7
+ },
8
+ { timestamps: true, collection: 'rbac_group_members' },
9
+ );
10
+
11
+ rbacGroupMemberSchema.index({ groupId: 1, userId: 1 }, { unique: true });
12
+
13
+ module.exports = mongoose.models.RbacGroupMember || mongoose.model('RbacGroupMember', rbacGroupMemberSchema);
@@ -0,0 +1,13 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rbacGroupRoleSchema = new mongoose.Schema(
4
+ {
5
+ groupId: { type: mongoose.Schema.Types.ObjectId, ref: 'RbacGroup', required: true, index: true },
6
+ roleId: { type: mongoose.Schema.Types.ObjectId, ref: 'RbacRole', required: true, index: true },
7
+ },
8
+ { timestamps: true, collection: 'rbac_group_roles' },
9
+ );
10
+
11
+ rbacGroupRoleSchema.index({ groupId: 1, roleId: 1 }, { unique: true });
12
+
13
+ module.exports = mongoose.models.RbacGroupRole || mongoose.model('RbacGroupRole', rbacGroupRoleSchema);
@@ -0,0 +1,25 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rbacRoleSchema = new mongoose.Schema(
4
+ {
5
+ key: { type: String, required: true, trim: true, lowercase: true, index: true },
6
+ name: { type: String, required: true, trim: true },
7
+ description: { type: String, default: '', trim: true },
8
+ status: { type: String, enum: ['active', 'disabled'], default: 'active', index: true },
9
+ isGlobal: { type: Boolean, default: true, index: true },
10
+ orgId: { type: mongoose.Schema.Types.ObjectId, ref: 'Organization', default: null, index: true },
11
+ },
12
+ { timestamps: true, collection: 'rbac_roles' },
13
+ );
14
+
15
+ rbacRoleSchema.index({ isGlobal: 1, orgId: 1, key: 1 });
16
+ rbacRoleSchema.index(
17
+ { key: 1 },
18
+ { unique: true, partialFilterExpression: { isGlobal: true } },
19
+ );
20
+ rbacRoleSchema.index(
21
+ { orgId: 1, key: 1 },
22
+ { unique: true, partialFilterExpression: { isGlobal: false } },
23
+ );
24
+
25
+ module.exports = mongoose.models.RbacRole || mongoose.model('RbacRole', rbacRoleSchema);
@@ -0,0 +1,13 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const rbacUserRoleSchema = new mongoose.Schema(
4
+ {
5
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true, index: true },
6
+ roleId: { type: mongoose.Schema.Types.ObjectId, ref: 'RbacRole', required: true, index: true },
7
+ },
8
+ { timestamps: true, collection: 'rbac_user_roles' },
9
+ );
10
+
11
+ rbacUserRoleSchema.index({ userId: 1, roleId: 1 }, { unique: true });
12
+
13
+ module.exports = mongoose.models.RbacUserRole || mongoose.model('RbacUserRole', rbacUserRoleSchema);
@@ -0,0 +1,21 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+
4
+ const { basicAuth } = require('../middleware/auth');
5
+ const controller = require('../controllers/blogAdmin.controller');
6
+
7
+ router.use(basicAuth);
8
+ router.use(express.json({ limit: '2mb' }));
9
+
10
+ router.get('/blog-posts', controller.list);
11
+ router.get('/blog-posts/suggestions', controller.suggestions);
12
+ router.post('/blog-posts', controller.create);
13
+ router.get('/blog-posts/:id', controller.get);
14
+ router.put('/blog-posts/:id', controller.update);
15
+ router.put('/blog-posts/:id/publish', controller.publish);
16
+ router.put('/blog-posts/:id/unpublish', controller.unpublish);
17
+ router.put('/blog-posts/:id/schedule', controller.schedule);
18
+ router.put('/blog-posts/:id/archive', controller.archive);
19
+ router.delete('/blog-posts/:id', controller.remove);
20
+
21
+ module.exports = router;
@@ -0,0 +1,16 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+
4
+ const { basicAuth } = require('../middleware/auth');
5
+ const controller = require('../controllers/blogAiAdmin.controller');
6
+ const rateLimiter = require('../services/rateLimiter.service');
7
+
8
+ router.use(basicAuth);
9
+ router.use(express.json({ limit: '2mb' }));
10
+
11
+ router.post('/blog-ai/generate-field', rateLimiter.limit('blogAiLimiter'), controller.generateField);
12
+ router.post('/blog-ai/generate-all', rateLimiter.limit('blogAiLimiter'), controller.generateAll);
13
+ router.post('/blog-ai/format-markdown', rateLimiter.limit('blogAiLimiter'), controller.formatMarkdown);
14
+ router.post('/blog-ai/refine-markdown', rateLimiter.limit('blogAiLimiter'), controller.refineMarkdown);
15
+
16
+ module.exports = router;
@@ -0,0 +1,27 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+
4
+ const { basicAuth } = require('../middleware/auth');
5
+ const controller = require('../controllers/blogAutomationAdmin.controller');
6
+ const rateLimiter = require('../services/rateLimiter.service');
7
+
8
+ router.use(basicAuth);
9
+ router.use(express.json({ limit: '2mb' }));
10
+
11
+ router.get('/blog-automation/config', controller.getConfig);
12
+ router.put('/blog-automation/config', controller.saveConfig);
13
+
14
+ router.get('/blog-automation/configs', controller.listConfigs);
15
+ router.post('/blog-automation/configs', controller.createConfig);
16
+ router.get('/blog-automation/configs/:id', controller.getConfigById);
17
+ router.post('/blog-automation/configs/:id/preview-prompts', controller.previewPromptsByConfigId);
18
+ router.put('/blog-automation/configs/:id', controller.updateConfigById);
19
+ router.delete('/blog-automation/configs/:id', controller.deleteConfigById);
20
+
21
+ router.get('/blog-automation/style-guide', controller.getStyleGuide);
22
+ router.put('/blog-automation/style-guide', controller.saveStyleGuide);
23
+
24
+ router.get('/blog-automation/runs', controller.listRuns);
25
+ router.post('/blog-automation/run-now', rateLimiter.limit('blogAiLimiter'), controller.runNow);
26
+
27
+ module.exports = router;
@@ -0,0 +1,20 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+
4
+ const { basicAuth } = require('../middleware/auth');
5
+ const controller = require('../controllers/adminCache.controller');
6
+
7
+ router.use(basicAuth);
8
+
9
+ router.get('/config', controller.getConfig);
10
+ router.put('/config', controller.updateConfig);
11
+
12
+ router.get('/keys', controller.listKeys);
13
+ router.get('/entry', controller.getEntry);
14
+ router.put('/entry', controller.setEntry);
15
+ router.delete('/entry', controller.deleteEntry);
16
+
17
+ router.post('/clear', controller.clearCache);
18
+ router.get('/metrics', controller.metrics);
19
+
20
+ module.exports = router;