@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.
Files changed (304) hide show
  1. package/README.md +78 -0
  2. package/dist/auth/auth.d.ts +87 -0
  3. package/dist/auth/auth.d.ts.map +1 -0
  4. package/dist/auth/auth.js +90 -0
  5. package/dist/auth/auth.js.map +1 -0
  6. package/dist/auth/routes.admin.d.ts +22 -0
  7. package/dist/auth/routes.admin.d.ts.map +1 -0
  8. package/dist/auth/routes.admin.js +106 -0
  9. package/dist/auth/routes.admin.js.map +1 -0
  10. package/dist/auth/schema.d.ts +1395 -0
  11. package/dist/auth/schema.d.ts.map +1 -0
  12. package/dist/auth/schema.js +76 -0
  13. package/dist/auth/schema.js.map +1 -0
  14. package/dist/env.d.ts +64 -0
  15. package/dist/env.d.ts.map +1 -0
  16. package/dist/env.js +48 -0
  17. package/dist/env.js.map +1 -0
  18. package/dist/index.d.ts +22 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +170 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/keys/middleware.d.ts +54 -0
  23. package/dist/keys/middleware.d.ts.map +1 -0
  24. package/dist/keys/middleware.js +84 -0
  25. package/dist/keys/middleware.js.map +1 -0
  26. package/dist/keys/routes.admin.d.ts +16 -0
  27. package/dist/keys/routes.admin.d.ts.map +1 -0
  28. package/dist/keys/routes.admin.js +34 -0
  29. package/dist/keys/routes.admin.js.map +1 -0
  30. package/dist/keys/service.d.ts +36 -0
  31. package/dist/keys/service.d.ts.map +1 -0
  32. package/dist/keys/service.js +120 -0
  33. package/dist/keys/service.js.map +1 -0
  34. package/dist/lib/bots.d.ts +24 -0
  35. package/dist/lib/bots.d.ts.map +1 -0
  36. package/dist/lib/bots.js +79 -0
  37. package/dist/lib/bots.js.map +1 -0
  38. package/dist/lib/email.d.ts +62 -0
  39. package/dist/lib/email.d.ts.map +1 -0
  40. package/dist/lib/email.js +64 -0
  41. package/dist/lib/email.js.map +1 -0
  42. package/dist/lib/errors.d.ts +12 -0
  43. package/dist/lib/errors.d.ts.map +1 -0
  44. package/dist/lib/errors.js +23 -0
  45. package/dist/lib/errors.js.map +1 -0
  46. package/dist/lib/notify.d.ts +20 -0
  47. package/dist/lib/notify.d.ts.map +1 -0
  48. package/dist/lib/notify.js +60 -0
  49. package/dist/lib/notify.js.map +1 -0
  50. package/dist/lib/retention.d.ts +15 -0
  51. package/dist/lib/retention.d.ts.map +1 -0
  52. package/dist/lib/retention.js +40 -0
  53. package/dist/lib/retention.js.map +1 -0
  54. package/dist/lib/sanitize.d.ts +14 -0
  55. package/dist/lib/sanitize.d.ts.map +1 -0
  56. package/dist/lib/sanitize.js +145 -0
  57. package/dist/lib/sanitize.js.map +1 -0
  58. package/dist/lib/turnstile.d.ts +7 -0
  59. package/dist/lib/turnstile.d.ts.map +1 -0
  60. package/dist/lib/turnstile.js +32 -0
  61. package/dist/lib/turnstile.js.map +1 -0
  62. package/dist/lib/validate.d.ts +11 -0
  63. package/dist/lib/validate.d.ts.map +1 -0
  64. package/dist/lib/validate.js +22 -0
  65. package/dist/lib/validate.js.map +1 -0
  66. package/dist/lib/word-count.d.ts +7 -0
  67. package/dist/lib/word-count.d.ts.map +1 -0
  68. package/dist/lib/word-count.js +14 -0
  69. package/dist/lib/word-count.js.map +1 -0
  70. package/dist/middleware/auth.d.ts +46 -0
  71. package/dist/middleware/auth.d.ts.map +1 -0
  72. package/dist/middleware/auth.js +60 -0
  73. package/dist/middleware/auth.js.map +1 -0
  74. package/dist/middleware/cors.d.ts +33 -0
  75. package/dist/middleware/cors.d.ts.map +1 -0
  76. package/dist/middleware/cors.js +55 -0
  77. package/dist/middleware/cors.js.map +1 -0
  78. package/dist/middleware/module-gate.d.ts +11 -0
  79. package/dist/middleware/module-gate.d.ts.map +1 -0
  80. package/dist/middleware/module-gate.js +18 -0
  81. package/dist/middleware/module-gate.js.map +1 -0
  82. package/dist/middleware/rate-limit.d.ts +13 -0
  83. package/dist/middleware/rate-limit.d.ts.map +1 -0
  84. package/dist/middleware/rate-limit.js +30 -0
  85. package/dist/middleware/rate-limit.js.map +1 -0
  86. package/dist/middleware/request-id.d.ts +8 -0
  87. package/dist/middleware/request-id.d.ts.map +1 -0
  88. package/dist/middleware/request-id.js +11 -0
  89. package/dist/middleware/request-id.js.map +1 -0
  90. package/dist/modules/aeo/checks.d.ts +41 -0
  91. package/dist/modules/aeo/checks.d.ts.map +1 -0
  92. package/dist/modules/aeo/checks.js +127 -0
  93. package/dist/modules/aeo/checks.js.map +1 -0
  94. package/dist/modules/aeo/llms.d.ts +24 -0
  95. package/dist/modules/aeo/llms.d.ts.map +1 -0
  96. package/dist/modules/aeo/llms.js +92 -0
  97. package/dist/modules/aeo/llms.js.map +1 -0
  98. package/dist/modules/aeo/routes.admin.d.ts +12 -0
  99. package/dist/modules/aeo/routes.admin.d.ts.map +1 -0
  100. package/dist/modules/aeo/routes.admin.js +37 -0
  101. package/dist/modules/aeo/routes.admin.js.map +1 -0
  102. package/dist/modules/analytics/db.d.ts +24 -0
  103. package/dist/modules/analytics/db.d.ts.map +1 -0
  104. package/dist/modules/analytics/db.js +24 -0
  105. package/dist/modules/analytics/db.js.map +1 -0
  106. package/dist/modules/analytics/routes.admin.d.ts +6 -0
  107. package/dist/modules/analytics/routes.admin.d.ts.map +1 -0
  108. package/dist/modules/analytics/routes.admin.js +21 -0
  109. package/dist/modules/analytics/routes.admin.js.map +1 -0
  110. package/dist/modules/analytics/routes.public.d.ts +6 -0
  111. package/dist/modules/analytics/routes.public.d.ts.map +1 -0
  112. package/dist/modules/analytics/routes.public.js +33 -0
  113. package/dist/modules/analytics/routes.public.js.map +1 -0
  114. package/dist/modules/analytics/service.d.ts +16 -0
  115. package/dist/modules/analytics/service.d.ts.map +1 -0
  116. package/dist/modules/analytics/service.js +148 -0
  117. package/dist/modules/analytics/service.js.map +1 -0
  118. package/dist/modules/authors/routes.admin.d.ts +6 -0
  119. package/dist/modules/authors/routes.admin.d.ts.map +1 -0
  120. package/dist/modules/authors/routes.admin.js +29 -0
  121. package/dist/modules/authors/routes.admin.js.map +1 -0
  122. package/dist/modules/authors/routes.public.d.ts +6 -0
  123. package/dist/modules/authors/routes.public.d.ts.map +1 -0
  124. package/dist/modules/authors/routes.public.js +11 -0
  125. package/dist/modules/authors/routes.public.js.map +1 -0
  126. package/dist/modules/authors/service.d.ts +26 -0
  127. package/dist/modules/authors/service.d.ts.map +1 -0
  128. package/dist/modules/authors/service.js +116 -0
  129. package/dist/modules/authors/service.js.map +1 -0
  130. package/dist/modules/content/db.d.ts +127 -0
  131. package/dist/modules/content/db.d.ts.map +1 -0
  132. package/dist/modules/content/db.js +203 -0
  133. package/dist/modules/content/db.js.map +1 -0
  134. package/dist/modules/content/publish.d.ts +38 -0
  135. package/dist/modules/content/publish.d.ts.map +1 -0
  136. package/dist/modules/content/publish.js +184 -0
  137. package/dist/modules/content/publish.js.map +1 -0
  138. package/dist/modules/content/routes.admin.d.ts +8 -0
  139. package/dist/modules/content/routes.admin.d.ts.map +1 -0
  140. package/dist/modules/content/routes.admin.js +122 -0
  141. package/dist/modules/content/routes.admin.js.map +1 -0
  142. package/dist/modules/content/routes.public.d.ts +6 -0
  143. package/dist/modules/content/routes.public.d.ts.map +1 -0
  144. package/dist/modules/content/routes.public.js +56 -0
  145. package/dist/modules/content/routes.public.js.map +1 -0
  146. package/dist/modules/content/service.d.ts +137 -0
  147. package/dist/modules/content/service.d.ts.map +1 -0
  148. package/dist/modules/content/service.js +348 -0
  149. package/dist/modules/content/service.js.map +1 -0
  150. package/dist/modules/forms/db.d.ts +42 -0
  151. package/dist/modules/forms/db.d.ts.map +1 -0
  152. package/dist/modules/forms/db.js +62 -0
  153. package/dist/modules/forms/db.js.map +1 -0
  154. package/dist/modules/forms/forms-routes.admin.d.ts +6 -0
  155. package/dist/modules/forms/forms-routes.admin.d.ts.map +1 -0
  156. package/dist/modules/forms/forms-routes.admin.js +37 -0
  157. package/dist/modules/forms/forms-routes.admin.js.map +1 -0
  158. package/dist/modules/forms/forms-service.d.ts +18 -0
  159. package/dist/modules/forms/forms-service.d.ts.map +1 -0
  160. package/dist/modules/forms/forms-service.js +121 -0
  161. package/dist/modules/forms/forms-service.js.map +1 -0
  162. package/dist/modules/forms/routes.public.d.ts +6 -0
  163. package/dist/modules/forms/routes.public.d.ts.map +1 -0
  164. package/dist/modules/forms/routes.public.js +41 -0
  165. package/dist/modules/forms/routes.public.js.map +1 -0
  166. package/dist/modules/forms/submissions-routes.admin.d.ts +6 -0
  167. package/dist/modules/forms/submissions-routes.admin.d.ts.map +1 -0
  168. package/dist/modules/forms/submissions-routes.admin.js +46 -0
  169. package/dist/modules/forms/submissions-routes.admin.js.map +1 -0
  170. package/dist/modules/forms/submissions-service.d.ts +45 -0
  171. package/dist/modules/forms/submissions-service.d.ts.map +1 -0
  172. package/dist/modules/forms/submissions-service.js +174 -0
  173. package/dist/modules/forms/submissions-service.js.map +1 -0
  174. package/dist/modules/media/db.d.ts +15 -0
  175. package/dist/modules/media/db.d.ts.map +1 -0
  176. package/dist/modules/media/db.js +24 -0
  177. package/dist/modules/media/db.js.map +1 -0
  178. package/dist/modules/media/routes.admin.d.ts +6 -0
  179. package/dist/modules/media/routes.admin.d.ts.map +1 -0
  180. package/dist/modules/media/routes.admin.js +30 -0
  181. package/dist/modules/media/routes.admin.js.map +1 -0
  182. package/dist/modules/media/routes.public.d.ts +6 -0
  183. package/dist/modules/media/routes.public.d.ts.map +1 -0
  184. package/dist/modules/media/routes.public.js +35 -0
  185. package/dist/modules/media/routes.public.js.map +1 -0
  186. package/dist/modules/media/service.d.ts +32 -0
  187. package/dist/modules/media/service.d.ts.map +1 -0
  188. package/dist/modules/media/service.js +99 -0
  189. package/dist/modules/media/service.js.map +1 -0
  190. package/dist/modules/openapi/spec.d.ts +21 -0
  191. package/dist/modules/openapi/spec.d.ts.map +1 -0
  192. package/dist/modules/openapi/spec.js +468 -0
  193. package/dist/modules/openapi/spec.js.map +1 -0
  194. package/dist/modules/seo/jsonld.d.ts +25 -0
  195. package/dist/modules/seo/jsonld.d.ts.map +1 -0
  196. package/dist/modules/seo/jsonld.js +67 -0
  197. package/dist/modules/seo/jsonld.js.map +1 -0
  198. package/dist/modules/seo/routes.public.d.ts +6 -0
  199. package/dist/modules/seo/routes.public.d.ts.map +1 -0
  200. package/dist/modules/seo/routes.public.js +39 -0
  201. package/dist/modules/seo/routes.public.js.map +1 -0
  202. package/dist/modules/seo/sitemap.d.ts +3 -0
  203. package/dist/modules/seo/sitemap.d.ts.map +1 -0
  204. package/dist/modules/seo/sitemap.js +39 -0
  205. package/dist/modules/seo/sitemap.js.map +1 -0
  206. package/dist/modules/settings/routes.admin.d.ts +12 -0
  207. package/dist/modules/settings/routes.admin.d.ts.map +1 -0
  208. package/dist/modules/settings/routes.admin.js +27 -0
  209. package/dist/modules/settings/routes.admin.js.map +1 -0
  210. package/dist/modules/site-documents/routes.admin.d.ts +10 -0
  211. package/dist/modules/site-documents/routes.admin.d.ts.map +1 -0
  212. package/dist/modules/site-documents/routes.admin.js +29 -0
  213. package/dist/modules/site-documents/routes.admin.js.map +1 -0
  214. package/dist/modules/site-documents/routes.public.d.ts +6 -0
  215. package/dist/modules/site-documents/routes.public.d.ts.map +1 -0
  216. package/dist/modules/site-documents/routes.public.js +16 -0
  217. package/dist/modules/site-documents/routes.public.js.map +1 -0
  218. package/dist/modules/site-documents/service.d.ts +11 -0
  219. package/dist/modules/site-documents/service.d.ts.map +1 -0
  220. package/dist/modules/site-documents/service.js +42 -0
  221. package/dist/modules/site-documents/service.js.map +1 -0
  222. package/dist/modules/webhooks/dispatch.d.ts +14 -0
  223. package/dist/modules/webhooks/dispatch.d.ts.map +1 -0
  224. package/dist/modules/webhooks/dispatch.js +84 -0
  225. package/dist/modules/webhooks/dispatch.js.map +1 -0
  226. package/dist/shared/constants.d.ts +79 -0
  227. package/dist/shared/constants.d.ts.map +1 -0
  228. package/dist/shared/constants.js +89 -0
  229. package/dist/shared/constants.js.map +1 -0
  230. package/dist/shared/envelope.d.ts +37 -0
  231. package/dist/shared/envelope.d.ts.map +1 -0
  232. package/dist/shared/envelope.js +46 -0
  233. package/dist/shared/envelope.js.map +1 -0
  234. package/dist/shared/hash.d.ts +2 -0
  235. package/dist/shared/hash.d.ts.map +1 -0
  236. package/dist/shared/hash.js +8 -0
  237. package/dist/shared/hash.js.map +1 -0
  238. package/dist/shared/index.d.ts +13 -0
  239. package/dist/shared/index.d.ts.map +1 -0
  240. package/dist/shared/index.js +13 -0
  241. package/dist/shared/index.js.map +1 -0
  242. package/dist/shared/schemas/aeo.d.ts +182 -0
  243. package/dist/shared/schemas/aeo.d.ts.map +1 -0
  244. package/dist/shared/schemas/aeo.js +43 -0
  245. package/dist/shared/schemas/aeo.js.map +1 -0
  246. package/dist/shared/schemas/analytics.d.ts +277 -0
  247. package/dist/shared/schemas/analytics.d.ts.map +1 -0
  248. package/dist/shared/schemas/analytics.js +74 -0
  249. package/dist/shared/schemas/analytics.js.map +1 -0
  250. package/dist/shared/schemas/api-key.d.ts +77 -0
  251. package/dist/shared/schemas/api-key.d.ts.map +1 -0
  252. package/dist/shared/schemas/api-key.js +27 -0
  253. package/dist/shared/schemas/api-key.js.map +1 -0
  254. package/dist/shared/schemas/author.d.ts +147 -0
  255. package/dist/shared/schemas/author.d.ts.map +1 -0
  256. package/dist/shared/schemas/author.js +27 -0
  257. package/dist/shared/schemas/author.js.map +1 -0
  258. package/dist/shared/schemas/form.d.ts +341 -0
  259. package/dist/shared/schemas/form.d.ts.map +1 -0
  260. package/dist/shared/schemas/form.js +117 -0
  261. package/dist/shared/schemas/form.js.map +1 -0
  262. package/dist/shared/schemas/index.d.ts +12 -0
  263. package/dist/shared/schemas/index.d.ts.map +1 -0
  264. package/dist/shared/schemas/index.js +12 -0
  265. package/dist/shared/schemas/index.js.map +1 -0
  266. package/dist/shared/schemas/media.d.ts +42 -0
  267. package/dist/shared/schemas/media.d.ts.map +1 -0
  268. package/dist/shared/schemas/media.js +16 -0
  269. package/dist/shared/schemas/media.js.map +1 -0
  270. package/dist/shared/schemas/pagination.d.ts +34 -0
  271. package/dist/shared/schemas/pagination.d.ts.map +1 -0
  272. package/dist/shared/schemas/pagination.js +21 -0
  273. package/dist/shared/schemas/pagination.js.map +1 -0
  274. package/dist/shared/schemas/post.d.ts +862 -0
  275. package/dist/shared/schemas/post.d.ts.map +1 -0
  276. package/dist/shared/schemas/post.js +187 -0
  277. package/dist/shared/schemas/post.js.map +1 -0
  278. package/dist/shared/schemas/settings.d.ts +839 -0
  279. package/dist/shared/schemas/settings.d.ts.map +1 -0
  280. package/dist/shared/schemas/settings.js +202 -0
  281. package/dist/shared/schemas/settings.js.map +1 -0
  282. package/dist/shared/schemas/site-document.d.ts +38 -0
  283. package/dist/shared/schemas/site-document.d.ts.map +1 -0
  284. package/dist/shared/schemas/site-document.js +21 -0
  285. package/dist/shared/schemas/site-document.js.map +1 -0
  286. package/dist/shared/schemas/webhook.d.ts +39 -0
  287. package/dist/shared/schemas/webhook.d.ts.map +1 -0
  288. package/dist/shared/schemas/webhook.js +20 -0
  289. package/dist/shared/schemas/webhook.js.map +1 -0
  290. package/dist/spa.d.ts +9 -0
  291. package/dist/spa.d.ts.map +1 -0
  292. package/dist/spa.js +57 -0
  293. package/dist/spa.js.map +1 -0
  294. package/migrations/0001_settings.sql +39 -0
  295. package/migrations/0002_content.sql +135 -0
  296. package/migrations/0003_forms.sql +35 -0
  297. package/migrations/0004_analytics.sql +42 -0
  298. package/migrations/0005_media.sql +17 -0
  299. package/migrations/0006_site_documents.sql +18 -0
  300. package/migrations/0007_auth.sql +81 -0
  301. package/migrations/0008_api_keys.sql +20 -0
  302. package/package.json +68 -0
  303. package/scripts/bundle-admin.mjs +54 -0
  304. package/seed/local.sql +63 -0
@@ -0,0 +1,203 @@
1
+ import { HttpError } from '../../lib/errors.js';
2
+ /** JSON-text `cta` column -> parsed value; NULL/empty means no CTA configured. */
3
+ function parseCta(raw) {
4
+ return raw ? JSON.parse(raw) : null;
5
+ }
6
+ export function toPostRecord(row, hasPendingDraft, authorIds) {
7
+ return {
8
+ id: row.id,
9
+ slug: row.slug,
10
+ type: row.type,
11
+ title: row.title,
12
+ excerpt: row.excerpt,
13
+ content: row.content,
14
+ coverImageUrl: row.cover_image_url,
15
+ category: row.category,
16
+ tags: JSON.parse(row.tags),
17
+ status: row.status,
18
+ ogTitle: row.og_title,
19
+ ogDescription: row.og_description,
20
+ ogImageUrl: row.og_image_url,
21
+ answerSummary: row.answer_summary,
22
+ questionHeading: row.question_heading,
23
+ cta: parseCta(row.cta),
24
+ currentVersionId: row.current_version_id,
25
+ currentVersionNumber: row.current_version_number,
26
+ viewCount: row.view_count,
27
+ likeCount: row.like_count,
28
+ wordCount: row.word_count,
29
+ publishedAt: row.published_at,
30
+ createdAt: row.created_at,
31
+ updatedAt: row.updated_at,
32
+ hasPendingDraft,
33
+ ...(authorIds ? { authorIds } : {}),
34
+ };
35
+ }
36
+ export function toPostVersionRecord(row) {
37
+ return {
38
+ id: row.id,
39
+ postId: row.post_id,
40
+ version: row.version,
41
+ title: row.title,
42
+ excerpt: row.excerpt,
43
+ content: row.content,
44
+ coverImageUrl: row.cover_image_url,
45
+ category: row.category,
46
+ tags: JSON.parse(row.tags),
47
+ ogTitle: row.og_title,
48
+ ogDescription: row.og_description,
49
+ ogImageUrl: row.og_image_url,
50
+ answerSummary: row.answer_summary,
51
+ questionHeading: row.question_heading,
52
+ cta: parseCta(row.cta),
53
+ authorIds: JSON.parse(row.author_ids),
54
+ wordCount: row.word_count,
55
+ note: row.note,
56
+ publishedAt: row.published_at,
57
+ publishedBy: row.published_by,
58
+ };
59
+ }
60
+ export async function fetchPostRow(db, id) {
61
+ const row = await db.prepare('SELECT * FROM posts WHERE id = ?1').bind(id).first();
62
+ if (!row)
63
+ throw new HttpError('not_found', 'post not found');
64
+ return row;
65
+ }
66
+ export async function fetchPostRowBySlug(db, slug) {
67
+ return db.prepare('SELECT * FROM posts WHERE slug = ?1').bind(slug).first();
68
+ }
69
+ export async function fetchDraftRow(db, postId) {
70
+ return db
71
+ .prepare('SELECT * FROM post_drafts WHERE post_id = ?1')
72
+ .bind(postId)
73
+ .first();
74
+ }
75
+ export async function hasDraft(db, postId) {
76
+ return (await fetchDraftRow(db, postId)) !== null;
77
+ }
78
+ export async function fetchVersionRow(db, postId, version) {
79
+ return db
80
+ .prepare('SELECT * FROM post_versions WHERE post_id = ?1 AND version = ?2')
81
+ .bind(postId, version)
82
+ .first();
83
+ }
84
+ /** Validates every id exists in authors; throws otherwise (D1 does not enforce the FK). */
85
+ export async function assertAuthorsExist(db, authorIds) {
86
+ if (authorIds.length === 0)
87
+ return;
88
+ const placeholders = authorIds.map((_, i) => `?${i + 1}`).join(', ');
89
+ const { results } = await db
90
+ .prepare(`SELECT id FROM authors WHERE id IN (${placeholders})`)
91
+ .bind(...authorIds)
92
+ .all();
93
+ const found = new Set(results.map((r) => r.id));
94
+ const missing = authorIds.filter((id) => !found.has(id));
95
+ if (missing.length > 0) {
96
+ throw new HttpError('validation_error', `unknown author id(s): ${missing.join(', ')}`);
97
+ }
98
+ }
99
+ /** Replaces a post's author associations to match authorIds, in order. */
100
+ export function syncPostAuthorsStatements(db, postId, authorIds) {
101
+ const statements = [db.prepare('DELETE FROM post_authors WHERE post_id = ?1').bind(postId)];
102
+ authorIds.forEach((authorId, position) => {
103
+ statements.push(db
104
+ .prepare('INSERT INTO post_authors (post_id, author_id, position) VALUES (?1, ?2, ?3)')
105
+ .bind(postId, authorId, position));
106
+ });
107
+ return statements;
108
+ }
109
+ export function postRowToContentFields(row) {
110
+ return {
111
+ title: row.title,
112
+ excerpt: row.excerpt,
113
+ content: row.content,
114
+ coverImageUrl: row.cover_image_url,
115
+ category: row.category,
116
+ tags: JSON.parse(row.tags),
117
+ ogTitle: row.og_title,
118
+ ogDescription: row.og_description,
119
+ ogImageUrl: row.og_image_url,
120
+ answerSummary: row.answer_summary,
121
+ questionHeading: row.question_heading,
122
+ cta: parseCta(row.cta),
123
+ };
124
+ }
125
+ export function draftRowToContentFields(row) {
126
+ return {
127
+ title: row.title,
128
+ excerpt: row.excerpt,
129
+ content: row.content,
130
+ coverImageUrl: row.cover_image_url,
131
+ category: row.category,
132
+ tags: JSON.parse(row.tags),
133
+ ogTitle: row.og_title,
134
+ ogDescription: row.og_description,
135
+ ogImageUrl: row.og_image_url,
136
+ answerSummary: row.answer_summary,
137
+ questionHeading: row.question_heading,
138
+ cta: parseCta(row.cta),
139
+ };
140
+ }
141
+ export function versionRowToContentFields(row) {
142
+ return {
143
+ title: row.title,
144
+ excerpt: row.excerpt,
145
+ content: row.content,
146
+ coverImageUrl: row.cover_image_url,
147
+ category: row.category,
148
+ tags: JSON.parse(row.tags),
149
+ ogTitle: row.og_title,
150
+ ogDescription: row.og_description,
151
+ ogImageUrl: row.og_image_url,
152
+ answerSummary: row.answer_summary,
153
+ questionHeading: row.question_heading,
154
+ cta: parseCta(row.cta),
155
+ };
156
+ }
157
+ /**
158
+ * Merges a partial content patch over a base. `sanitizedContent` is applied
159
+ * separately since sanitization happens once, before merge. A field that is
160
+ * `undefined` in the patch (key omitted) keeps the base value; an explicit
161
+ * value replaces it, and "" clears to NULL (the wire protocol for "unset" on
162
+ * a field-merge update: JSON cannot carry undefined, so "" is the only way a
163
+ * client can express clearing; normalized here, the single funnel every
164
+ * update path flows through, so rows never store empty strings). `cta` is a
165
+ * whole object, not a string, so it uses `null` directly as its own clear
166
+ * signal instead of the ""-trick: JSON already distinguishes an explicit
167
+ * null from an omitted key, which is all this needs.
168
+ */
169
+ export function mergeContentFields(base, patch, sanitizedContent) {
170
+ const clearable = (patchValue, baseValue) => patchValue !== undefined ? patchValue || null : baseValue;
171
+ return {
172
+ title: patch.title ?? base.title,
173
+ excerpt: clearable(patch.excerpt, base.excerpt),
174
+ content: sanitizedContent ?? base.content,
175
+ coverImageUrl: clearable(patch.coverImageUrl, base.coverImageUrl),
176
+ category: clearable(patch.category, base.category),
177
+ tags: patch.tags ?? base.tags,
178
+ ogTitle: clearable(patch.ogTitle, base.ogTitle),
179
+ ogDescription: clearable(patch.ogDescription, base.ogDescription),
180
+ ogImageUrl: clearable(patch.ogImageUrl, base.ogImageUrl),
181
+ answerSummary: clearable(patch.answerSummary, base.answerSummary),
182
+ questionHeading: clearable(patch.questionHeading, base.questionHeading),
183
+ cta: patch.cta !== undefined ? patch.cta : base.cta,
184
+ };
185
+ }
186
+ export async function currentAuthorIds(db, postId) {
187
+ const { results } = await db
188
+ .prepare('SELECT author_id FROM post_authors WHERE post_id = ?1 ORDER BY position')
189
+ .bind(postId)
190
+ .all();
191
+ return results.map((r) => r.author_id);
192
+ }
193
+ /** Appends "-2", "-3", ... until the slug is free. Used by duplicatePost. */
194
+ export async function generateUniqueSlug(db, baseSlug) {
195
+ let candidate = baseSlug;
196
+ let suffix = 2;
197
+ while (await fetchPostRowBySlug(db, candidate)) {
198
+ candidate = `${baseSlug}-${suffix}`;
199
+ suffix += 1;
200
+ }
201
+ return candidate;
202
+ }
203
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/modules/content/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { PostRecord, PostVersionRecord, VersionStatsEntry } from '../../shared/index.js';
2
+ export interface PublishOptions {
3
+ note?: string | undefined;
4
+ publishedBy: string | null;
5
+ }
6
+ /**
7
+ * The publish state machine, the heart of the CMS.
8
+ * Branches on whether this post has ever been published:
9
+ * - never published (current_version_id is null): snapshot the posts row
10
+ * itself as v1.
11
+ * - published before: requires a pending post_drafts row, snapshots it as
12
+ * vN+1, applies it onto posts, and deletes the draft.
13
+ * Both branches are single atomic db.batch() calls: a version snapshot and
14
+ * the posts row it's derived from must never go out of sync.
15
+ */
16
+ export declare function publishPost(db: D1Database, postId: string, options: PublishOptions): Promise<PostRecord>;
17
+ export declare function discardDraft(db: D1Database, postId: string): Promise<void>;
18
+ /**
19
+ * Copies a past version's fields forward as a brand new version (not a
20
+ * destructive rewrite of history). Also un-archives if needed and discards
21
+ * any pending draft, since that draft was staged on top of the version now
22
+ * being superseded and would otherwise silently resurrect on a later publish.
23
+ */
24
+ export declare function rollbackToVersion(db: D1Database, postId: string, targetVersion: number, publishedBy: string | null): Promise<PostRecord>;
25
+ export declare function archivePost(db: D1Database, postId: string): Promise<PostRecord>;
26
+ export declare function unarchivePost(db: D1Database, postId: string): Promise<PostRecord>;
27
+ export declare function listVersions(db: D1Database, postId: string): Promise<PostVersionRecord[]>;
28
+ export declare function getVersion(db: D1Database, postId: string, version: number): Promise<PostVersionRecord>;
29
+ /**
30
+ * Per-version view and like counts. Two flat GROUP BY queries merged
31
+ * in JS, the same shape as every other stats query in this codebase
32
+ * (getVisitStats): no CTEs, D1's SQLite build's support for them isn't worth
33
+ * betting on. `versionNumber: null` is the pre-tracking bucket (rows written
34
+ * before migration 0008 added the column); always sorted last, never merged
35
+ * into a real version's count.
36
+ */
37
+ export declare function getVersionStats(db: D1Database, postId: string): Promise<VersionStatsEntry[]>;
38
+ //# sourceMappingURL=publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../../src/modules/content/publish.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAe9F,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC,CAUrB;AA0HD,wBAAsB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhF;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,UAAU,CAAC,CAwErB;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAUrF;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAWvF;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAM/F;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA+B9B"}
@@ -0,0 +1,184 @@
1
+ import { HttpError } from '../../lib/errors.js';
2
+ import { countWords } from '../../lib/word-count.js';
3
+ import { currentAuthorIds, fetchDraftRow, fetchPostRow, fetchVersionRow, syncPostAuthorsStatements, toPostVersionRecord, } from './db.js';
4
+ import { getPostAdmin } from './service.js';
5
+ /**
6
+ * The publish state machine, the heart of the CMS.
7
+ * Branches on whether this post has ever been published:
8
+ * - never published (current_version_id is null): snapshot the posts row
9
+ * itself as v1.
10
+ * - published before: requires a pending post_drafts row, snapshots it as
11
+ * vN+1, applies it onto posts, and deletes the draft.
12
+ * Both branches are single atomic db.batch() calls: a version snapshot and
13
+ * the posts row it's derived from must never go out of sync.
14
+ */
15
+ export async function publishPost(db, postId, options) {
16
+ const row = await fetchPostRow(db, postId);
17
+ if (row.current_version_id === null) {
18
+ await publishFirstVersion(db, row, options);
19
+ }
20
+ else {
21
+ await publishPendingDraft(db, row, options);
22
+ }
23
+ return getPostAdmin(db, postId);
24
+ }
25
+ async function publishFirstVersion(db, row, options) {
26
+ const versionId = crypto.randomUUID();
27
+ const authorIds = await currentAuthorIds(db, row.id);
28
+ await db.batch([
29
+ db
30
+ .prepare(`INSERT INTO post_versions
31
+ (id, post_id, version, title, excerpt, content, cover_image_url, category, tags, og_title, og_description, og_image_url, answer_summary, question_heading, cta, author_ids, word_count, note, published_by)
32
+ VALUES (?1, ?2, 1, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)`)
33
+ .bind(versionId, row.id, row.title, row.excerpt, row.content, row.cover_image_url, row.category, row.tags, row.og_title, row.og_description, row.og_image_url, row.answer_summary, row.question_heading, row.cta, // already JSON text or null; copied verbatim, no re-encode
34
+ JSON.stringify(authorIds), row.word_count, options.note ?? null, options.publishedBy),
35
+ db
36
+ .prepare(`UPDATE posts SET status='published', current_version_id=?2, current_version_number=1,
37
+ published_at=datetime('now'), updated_at=datetime('now') WHERE id=?1`)
38
+ .bind(row.id, versionId),
39
+ ]);
40
+ }
41
+ async function publishPendingDraft(db, row, options) {
42
+ const draft = await fetchDraftRow(db, row.id);
43
+ if (!draft) {
44
+ throw new HttpError('validation_error', 'no pending draft to publish');
45
+ }
46
+ const newVersion = (row.current_version_number ?? 0) + 1;
47
+ const versionId = crypto.randomUUID();
48
+ const wordCount = countWords(draft.content);
49
+ const authorIds = JSON.parse(draft.author_ids);
50
+ await db.batch([
51
+ db
52
+ .prepare(`INSERT INTO post_versions
53
+ (id, post_id, version, title, excerpt, content, cover_image_url, category, tags, og_title, og_description, og_image_url, answer_summary, question_heading, cta, author_ids, word_count, note, published_by)
54
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19)`)
55
+ .bind(versionId, row.id, newVersion, draft.title, draft.excerpt, draft.content, draft.cover_image_url, draft.category, draft.tags, draft.og_title, draft.og_description, draft.og_image_url, draft.answer_summary, draft.question_heading, draft.cta, draft.author_ids, wordCount, options.note ?? null, options.publishedBy),
56
+ db
57
+ .prepare(`UPDATE posts SET
58
+ title=?2, excerpt=?3, content=?4, cover_image_url=?5, category=?6, tags=?7,
59
+ og_title=?8, og_description=?9, og_image_url=?10, answer_summary=?11, question_heading=?12,
60
+ cta=?13, word_count=?14,
61
+ current_version_id=?15, current_version_number=?16,
62
+ published_at=datetime('now'), updated_at=datetime('now'), status='published'
63
+ WHERE id=?1`)
64
+ .bind(row.id, draft.title, draft.excerpt, draft.content, draft.cover_image_url, draft.category, draft.tags, draft.og_title, draft.og_description, draft.og_image_url, draft.answer_summary, draft.question_heading, draft.cta, wordCount, versionId, newVersion),
65
+ ...syncPostAuthorsStatements(db, row.id, authorIds),
66
+ db.prepare('DELETE FROM post_drafts WHERE post_id = ?1').bind(row.id),
67
+ ]);
68
+ }
69
+ export async function discardDraft(db, postId) {
70
+ const draft = await fetchDraftRow(db, postId);
71
+ if (!draft) {
72
+ throw new HttpError('not_found', 'no pending draft to discard');
73
+ }
74
+ await db.prepare('DELETE FROM post_drafts WHERE post_id = ?1').bind(postId).run();
75
+ }
76
+ /**
77
+ * Copies a past version's fields forward as a brand new version (not a
78
+ * destructive rewrite of history). Also un-archives if needed and discards
79
+ * any pending draft, since that draft was staged on top of the version now
80
+ * being superseded and would otherwise silently resurrect on a later publish.
81
+ */
82
+ export async function rollbackToVersion(db, postId, targetVersion, publishedBy) {
83
+ const row = await fetchPostRow(db, postId);
84
+ const target = await fetchVersionRow(db, postId, targetVersion);
85
+ if (!target) {
86
+ throw new HttpError('not_found', 'version not found');
87
+ }
88
+ const newVersion = (row.current_version_number ?? 0) + 1;
89
+ const versionId = crypto.randomUUID();
90
+ const authorIds = JSON.parse(target.author_ids);
91
+ await db.batch([
92
+ db
93
+ .prepare(`INSERT INTO post_versions
94
+ (id, post_id, version, title, excerpt, content, cover_image_url, category, tags, og_title, og_description, og_image_url, answer_summary, question_heading, cta, author_ids, word_count, note, published_by)
95
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19)`)
96
+ .bind(versionId, postId, newVersion, target.title, target.excerpt, target.content, target.cover_image_url, target.category, target.tags, target.og_title, target.og_description, target.og_image_url, target.answer_summary, target.question_heading, target.cta, target.author_ids, target.word_count, `rollback to v${targetVersion}`, publishedBy),
97
+ db
98
+ .prepare(`UPDATE posts SET
99
+ status='published', title=?2, excerpt=?3, content=?4, cover_image_url=?5, category=?6, tags=?7,
100
+ og_title=?8, og_description=?9, og_image_url=?10, answer_summary=?11, question_heading=?12,
101
+ cta=?13, word_count=?14,
102
+ current_version_id=?15, current_version_number=?16,
103
+ published_at=datetime('now'), updated_at=datetime('now')
104
+ WHERE id=?1`)
105
+ .bind(postId, target.title, target.excerpt, target.content, target.cover_image_url, target.category, target.tags, target.og_title, target.og_description, target.og_image_url, target.answer_summary, target.question_heading, target.cta, target.word_count, versionId, newVersion),
106
+ ...syncPostAuthorsStatements(db, postId, authorIds),
107
+ db.prepare('DELETE FROM post_drafts WHERE post_id = ?1').bind(postId),
108
+ ]);
109
+ return getPostAdmin(db, postId);
110
+ }
111
+ export async function archivePost(db, postId) {
112
+ const row = await fetchPostRow(db, postId);
113
+ if (row.status === 'archived') {
114
+ throw new HttpError('validation_error', 'post is already archived');
115
+ }
116
+ await db
117
+ .prepare(`UPDATE posts SET status='archived', updated_at=datetime('now') WHERE id=?1`)
118
+ .bind(postId)
119
+ .run();
120
+ return getPostAdmin(db, postId);
121
+ }
122
+ export async function unarchivePost(db, postId) {
123
+ const row = await fetchPostRow(db, postId);
124
+ if (row.status !== 'archived') {
125
+ throw new HttpError('validation_error', 'post is not archived');
126
+ }
127
+ const newStatus = row.current_version_id ? 'published' : 'draft';
128
+ await db
129
+ .prepare(`UPDATE posts SET status=?2, updated_at=datetime('now') WHERE id=?1`)
130
+ .bind(postId, newStatus)
131
+ .run();
132
+ return getPostAdmin(db, postId);
133
+ }
134
+ export async function listVersions(db, postId) {
135
+ const { results } = await db
136
+ .prepare('SELECT * FROM post_versions WHERE post_id = ?1 ORDER BY version DESC')
137
+ .bind(postId)
138
+ .all();
139
+ return results.map(toPostVersionRecord);
140
+ }
141
+ export async function getVersion(db, postId, version) {
142
+ const row = await fetchVersionRow(db, postId, version);
143
+ if (!row) {
144
+ throw new HttpError('not_found', 'version not found');
145
+ }
146
+ return toPostVersionRecord(row);
147
+ }
148
+ /**
149
+ * Per-version view and like counts. Two flat GROUP BY queries merged
150
+ * in JS, the same shape as every other stats query in this codebase
151
+ * (getVisitStats): no CTEs, D1's SQLite build's support for them isn't worth
152
+ * betting on. `versionNumber: null` is the pre-tracking bucket (rows written
153
+ * before migration 0008 added the column); always sorted last, never merged
154
+ * into a real version's count.
155
+ */
156
+ export async function getVersionStats(db, postId) {
157
+ const [viewRows, likeRows] = await Promise.all([
158
+ db
159
+ .prepare('SELECT version_number, COUNT(*) as count FROM post_views WHERE post_id = ?1 GROUP BY version_number')
160
+ .bind(postId)
161
+ .all(),
162
+ db
163
+ .prepare('SELECT version_number, COUNT(*) as count FROM post_likes WHERE post_id = ?1 GROUP BY version_number')
164
+ .bind(postId)
165
+ .all(),
166
+ ]);
167
+ const viewsByVersion = new Map(viewRows.results.map((r) => [r.version_number, r.count]));
168
+ const likesByVersion = new Map(likeRows.results.map((r) => [r.version_number, r.count]));
169
+ const versionNumbers = new Set([...viewsByVersion.keys(), ...likesByVersion.keys()]);
170
+ return [...versionNumbers]
171
+ .map((versionNumber) => ({
172
+ versionNumber,
173
+ viewCount: viewsByVersion.get(versionNumber) ?? 0,
174
+ likeCount: likesByVersion.get(versionNumber) ?? 0,
175
+ }))
176
+ .sort((a, b) => {
177
+ if (a.versionNumber === null)
178
+ return 1;
179
+ if (b.versionNumber === null)
180
+ return -1;
181
+ return b.versionNumber - a.versionNumber;
182
+ });
183
+ }
184
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../../src/modules/content/publish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Hono } from 'hono';
2
+ import type { AuthVariables } from '../../middleware/auth.js';
3
+ import type { Bindings } from '../../env.js';
4
+ export declare const contentAdminRoutes: Hono<{
5
+ Bindings: Bindings;
6
+ Variables: AuthVariables;
7
+ }, import("hono/types").BlankSchema, "/">;
8
+ //# sourceMappingURL=routes.admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../../src/modules/content/routes.admin.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAsC7C,eAAO,MAAM,kBAAkB;cAAwB,QAAQ;eAAa,aAAa;yCAAK,CAAC"}
@@ -0,0 +1,122 @@
1
+ import { z } from 'zod';
2
+ import { createPostInputSchema, ok, paginationQuerySchema, postStatusSchema, postTypeSchema, publishInputSchema, rollbackInputSchema, updatePostInputSchema, } from '../../shared/index.js';
3
+ import { Hono } from 'hono';
4
+ import { HttpError } from '../../lib/errors.js';
5
+ import { parseOrThrow } from '../../lib/validate.js';
6
+ import { dispatchWebhook } from '../webhooks/dispatch.js';
7
+ import { archivePost, discardDraft, getVersion, getVersionStats, listVersions, publishPost, rollbackToVersion, unarchivePost, } from './publish.js';
8
+ import { createPost, duplicatePost, getContentStats, getPostAdmin, hardDeletePost, listPostsAdmin, updateWorkingCopy, } from './service.js';
9
+ const adminListQuerySchema = paginationQuerySchema.extend({
10
+ status: postStatusSchema.optional(),
11
+ type: postTypeSchema.optional(),
12
+ category: z.string().optional(),
13
+ });
14
+ function parseVersionParam(raw) {
15
+ const version = Number(raw);
16
+ if (!Number.isInteger(version) || version < 1) {
17
+ throw new HttpError('validation_error', 'version must be a positive integer');
18
+ }
19
+ return version;
20
+ }
21
+ export const contentAdminRoutes = new Hono();
22
+ contentAdminRoutes.get('/', async (c) => {
23
+ const query = parseOrThrow(adminListQuerySchema, c.req.query());
24
+ const { items, total } = await listPostsAdmin(c.env.DB, query);
25
+ return c.json(ok({ items, page: query.page, pageSize: query.pageSize, total }));
26
+ });
27
+ contentAdminRoutes.post('/', async (c) => {
28
+ const input = parseOrThrow(createPostInputSchema, await c.req.json());
29
+ const post = await createPost(c.env.DB, input);
30
+ return c.json(ok({ post }), 201);
31
+ });
32
+ // Registered BEFORE the parameterized /:id route below, same reasoning as
33
+ // /:id/versions/stats vs /:id/versions/:version further down: an
34
+ // unconstrained :id would otherwise swallow the literal "stats" segment.
35
+ contentAdminRoutes.get('/stats', async (c) => {
36
+ const stats = await getContentStats(c.env.DB);
37
+ return c.json(ok(stats));
38
+ });
39
+ contentAdminRoutes.get('/:id', async (c) => {
40
+ const post = await getPostAdmin(c.env.DB, c.req.param('id'));
41
+ return c.json(ok({ post }));
42
+ });
43
+ contentAdminRoutes.put('/:id/save', async (c) => {
44
+ const patch = parseOrThrow(updatePostInputSchema, await c.req.json());
45
+ const post = await updateWorkingCopy(c.env.DB, c.req.param('id'), patch);
46
+ return c.json(ok({ post }));
47
+ });
48
+ contentAdminRoutes.post('/:id/publish', async (c) => {
49
+ const input = parseOrThrow(publishInputSchema, await c.req.json().catch(() => ({})));
50
+ const publishedBy = c.get('auth').user;
51
+ const post = await publishPost(c.env.DB, c.req.param('id'), { ...input, publishedBy });
52
+ await dispatchWebhook(c.env.DB, c.executionCtx, c.env.SITE_URL, 'post.published', {
53
+ postId: post.id,
54
+ slug: post.slug,
55
+ title: post.title,
56
+ });
57
+ return c.json(ok({ post }));
58
+ });
59
+ contentAdminRoutes.delete('/:id/draft', async (c) => {
60
+ await discardDraft(c.env.DB, c.req.param('id'));
61
+ // Return the reverted record like every other single-post mutation here
62
+ // (publish/rollback/archive all return getPostAdmin's resolved shape):
63
+ // the admin editor re-seeds its form directly from this response, so an
64
+ // empty body would leave it showing the just-discarded draft content.
65
+ const post = await getPostAdmin(c.env.DB, c.req.param('id'));
66
+ return c.json(ok({ post }));
67
+ });
68
+ contentAdminRoutes.post('/:id/duplicate', async (c) => {
69
+ const post = await duplicatePost(c.env.DB, c.req.param('id'));
70
+ return c.json(ok({ post }), 201);
71
+ });
72
+ contentAdminRoutes.post('/:id/archive', async (c) => {
73
+ // archivePost() itself does not expose the prior status; a cheap pre-fetch
74
+ // (archivePost's own validation does an equivalent read internally anyway)
75
+ // is simpler than changing its already-tested return shape just for this.
76
+ const before = await getPostAdmin(c.env.DB, c.req.param('id'));
77
+ const post = await archivePost(c.env.DB, c.req.param('id'));
78
+ if (before.status === 'published') {
79
+ await dispatchWebhook(c.env.DB, c.executionCtx, c.env.SITE_URL, 'post.unpublished', {
80
+ postId: post.id,
81
+ slug: post.slug,
82
+ });
83
+ }
84
+ return c.json(ok({ post }));
85
+ });
86
+ contentAdminRoutes.post('/:id/unarchive', async (c) => {
87
+ const post = await unarchivePost(c.env.DB, c.req.param('id'));
88
+ return c.json(ok({ post }));
89
+ });
90
+ contentAdminRoutes.post('/:id/rollback', async (c) => {
91
+ const input = parseOrThrow(rollbackInputSchema, await c.req.json());
92
+ const publishedBy = c.get('auth').user;
93
+ const post = await rollbackToVersion(c.env.DB, c.req.param('id'), input.version, publishedBy);
94
+ // Rollback republishes, so it fires post.published like any other publish.
95
+ await dispatchWebhook(c.env.DB, c.executionCtx, c.env.SITE_URL, 'post.published', {
96
+ postId: post.id,
97
+ slug: post.slug,
98
+ title: post.title,
99
+ });
100
+ return c.json(ok({ post }));
101
+ });
102
+ contentAdminRoutes.get('/:id/versions', async (c) => {
103
+ const versions = await listVersions(c.env.DB, c.req.param('id'));
104
+ return c.json(ok({ versions }));
105
+ });
106
+ // Registered BEFORE the parameterized /:version route below: Hono matches
107
+ // route registration order, and an unconstrained `:version` param would
108
+ // otherwise swallow the literal "stats" segment first.
109
+ contentAdminRoutes.get('/:id/versions/stats', async (c) => {
110
+ const stats = await getVersionStats(c.env.DB, c.req.param('id'));
111
+ return c.json(ok({ stats }));
112
+ });
113
+ contentAdminRoutes.get('/:id/versions/:version', async (c) => {
114
+ const version = parseVersionParam(c.req.param('version'));
115
+ const versionRecord = await getVersion(c.env.DB, c.req.param('id'), version);
116
+ return c.json(ok({ version: versionRecord }));
117
+ });
118
+ contentAdminRoutes.delete('/:id', async (c) => {
119
+ await hardDeletePost(c.env.DB, c.req.param('id'));
120
+ return c.json(ok({}));
121
+ });
122
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../../src/modules/content/routes.admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Hono } from 'hono';
2
+ import { type Bindings } from '../../env.js';
3
+ export declare const contentPublicRoutes: Hono<{
4
+ Bindings: Bindings;
5
+ }, import("hono/types").BlankSchema, "/">;
6
+ //# sourceMappingURL=routes.public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.public.d.ts","sourceRoot":"","sources":["../../../src/modules/content/routes.public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAkB1D,eAAO,MAAM,mBAAmB;cAAwB,QAAQ;yCAAK,CAAC"}