@we8/cms 0.1.0
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/README.md +78 -0
- package/dist/auth/auth.d.ts +87 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +90 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/routes.admin.d.ts +22 -0
- package/dist/auth/routes.admin.d.ts.map +1 -0
- package/dist/auth/routes.admin.js +106 -0
- package/dist/auth/routes.admin.js.map +1 -0
- package/dist/auth/schema.d.ts +1395 -0
- package/dist/auth/schema.d.ts.map +1 -0
- package/dist/auth/schema.js +76 -0
- package/dist/auth/schema.js.map +1 -0
- package/dist/env.d.ts +64 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +48 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +170 -0
- package/dist/index.js.map +1 -0
- package/dist/keys/middleware.d.ts +54 -0
- package/dist/keys/middleware.d.ts.map +1 -0
- package/dist/keys/middleware.js +84 -0
- package/dist/keys/middleware.js.map +1 -0
- package/dist/keys/routes.admin.d.ts +16 -0
- package/dist/keys/routes.admin.d.ts.map +1 -0
- package/dist/keys/routes.admin.js +34 -0
- package/dist/keys/routes.admin.js.map +1 -0
- package/dist/keys/service.d.ts +36 -0
- package/dist/keys/service.d.ts.map +1 -0
- package/dist/keys/service.js +120 -0
- package/dist/keys/service.js.map +1 -0
- package/dist/lib/bots.d.ts +24 -0
- package/dist/lib/bots.d.ts.map +1 -0
- package/dist/lib/bots.js +79 -0
- package/dist/lib/bots.js.map +1 -0
- package/dist/lib/email.d.ts +62 -0
- package/dist/lib/email.d.ts.map +1 -0
- package/dist/lib/email.js +64 -0
- package/dist/lib/email.js.map +1 -0
- package/dist/lib/errors.d.ts +12 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +23 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/notify.d.ts +20 -0
- package/dist/lib/notify.d.ts.map +1 -0
- package/dist/lib/notify.js +60 -0
- package/dist/lib/notify.js.map +1 -0
- package/dist/lib/retention.d.ts +15 -0
- package/dist/lib/retention.d.ts.map +1 -0
- package/dist/lib/retention.js +40 -0
- package/dist/lib/retention.js.map +1 -0
- package/dist/lib/sanitize.d.ts +14 -0
- package/dist/lib/sanitize.d.ts.map +1 -0
- package/dist/lib/sanitize.js +145 -0
- package/dist/lib/sanitize.js.map +1 -0
- package/dist/lib/turnstile.d.ts +7 -0
- package/dist/lib/turnstile.d.ts.map +1 -0
- package/dist/lib/turnstile.js +32 -0
- package/dist/lib/turnstile.js.map +1 -0
- package/dist/lib/validate.d.ts +11 -0
- package/dist/lib/validate.d.ts.map +1 -0
- package/dist/lib/validate.js +22 -0
- package/dist/lib/validate.js.map +1 -0
- package/dist/lib/word-count.d.ts +7 -0
- package/dist/lib/word-count.d.ts.map +1 -0
- package/dist/lib/word-count.js +14 -0
- package/dist/lib/word-count.js.map +1 -0
- package/dist/middleware/auth.d.ts +46 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +60 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/cors.d.ts +33 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +55 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/module-gate.d.ts +11 -0
- package/dist/middleware/module-gate.d.ts.map +1 -0
- package/dist/middleware/module-gate.js +18 -0
- package/dist/middleware/module-gate.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +13 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +30 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/request-id.d.ts +8 -0
- package/dist/middleware/request-id.d.ts.map +1 -0
- package/dist/middleware/request-id.js +11 -0
- package/dist/middleware/request-id.js.map +1 -0
- package/dist/modules/aeo/checks.d.ts +41 -0
- package/dist/modules/aeo/checks.d.ts.map +1 -0
- package/dist/modules/aeo/checks.js +127 -0
- package/dist/modules/aeo/checks.js.map +1 -0
- package/dist/modules/aeo/llms.d.ts +24 -0
- package/dist/modules/aeo/llms.d.ts.map +1 -0
- package/dist/modules/aeo/llms.js +92 -0
- package/dist/modules/aeo/llms.js.map +1 -0
- package/dist/modules/aeo/routes.admin.d.ts +12 -0
- package/dist/modules/aeo/routes.admin.d.ts.map +1 -0
- package/dist/modules/aeo/routes.admin.js +37 -0
- package/dist/modules/aeo/routes.admin.js.map +1 -0
- package/dist/modules/analytics/db.d.ts +24 -0
- package/dist/modules/analytics/db.d.ts.map +1 -0
- package/dist/modules/analytics/db.js +24 -0
- package/dist/modules/analytics/db.js.map +1 -0
- package/dist/modules/analytics/routes.admin.d.ts +6 -0
- package/dist/modules/analytics/routes.admin.d.ts.map +1 -0
- package/dist/modules/analytics/routes.admin.js +21 -0
- package/dist/modules/analytics/routes.admin.js.map +1 -0
- package/dist/modules/analytics/routes.public.d.ts +6 -0
- package/dist/modules/analytics/routes.public.d.ts.map +1 -0
- package/dist/modules/analytics/routes.public.js +33 -0
- package/dist/modules/analytics/routes.public.js.map +1 -0
- package/dist/modules/analytics/service.d.ts +16 -0
- package/dist/modules/analytics/service.d.ts.map +1 -0
- package/dist/modules/analytics/service.js +148 -0
- package/dist/modules/analytics/service.js.map +1 -0
- package/dist/modules/authors/routes.admin.d.ts +6 -0
- package/dist/modules/authors/routes.admin.d.ts.map +1 -0
- package/dist/modules/authors/routes.admin.js +29 -0
- package/dist/modules/authors/routes.admin.js.map +1 -0
- package/dist/modules/authors/routes.public.d.ts +6 -0
- package/dist/modules/authors/routes.public.d.ts.map +1 -0
- package/dist/modules/authors/routes.public.js +11 -0
- package/dist/modules/authors/routes.public.js.map +1 -0
- package/dist/modules/authors/service.d.ts +26 -0
- package/dist/modules/authors/service.d.ts.map +1 -0
- package/dist/modules/authors/service.js +116 -0
- package/dist/modules/authors/service.js.map +1 -0
- package/dist/modules/content/db.d.ts +127 -0
- package/dist/modules/content/db.d.ts.map +1 -0
- package/dist/modules/content/db.js +203 -0
- package/dist/modules/content/db.js.map +1 -0
- package/dist/modules/content/publish.d.ts +38 -0
- package/dist/modules/content/publish.d.ts.map +1 -0
- package/dist/modules/content/publish.js +184 -0
- package/dist/modules/content/publish.js.map +1 -0
- package/dist/modules/content/routes.admin.d.ts +8 -0
- package/dist/modules/content/routes.admin.d.ts.map +1 -0
- package/dist/modules/content/routes.admin.js +122 -0
- package/dist/modules/content/routes.admin.js.map +1 -0
- package/dist/modules/content/routes.public.d.ts +6 -0
- package/dist/modules/content/routes.public.d.ts.map +1 -0
- package/dist/modules/content/routes.public.js +56 -0
- package/dist/modules/content/routes.public.js.map +1 -0
- package/dist/modules/content/service.d.ts +137 -0
- package/dist/modules/content/service.d.ts.map +1 -0
- package/dist/modules/content/service.js +348 -0
- package/dist/modules/content/service.js.map +1 -0
- package/dist/modules/forms/db.d.ts +42 -0
- package/dist/modules/forms/db.d.ts.map +1 -0
- package/dist/modules/forms/db.js +62 -0
- package/dist/modules/forms/db.js.map +1 -0
- package/dist/modules/forms/forms-routes.admin.d.ts +6 -0
- package/dist/modules/forms/forms-routes.admin.d.ts.map +1 -0
- package/dist/modules/forms/forms-routes.admin.js +37 -0
- package/dist/modules/forms/forms-routes.admin.js.map +1 -0
- package/dist/modules/forms/forms-service.d.ts +18 -0
- package/dist/modules/forms/forms-service.d.ts.map +1 -0
- package/dist/modules/forms/forms-service.js +121 -0
- package/dist/modules/forms/forms-service.js.map +1 -0
- package/dist/modules/forms/routes.public.d.ts +6 -0
- package/dist/modules/forms/routes.public.d.ts.map +1 -0
- package/dist/modules/forms/routes.public.js +41 -0
- package/dist/modules/forms/routes.public.js.map +1 -0
- package/dist/modules/forms/submissions-routes.admin.d.ts +6 -0
- package/dist/modules/forms/submissions-routes.admin.d.ts.map +1 -0
- package/dist/modules/forms/submissions-routes.admin.js +46 -0
- package/dist/modules/forms/submissions-routes.admin.js.map +1 -0
- package/dist/modules/forms/submissions-service.d.ts +45 -0
- package/dist/modules/forms/submissions-service.d.ts.map +1 -0
- package/dist/modules/forms/submissions-service.js +174 -0
- package/dist/modules/forms/submissions-service.js.map +1 -0
- package/dist/modules/media/db.d.ts +15 -0
- package/dist/modules/media/db.d.ts.map +1 -0
- package/dist/modules/media/db.js +24 -0
- package/dist/modules/media/db.js.map +1 -0
- package/dist/modules/media/routes.admin.d.ts +6 -0
- package/dist/modules/media/routes.admin.d.ts.map +1 -0
- package/dist/modules/media/routes.admin.js +30 -0
- package/dist/modules/media/routes.admin.js.map +1 -0
- package/dist/modules/media/routes.public.d.ts +6 -0
- package/dist/modules/media/routes.public.d.ts.map +1 -0
- package/dist/modules/media/routes.public.js +35 -0
- package/dist/modules/media/routes.public.js.map +1 -0
- package/dist/modules/media/service.d.ts +32 -0
- package/dist/modules/media/service.d.ts.map +1 -0
- package/dist/modules/media/service.js +99 -0
- package/dist/modules/media/service.js.map +1 -0
- package/dist/modules/openapi/spec.d.ts +21 -0
- package/dist/modules/openapi/spec.d.ts.map +1 -0
- package/dist/modules/openapi/spec.js +468 -0
- package/dist/modules/openapi/spec.js.map +1 -0
- package/dist/modules/seo/jsonld.d.ts +25 -0
- package/dist/modules/seo/jsonld.d.ts.map +1 -0
- package/dist/modules/seo/jsonld.js +67 -0
- package/dist/modules/seo/jsonld.js.map +1 -0
- package/dist/modules/seo/routes.public.d.ts +6 -0
- package/dist/modules/seo/routes.public.d.ts.map +1 -0
- package/dist/modules/seo/routes.public.js +39 -0
- package/dist/modules/seo/routes.public.js.map +1 -0
- package/dist/modules/seo/sitemap.d.ts +3 -0
- package/dist/modules/seo/sitemap.d.ts.map +1 -0
- package/dist/modules/seo/sitemap.js +39 -0
- package/dist/modules/seo/sitemap.js.map +1 -0
- package/dist/modules/settings/routes.admin.d.ts +12 -0
- package/dist/modules/settings/routes.admin.d.ts.map +1 -0
- package/dist/modules/settings/routes.admin.js +27 -0
- package/dist/modules/settings/routes.admin.js.map +1 -0
- package/dist/modules/site-documents/routes.admin.d.ts +10 -0
- package/dist/modules/site-documents/routes.admin.d.ts.map +1 -0
- package/dist/modules/site-documents/routes.admin.js +29 -0
- package/dist/modules/site-documents/routes.admin.js.map +1 -0
- package/dist/modules/site-documents/routes.public.d.ts +6 -0
- package/dist/modules/site-documents/routes.public.d.ts.map +1 -0
- package/dist/modules/site-documents/routes.public.js +16 -0
- package/dist/modules/site-documents/routes.public.js.map +1 -0
- package/dist/modules/site-documents/service.d.ts +11 -0
- package/dist/modules/site-documents/service.d.ts.map +1 -0
- package/dist/modules/site-documents/service.js +42 -0
- package/dist/modules/site-documents/service.js.map +1 -0
- package/dist/modules/webhooks/dispatch.d.ts +14 -0
- package/dist/modules/webhooks/dispatch.d.ts.map +1 -0
- package/dist/modules/webhooks/dispatch.js +84 -0
- package/dist/modules/webhooks/dispatch.js.map +1 -0
- package/dist/shared/constants.d.ts +79 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +89 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/envelope.d.ts +37 -0
- package/dist/shared/envelope.d.ts.map +1 -0
- package/dist/shared/envelope.js +46 -0
- package/dist/shared/envelope.js.map +1 -0
- package/dist/shared/hash.d.ts +2 -0
- package/dist/shared/hash.d.ts.map +1 -0
- package/dist/shared/hash.js +8 -0
- package/dist/shared/hash.js.map +1 -0
- package/dist/shared/index.d.ts +13 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +13 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/schemas/aeo.d.ts +182 -0
- package/dist/shared/schemas/aeo.d.ts.map +1 -0
- package/dist/shared/schemas/aeo.js +43 -0
- package/dist/shared/schemas/aeo.js.map +1 -0
- package/dist/shared/schemas/analytics.d.ts +277 -0
- package/dist/shared/schemas/analytics.d.ts.map +1 -0
- package/dist/shared/schemas/analytics.js +74 -0
- package/dist/shared/schemas/analytics.js.map +1 -0
- package/dist/shared/schemas/api-key.d.ts +77 -0
- package/dist/shared/schemas/api-key.d.ts.map +1 -0
- package/dist/shared/schemas/api-key.js +27 -0
- package/dist/shared/schemas/api-key.js.map +1 -0
- package/dist/shared/schemas/author.d.ts +147 -0
- package/dist/shared/schemas/author.d.ts.map +1 -0
- package/dist/shared/schemas/author.js +27 -0
- package/dist/shared/schemas/author.js.map +1 -0
- package/dist/shared/schemas/form.d.ts +341 -0
- package/dist/shared/schemas/form.d.ts.map +1 -0
- package/dist/shared/schemas/form.js +117 -0
- package/dist/shared/schemas/form.js.map +1 -0
- package/dist/shared/schemas/index.d.ts +12 -0
- package/dist/shared/schemas/index.d.ts.map +1 -0
- package/dist/shared/schemas/index.js +12 -0
- package/dist/shared/schemas/index.js.map +1 -0
- package/dist/shared/schemas/media.d.ts +42 -0
- package/dist/shared/schemas/media.d.ts.map +1 -0
- package/dist/shared/schemas/media.js +16 -0
- package/dist/shared/schemas/media.js.map +1 -0
- package/dist/shared/schemas/pagination.d.ts +34 -0
- package/dist/shared/schemas/pagination.d.ts.map +1 -0
- package/dist/shared/schemas/pagination.js +21 -0
- package/dist/shared/schemas/pagination.js.map +1 -0
- package/dist/shared/schemas/post.d.ts +862 -0
- package/dist/shared/schemas/post.d.ts.map +1 -0
- package/dist/shared/schemas/post.js +187 -0
- package/dist/shared/schemas/post.js.map +1 -0
- package/dist/shared/schemas/settings.d.ts +839 -0
- package/dist/shared/schemas/settings.d.ts.map +1 -0
- package/dist/shared/schemas/settings.js +202 -0
- package/dist/shared/schemas/settings.js.map +1 -0
- package/dist/shared/schemas/site-document.d.ts +38 -0
- package/dist/shared/schemas/site-document.d.ts.map +1 -0
- package/dist/shared/schemas/site-document.js +21 -0
- package/dist/shared/schemas/site-document.js.map +1 -0
- package/dist/shared/schemas/webhook.d.ts +39 -0
- package/dist/shared/schemas/webhook.d.ts.map +1 -0
- package/dist/shared/schemas/webhook.js +20 -0
- package/dist/shared/schemas/webhook.js.map +1 -0
- package/dist/spa.d.ts +9 -0
- package/dist/spa.d.ts.map +1 -0
- package/dist/spa.js +57 -0
- package/dist/spa.js.map +1 -0
- package/migrations/0001_settings.sql +39 -0
- package/migrations/0002_content.sql +135 -0
- package/migrations/0003_forms.sql +35 -0
- package/migrations/0004_analytics.sql +42 -0
- package/migrations/0005_media.sql +17 -0
- package/migrations/0006_site_documents.sql +18 -0
- package/migrations/0007_auth.sql +81 -0
- package/migrations/0008_api_keys.sql +20 -0
- package/package.json +68 -0
- package/scripts/bundle-admin.mjs +54 -0
- package/seed/local.sql +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../src/shared/schemas/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { POST_TYPES } from '../constants.js';
|
|
3
|
+
import { webhookConfigSchema } from './webhook.js';
|
|
4
|
+
export const moduleFlagsSchema = z.object({
|
|
5
|
+
content: z.boolean().default(true),
|
|
6
|
+
forms: z.boolean().default(true),
|
|
7
|
+
analytics: z.boolean().default(true),
|
|
8
|
+
media: z.boolean().default(true),
|
|
9
|
+
seo: z.boolean().default(true),
|
|
10
|
+
});
|
|
11
|
+
export const retentionSettingsSchema = z.object({
|
|
12
|
+
visits: z.number().int().min(1).max(3650).default(90),
|
|
13
|
+
consent: z.number().int().min(1).max(3650).default(365),
|
|
14
|
+
});
|
|
15
|
+
export const turnstileSettingsSchema = z.object({
|
|
16
|
+
enabled: z.boolean().default(false),
|
|
17
|
+
sitekey: z.string().optional(),
|
|
18
|
+
});
|
|
19
|
+
/** schema.org Organization, minimal shape. Used as JSON-LD `publisher` on post detail responses. */
|
|
20
|
+
export const publisherSettingsSchema = z.object({
|
|
21
|
+
name: z.string().min(1),
|
|
22
|
+
logoUrl: z.string().url().optional(),
|
|
23
|
+
});
|
|
24
|
+
const pathPrefixSchema = z
|
|
25
|
+
.string()
|
|
26
|
+
.min(1)
|
|
27
|
+
.max(100)
|
|
28
|
+
.regex(/^\/[a-z0-9\-/]*$/, 'path must start with / and be lowercase kebab segments');
|
|
29
|
+
/**
|
|
30
|
+
* Per-type URL sections: a PARTIAL map from post type to the path prefix the
|
|
31
|
+
* site serves that section at, e.g. `{ blog: "/insights", news: "/newsroom" }`.
|
|
32
|
+
* Types absent from the map fall back to `postsPathPrefix`, the universal
|
|
33
|
+
* prefix, so an empty map behaves exactly as a site with one section does. All
|
|
34
|
+
* fields optional, so the object replaces wholesale on update. Explicit
|
|
35
|
+
* optional keys instead of z.record: a record with an enum key types as a FULL
|
|
36
|
+
* Record while parsing partially, a lie the resolver would trip on.
|
|
37
|
+
*/
|
|
38
|
+
export const contentPathsSchema = z.object({
|
|
39
|
+
blog: pathPrefixSchema.optional(),
|
|
40
|
+
article: pathPrefixSchema.optional(),
|
|
41
|
+
news: pathPrefixSchema.optional(),
|
|
42
|
+
research: pathPrefixSchema.optional(),
|
|
43
|
+
whitepaper: pathPrefixSchema.optional(),
|
|
44
|
+
'case-study': pathPrefixSchema.optional(),
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* THE resolution rule, defined once: a type's own path, else the legacy
|
|
48
|
+
* universal prefix. Sitemap, JSON-LD, the public site config, and the astro
|
|
49
|
+
* postUrl helper must all route through this so they can never disagree.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveContentPath(type, settings) {
|
|
52
|
+
return settings.contentPaths?.[type] ?? settings.postsPathPrefix;
|
|
53
|
+
}
|
|
54
|
+
/** The fully-resolved map served in the public site config (every type has a path). */
|
|
55
|
+
export function resolveAllContentPaths(settings) {
|
|
56
|
+
return Object.fromEntries(POST_TYPES.map((type) => [type, resolveContentPath(type, settings)]));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Site-level SEO FALLBACKS: a template's own values always win, and these fill
|
|
60
|
+
* the gaps when it omits one (the code-first merge lives in @we8/astro, not
|
|
61
|
+
* here). titleTemplate uses "%s" as the page-title placeholder, e.g.
|
|
62
|
+
* "%s | Acme Co". discourageSearchEngines is advertised in the public site
|
|
63
|
+
* config so a template can emit a disallow robots.txt and a noindex tag; this
|
|
64
|
+
* package only stores and serves it.
|
|
65
|
+
*/
|
|
66
|
+
export const seoSettingsSchema = z.object({
|
|
67
|
+
defaultTitle: z.string().max(120).optional(),
|
|
68
|
+
titleTemplate: z.string().max(160).optional(),
|
|
69
|
+
metaDescription: z.string().max(300).optional(),
|
|
70
|
+
ogTitle: z.string().max(120).optional(),
|
|
71
|
+
ogDescription: z.string().max(300).optional(),
|
|
72
|
+
ogImageUrl: z.string().url().optional(),
|
|
73
|
+
twitterHandle: z.string().max(30).optional(),
|
|
74
|
+
discourageSearchEngines: z.boolean().default(false),
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* A flat optional scalar, not a nested object: there is exactly one value to
|
|
78
|
+
* configure, so nesting would only reproduce the all-or-nothing footgun the
|
|
79
|
+
* update-variant comment below exists to avoid, for no benefit.
|
|
80
|
+
* ""-accepted-then-normalized-to-null on read, the same clear protocol the
|
|
81
|
+
* post content fields use, since JSON can express "" but not undefined.
|
|
82
|
+
*/
|
|
83
|
+
const gaMeasurementIdSchema = z
|
|
84
|
+
.string()
|
|
85
|
+
.regex(/^G-[A-Z0-9]{4,}$/, 'must look like a GA4 measurement ID (G-XXXXXXX)')
|
|
86
|
+
.or(z.literal(''))
|
|
87
|
+
.optional();
|
|
88
|
+
/**
|
|
89
|
+
* Brand asset slots: media URLs for the minimal icon set (favicon.ico,
|
|
90
|
+
* icon.svg, apple-touch-icon, 192 and 512 plus maskable) and the display logos.
|
|
91
|
+
* Every slot is optional and the object defaults to `{}`, so a site owner who
|
|
92
|
+
* never opens the Brand screen still gets a fully-formed object rather than
|
|
93
|
+
* undefined, and consumers never need optional chaining. The OG default is not
|
|
94
|
+
* duplicated here; `seo.ogImageUrl` already covers it. Generating the icon set
|
|
95
|
+
* from one source image is out of scope, since it drags in image resizing;
|
|
96
|
+
* each slot is an already-correctly-sized upload.
|
|
97
|
+
*/
|
|
98
|
+
export const brandSettingsSchema = z.object({
|
|
99
|
+
logoUrl: z.string().url().optional(),
|
|
100
|
+
logoDarkUrl: z.string().url().optional(),
|
|
101
|
+
iconSvgUrl: z.string().url().optional(),
|
|
102
|
+
faviconIcoUrl: z.string().url().optional(),
|
|
103
|
+
appleTouchIconUrl: z.string().url().optional(),
|
|
104
|
+
icon192Url: z.string().url().optional(),
|
|
105
|
+
icon512Url: z.string().url().optional(),
|
|
106
|
+
icon512MaskUrl: z.string().url().optional(),
|
|
107
|
+
});
|
|
108
|
+
/**
|
|
109
|
+
* AEO settings (0.1 headline feature). `siteSummary` is the one sentence an
|
|
110
|
+
* answer engine should be able to lift about the site as a whole; it heads the
|
|
111
|
+
* generated llms.txt and is the site-level counterpart of a post's
|
|
112
|
+
* `answerSummary`. The generated document can be turned off entirely, and the
|
|
113
|
+
* post cap bounds it so a large site does not publish a megabyte of index.
|
|
114
|
+
*/
|
|
115
|
+
export const aeoSettingsSchema = z.object({
|
|
116
|
+
siteSummary: z.string().max(1000).optional(),
|
|
117
|
+
llmsTxtEnabled: z.boolean().default(true),
|
|
118
|
+
llmsTxtMaxPosts: z.number().int().min(1).max(500).default(100),
|
|
119
|
+
});
|
|
120
|
+
export const siteSettingsSchema = z.object({
|
|
121
|
+
allowedOrigins: z.array(z.string()).default([]),
|
|
122
|
+
modules: moduleFlagsSchema.default({
|
|
123
|
+
content: true,
|
|
124
|
+
forms: true,
|
|
125
|
+
analytics: true,
|
|
126
|
+
media: true,
|
|
127
|
+
seo: true,
|
|
128
|
+
}),
|
|
129
|
+
webhooks: z.array(webhookConfigSchema).default([]),
|
|
130
|
+
// Capped: a form notification pings a handful of site admins, not a mailing
|
|
131
|
+
// list, and the bound belongs at the source rather than at the sender.
|
|
132
|
+
notifyEmails: z.array(z.string().email()).max(10).default([]),
|
|
133
|
+
retentionDays: retentionSettingsSchema.default({ visits: 90, consent: 365 }),
|
|
134
|
+
turnstile: turnstileSettingsSchema.default({ enabled: false }),
|
|
135
|
+
sitemapStatic: z
|
|
136
|
+
.array(z.object({
|
|
137
|
+
path: z.string(),
|
|
138
|
+
changefreq: z.string().optional(),
|
|
139
|
+
priority: z.number().min(0).max(1).optional(),
|
|
140
|
+
}))
|
|
141
|
+
.default([]),
|
|
142
|
+
/**
|
|
143
|
+
* Where the frontend serves individual posts, e.g. "/insights" for
|
|
144
|
+
* SITE_URL + "/insights/:slug". Needed to build absolute sitemap <loc> URLs
|
|
145
|
+
* and the JSON-LD `mainEntityOfPage`.
|
|
146
|
+
*/
|
|
147
|
+
postsPathPrefix: z.string().default('/insights'),
|
|
148
|
+
contentPaths: contentPathsSchema.default({}),
|
|
149
|
+
publisher: publisherSettingsSchema.optional(),
|
|
150
|
+
seo: seoSettingsSchema.default({}),
|
|
151
|
+
aeo: aeoSettingsSchema.default({ llmsTxtEnabled: true, llmsTxtMaxPosts: 100 }),
|
|
152
|
+
gaMeasurementId: gaMeasurementIdSchema,
|
|
153
|
+
brand: brandSettingsSchema.default({}),
|
|
154
|
+
});
|
|
155
|
+
/**
|
|
156
|
+
* Update-input variants of the nested settings objects with every field
|
|
157
|
+
* REQUIRED and no defaults. The write path (setSettings) shallow-merges the
|
|
158
|
+
* patch at the TOP level and re-parses the result through
|
|
159
|
+
* siteSettingsSchema, whose per-field .default()s would otherwise turn a
|
|
160
|
+
* partial nested object into a silent reset of the omitted siblings:
|
|
161
|
+
* PATCH {modules: {content: false}} would flip the other four flags back
|
|
162
|
+
* to true, {turnstile: {sitekey}} would silently disable turnstile
|
|
163
|
+
* (enabled defaults false), {retentionDays: {visits: 30}} would reset
|
|
164
|
+
* consent to 365. Nested objects are therefore all-or-nothing on update:
|
|
165
|
+
* a partial one is a 400, never a silent surprise. Arrays (webhooks,
|
|
166
|
+
* sitemapStatic, allowedOrigins, notifyEmails) replace wholesale, which
|
|
167
|
+
* is the intuitive PATCH semantic for lists, and publisher already
|
|
168
|
+
* requires its one non-optional field.
|
|
169
|
+
*/
|
|
170
|
+
const moduleFlagsUpdateSchema = z.object({
|
|
171
|
+
content: z.boolean(),
|
|
172
|
+
forms: z.boolean(),
|
|
173
|
+
analytics: z.boolean(),
|
|
174
|
+
media: z.boolean(),
|
|
175
|
+
seo: z.boolean(),
|
|
176
|
+
});
|
|
177
|
+
const retentionUpdateSchema = z.object({
|
|
178
|
+
visits: z.number().int().min(1).max(3650),
|
|
179
|
+
consent: z.number().int().min(1).max(3650),
|
|
180
|
+
});
|
|
181
|
+
const turnstileUpdateSchema = z.object({
|
|
182
|
+
enabled: z.boolean(),
|
|
183
|
+
sitekey: z.string().optional(),
|
|
184
|
+
});
|
|
185
|
+
/** All-required for the same reason as the three above: two of its fields default. */
|
|
186
|
+
const aeoUpdateSchema = z.object({
|
|
187
|
+
siteSummary: z.string().max(1000).optional(),
|
|
188
|
+
llmsTxtEnabled: z.boolean(),
|
|
189
|
+
llmsTxtMaxPosts: z.number().int().min(1).max(500),
|
|
190
|
+
});
|
|
191
|
+
export const siteSettingsUpdateInputSchema = siteSettingsSchema.partial().extend({
|
|
192
|
+
modules: moduleFlagsUpdateSchema.optional(),
|
|
193
|
+
retentionDays: retentionUpdateSchema.optional(),
|
|
194
|
+
turnstile: turnstileUpdateSchema.optional(),
|
|
195
|
+
aeo: aeoUpdateSchema.optional(),
|
|
196
|
+
// seo and brand need no update variant: every field in both is optional,
|
|
197
|
+
// so the object replaces wholesale when present (the nested-update
|
|
198
|
+
// convention; omitted optional fields mean cleared, discourageSearchEngines
|
|
199
|
+
// re-fills its false default). Their screens always send the complete
|
|
200
|
+
// object.
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../src/shared/schemas/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const siteDocumentKindSchema: z.ZodEnum<["design-md", "llms-txt"]>;
|
|
3
|
+
export declare const siteDocumentRecordSchema: z.ZodObject<{
|
|
4
|
+
kind: z.ZodEnum<["design-md", "llms-txt"]>;
|
|
5
|
+
content: z.ZodString;
|
|
6
|
+
/**
|
|
7
|
+
* AEO (0.1): the same answer-oriented summary posts carry, on the site's
|
|
8
|
+
* standing documents. A document is often the page an answer engine lands on
|
|
9
|
+
* first, and the body of a design.md or an llms.txt is a long structured
|
|
10
|
+
* text with no quotable opening; this gives it one.
|
|
11
|
+
*/
|
|
12
|
+
answerSummary: z.ZodNullable<z.ZodString>;
|
|
13
|
+
updatedAt: z.ZodNullable<z.ZodString>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
updatedAt: string | null;
|
|
16
|
+
content: string;
|
|
17
|
+
answerSummary: string | null;
|
|
18
|
+
kind: "design-md" | "llms-txt";
|
|
19
|
+
}, {
|
|
20
|
+
updatedAt: string | null;
|
|
21
|
+
content: string;
|
|
22
|
+
answerSummary: string | null;
|
|
23
|
+
kind: "design-md" | "llms-txt";
|
|
24
|
+
}>;
|
|
25
|
+
export declare const siteDocumentUpdateInputSchema: z.ZodObject<{
|
|
26
|
+
content: z.ZodString;
|
|
27
|
+
/** "" clears, matching the clear protocol every other optional string uses. */
|
|
28
|
+
answerSummary: z.ZodOptional<z.ZodString>;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
content: string;
|
|
31
|
+
answerSummary?: string | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
content: string;
|
|
34
|
+
answerSummary?: string | undefined;
|
|
35
|
+
}>;
|
|
36
|
+
export type SiteDocumentRecord = z.infer<typeof siteDocumentRecordSchema>;
|
|
37
|
+
export type SiteDocumentUpdateInput = z.infer<typeof siteDocumentUpdateInputSchema>;
|
|
38
|
+
//# sourceMappingURL=site-document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-document.d.ts","sourceRoot":"","sources":["../../../src/shared/schemas/site-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,sBAAsB,sCAA8B,CAAC;AAElE,eAAO,MAAM,wBAAwB;;;IAGnC;;;;;OAKG;;;;;;;;;;;;;EAGH,CAAC;AAEH,eAAO,MAAM,6BAA6B;;IAExC,+EAA+E;;;;;;;;EAE/E,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { SITE_DOCUMENT_KINDS } from '../constants.js';
|
|
3
|
+
export const siteDocumentKindSchema = z.enum(SITE_DOCUMENT_KINDS);
|
|
4
|
+
export const siteDocumentRecordSchema = z.object({
|
|
5
|
+
kind: siteDocumentKindSchema,
|
|
6
|
+
content: z.string(),
|
|
7
|
+
/**
|
|
8
|
+
* AEO (0.1): the same answer-oriented summary posts carry, on the site's
|
|
9
|
+
* standing documents. A document is often the page an answer engine lands on
|
|
10
|
+
* first, and the body of a design.md or an llms.txt is a long structured
|
|
11
|
+
* text with no quotable opening; this gives it one.
|
|
12
|
+
*/
|
|
13
|
+
answerSummary: z.string().nullable(),
|
|
14
|
+
updatedAt: z.string().nullable(),
|
|
15
|
+
});
|
|
16
|
+
export const siteDocumentUpdateInputSchema = z.object({
|
|
17
|
+
content: z.string().max(100_000),
|
|
18
|
+
/** "" clears, matching the clear protocol every other optional string uses. */
|
|
19
|
+
answerSummary: z.string().max(600).optional(),
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=site-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-document.js","sourceRoot":"","sources":["../../../src/shared/schemas/site-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;AAElE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,IAAI,EAAE,sBAAsB;IAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB;;;;;OAKG;IACH,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;IAChC,+EAA+E;IAC/E,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CAC9C,CAAC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const webhookEventNameSchema: z.ZodEnum<["post.published", "post.unpublished", "form.submission.created"]>;
|
|
3
|
+
export declare const webhookConfigSchema: z.ZodObject<{
|
|
4
|
+
url: z.ZodString;
|
|
5
|
+
events: z.ZodArray<z.ZodEnum<["post.published", "post.unpublished", "form.submission.created"]>, "many">;
|
|
6
|
+
secret: z.ZodString;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
url: string;
|
|
9
|
+
events: ("post.published" | "post.unpublished" | "form.submission.created")[];
|
|
10
|
+
secret: string;
|
|
11
|
+
}, {
|
|
12
|
+
url: string;
|
|
13
|
+
events: ("post.published" | "post.unpublished" | "form.submission.created")[];
|
|
14
|
+
secret: string;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* What a receiver gets. `site` is the site's public URL, which is the only
|
|
18
|
+
* identity a standalone install has (managed we8 sends a tenant id here; there
|
|
19
|
+
* is no tenant to name in this one).
|
|
20
|
+
*/
|
|
21
|
+
export declare const webhookEnvelopeSchema: z.ZodObject<{
|
|
22
|
+
event: z.ZodEnum<["post.published", "post.unpublished", "form.submission.created"]>;
|
|
23
|
+
site: z.ZodString;
|
|
24
|
+
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
25
|
+
timestamp: z.ZodString;
|
|
26
|
+
}, "strip", z.ZodTypeAny, {
|
|
27
|
+
event: "post.published" | "post.unpublished" | "form.submission.created";
|
|
28
|
+
site: string;
|
|
29
|
+
data: Record<string, unknown>;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
}, {
|
|
32
|
+
event: "post.published" | "post.unpublished" | "form.submission.created";
|
|
33
|
+
site: string;
|
|
34
|
+
data: Record<string, unknown>;
|
|
35
|
+
timestamp: string;
|
|
36
|
+
}>;
|
|
37
|
+
export type WebhookConfig = z.infer<typeof webhookConfigSchema>;
|
|
38
|
+
export type WebhookEnvelope = z.infer<typeof webhookEnvelopeSchema>;
|
|
39
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/shared/schemas/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,sBAAsB,8EAAyB,CAAC;AAE7D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAI9B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;EAKhC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WEBHOOK_EVENTS } from '../constants.js';
|
|
3
|
+
export const webhookEventNameSchema = z.enum(WEBHOOK_EVENTS);
|
|
4
|
+
export const webhookConfigSchema = z.object({
|
|
5
|
+
url: z.string().url(),
|
|
6
|
+
events: z.array(webhookEventNameSchema).min(1),
|
|
7
|
+
secret: z.string().min(16),
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* What a receiver gets. `site` is the site's public URL, which is the only
|
|
11
|
+
* identity a standalone install has (managed we8 sends a tenant id here; there
|
|
12
|
+
* is no tenant to name in this one).
|
|
13
|
+
*/
|
|
14
|
+
export const webhookEnvelopeSchema = z.object({
|
|
15
|
+
event: webhookEventNameSchema,
|
|
16
|
+
site: z.string(),
|
|
17
|
+
data: z.record(z.string(), z.unknown()),
|
|
18
|
+
timestamp: z.string(),
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../../src/shared/schemas/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AAE7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;CAC3B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,KAAK,EAAE,sBAAsB;IAC7B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACvC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC"}
|
package/dist/spa.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Bindings } from './env.js';
|
|
2
|
+
export declare function isApiPath(pathname: string): boolean;
|
|
3
|
+
export declare function wantsHtml(request: Request): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* The SPA shell for a client-side route, or null when this request is not one
|
|
6
|
+
* (or when no admin bundle is deployed at all).
|
|
7
|
+
*/
|
|
8
|
+
export declare function serveSpaShell(request: Request, env: Bindings): Promise<Response | null>;
|
|
9
|
+
//# sourceMappingURL=spa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spa.d.ts","sourceRoot":"","sources":["../src/spa.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AA4BzC,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAInD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAsB7F"}
|
package/dist/spa.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serving the admin portal out of this same worker.
|
|
3
|
+
*
|
|
4
|
+
* The portal is a single-page application, so every one of its routes has to
|
|
5
|
+
* answer with the same index.html and let the client-side router take over.
|
|
6
|
+
* Cloudflare's asset serving handles the files that really exist; what it
|
|
7
|
+
* cannot know is which of the remaining paths are the SPA's own routes and
|
|
8
|
+
* which are this worker's API. This does: the API prefixes are checked first,
|
|
9
|
+
* and only a document request that is none of them falls through to the shell.
|
|
10
|
+
*
|
|
11
|
+
* Two conditions keep the fallback narrow. It answers GET only, and only when
|
|
12
|
+
* the request actually asked for HTML, so a fetch for a missing JSON file gets
|
|
13
|
+
* the API's 404 envelope rather than a page of markup that would fail to parse
|
|
14
|
+
* somewhere far from the cause.
|
|
15
|
+
*
|
|
16
|
+
* The whole thing is optional. A deployment that binds no assets, which is the
|
|
17
|
+
* API-only mode a headless install runs in, has no ASSETS binding, and this
|
|
18
|
+
* returns null so the normal 404 stands.
|
|
19
|
+
*/
|
|
20
|
+
/** Path prefixes this worker owns. Anything under them is never the SPA. */
|
|
21
|
+
const API_PREFIXES = ['/v1/', '/api/'];
|
|
22
|
+
/** Exact paths this worker owns that carry no trailing segment. */
|
|
23
|
+
const API_PATHS = ['/v1', '/api', '/llms.txt'];
|
|
24
|
+
export function isApiPath(pathname) {
|
|
25
|
+
return (API_PATHS.includes(pathname) || API_PREFIXES.some((prefix) => pathname.startsWith(prefix)));
|
|
26
|
+
}
|
|
27
|
+
export function wantsHtml(request) {
|
|
28
|
+
return request.method === 'GET' && (request.headers.get('Accept') ?? '').includes('text/html');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The SPA shell for a client-side route, or null when this request is not one
|
|
32
|
+
* (or when no admin bundle is deployed at all).
|
|
33
|
+
*/
|
|
34
|
+
export async function serveSpaShell(request, env) {
|
|
35
|
+
const assets = env.ASSETS;
|
|
36
|
+
if (!assets)
|
|
37
|
+
return null;
|
|
38
|
+
const url = new URL(request.url);
|
|
39
|
+
if (isApiPath(url.pathname) || !wantsHtml(request))
|
|
40
|
+
return null;
|
|
41
|
+
const shell = await assets.fetch(new Request(new URL('/index.html', url), { method: 'GET' }));
|
|
42
|
+
// An assets binding with no bundle behind it answers 404 for index.html too,
|
|
43
|
+
// which is the API-only case: fall through rather than serving that 404 as
|
|
44
|
+
// though it were the admin.
|
|
45
|
+
if (!shell.ok)
|
|
46
|
+
return null;
|
|
47
|
+
return new Response(shell.body, {
|
|
48
|
+
status: 200,
|
|
49
|
+
headers: {
|
|
50
|
+
'Content-Type': 'text/html; charset=UTF-8',
|
|
51
|
+
// The shell names hashed asset files, so it must never be the stale copy
|
|
52
|
+
// pointing at a bundle that no longer exists.
|
|
53
|
+
'Cache-Control': 'no-cache',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=spa.js.map
|
package/dist/spa.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spa.js","sourceRoot":"","sources":["../src/spa.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,4EAA4E;AAC5E,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvC,mEAAmE;AACnE,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAE/C,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,OAAO,CACL,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAC3F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAgB;IACxC,OAAO,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACjG,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB,EAAE,GAAa;IACjE,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAEhE,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAC9F,6EAA6E;IAC7E,2EAA2E;IAC3E,4BAA4B;IAC5B,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAE3B,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE;QAC9B,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACP,cAAc,EAAE,0BAA0B;YAC1C,yEAAyE;YACzE,8CAA8C;YAC9C,eAAe,EAAE,UAAU;SAC5B;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- 0001: site settings plus the two operational tables every later module
|
|
2
|
+
-- leans on (rate limiting and the webhook delivery log).
|
|
3
|
+
--
|
|
4
|
+
-- This sequence is a fresh single-site schema, not a replay of managed we8's
|
|
5
|
+
-- migration history: every later ALTER there is folded into the CREATE here,
|
|
6
|
+
-- so a self-hosted database starts at the shape the code actually reads.
|
|
7
|
+
--
|
|
8
|
+
-- Id strategy: operational tables use INTEGER PRIMARY KEY AUTOINCREMENT (cheap,
|
|
9
|
+
-- nothing outside the database references them); public-facing entities use
|
|
10
|
+
-- TEXT ids the application generates with crypto.randomUUID().
|
|
11
|
+
|
|
12
|
+
CREATE TABLE site_settings (
|
|
13
|
+
key TEXT PRIMARY KEY,
|
|
14
|
+
value TEXT NOT NULL, -- JSON, one row per top-level settings key
|
|
15
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE rate_events (
|
|
19
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
+
bucket TEXT NOT NULL,
|
|
21
|
+
ip_hash TEXT NOT NULL,
|
|
22
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
CREATE INDEX idx_rate_events_bucket_ip_created
|
|
26
|
+
ON rate_events (bucket, ip_hash, created_at);
|
|
27
|
+
|
|
28
|
+
CREATE TABLE webhook_deliveries (
|
|
29
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
30
|
+
event TEXT NOT NULL,
|
|
31
|
+
url TEXT NOT NULL,
|
|
32
|
+
status TEXT NOT NULL DEFAULT 'pending', -- pending | delivered | failed
|
|
33
|
+
response_code INTEGER,
|
|
34
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
36
|
+
delivered_at TEXT
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX idx_webhook_deliveries_created ON webhook_deliveries (created_at DESC);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
-- 0002: the CMS proper. posts / post_drafts / post_versions is the
|
|
2
|
+
-- draft-stage plus immutable-snapshot pattern: `posts` holds the working copy
|
|
3
|
+
-- and the mirror of the live version, `post_drafts` stages an edit to an
|
|
4
|
+
-- already-published post, and `post_versions` is insert-only history that
|
|
5
|
+
-- public reads are served from.
|
|
6
|
+
--
|
|
7
|
+
-- answer_summary and question_heading are the 0.1 AEO content fields. They sit
|
|
8
|
+
-- on all three content-carrying tables so they flow through the same
|
|
9
|
+
-- working-copy, draft, and publish machinery as every other content field,
|
|
10
|
+
-- with no separate code path.
|
|
11
|
+
--
|
|
12
|
+
-- Foreign keys are declared for documentation; D1 does not enforce them by
|
|
13
|
+
-- default, so delete cascades happen in the application layer.
|
|
14
|
+
|
|
15
|
+
CREATE TABLE authors (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
slug TEXT NOT NULL UNIQUE,
|
|
18
|
+
name TEXT NOT NULL,
|
|
19
|
+
type TEXT NOT NULL DEFAULT 'team' CHECK (type IN ('team', 'guest', 'advisor', 'partner')),
|
|
20
|
+
role_title TEXT,
|
|
21
|
+
avatar_url TEXT,
|
|
22
|
+
bio TEXT,
|
|
23
|
+
links TEXT NOT NULL DEFAULT '[]', -- JSON {label,url}[]
|
|
24
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
25
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE INDEX idx_authors_slug ON authors (slug);
|
|
29
|
+
|
|
30
|
+
CREATE TABLE posts (
|
|
31
|
+
id TEXT PRIMARY KEY,
|
|
32
|
+
slug TEXT NOT NULL UNIQUE,
|
|
33
|
+
-- Structural, like slug: type picks the site section and the JSON-LD type,
|
|
34
|
+
-- so it is editable only while never-published and locked after.
|
|
35
|
+
type TEXT NOT NULL DEFAULT 'blog',
|
|
36
|
+
title TEXT NOT NULL,
|
|
37
|
+
excerpt TEXT,
|
|
38
|
+
content TEXT NOT NULL,
|
|
39
|
+
cover_image_url TEXT,
|
|
40
|
+
category TEXT,
|
|
41
|
+
tags TEXT NOT NULL DEFAULT '[]', -- JSON string[]
|
|
42
|
+
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'archived')),
|
|
43
|
+
og_title TEXT,
|
|
44
|
+
og_description TEXT,
|
|
45
|
+
og_image_url TEXT,
|
|
46
|
+
answer_summary TEXT,
|
|
47
|
+
question_heading TEXT,
|
|
48
|
+
cta TEXT, -- JSON Cta, NULL means no CTA configured
|
|
49
|
+
current_version_id TEXT,
|
|
50
|
+
current_version_number INTEGER,
|
|
51
|
+
view_count INTEGER NOT NULL DEFAULT 0,
|
|
52
|
+
like_count INTEGER NOT NULL DEFAULT 0,
|
|
53
|
+
word_count INTEGER,
|
|
54
|
+
published_at TEXT,
|
|
55
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
56
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
CREATE INDEX idx_posts_slug ON posts (slug);
|
|
60
|
+
CREATE INDEX idx_posts_status ON posts (status);
|
|
61
|
+
CREATE INDEX idx_posts_type ON posts (type);
|
|
62
|
+
CREATE INDEX idx_posts_published_at ON posts (published_at DESC);
|
|
63
|
+
|
|
64
|
+
CREATE TABLE post_drafts (
|
|
65
|
+
post_id TEXT PRIMARY KEY REFERENCES posts (id),
|
|
66
|
+
title TEXT NOT NULL,
|
|
67
|
+
excerpt TEXT,
|
|
68
|
+
content TEXT NOT NULL,
|
|
69
|
+
cover_image_url TEXT,
|
|
70
|
+
category TEXT,
|
|
71
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
72
|
+
og_title TEXT,
|
|
73
|
+
og_description TEXT,
|
|
74
|
+
og_image_url TEXT,
|
|
75
|
+
answer_summary TEXT,
|
|
76
|
+
question_heading TEXT,
|
|
77
|
+
cta TEXT,
|
|
78
|
+
author_ids TEXT NOT NULL DEFAULT '[]', -- JSON string[]
|
|
79
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
CREATE TABLE post_versions (
|
|
83
|
+
id TEXT PRIMARY KEY,
|
|
84
|
+
post_id TEXT NOT NULL REFERENCES posts (id),
|
|
85
|
+
version INTEGER NOT NULL,
|
|
86
|
+
title TEXT NOT NULL,
|
|
87
|
+
excerpt TEXT,
|
|
88
|
+
content TEXT NOT NULL,
|
|
89
|
+
cover_image_url TEXT,
|
|
90
|
+
category TEXT,
|
|
91
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
92
|
+
og_title TEXT,
|
|
93
|
+
og_description TEXT,
|
|
94
|
+
og_image_url TEXT,
|
|
95
|
+
answer_summary TEXT,
|
|
96
|
+
question_heading TEXT,
|
|
97
|
+
cta TEXT,
|
|
98
|
+
author_ids TEXT NOT NULL DEFAULT '[]',
|
|
99
|
+
word_count INTEGER,
|
|
100
|
+
note TEXT,
|
|
101
|
+
published_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
102
|
+
published_by TEXT,
|
|
103
|
+
UNIQUE (post_id, version)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
CREATE INDEX idx_post_versions_post_version ON post_versions (post_id, version DESC);
|
|
107
|
+
|
|
108
|
+
CREATE TABLE post_authors (
|
|
109
|
+
post_id TEXT NOT NULL REFERENCES posts (id),
|
|
110
|
+
author_id TEXT NOT NULL REFERENCES authors (id),
|
|
111
|
+
position INTEGER NOT NULL DEFAULT 0,
|
|
112
|
+
PRIMARY KEY (post_id, author_id)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
CREATE INDEX idx_post_authors_post_position ON post_authors (post_id, position);
|
|
116
|
+
CREATE INDEX idx_post_authors_author ON post_authors (author_id);
|
|
117
|
+
|
|
118
|
+
-- version_number stamps which version was live when the view or like happened.
|
|
119
|
+
-- Nullable because a row can be recorded against a post that has never been
|
|
120
|
+
-- published, which has no version to attribute to.
|
|
121
|
+
CREATE TABLE post_views (
|
|
122
|
+
post_id TEXT NOT NULL REFERENCES posts (id),
|
|
123
|
+
session_id TEXT NOT NULL,
|
|
124
|
+
version_number INTEGER,
|
|
125
|
+
viewed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
126
|
+
PRIMARY KEY (post_id, session_id)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
CREATE TABLE post_likes (
|
|
130
|
+
post_id TEXT NOT NULL REFERENCES posts (id),
|
|
131
|
+
session_id TEXT NOT NULL,
|
|
132
|
+
version_number INTEGER,
|
|
133
|
+
liked_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
134
|
+
PRIMARY KEY (post_id, session_id)
|
|
135
|
+
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
-- 0003: forms and their submissions. A form is a first-class, named, typed
|
|
2
|
+
-- entity embedded by its stable `form_key`; the submissions table is the
|
|
3
|
+
-- inbox of record, which is why a form with submissions is archived rather
|
|
4
|
+
-- than deleted.
|
|
5
|
+
|
|
6
|
+
CREATE TABLE forms (
|
|
7
|
+
id TEXT PRIMARY KEY,
|
|
8
|
+
form_key TEXT NOT NULL UNIQUE,
|
|
9
|
+
name TEXT NOT NULL,
|
|
10
|
+
type TEXT NOT NULL DEFAULT 'custom',
|
|
11
|
+
settings TEXT NOT NULL DEFAULT '{}', -- JSON: {notifyEmails?, turnstileEnabled?, honeypotField?, successMessage?}
|
|
12
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
13
|
+
updated_at TEXT,
|
|
14
|
+
archived_at TEXT
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE TABLE submissions (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
form_id TEXT NOT NULL REFERENCES forms (id),
|
|
20
|
+
submitted_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
21
|
+
payload TEXT NOT NULL, -- JSON: the full submitted body, capped by the application
|
|
22
|
+
email TEXT,
|
|
23
|
+
name TEXT,
|
|
24
|
+
status TEXT NOT NULL DEFAULT 'new' CHECK (status IN ('new', 'reviewing', 'resolved', 'spam', 'archived')),
|
|
25
|
+
notes TEXT,
|
|
26
|
+
ip_hash TEXT,
|
|
27
|
+
user_agent TEXT,
|
|
28
|
+
source_page TEXT,
|
|
29
|
+
referrer TEXT
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE INDEX idx_submissions_submitted_at ON submissions (submitted_at DESC);
|
|
33
|
+
CREATE INDEX idx_submissions_status ON submissions (status);
|
|
34
|
+
CREATE INDEX idx_submissions_email ON submissions (email);
|
|
35
|
+
CREATE INDEX idx_submissions_form ON submissions (form_id);
|