webspresso 0.0.96 → 0.0.98
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.
- package/.agents/ADMIN.md +153 -5
- package/.agents/ARCHITECTURE.md +10 -0
- package/.agents/CLI.md +195 -1
- package/.agents/PLUGINS.md +153 -9
- package/.agents/QUEUE.md +43 -0
- package/.agents/skills/webspresso-admin-and-plugins/SKILL.md +122 -0
- package/.agents/skills/webspresso-framework/SKILL.md +56 -0
- package/.agents/skills/webspresso-orm-and-database/SKILL.md +119 -0
- package/.agents/skills/webspresso-routing-and-api/SKILL.md +114 -0
- package/.agents/skills/webspresso-services/SKILL.md +101 -0
- package/bin/commands/add-esbuild.js +80 -0
- package/bin/commands/add-tailwind.js +76 -31
- package/bin/commands/admin-password.js +2 -1
- package/bin/commands/admin-setup.js +1 -1
- package/bin/commands/agents-init.js +51 -11
- package/bin/commands/analytics-prune.js +57 -0
- package/bin/commands/api.js +207 -15
- package/bin/commands/audit-prune.js +1 -1
- package/bin/commands/build.js +99 -0
- package/bin/commands/dev.js +64 -35
- package/bin/commands/doctor.js +26 -0
- package/bin/commands/email-prune.js +47 -0
- package/bin/commands/favicon-generate.js +1 -1
- package/bin/commands/make-job.js +86 -0
- package/bin/commands/make-model.js +250 -0
- package/bin/commands/new.js +45 -28
- package/bin/commands/openapi.js +102 -0
- package/bin/commands/orm-map.js +5 -4
- package/bin/commands/page.js +6 -3
- package/bin/commands/polar-migrate.js +1 -1
- package/bin/commands/queue-prune.js +59 -0
- package/bin/commands/routes.js +52 -0
- package/bin/commands/seed.js +1 -1
- package/bin/commands/service.js +6 -3
- package/bin/commands/typegen.js +106 -0
- package/bin/commands/upload-prune.js +79 -0
- package/bin/webspresso.js +22 -0
- package/core/applySchema.js +13 -6
- package/core/auth/hash.js +31 -17
- package/core/compression/middleware.js +23 -17
- package/core/openapi/build-from-api-routes.js +160 -29
- package/core/orm/cache/fingerprint.js +15 -1
- package/core/orm/cache/memory-provider.js +27 -2
- package/core/orm/index.js +12 -7
- package/core/orm/model.js +49 -7
- package/core/queue/adapters/database.js +94 -0
- package/core/queue/adapters/memory.js +62 -0
- package/core/queue/adapters/redis.js +80 -0
- package/core/queue/manager.js +123 -0
- package/core/ssr/stream.js +11 -0
- package/index.d.ts +660 -10
- package/index.js +44 -96
- package/package.json +32 -12
- package/plugins/admin-panel/api.js +279 -17
- package/plugins/admin-panel/app.js +124 -31
- package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +314 -9
- package/plugins/admin-panel/client/parts/04-field-renderers.js +264 -67
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +10 -5
- package/plugins/admin-panel/client/parts/06-login-setup-forms.js +119 -28
- package/plugins/admin-panel/client/parts/07-model-list.js +49 -8
- package/plugins/admin-panel/client/parts/08-record-list.js +243 -23
- package/plugins/admin-panel/client/parts/09-record-form.js +59 -12
- package/plugins/admin-panel/client/parts/10-export-registry.js +23 -0
- package/plugins/admin-panel/core/admin-module.js +13 -7
- package/plugins/admin-panel/core/api-extensions.js +34 -2
- package/plugins/admin-panel/core/registry.js +96 -16
- package/plugins/admin-panel/index.js +96 -0
- package/plugins/admin-panel/lib/login-rate-limiter.js +119 -0
- package/plugins/admin-panel/modules/admin-users.js +16 -1
- package/plugins/admin-panel/modules/bulk-actions.js +13 -5
- package/plugins/admin-panel/modules/custom-pages.js +492 -3
- package/plugins/admin-panel/modules/dashboard.js +283 -0
- package/plugins/admin-panel/modules/menu.js +56 -18
- package/plugins/admin-panel/modules/user-management.js +24 -6
- package/plugins/audit-log/admin-component.js +11 -0
- package/plugins/audit-log/api-handlers.js +34 -0
- package/plugins/audit-log/index.js +129 -3
- package/plugins/audit-log/purge.js +60 -10
- package/plugins/basic-auth/index.js +3 -1
- package/plugins/content/client/inline-edit.js +12 -12
- package/plugins/cors.js +6 -7
- package/plugins/dashboard/app.js +152 -27
- package/plugins/dashboard/index.js +143 -39
- package/plugins/dashboard/styles.js +78 -0
- package/plugins/data-exchange/export-xlsx.js +9 -2
- package/plugins/data-exchange/import.js +9 -2
- package/plugins/data-exchange/index.js +6 -3
- package/plugins/email/index.js +132 -2
- package/plugins/email/purge.js +60 -10
- package/plugins/file-manager/core.js +1 -1
- package/plugins/file-manager/security.js +33 -23
- package/plugins/index.js +49 -87
- package/plugins/mcp/transports/sse.js +4 -2
- package/plugins/orm-cache-admin/index.js +4 -2
- package/plugins/queue/index.js +11 -0
- package/plugins/schema-explorer/admin-component.js +714 -0
- package/plugins/schema-explorer/relations-graph.js +205 -0
- package/plugins/schema-explorer.js +248 -69
- package/plugins/site-analytics/admin-component.js +30 -8
- package/plugins/site-analytics/api-handlers.js +42 -0
- package/plugins/site-analytics/index.js +147 -2
- package/plugins/site-analytics/purge.js +168 -0
- package/plugins/swagger.js +261 -13
- package/plugins/upload/index.js +26 -0
- package/plugins/upload/orphan-pruner.js +292 -0
- package/src/api/api-loader.js +3 -1
- package/src/bundle/index.js +517 -0
- package/src/bundle/manifest.js +165 -0
- package/src/dev/banner.js +296 -0
- package/src/dev/colors.js +70 -0
- package/src/dev/index.js +62 -0
- package/src/dev/profiler.js +354 -0
- package/src/dev/typegen.js +497 -0
- package/src/dev-reload/index.js +611 -0
- package/src/file-router.js +213 -63
- package/src/helpers.js +958 -98
- package/src/njk-frontmatter.js +12 -0
- package/src/pages/page-loader.js +61 -11
- package/src/server.js +629 -63
- package/src/services/discovery.js +6 -1
- package/src/services/index.js +6 -13
package/plugins/email/purge.js
CHANGED
|
@@ -3,24 +3,74 @@
|
|
|
3
3
|
* @module plugins/email/purge
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* @param {import('knex').Knex} knex
|
|
8
|
-
* @param {Object} options
|
|
10
|
+
* @param {Object} [options={}]
|
|
9
11
|
* @param {string} [options.tableName='email_logs']
|
|
10
|
-
* @param {Date|string} options.olderThan
|
|
11
|
-
* @
|
|
12
|
+
* @param {Date|string|number} [options.olderThan] - Rows with created_at strictly before this are removed
|
|
13
|
+
* @param {number} [options.days] - Number of days of email logs to keep
|
|
14
|
+
* @param {number} [options.retentionDays=90] - Alias for days
|
|
15
|
+
* @returns {Promise<number>} Deleted row count
|
|
12
16
|
*/
|
|
13
|
-
async function purgeEmailLogs(knex, options) {
|
|
17
|
+
async function purgeEmailLogs(knex, options = {}) {
|
|
18
|
+
if (!knex) {
|
|
19
|
+
throw new Error('purgeEmailLogs requires a database (knex) instance');
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
const tableName = options.tableName || 'email_logs';
|
|
15
|
-
const olderThan = options.olderThan instanceof Date
|
|
16
|
-
? options.olderThan
|
|
17
|
-
: new Date(options.olderThan);
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
let olderThan;
|
|
25
|
+
if (options.olderThan !== undefined) {
|
|
26
|
+
olderThan = options.olderThan instanceof Date
|
|
27
|
+
? options.olderThan
|
|
28
|
+
: new Date(options.olderThan);
|
|
29
|
+
|
|
30
|
+
if (Number.isNaN(olderThan.getTime())) {
|
|
31
|
+
throw new Error('purgeEmailLogs: invalid olderThan date');
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
const rawDays = options.days !== undefined ? options.days : (options.retentionDays !== undefined ? options.retentionDays : 90);
|
|
35
|
+
const days = typeof rawDays === 'number' ? rawDays : Number(rawDays);
|
|
36
|
+
if (Number.isNaN(days) || days <= 0) {
|
|
37
|
+
throw new Error('purgeEmailLogs: days must be a positive number');
|
|
38
|
+
}
|
|
39
|
+
olderThan = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let hasTable = true;
|
|
43
|
+
if (knex.schema && typeof knex.schema.hasTable === 'function') {
|
|
44
|
+
try {
|
|
45
|
+
hasTable = await knex.schema.hasTable(tableName);
|
|
46
|
+
} catch {
|
|
47
|
+
hasTable = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!hasTable) {
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const client = knex?.client?.config?.client;
|
|
56
|
+
const isSqlite = client === 'sqlite3' || client === 'better-sqlite3';
|
|
57
|
+
|
|
58
|
+
let query = knex(tableName);
|
|
59
|
+
if (isSqlite) {
|
|
60
|
+
const iso = olderThan.toISOString();
|
|
61
|
+
const cutoffMs = olderThan.getTime();
|
|
62
|
+
query = query.where((builder) => {
|
|
63
|
+
builder.whereRaw(
|
|
64
|
+
"((typeof(created_at) = 'integer' and created_at < ?) or (typeof(created_at) = 'text' and created_at < ?))",
|
|
65
|
+
[cutoffMs, iso]
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
query = query.where('created_at', '<', olderThan);
|
|
21
70
|
}
|
|
22
71
|
|
|
23
|
-
|
|
72
|
+
const deleted = await query.delete();
|
|
73
|
+
return Number(deleted) || 0;
|
|
24
74
|
}
|
|
25
75
|
|
|
26
76
|
module.exports = {
|
|
@@ -217,7 +217,7 @@ function buildBreadcrumbs(relPath) {
|
|
|
217
217
|
const breadcrumbs = [{ name: 'Root', path: '' }];
|
|
218
218
|
if (!relPath) return breadcrumbs;
|
|
219
219
|
|
|
220
|
-
const segments = relPath.replace(/\\/g, '/').
|
|
220
|
+
const segments = relPath.replace(/\\/g, '/').split('/').filter(Boolean);
|
|
221
221
|
let current = '';
|
|
222
222
|
|
|
223
223
|
for (const seg of segments) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module plugins/file-manager/security
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const sanitizeHtml = require('sanitize-html');
|
|
7
8
|
const { SecurityError, ValidationError } = require('../../core/errors');
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -164,6 +165,37 @@ function validateMagicBytes(buffer, ext) {
|
|
|
164
165
|
return true;
|
|
165
166
|
}
|
|
166
167
|
|
|
168
|
+
const SVG_SANITIZE_OPTIONS = {
|
|
169
|
+
allowedTags: [
|
|
170
|
+
'svg', 'g', 'path', 'circle', 'rect', 'line', 'polyline', 'polygon',
|
|
171
|
+
'ellipse', 'text', 'tspan', 'defs', 'clipPath', 'mask', 'pattern',
|
|
172
|
+
'image', 'linearGradient', 'radialGradient', 'stop', 'use', 'symbol',
|
|
173
|
+
'title', 'desc', 'a',
|
|
174
|
+
],
|
|
175
|
+
allowedAttributes: {
|
|
176
|
+
'*': [
|
|
177
|
+
'id', 'class', 'style', 'xml:space', 'xmlns', 'xmlns:xlink', 'xmlns:svg',
|
|
178
|
+
'version', 'viewbox', 'viewBox', 'width', 'height', 'x', 'y', 'dx', 'dy',
|
|
179
|
+
'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'r', 'rx', 'ry', 'd', 'points',
|
|
180
|
+
'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin',
|
|
181
|
+
'stroke-miterlimit', 'stroke-dasharray', 'stroke-dashoffset', 'opacity',
|
|
182
|
+
'fill-opacity', 'stroke-opacity', 'fill-rule', 'clip-rule',
|
|
183
|
+
'transform', 'font-family', 'font-size', 'font-weight', 'text-anchor',
|
|
184
|
+
'clip-path', 'mask', 'gradientUnits', 'gradientTransform', 'offset',
|
|
185
|
+
'stop-color', 'stop-opacity', 'href', 'xlink:href', 'preserveAspectRatio',
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
allowedSchemes: ['http', 'https'],
|
|
189
|
+
allowedSchemesByTag: {
|
|
190
|
+
a: ['http', 'https', 'mailto'],
|
|
191
|
+
image: ['http', 'https', 'data'],
|
|
192
|
+
use: ['#'],
|
|
193
|
+
},
|
|
194
|
+
allowProtocolRelative: false,
|
|
195
|
+
selfClosing: ['rect', 'circle', 'line', 'polyline', 'polygon', 'path', 'stop', 'use', 'image', 'ellipse'],
|
|
196
|
+
disallowedTagsMode: 'discard',
|
|
197
|
+
};
|
|
198
|
+
|
|
167
199
|
/**
|
|
168
200
|
* Sanitize SVG content against Stored XSS attacks.
|
|
169
201
|
* Strips <script>, <foreignObject>, <iframe>, <embed>, <object> and on* event handlers.
|
|
@@ -172,29 +204,7 @@ function validateMagicBytes(buffer, ext) {
|
|
|
172
204
|
*/
|
|
173
205
|
function sanitizeSvgContent(content) {
|
|
174
206
|
const str = Buffer.isBuffer(content) ? content.toString('utf8') : String(content);
|
|
175
|
-
|
|
176
|
-
// Quick check: if no dangerous keywords, return original buffer directly for maximum speed
|
|
177
|
-
if (!/<(script|foreignobject|iframe|embed|object|meta|link|style)/i.test(str) &&
|
|
178
|
-
!/\s+on[a-z]+\s*=/i.test(str) &&
|
|
179
|
-
!/javascript:/i.test(str) &&
|
|
180
|
-
!/data:text\/html/i.test(str)) {
|
|
181
|
-
return Buffer.isBuffer(content) ? content : Buffer.from(str, 'utf8');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
let sanitized = str
|
|
185
|
-
// Remove script tags and content
|
|
186
|
-
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script\s*>/gi, '')
|
|
187
|
-
// Remove dangerous containers
|
|
188
|
-
.replace(/<foreignObject\b[^<]*(?:(?!<\/foreignObject>)<[^<]*)*<\/foreignObject\s*>/gi, '')
|
|
189
|
-
.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe\s*>/gi, '')
|
|
190
|
-
.replace(/<embed\b[^>]*>/gi, '')
|
|
191
|
-
.replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object\s*>/gi, '')
|
|
192
|
-
// Remove inline event handlers (onload, onclick, onerror, onmouseover, etc.)
|
|
193
|
-
.replace(/\s+on[a-zA-Z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, '')
|
|
194
|
-
// Remove javascript: and data:text/html URIs in href and xlink:href
|
|
195
|
-
.replace(/(?:href|xlink:href)\s*=\s*(?:"\s*javascript:[^"]*"|'\s*javascript:[^']*')/gi, 'href=""')
|
|
196
|
-
.replace(/(?:href|xlink:href)\s*=\s*(?:"\s*data:text\/html[^"]*"|'\s*data:text\/html[^']*')/gi, 'href=""');
|
|
197
|
-
|
|
207
|
+
const sanitized = sanitizeHtml(str, SVG_SANITIZE_OPTIONS);
|
|
198
208
|
return Buffer.from(sanitized, 'utf8');
|
|
199
209
|
}
|
|
200
210
|
|
package/plugins/index.js
CHANGED
|
@@ -3,94 +3,56 @@
|
|
|
3
3
|
* Export all built-in plugins
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const sitemapPlugin = require('./sitemap');
|
|
7
|
-
const analyticsPlugin = require('./analytics');
|
|
8
|
-
const dashboardPlugin = require('./dashboard/index');
|
|
9
|
-
const schemaExplorerPlugin = require('./schema-explorer');
|
|
10
|
-
const adminPanelPlugin = require('./admin-panel');
|
|
11
|
-
const seoCheckerPlugin = require('./seo-checker');
|
|
12
|
-
const siteAnalyticsPlugin = require('./site-analytics');
|
|
13
|
-
const auditLogPlugin = require('./audit-log');
|
|
14
|
-
const recaptchaPlugin = require('./recaptcha');
|
|
15
|
-
const swaggerPlugin = require('./swagger');
|
|
16
|
-
const healthCheckPlugin = require('./health-check');
|
|
17
|
-
const restResourcePlugin = require('./rest-resources');
|
|
18
|
-
const ormCacheAdminPlugin = require('./orm-cache-admin');
|
|
19
|
-
const { uploadPlugin, createLocalFileProvider } = require('./upload');
|
|
20
|
-
/** Register after adminPanelPlugin (same db + adminPath) for session and routes. */
|
|
21
|
-
const { dataExchangePlugin } = require('./data-exchange');
|
|
22
|
-
const { redirectPlugin } = require('./redirect');
|
|
23
|
-
const { rateLimitPlugin } = require('./rate-limit');
|
|
24
|
-
const emailPlugin = require('./email');
|
|
25
|
-
const contentPlugin = require('./content');
|
|
26
|
-
const csrfPlugin = require('./csrf');
|
|
27
|
-
const corsPlugin = require('./cors');
|
|
28
|
-
const { basicAuthPlugin, createBasicAuthMiddleware } = require('./basic-auth');
|
|
29
|
-
const {
|
|
30
|
-
realtimePlugin,
|
|
31
|
-
realtime,
|
|
32
|
-
createRealtime,
|
|
33
|
-
websocket,
|
|
34
|
-
createWebSocketAdapter,
|
|
35
|
-
sse,
|
|
36
|
-
createSseAdapter,
|
|
37
|
-
socketIo,
|
|
38
|
-
createSocketIoAdapter,
|
|
39
|
-
redis,
|
|
40
|
-
createRedisAdapter,
|
|
41
|
-
} = require('./realtime');
|
|
42
|
-
const { mcpPlugin, createMcpServer, McpServer } = require('./mcp');
|
|
43
|
-
const polarPlugin = require('./polar');
|
|
44
|
-
const fileManagerPlugin = require('./file-manager');
|
|
45
|
-
const { xlsxPlugin, xlsx } = require('./xlsx');
|
|
46
|
-
const { csvPlugin, csv } = require('./csv');
|
|
47
|
-
|
|
48
6
|
module.exports = {
|
|
49
|
-
sitemapPlugin,
|
|
50
|
-
analyticsPlugin,
|
|
51
|
-
dashboardPlugin,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
7
|
+
get sitemapPlugin() { return require('./sitemap'); },
|
|
8
|
+
get analyticsPlugin() { return require('./analytics'); },
|
|
9
|
+
get dashboardPlugin() { return require('./dashboard/index'); },
|
|
10
|
+
get devToolbarPlugin() { return require('./dashboard/index').devToolbarPlugin; },
|
|
11
|
+
get devtoolsPlugin() { return require('./dashboard/index').devtoolsPlugin; },
|
|
12
|
+
get schemaExplorerPlugin() { return require('./schema-explorer'); },
|
|
13
|
+
get adminPanelPlugin() { return require('./admin-panel'); },
|
|
14
|
+
get seoCheckerPlugin() { return require('./seo-checker'); },
|
|
15
|
+
get siteAnalyticsPlugin() { return require('./site-analytics'); },
|
|
16
|
+
get auditLogPlugin() { return require('./audit-log'); },
|
|
17
|
+
get recaptchaPlugin() { return require('./recaptcha'); },
|
|
18
|
+
get swaggerPlugin() { return require('./swagger'); },
|
|
19
|
+
get openapiPlugin() { return require('./swagger'); },
|
|
20
|
+
get apiDocsPlugin() { return require('./swagger'); },
|
|
21
|
+
get healthCheckPlugin() { return require('./health-check'); },
|
|
22
|
+
get restResourcePlugin() { return require('./rest-resources'); },
|
|
23
|
+
get ormCacheAdminPlugin() { return require('./orm-cache-admin'); },
|
|
24
|
+
get uploadPlugin() { return require('./upload').uploadPlugin; },
|
|
25
|
+
get createLocalFileProvider() { return require('./upload').createLocalFileProvider; },
|
|
26
|
+
get dataExchangePlugin() { return require('./data-exchange').dataExchangePlugin; },
|
|
27
|
+
get redirectPlugin() { return require('./redirect').redirectPlugin; },
|
|
28
|
+
get rateLimitPlugin() { return require('./rate-limit').rateLimitPlugin; },
|
|
29
|
+
get emailPlugin() { return require('./email'); },
|
|
30
|
+
get contentPlugin() { return require('./content'); },
|
|
31
|
+
get csrfPlugin() { return require('./csrf'); },
|
|
32
|
+
get corsPlugin() { return require('./cors'); },
|
|
33
|
+
get basicAuthPlugin() { return require('./basic-auth').basicAuthPlugin; },
|
|
34
|
+
get createBasicAuthMiddleware() { return require('./basic-auth').createBasicAuthMiddleware; },
|
|
35
|
+
get realtimePlugin() { return require('./realtime').realtimePlugin; },
|
|
36
|
+
get realtime() { return require('./realtime').realtime; },
|
|
37
|
+
get createRealtime() { return require('./realtime').createRealtime; },
|
|
38
|
+
get websocket() { return require('./realtime').websocket; },
|
|
39
|
+
get createWebSocketAdapter() { return require('./realtime').createWebSocketAdapter; },
|
|
40
|
+
get sse() { return require('./realtime').sse; },
|
|
41
|
+
get createSseAdapter() { return require('./realtime').createSseAdapter; },
|
|
42
|
+
get socketIo() { return require('./realtime').socketIo; },
|
|
43
|
+
get createSocketIoAdapter() { return require('./realtime').createSocketIoAdapter; },
|
|
44
|
+
get redis() { return require('./realtime').redis; },
|
|
45
|
+
get createRedisAdapter() { return require('./realtime').createRedisAdapter; },
|
|
46
|
+
get queuePlugin() { return require('./queue'); },
|
|
47
|
+
get mcpPlugin() { return require('./mcp').mcpPlugin; },
|
|
48
|
+
get createMcpServer() { return require('./mcp').createMcpServer; },
|
|
49
|
+
get McpServer() { return require('./mcp').McpServer; },
|
|
50
|
+
get polarPlugin() { return require('./polar'); },
|
|
51
|
+
get fileManagerPlugin() { return require('./file-manager'); },
|
|
52
|
+
get xlsxPlugin() { return require('./xlsx').xlsxPlugin; },
|
|
53
|
+
get xlsx() { return require('./xlsx').xlsx; },
|
|
54
|
+
get csvPlugin() { return require('./csv').csvPlugin; },
|
|
55
|
+
get csv() { return require('./csv').csv; },
|
|
94
56
|
};
|
|
95
57
|
|
|
96
58
|
|
|
@@ -18,8 +18,10 @@ const { createJsonRpcError, ERROR_CODES } = require('../server');
|
|
|
18
18
|
*/
|
|
19
19
|
function mountSseTransport(options) {
|
|
20
20
|
const { app, server, path: basePath = '/_mcp', authMiddleware } = options;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
let normalizedBase = String(basePath || '/_mcp');
|
|
22
|
+
while (normalizedBase.length > 1 && normalizedBase.endsWith('/')) {
|
|
23
|
+
normalizedBase = normalizedBase.slice(0, -1);
|
|
24
|
+
}
|
|
23
25
|
const sessions = new Map();
|
|
24
26
|
|
|
25
27
|
const middlewareChain = authMiddleware ? [authMiddleware] : [];
|
|
@@ -11,7 +11,8 @@ const { generateOrmCacheAdminComponent } = require('./admin-component');
|
|
|
11
11
|
* @param {import('../../core/orm/types').DatabaseInstance} options.db - From createDatabase({ cache: true })
|
|
12
12
|
*/
|
|
13
13
|
function ormCacheAdminPlugin(options = {}) {
|
|
14
|
-
const { db } = options;
|
|
14
|
+
const { db, title, adminTitle } = options;
|
|
15
|
+
const pageTitle = title || adminTitle || 'ORM Cache';
|
|
15
16
|
|
|
16
17
|
if (!db) {
|
|
17
18
|
throw new Error('orm-cache-admin plugin requires `db` (database instance from createDatabase)');
|
|
@@ -41,9 +42,10 @@ function ormCacheAdminPlugin(options = {}) {
|
|
|
41
42
|
|
|
42
43
|
adminApi.registerModule({
|
|
43
44
|
id: 'orm-cache',
|
|
45
|
+
title: pageTitle,
|
|
44
46
|
pages: [{
|
|
45
47
|
id: 'orm-cache',
|
|
46
|
-
title:
|
|
48
|
+
title: pageTitle,
|
|
47
49
|
path: '/orm-cache',
|
|
48
50
|
icon: 'database',
|
|
49
51
|
component: generateOrmCacheAdminComponent(),
|
package/plugins/queue/index.js
CHANGED
|
@@ -72,6 +72,12 @@ function queuePlugin(options = {}) {
|
|
|
72
72
|
pollInterval,
|
|
73
73
|
jobsDir,
|
|
74
74
|
autoStart,
|
|
75
|
+
retention: options.retention,
|
|
76
|
+
retentionDays: options.retentionDays,
|
|
77
|
+
retentionStatuses: options.retentionStatuses,
|
|
78
|
+
cleanupIntervalMs: options.cleanupIntervalMs,
|
|
79
|
+
autoCleanup: options.autoCleanup,
|
|
80
|
+
cleanupOnStart: options.cleanupOnStart,
|
|
75
81
|
});
|
|
76
82
|
|
|
77
83
|
// Expose to ctx & app
|
|
@@ -90,6 +96,11 @@ function queuePlugin(options = {}) {
|
|
|
90
96
|
this.queue = queueManager;
|
|
91
97
|
},
|
|
92
98
|
|
|
99
|
+
api: {
|
|
100
|
+
getQueueManager: () => queueManager,
|
|
101
|
+
clean: (opts) => queueManager?.clean(opts),
|
|
102
|
+
},
|
|
103
|
+
|
|
93
104
|
onRoutesReady(ctx) {
|
|
94
105
|
if (jobsDir && fs.existsSync(jobsDir)) {
|
|
95
106
|
queueManager.loadJobsFromDirectory(jobsDir);
|