webspresso 0.0.97 → 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.
Files changed (100) hide show
  1. package/.agents/ADMIN.md +153 -5
  2. package/.agents/ARCHITECTURE.md +10 -0
  3. package/.agents/CLI.md +195 -1
  4. package/.agents/PLUGINS.md +153 -9
  5. package/.agents/QUEUE.md +43 -0
  6. package/bin/commands/add-esbuild.js +80 -0
  7. package/bin/commands/add-tailwind.js +76 -31
  8. package/bin/commands/analytics-prune.js +57 -0
  9. package/bin/commands/api.js +207 -15
  10. package/bin/commands/build.js +99 -0
  11. package/bin/commands/dev.js +64 -35
  12. package/bin/commands/doctor.js +26 -0
  13. package/bin/commands/email-prune.js +47 -0
  14. package/bin/commands/make-job.js +86 -0
  15. package/bin/commands/make-model.js +250 -0
  16. package/bin/commands/new.js +45 -28
  17. package/bin/commands/openapi.js +102 -0
  18. package/bin/commands/page.js +6 -3
  19. package/bin/commands/queue-prune.js +59 -0
  20. package/bin/commands/routes.js +52 -0
  21. package/bin/commands/service.js +6 -3
  22. package/bin/commands/typegen.js +106 -0
  23. package/bin/commands/upload-prune.js +79 -0
  24. package/bin/webspresso.js +22 -0
  25. package/core/applySchema.js +13 -6
  26. package/core/auth/hash.js +21 -12
  27. package/core/compression/middleware.js +23 -17
  28. package/core/openapi/build-from-api-routes.js +160 -29
  29. package/core/orm/cache/fingerprint.js +15 -1
  30. package/core/orm/cache/memory-provider.js +27 -2
  31. package/core/orm/index.js +12 -7
  32. package/core/orm/model.js +49 -7
  33. package/core/queue/adapters/database.js +94 -0
  34. package/core/queue/adapters/memory.js +62 -0
  35. package/core/queue/adapters/redis.js +80 -0
  36. package/core/queue/manager.js +123 -0
  37. package/core/ssr/stream.js +11 -0
  38. package/index.d.ts +660 -10
  39. package/index.js +44 -96
  40. package/package.json +1 -1
  41. package/plugins/admin-panel/api.js +279 -17
  42. package/plugins/admin-panel/app.js +114 -22
  43. package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +314 -9
  44. package/plugins/admin-panel/client/parts/04-field-renderers.js +264 -67
  45. package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +10 -5
  46. package/plugins/admin-panel/client/parts/06-login-setup-forms.js +119 -28
  47. package/plugins/admin-panel/client/parts/07-model-list.js +49 -8
  48. package/plugins/admin-panel/client/parts/08-record-list.js +243 -23
  49. package/plugins/admin-panel/client/parts/09-record-form.js +59 -12
  50. package/plugins/admin-panel/client/parts/10-export-registry.js +23 -0
  51. package/plugins/admin-panel/core/admin-module.js +13 -7
  52. package/plugins/admin-panel/core/api-extensions.js +30 -2
  53. package/plugins/admin-panel/core/registry.js +96 -16
  54. package/plugins/admin-panel/index.js +96 -0
  55. package/plugins/admin-panel/lib/login-rate-limiter.js +119 -0
  56. package/plugins/admin-panel/modules/admin-users.js +16 -1
  57. package/plugins/admin-panel/modules/bulk-actions.js +13 -5
  58. package/plugins/admin-panel/modules/custom-pages.js +492 -3
  59. package/plugins/admin-panel/modules/dashboard.js +283 -0
  60. package/plugins/admin-panel/modules/menu.js +56 -18
  61. package/plugins/admin-panel/modules/user-management.js +24 -6
  62. package/plugins/audit-log/admin-component.js +11 -0
  63. package/plugins/audit-log/api-handlers.js +34 -0
  64. package/plugins/audit-log/index.js +129 -3
  65. package/plugins/audit-log/purge.js +60 -10
  66. package/plugins/dashboard/app.js +152 -27
  67. package/plugins/dashboard/index.js +143 -39
  68. package/plugins/dashboard/styles.js +78 -0
  69. package/plugins/data-exchange/export-xlsx.js +9 -2
  70. package/plugins/data-exchange/import.js +9 -2
  71. package/plugins/data-exchange/index.js +6 -3
  72. package/plugins/email/index.js +132 -2
  73. package/plugins/email/purge.js +60 -10
  74. package/plugins/index.js +4 -0
  75. package/plugins/orm-cache-admin/index.js +4 -2
  76. package/plugins/queue/index.js +11 -0
  77. package/plugins/schema-explorer/admin-component.js +714 -0
  78. package/plugins/schema-explorer/relations-graph.js +205 -0
  79. package/plugins/schema-explorer.js +248 -69
  80. package/plugins/site-analytics/admin-component.js +23 -0
  81. package/plugins/site-analytics/api-handlers.js +42 -0
  82. package/plugins/site-analytics/index.js +147 -2
  83. package/plugins/site-analytics/purge.js +168 -0
  84. package/plugins/swagger.js +261 -13
  85. package/plugins/upload/index.js +26 -0
  86. package/plugins/upload/orphan-pruner.js +292 -0
  87. package/src/api/api-loader.js +3 -1
  88. package/src/bundle/index.js +517 -0
  89. package/src/bundle/manifest.js +165 -0
  90. package/src/dev/banner.js +296 -0
  91. package/src/dev/colors.js +70 -0
  92. package/src/dev/index.js +62 -0
  93. package/src/dev/profiler.js +354 -0
  94. package/src/dev/typegen.js +497 -0
  95. package/src/dev-reload/index.js +611 -0
  96. package/src/file-router.js +210 -62
  97. package/src/helpers.js +958 -98
  98. package/src/njk-frontmatter.js +12 -0
  99. package/src/pages/page-loader.js +61 -11
  100. package/src/server.js +629 -63
@@ -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
- * @returns {Promise<number>}
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
- if (Number.isNaN(olderThan.getTime())) {
20
- throw new Error('purgeEmailLogs: invalid olderThan date');
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
- return knex(tableName).where('created_at', '<', olderThan).delete();
72
+ const deleted = await query.delete();
73
+ return Number(deleted) || 0;
24
74
  }
25
75
 
26
76
  module.exports = {
package/plugins/index.js CHANGED
@@ -7,6 +7,8 @@ module.exports = {
7
7
  get sitemapPlugin() { return require('./sitemap'); },
8
8
  get analyticsPlugin() { return require('./analytics'); },
9
9
  get dashboardPlugin() { return require('./dashboard/index'); },
10
+ get devToolbarPlugin() { return require('./dashboard/index').devToolbarPlugin; },
11
+ get devtoolsPlugin() { return require('./dashboard/index').devtoolsPlugin; },
10
12
  get schemaExplorerPlugin() { return require('./schema-explorer'); },
11
13
  get adminPanelPlugin() { return require('./admin-panel'); },
12
14
  get seoCheckerPlugin() { return require('./seo-checker'); },
@@ -14,6 +16,8 @@ module.exports = {
14
16
  get auditLogPlugin() { return require('./audit-log'); },
15
17
  get recaptchaPlugin() { return require('./recaptcha'); },
16
18
  get swaggerPlugin() { return require('./swagger'); },
19
+ get openapiPlugin() { return require('./swagger'); },
20
+ get apiDocsPlugin() { return require('./swagger'); },
17
21
  get healthCheckPlugin() { return require('./health-check'); },
18
22
  get restResourcePlugin() { return require('./rest-resources'); },
19
23
  get ormCacheAdminPlugin() { return require('./orm-cache-admin'); },
@@ -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: 'ORM Cache',
48
+ title: pageTitle,
47
49
  path: '/orm-cache',
48
50
  icon: 'database',
49
51
  component: generateOrmCacheAdminComponent(),
@@ -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);