@actuate-media/cms-core 0.57.0 → 0.57.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 (118) hide show
  1. package/dist/__tests__/api/stats-seo-cache.test.js +1 -1
  2. package/dist/__tests__/api/stats-seo-cache.test.js.map +1 -1
  3. package/dist/api/guarded-db.d.ts +13 -0
  4. package/dist/api/guarded-db.d.ts.map +1 -0
  5. package/dist/api/guarded-db.js +58 -0
  6. package/dist/api/guarded-db.js.map +1 -0
  7. package/dist/api/handler-factory.d.ts.map +1 -1
  8. package/dist/api/handler-factory.js +2 -1
  9. package/dist/api/handler-factory.js.map +1 -1
  10. package/dist/api/handlers.d.ts +5 -3
  11. package/dist/api/handlers.d.ts.map +1 -1
  12. package/dist/api/handlers.js +54 -13068
  13. package/dist/api/handlers.js.map +1 -1
  14. package/dist/api/route-helpers.d.ts +320 -0
  15. package/dist/api/route-helpers.d.ts.map +1 -0
  16. package/dist/api/route-helpers.js +1307 -0
  17. package/dist/api/route-helpers.js.map +1 -0
  18. package/dist/api/routes/ai-seo.d.ts +3 -0
  19. package/dist/api/routes/ai-seo.d.ts.map +1 -0
  20. package/dist/api/routes/ai-seo.js +377 -0
  21. package/dist/api/routes/ai-seo.js.map +1 -0
  22. package/dist/api/routes/ai-settings.d.ts +3 -0
  23. package/dist/api/routes/ai-settings.d.ts.map +1 -0
  24. package/dist/api/routes/ai-settings.js +764 -0
  25. package/dist/api/routes/ai-settings.js.map +1 -0
  26. package/dist/api/routes/auth.d.ts +3 -0
  27. package/dist/api/routes/auth.d.ts.map +1 -0
  28. package/dist/api/routes/auth.js +787 -0
  29. package/dist/api/routes/auth.js.map +1 -0
  30. package/dist/api/routes/collab.d.ts +3 -0
  31. package/dist/api/routes/collab.d.ts.map +1 -0
  32. package/dist/api/routes/collab.js +426 -0
  33. package/dist/api/routes/collab.js.map +1 -0
  34. package/dist/api/routes/dashboard.d.ts +7 -0
  35. package/dist/api/routes/dashboard.d.ts.map +1 -0
  36. package/dist/api/routes/dashboard.js +540 -0
  37. package/dist/api/routes/dashboard.js.map +1 -0
  38. package/dist/api/routes/documents.d.ts +3 -0
  39. package/dist/api/routes/documents.d.ts.map +1 -0
  40. package/dist/api/routes/documents.js +180 -0
  41. package/dist/api/routes/documents.js.map +1 -0
  42. package/dist/api/routes/folders.d.ts +3 -0
  43. package/dist/api/routes/folders.d.ts.map +1 -0
  44. package/dist/api/routes/folders.js +195 -0
  45. package/dist/api/routes/folders.js.map +1 -0
  46. package/dist/api/routes/forms.d.ts +3 -0
  47. package/dist/api/routes/forms.d.ts.map +1 -0
  48. package/dist/api/routes/forms.js +661 -0
  49. package/dist/api/routes/forms.js.map +1 -0
  50. package/dist/api/routes/globals.d.ts +3 -0
  51. package/dist/api/routes/globals.d.ts.map +1 -0
  52. package/dist/api/routes/globals.js +131 -0
  53. package/dist/api/routes/globals.js.map +1 -0
  54. package/dist/api/routes/media.d.ts +3 -0
  55. package/dist/api/routes/media.d.ts.map +1 -0
  56. package/dist/api/routes/media.js +521 -0
  57. package/dist/api/routes/media.js.map +1 -0
  58. package/dist/api/routes/meta.d.ts +3 -0
  59. package/dist/api/routes/meta.d.ts.map +1 -0
  60. package/dist/api/routes/meta.js +96 -0
  61. package/dist/api/routes/meta.js.map +1 -0
  62. package/dist/api/routes/page-templates.d.ts +3 -0
  63. package/dist/api/routes/page-templates.d.ts.map +1 -0
  64. package/dist/api/routes/page-templates.js +265 -0
  65. package/dist/api/routes/page-templates.js.map +1 -0
  66. package/dist/api/routes/presence.d.ts +3 -0
  67. package/dist/api/routes/presence.d.ts.map +1 -0
  68. package/dist/api/routes/presence.js +35 -0
  69. package/dist/api/routes/presence.js.map +1 -0
  70. package/dist/api/routes/preview.d.ts +3 -0
  71. package/dist/api/routes/preview.d.ts.map +1 -0
  72. package/dist/api/routes/preview.js +111 -0
  73. package/dist/api/routes/preview.js.map +1 -0
  74. package/dist/api/routes/redirects.d.ts +3 -0
  75. package/dist/api/routes/redirects.d.ts.map +1 -0
  76. package/dist/api/routes/redirects.js +790 -0
  77. package/dist/api/routes/redirects.js.map +1 -0
  78. package/dist/api/routes/saved-sections.d.ts +3 -0
  79. package/dist/api/routes/saved-sections.d.ts.map +1 -0
  80. package/dist/api/routes/saved-sections.js +1036 -0
  81. package/dist/api/routes/saved-sections.js.map +1 -0
  82. package/dist/api/routes/scheduling.d.ts +3 -0
  83. package/dist/api/routes/scheduling.d.ts.map +1 -0
  84. package/dist/api/routes/scheduling.js +373 -0
  85. package/dist/api/routes/scheduling.js.map +1 -0
  86. package/dist/api/routes/sections.d.ts +3 -0
  87. package/dist/api/routes/sections.d.ts.map +1 -0
  88. package/dist/api/routes/sections.js +500 -0
  89. package/dist/api/routes/sections.js.map +1 -0
  90. package/dist/api/routes/security-center.d.ts +3 -0
  91. package/dist/api/routes/security-center.d.ts.map +1 -0
  92. package/dist/api/routes/security-center.js +71 -0
  93. package/dist/api/routes/security-center.js.map +1 -0
  94. package/dist/api/routes/seo-public.d.ts +14 -0
  95. package/dist/api/routes/seo-public.d.ts.map +1 -0
  96. package/dist/api/routes/seo-public.js +930 -0
  97. package/dist/api/routes/seo-public.js.map +1 -0
  98. package/dist/api/routes/seo.d.ts +8 -0
  99. package/dist/api/routes/seo.d.ts.map +1 -0
  100. package/dist/api/routes/seo.js +848 -0
  101. package/dist/api/routes/seo.js.map +1 -0
  102. package/dist/api/routes/system.d.ts +3 -0
  103. package/dist/api/routes/system.d.ts.map +1 -0
  104. package/dist/api/routes/system.js +340 -0
  105. package/dist/api/routes/system.js.map +1 -0
  106. package/dist/api/routes/url-resolution.d.ts +3 -0
  107. package/dist/api/routes/url-resolution.d.ts.map +1 -0
  108. package/dist/api/routes/url-resolution.js +457 -0
  109. package/dist/api/routes/url-resolution.js.map +1 -0
  110. package/dist/api/routes/users.d.ts +3 -0
  111. package/dist/api/routes/users.d.ts.map +1 -0
  112. package/dist/api/routes/users.js +1162 -0
  113. package/dist/api/routes/users.js.map +1 -0
  114. package/dist/api/routes/webhooks.d.ts +3 -0
  115. package/dist/api/routes/webhooks.d.ts.map +1 -0
  116. package/dist/api/routes/webhooks.js +338 -0
  117. package/dist/api/routes/webhooks.js.map +1 -0
  118. package/package.json +1 -1
@@ -0,0 +1,540 @@
1
+ import { getActuateConfig } from '../../config/runtime.js';
2
+ import { guardedDb } from '../guarded-db.js';
3
+ import { json, internalError, logSafeQueryFailure, safeCount, safeFindMany, isSecretMissing, requireAuth, } from '../route-helpers.js';
4
+ /**
5
+ * Short-TTL cache for the dashboard's average SEO score.
6
+ *
7
+ * `/stats` otherwise reads the `data` JSON of up to 200 published
8
+ * documents and scores each one on every load — the single heaviest
9
+ * part of the endpoint. The score barely moves minute-to-minute, so we
10
+ * cache it per content-scope (the JSON-stringified Prisma `where`, which
11
+ * encodes the registered collection allowlist) for a short window.
12
+ *
13
+ * Module-level so it survives across requests within a warm isolate;
14
+ * a cold start simply recomputes once. Keyed by scope so multi-config
15
+ * isolates never read each other's score.
16
+ */
17
+ let dashboardSeoCache = null;
18
+ const DASHBOARD_SEO_CACHE_TTL_MS = 60_000;
19
+ /** Test-only: reset the SEO score cache so cases don't bleed into each other. */
20
+ export function __resetDashboardSeoCache() {
21
+ dashboardSeoCache = null;
22
+ }
23
+ /** Cheap meta-completeness score used by /stats and the SEO summary surface. */
24
+ export function computeLightSeoScore(data) {
25
+ let score = 0;
26
+ if (data.metaTitle || data.seoTitle)
27
+ score += 25;
28
+ if (data.metaDescription || data.seoDescription)
29
+ score += 25;
30
+ if (data.canonical)
31
+ score += 25;
32
+ if (data.schemaType)
33
+ score += 25;
34
+ return score;
35
+ }
36
+ export function registerDashboardRoutes(router) {
37
+ const db = guardedDb;
38
+ // ---------------------------------------------------------------------------
39
+ // Stats route
40
+ // ---------------------------------------------------------------------------
41
+ const EMPTY_STATS = {
42
+ totalDocuments: 0,
43
+ totalMedia: 0,
44
+ totalUsers: 0,
45
+ formCount: 0,
46
+ avgSeoScore: 0,
47
+ webhookCount: 0,
48
+ webhookActiveCount: 0,
49
+ collectionCounts: {},
50
+ statusCounts: {},
51
+ recentDocuments: [],
52
+ };
53
+ const EMPTY_DELIVERY_STATS = {
54
+ hasData: false,
55
+ requests24h: 0,
56
+ requestsPrev24h: 0,
57
+ deltaPercent: null,
58
+ errorRate: 0,
59
+ errorCount: 0,
60
+ avgLatencyMs: 0,
61
+ p95LatencyMs: 0,
62
+ maxLatencyMs: 0,
63
+ webhookCount: 0,
64
+ webhookActiveCount: 0,
65
+ };
66
+ const EMPTY_CONTENT_HEALTH = {
67
+ score: 100,
68
+ label: 'Good',
69
+ counts: {
70
+ MISSING_META_DESCRIPTION: 0,
71
+ MISSING_ALT_TEXT: 0,
72
+ BROKEN_INTERNAL_LINK: 0,
73
+ OUTDATED_CONTENT: 0,
74
+ },
75
+ totalIssues: 0,
76
+ lastScanAt: null,
77
+ };
78
+ async function safeGroupBy(model, by, where) {
79
+ if (!model || typeof model !== 'object')
80
+ return [];
81
+ let groupByFn;
82
+ try {
83
+ groupByFn = model.groupBy;
84
+ }
85
+ catch {
86
+ return [];
87
+ }
88
+ if (typeof groupByFn !== 'function')
89
+ return [];
90
+ try {
91
+ return await groupByFn.call(model, { by, where: where ?? {}, _count: { _all: true } });
92
+ }
93
+ catch (err) {
94
+ logSafeQueryFailure('safeGroupBy', err);
95
+ return [];
96
+ }
97
+ }
98
+ /**
99
+ * Average SEO score across published content, cached per content-scope
100
+ * for `DASHBOARD_SEO_CACHE_TTL_MS`. On a cache hit this returns without
101
+ * touching the database — avoiding the 200-document `data` read + scan
102
+ * that dominates `/stats`. Failures from `safeFindMany` surface as an
103
+ * empty list (score 0) and are not cached, so a transient DB blip
104
+ * doesn't pin a stale 0.
105
+ */
106
+ async function getCachedAvgSeoScore(model, where) {
107
+ const key = JSON.stringify(where);
108
+ const now = Date.now();
109
+ if (dashboardSeoCache && dashboardSeoCache.key === key && dashboardSeoCache.expires > now) {
110
+ return dashboardSeoCache.value;
111
+ }
112
+ const docs = await safeFindMany(model, {
113
+ where,
114
+ select: { data: true },
115
+ take: 200,
116
+ });
117
+ let value = 0;
118
+ if (docs.length > 0) {
119
+ const total = docs.reduce((sum, doc) => sum + computeLightSeoScore(doc.data ?? {}), 0);
120
+ value = Math.round(total / docs.length);
121
+ }
122
+ dashboardSeoCache = { key, value, expires: now + DASHBOARD_SEO_CACHE_TTL_MS };
123
+ return value;
124
+ }
125
+ /**
126
+ * Collection slugs that are written by the benchmark suite and a few
127
+ * other internal/test fixtures. Documents in these collections are
128
+ * never editor-facing content and should never appear in the
129
+ * dashboard's stats, counts, or recent-activity feed.
130
+ *
131
+ * This list is the fallback when the consumer ships collection
132
+ * definitions only to the admin client (e.g. `apps/dev`) and
133
+ * `getActuateConfig()?.collections` is therefore empty on the
134
+ * server. Production consumers normally pass `collections` to
135
+ * `handleActuateAPI({ config })`, in which case the registered
136
+ * slugs become the allowlist and this denylist is unused.
137
+ */
138
+ const INTERNAL_COLLECTION_SLUGS = ['benchmarks', 'bench'];
139
+ /**
140
+ * Build the Prisma `where` clause for "user-facing content".
141
+ *
142
+ * - If the CMS config registered collections, restrict to those
143
+ * slugs — anything else is orphan/test data.
144
+ * - Otherwise fall back to excluding the internal denylist.
145
+ *
146
+ * Always excludes soft-deleted rows. Callers can spread additional
147
+ * filters on top (`status: 'PUBLISHED'`, etc.).
148
+ */
149
+ function userContentWhere(extra = {}) {
150
+ const cfg = getActuateConfig();
151
+ const registered = Object.keys(cfg?.collections ?? {});
152
+ const collectionFilter = registered.length > 0 ? { in: registered } : { notIn: [...INTERNAL_COLLECTION_SLUGS] };
153
+ return { deletedAt: null, collection: collectionFilter, ...extra };
154
+ }
155
+ router.get('/stats', async (request) => {
156
+ try {
157
+ if (!isSecretMissing()) {
158
+ const auth = await requireAuth(request);
159
+ if (auth.error)
160
+ return auth.error;
161
+ }
162
+ let d;
163
+ try {
164
+ d = db();
165
+ }
166
+ catch {
167
+ return json({ data: EMPTY_STATS });
168
+ }
169
+ // `userContentWhere()` filters out benchmark / orphan / internal
170
+ // collections so the dashboard surfaces editor content only. See
171
+ // the helper above for the allowlist-vs-denylist behaviour.
172
+ const contentWhere = userContentWhere();
173
+ const publishedContentWhere = userContentWhere({ status: 'PUBLISHED' });
174
+ const [docResult, mediaResult, userResult, recentResult, collGroupResult, statusGroupResult, formResult, seoScoreResult, webhookTotalResult, webhookActiveResult,] = await Promise.allSettled([
175
+ safeCount(d.document, contentWhere),
176
+ safeCount(d.media),
177
+ safeCount(d.user),
178
+ safeFindMany(d.document, {
179
+ // Prisma 7's `'join'` strategy folds the User relation into a
180
+ // single LATERAL SQL query so the dashboard read costs one
181
+ // round-trip instead of two. On the production Vercel Function
182
+ // -> Prisma Postgres path that saves ~80 ms (one full network
183
+ // RTT) on every dashboard load.
184
+ relationLoadStrategy: 'join',
185
+ where: contentWhere,
186
+ orderBy: { updatedAt: 'desc' },
187
+ take: 20,
188
+ select: {
189
+ id: true,
190
+ title: true,
191
+ status: true,
192
+ collection: true,
193
+ updatedAt: true,
194
+ createdById: true,
195
+ createdBy: { select: { name: true, email: true } },
196
+ },
197
+ }),
198
+ safeGroupBy(d.document, ['collection'], contentWhere),
199
+ safeGroupBy(d.document, ['status'], contentWhere),
200
+ safeCount(d.formSubmission),
201
+ // Cached per content-scope so the 200-doc SEO read+scan runs at
202
+ // most once per TTL window across warm requests.
203
+ getCachedAvgSeoScore(d.document, publishedContentWhere),
204
+ // Cheap counts so the dashboard's "Content Delivery" tile doesn't need
205
+ // a second round-trip. `webhookEndpoint` may not exist on every host,
206
+ // so `safeCount` swallows missing-model errors and returns 0.
207
+ safeCount(d.webhookEndpoint),
208
+ safeCount(d.webhookEndpoint, { active: true }),
209
+ ]);
210
+ const collectionCounts = {};
211
+ if (collGroupResult.status === 'fulfilled') {
212
+ for (const g of collGroupResult.value)
213
+ collectionCounts[g.collection] = g._count._all;
214
+ }
215
+ const statusCounts = {};
216
+ if (statusGroupResult.status === 'fulfilled') {
217
+ for (const g of statusGroupResult.value)
218
+ statusCounts[g.status] = g._count._all;
219
+ }
220
+ const avgSeoScore = seoScoreResult.status === 'fulfilled' ? seoScoreResult.value : 0;
221
+ const recentDocs = recentResult.status === 'fulfilled'
222
+ ? recentResult.value.map((doc) => ({
223
+ id: doc.id,
224
+ title: doc.title,
225
+ status: doc.status,
226
+ collection: doc.collection,
227
+ updatedAt: doc.updatedAt,
228
+ author: doc.createdBy?.name ?? doc.createdBy?.email ?? doc.createdById,
229
+ }))
230
+ : [];
231
+ return json({
232
+ data: {
233
+ totalDocuments: docResult.status === 'fulfilled' ? docResult.value : 0,
234
+ totalMedia: mediaResult.status === 'fulfilled' ? mediaResult.value : 0,
235
+ totalUsers: userResult.status === 'fulfilled' ? userResult.value : 0,
236
+ formCount: formResult.status === 'fulfilled' ? formResult.value : 0,
237
+ avgSeoScore,
238
+ webhookCount: webhookTotalResult.status === 'fulfilled' ? webhookTotalResult.value : 0,
239
+ webhookActiveCount: webhookActiveResult.status === 'fulfilled' ? webhookActiveResult.value : 0,
240
+ collectionCounts,
241
+ statusCounts,
242
+ recentDocuments: recentDocs,
243
+ },
244
+ });
245
+ }
246
+ catch {
247
+ return json({ data: EMPTY_STATS });
248
+ }
249
+ });
250
+ // ---------------------------------------------------------------------------
251
+ // Content Health — backs the dashboard "Content Health" issue rows
252
+ // ---------------------------------------------------------------------------
253
+ //
254
+ // Aggregates rows from `actuate_content_issues` (populated nightly by
255
+ // /cron/content-health) by `type`, returning the totals the dashboard
256
+ // surfaces. We also compute an overall content-health score so the
257
+ // big number + progress bar stay consistent with the per-row counts.
258
+ //
259
+ // Score model:
260
+ // - Start at 100.
261
+ // - Subtract 5 per missing-meta-description issue.
262
+ // - Subtract 2 per missing-alt image.
263
+ // - Subtract 10 per broken internal link.
264
+ // - Subtract 3 per outdated document.
265
+ // Floor at 0. The weights reflect SEO impact (broken links and
266
+ // missing meta hit hardest) and are kept here, not in the DB, so we
267
+ // can tune them without a migration.
268
+ router.get('/stats/content-health', async (request) => {
269
+ try {
270
+ if (!isSecretMissing()) {
271
+ const auth = await requireAuth(request);
272
+ if (auth.error)
273
+ return auth.error;
274
+ }
275
+ let d;
276
+ try {
277
+ d = db();
278
+ }
279
+ catch {
280
+ return json({ data: EMPTY_CONTENT_HEALTH });
281
+ }
282
+ if (!d?.contentIssue) {
283
+ return json({ data: EMPTY_CONTENT_HEALTH });
284
+ }
285
+ let rows = [];
286
+ try {
287
+ rows = await d.contentIssue.groupBy({
288
+ by: ['type'],
289
+ _sum: { count: true },
290
+ });
291
+ }
292
+ catch {
293
+ return json({ data: EMPTY_CONTENT_HEALTH });
294
+ }
295
+ const counts = {
296
+ MISSING_META_DESCRIPTION: 0,
297
+ MISSING_ALT_TEXT: 0,
298
+ BROKEN_INTERNAL_LINK: 0,
299
+ OUTDATED_CONTENT: 0,
300
+ };
301
+ for (const r of rows) {
302
+ counts[r.type] = r._sum.count ?? 0;
303
+ }
304
+ // Read-out via locals so TS narrows away the Record<string,..>
305
+ // indexed-access undefined union — we know the keys are present
306
+ // because we initialised them above.
307
+ const cMeta = counts['MISSING_META_DESCRIPTION'] ?? 0;
308
+ const cAlt = counts['MISSING_ALT_TEXT'] ?? 0;
309
+ const cLinks = counts['BROKEN_INTERNAL_LINK'] ?? 0;
310
+ const cOutdated = counts['OUTDATED_CONTENT'] ?? 0;
311
+ // Penalty model — see comment above.
312
+ let score = 100;
313
+ score -= cMeta * 5;
314
+ score -= cAlt * 2;
315
+ score -= cLinks * 10;
316
+ score -= cOutdated * 3;
317
+ score = Math.max(0, Math.min(100, score));
318
+ // Last scan timestamp — useful for the dashboard to display
319
+ // "Last scanned X minutes ago" so editors don't think the
320
+ // numbers are live.
321
+ let lastScanAt = null;
322
+ try {
323
+ const latest = await d.contentIssue.findFirst({
324
+ orderBy: { lastSeenAt: 'desc' },
325
+ select: { lastSeenAt: true },
326
+ });
327
+ lastScanAt = latest?.lastSeenAt ?? null;
328
+ }
329
+ catch {
330
+ /* ignore — empty table or stale schema */
331
+ }
332
+ return json({
333
+ data: {
334
+ score,
335
+ label: score >= 70 ? 'Good' : score >= 40 ? 'Fair' : 'Poor',
336
+ counts,
337
+ totalIssues: cMeta + cAlt + cLinks + cOutdated,
338
+ lastScanAt: lastScanAt?.toISOString() ?? null,
339
+ },
340
+ });
341
+ }
342
+ catch {
343
+ return json({ data: EMPTY_CONTENT_HEALTH });
344
+ }
345
+ });
346
+ // ---------------------------------------------------------------------------
347
+ // Delivery API metrics — backs the dashboard "Delivery API" tiles
348
+ // ---------------------------------------------------------------------------
349
+ //
350
+ // Reads from `actuate_api_request_metrics` (per-minute pre-aggregated
351
+ // counters populated by `recordApiRequest` in handler-factory.ts) and
352
+ // folds the last 24h into the four scalars the dashboard surfaces:
353
+ //
354
+ // - requests24h, requestsPrev24h (delta = "+12% vs yesterday")
355
+ // - errorRate (4xx + 5xx as fraction of total)
356
+ // - avgLatencyMs, p95LatencyMs (latter via histogram merge)
357
+ // - webhooks (total + active count)
358
+ //
359
+ // The endpoint returns a complete, never-undefined object even when
360
+ // the metrics table is empty — the dashboard distinguishes
361
+ // "no traffic yet" from "table missing" via the boolean `hasData`
362
+ // field so it can render an empty state without an extra round-trip.
363
+ router.get('/stats/api-delivery', async (request) => {
364
+ try {
365
+ if (!isSecretMissing()) {
366
+ const auth = await requireAuth(request);
367
+ if (auth.error)
368
+ return auth.error;
369
+ }
370
+ let d;
371
+ try {
372
+ d = db();
373
+ }
374
+ catch {
375
+ return json({ data: EMPTY_DELIVERY_STATS });
376
+ }
377
+ if (!d?.apiRequestMetric) {
378
+ return json({ data: EMPTY_DELIVERY_STATS });
379
+ }
380
+ const now = new Date();
381
+ const since24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
382
+ const sincePrev24h = new Date(now.getTime() - 48 * 60 * 60 * 1000);
383
+ const [currentResult, prevResult, webhookTotalResult, webhookActiveResult] = await Promise.allSettled([
384
+ safeFindMany(d.apiRequestMetric, {
385
+ where: { bucketStart: { gte: since24h } },
386
+ select: {
387
+ statusBucket: true,
388
+ count: true,
389
+ sumLatencyMs: true,
390
+ maxLatencyMs: true,
391
+ latencyHistogram: true,
392
+ },
393
+ }),
394
+ safeFindMany(d.apiRequestMetric, {
395
+ where: { bucketStart: { gte: sincePrev24h, lt: since24h } },
396
+ select: { count: true },
397
+ }),
398
+ safeCount(d.webhookEndpoint),
399
+ safeCount(d.webhookEndpoint, { active: true }),
400
+ ]);
401
+ const currentRows = currentResult.status === 'fulfilled' ? currentResult.value : [];
402
+ const prevRows = prevResult.status === 'fulfilled' ? prevResult.value : [];
403
+ let total = 0;
404
+ let errorTotal = 0; // 4xx + 5xx
405
+ let sumLatency = 0;
406
+ let maxLatency = 0;
407
+ // Histogram merge for p95 — fixed 12-bucket array (see
408
+ // diagnostics/api-metrics.ts `HISTOGRAM_LENGTH`).
409
+ const mergedHistogram = new Array(12).fill(0);
410
+ for (const row of currentRows) {
411
+ total += row.count;
412
+ if (row.statusBucket === '4xx' || row.statusBucket === '5xx')
413
+ errorTotal += row.count;
414
+ sumLatency += row.sumLatencyMs;
415
+ if (row.maxLatencyMs > maxLatency)
416
+ maxLatency = row.maxLatencyMs;
417
+ const h = row.latencyHistogram;
418
+ if (Array.isArray(h)) {
419
+ for (let i = 0; i < mergedHistogram.length; i++) {
420
+ const v = h[i];
421
+ const current = mergedHistogram[i] ?? 0;
422
+ if (typeof v === 'number')
423
+ mergedHistogram[i] = current + v;
424
+ }
425
+ }
426
+ }
427
+ const prevTotal = prevRows.reduce((s, r) => s + r.count, 0);
428
+ const delta = prevTotal === 0 ? null : Math.round(((total - prevTotal) / prevTotal) * 1000) / 10; // 1-decimal %
429
+ const errorRate = total === 0 ? 0 : errorTotal / total;
430
+ const avgLatency = total === 0 ? 0 : sumLatency / total;
431
+ // p95: walk the merged histogram cumulatively, return the upper
432
+ // bound of the bucket that crosses the 0.95 watermark.
433
+ const LATENCY_BUCKET_UPPER = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024];
434
+ let p95Latency = 0;
435
+ if (total > 0) {
436
+ const target = total * 0.95;
437
+ let acc = 0;
438
+ for (let i = 0; i < mergedHistogram.length; i++) {
439
+ acc += mergedHistogram[i] ?? 0;
440
+ if (acc >= target) {
441
+ p95Latency =
442
+ i < LATENCY_BUCKET_UPPER.length
443
+ ? LATENCY_BUCKET_UPPER[i]
444
+ : LATENCY_BUCKET_UPPER[LATENCY_BUCKET_UPPER.length - 1] * 2;
445
+ break;
446
+ }
447
+ }
448
+ }
449
+ const webhookTotal = webhookTotalResult.status === 'fulfilled' ? webhookTotalResult.value : 0;
450
+ const webhookActive = webhookActiveResult.status === 'fulfilled' ? webhookActiveResult.value : 0;
451
+ return json({
452
+ data: {
453
+ hasData: total > 0,
454
+ requests24h: total,
455
+ requestsPrev24h: prevTotal,
456
+ deltaPercent: delta,
457
+ errorRate: Math.round(errorRate * 10000) / 10000, // 4-dec precision
458
+ errorCount: errorTotal,
459
+ avgLatencyMs: Math.round(avgLatency * 10) / 10,
460
+ p95LatencyMs: p95Latency,
461
+ maxLatencyMs: Math.round(maxLatency * 10) / 10,
462
+ webhookCount: webhookTotal,
463
+ webhookActiveCount: webhookActive,
464
+ },
465
+ });
466
+ }
467
+ catch {
468
+ return json({ data: EMPTY_DELIVERY_STATS });
469
+ }
470
+ });
471
+ // ---------------------------------------------------------------------------
472
+ // Global search
473
+ // ---------------------------------------------------------------------------
474
+ router.get('/search/global', async (request) => {
475
+ try {
476
+ const auth = await requireAuth(request);
477
+ if (auth.error)
478
+ return auth.error;
479
+ const url = new URL(request.url, 'http://localhost');
480
+ const q = (url.searchParams.get('q') ?? '').trim();
481
+ if (!q)
482
+ return json({ data: { documents: [], media: [], users: [] } });
483
+ const d = db();
484
+ // Documents and media are visible to all signed-in users; the user
485
+ // directory is gated to EDITOR+ so a CLIENT can't enumerate the team
486
+ // (or harvest emails for spear-phishing).
487
+ const canSeeUserDirectory = auth.session.role === 'ADMIN' || auth.session.role === 'EDITOR';
488
+ const [documents, media, users] = await Promise.all([
489
+ safeFindMany(d.document, {
490
+ where: {
491
+ deletedAt: null,
492
+ OR: [
493
+ { title: { contains: q, mode: 'insensitive' } },
494
+ { plainText: { contains: q, mode: 'insensitive' } },
495
+ ],
496
+ },
497
+ take: 10,
498
+ orderBy: { updatedAt: 'desc' },
499
+ select: {
500
+ id: true,
501
+ title: true,
502
+ slug: true,
503
+ collection: true,
504
+ status: true,
505
+ updatedAt: true,
506
+ },
507
+ }),
508
+ safeFindMany(d.media, {
509
+ where: {
510
+ OR: [
511
+ { filename: { contains: q, mode: 'insensitive' } },
512
+ { altText: { contains: q, mode: 'insensitive' } },
513
+ ],
514
+ },
515
+ take: 5,
516
+ orderBy: { createdAt: 'desc' },
517
+ select: { id: true, filename: true, altText: true, mimeType: true, storageKey: true },
518
+ }),
519
+ canSeeUserDirectory
520
+ ? safeFindMany(d.user, {
521
+ where: {
522
+ isActive: true,
523
+ OR: [
524
+ { name: { contains: q, mode: 'insensitive' } },
525
+ { email: { contains: q, mode: 'insensitive' } },
526
+ ],
527
+ },
528
+ take: 5,
529
+ select: { id: true, name: true, email: true, role: true },
530
+ })
531
+ : Promise.resolve([]),
532
+ ]);
533
+ return json({ data: { documents, media, users } });
534
+ }
535
+ catch (err) {
536
+ return internalError(err);
537
+ }
538
+ });
539
+ }
540
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../src/api/routes/dashboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ApiRouter } from '../router.js';
2
+ export declare function registerDocumentRoutes(router: ApiRouter): void;
3
+ //# sourceMappingURL=documents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../../src/api/routes/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AA2B7C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgM9D"}