dineway 0.1.3 → 0.1.5

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 (296) hide show
  1. package/README.md +6 -3
  2. package/dist/{apply-CAPvMfoU.mjs → apply-iVSqz2qs.mjs} +132 -39
  3. package/dist/astro/index.d.mts +18 -9
  4. package/dist/astro/index.mjs +238 -16
  5. package/dist/astro/middleware/auth.d.mts +16 -5
  6. package/dist/astro/middleware/auth.mjs +74 -37
  7. package/dist/astro/middleware/redirect.mjs +24 -8
  8. package/dist/astro/middleware/request-context.mjs +18 -5
  9. package/dist/astro/middleware/setup.mjs +1 -1
  10. package/dist/astro/middleware.mjs +411 -169
  11. package/dist/astro/types.d.mts +25 -8
  12. package/dist/{byline-DeWCMU_i.mjs → byline-OhH2dlRu.mjs} +6 -21
  13. package/dist/{bylines-DyqBV9EQ.mjs → bylines-BGpD9_hy.mjs} +16 -6
  14. package/dist/cache-BdSY-gQN.mjs +42 -0
  15. package/dist/chunks--4F8ddV4.mjs +18 -0
  16. package/dist/cli/index.mjs +935 -15
  17. package/dist/client/external-auth-headers.d.mts +1 -1
  18. package/dist/client/index.d.mts +11 -3
  19. package/dist/client/index.mjs +4 -3
  20. package/dist/{connection-C9pxzuag.mjs → connection-BCNICDWN.mjs} +22 -5
  21. package/dist/{content-zSgdNmnt.mjs → content-DWi4d0rT.mjs} +41 -2
  22. package/dist/database/instrumentation.d.mts +34 -0
  23. package/dist/database/instrumentation.mjs +53 -0
  24. package/dist/db/index.d.mts +3 -3
  25. package/dist/db/index.mjs +2 -2
  26. package/dist/db/libsql.d.mts +1 -1
  27. package/dist/db/libsql.mjs +11 -5
  28. package/dist/db/postgres.d.mts +1 -1
  29. package/dist/db/sqlite.d.mts +1 -1
  30. package/dist/db/sqlite.mjs +7 -1
  31. package/dist/db-errors-CEqD7qH9.mjs +23 -0
  32. package/dist/{default-WYlzADZL.mjs → default-VjJyuuG9.mjs} +2 -0
  33. package/dist/{dialect-helpers-B9uSp2GJ.mjs → dialect-helpers-DhTzaUxP.mjs} +3 -0
  34. package/dist/{error-DrxtnGPg.mjs → error-BmL6QipT.mjs} +7 -3
  35. package/dist/{index-C-jx21qs.d.mts → index-yvc6E_17.d.mts} +157 -30
  36. package/dist/index.d.mts +11 -11
  37. package/dist/index.mjs +24 -22
  38. package/dist/{loader-qKmo0wAY.mjs → loader-sMG4TZ-u.mjs} +9 -3
  39. package/dist/media/index.d.mts +1 -1
  40. package/dist/media/index.mjs +1 -1
  41. package/dist/media/local-runtime.d.mts +7 -7
  42. package/dist/page/index.d.mts +10 -2
  43. package/dist/page/index.mjs +22 -1
  44. package/dist/patterns-CrCYkMBb.mjs +92 -0
  45. package/dist/{placeholder-bOx1xCTY.d.mts → placeholder--wOi4TbO.d.mts} +1 -1
  46. package/dist/{placeholder-B3knXwNc.mjs → placeholder-Cp8g5Emj.mjs} +1 -1
  47. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  48. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  49. package/dist/{query-BiaPl_g2.mjs → query-kDmwCsHh.mjs} +118 -50
  50. package/dist/{redirect-JPqLAbxa.mjs → redirect-DnEWAkVg.mjs} +43 -99
  51. package/dist/{registry-DSd1GWB8.mjs → registry-C0zjeB9P.mjs} +191 -123
  52. package/dist/request-cache-Dk5qPSOx.mjs +66 -0
  53. package/dist/request-context.d.mts +4 -16
  54. package/dist/{runner-B5l1JfOj.d.mts → runner-CFI6B6J2.d.mts} +1 -1
  55. package/dist/{runner-BGUGywgG.mjs → runner-DWZm2KQm.mjs} +589 -137
  56. package/dist/runtime.d.mts +6 -6
  57. package/dist/runtime.mjs +2 -2
  58. package/dist/{search-BNruJHDL.mjs → search-ByRGV2pq.mjs} +570 -424
  59. package/dist/seed/index.d.mts +2 -2
  60. package/dist/seed/index.mjs +11 -10
  61. package/dist/seo/index.d.mts +1 -1
  62. package/dist/storage/local.d.mts +1 -1
  63. package/dist/storage/local.mjs +1 -1
  64. package/dist/storage/s3.d.mts +11 -3
  65. package/dist/storage/s3.mjs +78 -15
  66. package/dist/taxonomies-1s5PaS_8.mjs +266 -0
  67. package/dist/transaction-Cn2rjY78.mjs +27 -0
  68. package/dist/{types-BgQeVaPj.d.mts → types-BuMDPy5C.d.mts} +52 -3
  69. package/dist/{types-DuNbGKjF.mjs → types-COeOq9nK.mjs} +6 -1
  70. package/dist/{types-ju-_ORz7.d.mts → types-CWbdtiux.d.mts} +13 -5
  71. package/dist/{types-D38djUXv.d.mts → types-Cj0KMIZV.d.mts} +16 -3
  72. package/dist/{types-DkvMXalq.d.mts → types-DOrVigru.d.mts} +159 -0
  73. package/dist/{validate-CXnRKfJK.mjs → validate-BZ5wnLLp.mjs} +2 -1
  74. package/dist/{validate-DVKJJ-M_.d.mts → validate-IPf8n4Fj.d.mts} +4 -51
  75. package/dist/{validate-CqRJb_xU.mjs → validate-VPnKoIzW.mjs} +10 -10
  76. package/dist/version-BKXPsfmJ.mjs +6 -0
  77. package/package.json +53 -39
  78. package/src/astro/routes/PluginRegistry.tsx +21 -0
  79. package/src/astro/routes/admin.astro +99 -0
  80. package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
  81. package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
  82. package/src/astro/routes/api/admin/api-tokens/[id].ts +44 -0
  83. package/src/astro/routes/api/admin/api-tokens/index.ts +90 -0
  84. package/src/astro/routes/api/admin/briefing.ts +76 -0
  85. package/src/astro/routes/api/admin/bylines/[id]/index.ts +90 -0
  86. package/src/astro/routes/api/admin/bylines/index.ts +74 -0
  87. package/src/astro/routes/api/admin/comments/[id]/status.ts +120 -0
  88. package/src/astro/routes/api/admin/comments/[id].ts +64 -0
  89. package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
  90. package/src/astro/routes/api/admin/comments/counts.ts +30 -0
  91. package/src/astro/routes/api/admin/comments/index.ts +46 -0
  92. package/src/astro/routes/api/admin/context/[id]/history.ts +35 -0
  93. package/src/astro/routes/api/admin/context/[id]/index.ts +35 -0
  94. package/src/astro/routes/api/admin/context/[id]/review.ts +57 -0
  95. package/src/astro/routes/api/admin/context/[id]/supersede.ts +58 -0
  96. package/src/astro/routes/api/admin/context/diff.ts +35 -0
  97. package/src/astro/routes/api/admin/context/index.ts +69 -0
  98. package/src/astro/routes/api/admin/context/stale.ts +35 -0
  99. package/src/astro/routes/api/admin/hitl-requests/[id]/index.ts +38 -0
  100. package/src/astro/routes/api/admin/hitl-requests/[id]/resolve.ts +54 -0
  101. package/src/astro/routes/api/admin/hitl-requests/index.ts +38 -0
  102. package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +132 -0
  103. package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
  104. package/src/astro/routes/api/admin/oauth-clients/[id].ts +137 -0
  105. package/src/astro/routes/api/admin/oauth-clients/index.ts +95 -0
  106. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +91 -0
  107. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +91 -0
  108. package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
  109. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +98 -0
  110. package/src/astro/routes/api/admin/plugins/[id]/update.ts +154 -0
  111. package/src/astro/routes/api/admin/plugins/index.ts +32 -0
  112. package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +62 -0
  113. package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
  114. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +135 -0
  115. package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
  116. package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
  117. package/src/astro/routes/api/admin/review-requests/[id]/index.ts +35 -0
  118. package/src/astro/routes/api/admin/review-requests/[id]/resolve.ts +52 -0
  119. package/src/astro/routes/api/admin/review-requests/index.ts +35 -0
  120. package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
  121. package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +62 -0
  122. package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
  123. package/src/astro/routes/api/admin/users/[id]/disable.ts +72 -0
  124. package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
  125. package/src/astro/routes/api/admin/users/[id]/index.ts +166 -0
  126. package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
  127. package/src/astro/routes/api/admin/users/index.ts +66 -0
  128. package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
  129. package/src/astro/routes/api/auth/invite/accept.ts +52 -0
  130. package/src/astro/routes/api/auth/invite/complete.ts +86 -0
  131. package/src/astro/routes/api/auth/invite/index.ts +99 -0
  132. package/src/astro/routes/api/auth/invite/register-options.ts +73 -0
  133. package/src/astro/routes/api/auth/logout.ts +40 -0
  134. package/src/astro/routes/api/auth/magic-link/send.ts +90 -0
  135. package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
  136. package/src/astro/routes/api/auth/me.ts +60 -0
  137. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +221 -0
  138. package/src/astro/routes/api/auth/oauth/[provider].ts +120 -0
  139. package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
  140. package/src/astro/routes/api/auth/passkey/index.ts +54 -0
  141. package/src/astro/routes/api/auth/passkey/options.ts +85 -0
  142. package/src/astro/routes/api/auth/passkey/register/options.ts +88 -0
  143. package/src/astro/routes/api/auth/passkey/register/verify.ts +119 -0
  144. package/src/astro/routes/api/auth/passkey/verify.ts +72 -0
  145. package/src/astro/routes/api/auth/signup/complete.ts +87 -0
  146. package/src/astro/routes/api/auth/signup/request.ts +89 -0
  147. package/src/astro/routes/api/auth/signup/verify.ts +53 -0
  148. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +310 -0
  149. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
  150. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +68 -0
  151. package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +77 -0
  152. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +42 -0
  153. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
  154. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +100 -0
  155. package/src/astro/routes/api/content/[collection]/[id]/restore.ts +64 -0
  156. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
  157. package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +129 -0
  158. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +143 -0
  159. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +50 -0
  160. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +69 -0
  161. package/src/astro/routes/api/content/[collection]/[id].ts +173 -0
  162. package/src/astro/routes/api/content/[collection]/index.ts +103 -0
  163. package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
  164. package/src/astro/routes/api/dashboard.ts +32 -0
  165. package/src/astro/routes/api/dev/emails.ts +36 -0
  166. package/src/astro/routes/api/health.ts +54 -0
  167. package/src/astro/routes/api/import/probe.ts +47 -0
  168. package/src/astro/routes/api/import/wordpress/analyze.ts +523 -0
  169. package/src/astro/routes/api/import/wordpress/execute.ts +330 -0
  170. package/src/astro/routes/api/import/wordpress/media.ts +338 -0
  171. package/src/astro/routes/api/import/wordpress/prepare.ts +212 -0
  172. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +425 -0
  173. package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
  174. package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
  175. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +399 -0
  176. package/src/astro/routes/api/manifest.ts +75 -0
  177. package/src/astro/routes/api/mcp.ts +125 -0
  178. package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
  179. package/src/astro/routes/api/media/[id].ts +145 -0
  180. package/src/astro/routes/api/media/file/[...key].ts +79 -0
  181. package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +91 -0
  182. package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
  183. package/src/astro/routes/api/media/providers/index.ts +30 -0
  184. package/src/astro/routes/api/media/upload-url.ts +146 -0
  185. package/src/astro/routes/api/media.ts +204 -0
  186. package/src/astro/routes/api/menus/[name]/items.ts +206 -0
  187. package/src/astro/routes/api/menus/[name]/reorder.ts +79 -0
  188. package/src/astro/routes/api/menus/[name].ts +145 -0
  189. package/src/astro/routes/api/menus/index.ts +91 -0
  190. package/src/astro/routes/api/oauth/authorize.ts +430 -0
  191. package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
  192. package/src/astro/routes/api/oauth/device/code.ts +56 -0
  193. package/src/astro/routes/api/oauth/device/token.ts +70 -0
  194. package/src/astro/routes/api/oauth/register.ts +182 -0
  195. package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
  196. package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
  197. package/src/astro/routes/api/oauth/token.ts +195 -0
  198. package/src/astro/routes/api/openapi.json.ts +33 -0
  199. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +109 -0
  200. package/src/astro/routes/api/redirects/404s/index.ts +72 -0
  201. package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
  202. package/src/astro/routes/api/redirects/[id].ts +183 -0
  203. package/src/astro/routes/api/redirects/index.ts +100 -0
  204. package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
  205. package/src/astro/routes/api/revisions/[revisionId]/restore.ts +62 -0
  206. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +104 -0
  207. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +67 -0
  208. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +45 -0
  209. package/src/astro/routes/api/schema/collections/[slug]/index.ts +107 -0
  210. package/src/astro/routes/api/schema/collections/index.ts +61 -0
  211. package/src/astro/routes/api/schema/index.ts +109 -0
  212. package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
  213. package/src/astro/routes/api/schema/orphans/index.ts +26 -0
  214. package/src/astro/routes/api/search/enable.ts +64 -0
  215. package/src/astro/routes/api/search/index.ts +52 -0
  216. package/src/astro/routes/api/search/rebuild.ts +72 -0
  217. package/src/astro/routes/api/search/stats.ts +35 -0
  218. package/src/astro/routes/api/search/suggest.ts +50 -0
  219. package/src/astro/routes/api/sections/[slug].ts +203 -0
  220. package/src/astro/routes/api/sections/index.ts +107 -0
  221. package/src/astro/routes/api/settings/email.ts +150 -0
  222. package/src/astro/routes/api/settings.ts +116 -0
  223. package/src/astro/routes/api/setup/admin-verify.ts +122 -0
  224. package/src/astro/routes/api/setup/admin.ts +104 -0
  225. package/src/astro/routes/api/setup/dev-bypass.ts +200 -0
  226. package/src/astro/routes/api/setup/dev-reset.ts +40 -0
  227. package/src/astro/routes/api/setup/index.ts +128 -0
  228. package/src/astro/routes/api/setup/status.ts +122 -0
  229. package/src/astro/routes/api/snapshot.ts +76 -0
  230. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +232 -0
  231. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +131 -0
  232. package/src/astro/routes/api/taxonomies/index.ts +114 -0
  233. package/src/astro/routes/api/themes/preview.ts +78 -0
  234. package/src/astro/routes/api/typegen.ts +114 -0
  235. package/src/astro/routes/api/well-known/auth.ts +71 -0
  236. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +48 -0
  237. package/src/astro/routes/api/well-known/oauth-protected-resource.ts +39 -0
  238. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +114 -0
  239. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +213 -0
  240. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +126 -0
  241. package/src/astro/routes/api/widget-areas/[name].ts +135 -0
  242. package/src/astro/routes/api/widget-areas/index.ts +149 -0
  243. package/src/astro/routes/api/widget-components.ts +22 -0
  244. package/src/astro/routes/robots.txt.ts +81 -0
  245. package/src/astro/routes/sitemap-[collection].xml.ts +104 -0
  246. package/src/astro/routes/sitemap.xml.ts +92 -0
  247. package/src/components/Break.astro +45 -0
  248. package/src/components/Button.astro +71 -0
  249. package/src/components/Buttons.astro +49 -0
  250. package/src/components/Code.astro +59 -0
  251. package/src/components/Columns.astro +59 -0
  252. package/src/components/CommentForm.astro +315 -0
  253. package/src/components/Comments.astro +232 -0
  254. package/src/components/Cover.astro +128 -0
  255. package/src/components/DinewayBodyEnd.astro +32 -0
  256. package/src/components/DinewayBodyStart.astro +32 -0
  257. package/src/components/DinewayHead.astro +61 -0
  258. package/src/components/DinewayImage.astro +178 -0
  259. package/src/components/DinewayMedia.astro +167 -0
  260. package/src/components/Embed.astro +128 -0
  261. package/src/components/File.astro +122 -0
  262. package/src/components/Gallery.astro +93 -0
  263. package/src/components/HtmlBlock.astro +33 -0
  264. package/src/components/Image.astro +178 -0
  265. package/src/components/InlineEditor.astro +27 -0
  266. package/src/components/InlinePortableTextEditor.tsx +1937 -0
  267. package/src/components/LiveSearch.astro +614 -0
  268. package/src/components/PortableText.astro +51 -0
  269. package/src/components/Pullquote.astro +51 -0
  270. package/src/components/Table.astro +135 -0
  271. package/src/components/WidgetArea.astro +22 -0
  272. package/src/components/WidgetRenderer.astro +72 -0
  273. package/src/components/index.ts +106 -0
  274. package/src/components/marks/Link.astro +31 -0
  275. package/src/components/marks/StrikeThrough.astro +7 -0
  276. package/src/components/marks/Subscript.astro +7 -0
  277. package/src/components/marks/Superscript.astro +7 -0
  278. package/src/components/marks/Underline.astro +7 -0
  279. package/src/components/marks.ts +19 -0
  280. package/src/components/widgets/Archives.astro +65 -0
  281. package/src/components/widgets/Categories.astro +35 -0
  282. package/src/components/widgets/RecentPosts.astro +51 -0
  283. package/src/components/widgets/Search.astro +18 -0
  284. package/src/components/widgets/Tags.astro +38 -0
  285. package/src/ui.ts +75 -0
  286. package/LICENSE +0 -9
  287. /package/dist/{adapters-BlzWJG82.d.mts → adapters-C2ypTrZZ.d.mts} +0 -0
  288. /package/dist/{config-Cq8H0SfX.mjs → config-BXwuX8Bx.mjs} +0 -0
  289. /package/dist/{load-C6FCD1FU.mjs → load-Coc9HpHH.mjs} +0 -0
  290. /package/dist/{manifest-schema-CTSEyIJ3.mjs → manifest-schema-D1MSVnoI.mjs} +0 -0
  291. /package/dist/{mode-BlyYtIFO.mjs → mode-47goXBBK.mjs} +0 -0
  292. /package/dist/{tokens-4vgYuXsZ.mjs → tokens-CJz9ubV6.mjs} +0 -0
  293. /package/dist/{transport-C5FYnid7.mjs → transport-DB5eDN4x.mjs} +0 -0
  294. /package/dist/{transport-gIL-e43D.d.mts → transport-Wge_IzKl.d.mts} +0 -0
  295. /package/dist/{types-CLLdsG3g.d.mts → types-BzcUjoqg.d.mts} +0 -0
  296. /package/dist/{types-DShnjzb6.mjs → types-griIBQOQ.mjs} +0 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Publish content - promotes draft to live
3
+ *
4
+ * POST /_dineway/api/content/{collection}/{id}/publish
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { requireOwnerPerm } from "#api/authorize.js";
10
+ import { apiError, mapErrorStatus, unwrapResult } from "#api/error.js";
11
+ import { isParseError, parseOptionalBody } from "#api/parse.js";
12
+ import { contentPublishBody } from "#api/schemas.js";
13
+ import {
14
+ contentApiRouteSource,
15
+ extractActivityItemId,
16
+ logContentActivity,
17
+ } from "#site-context/activity-events.js";
18
+ import { resolveActorIdentity, RiskPolicyEvaluator } from "#site-context/index.js";
19
+
20
+ export const prerender = false;
21
+
22
+ export const POST: APIRoute = async ({ params, request, locals, cache }) => {
23
+ const { dineway, user } = locals;
24
+ const collection = params.collection!;
25
+ const id = params.id!;
26
+
27
+ if (!dineway?.handleContentPublish || !dineway?.handleContentGet) {
28
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
29
+ }
30
+
31
+ // Fetch item to check ownership
32
+ const existing = await dineway.handleContentGet(collection, id);
33
+ if (!existing.success) {
34
+ return apiError(
35
+ existing.error?.code ?? "UNKNOWN_ERROR",
36
+ existing.error?.message ?? "Unknown error",
37
+ mapErrorStatus(existing.error?.code),
38
+ );
39
+ }
40
+
41
+ const existingData =
42
+ existing.data && typeof existing.data === "object"
43
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- handler returns unknown data; narrowed by typeof check above
44
+ (existing.data as Record<string, unknown>)
45
+ : undefined;
46
+ const existingItem =
47
+ existingData?.item && typeof existingData.item === "object"
48
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- narrowed by typeof check above
49
+ (existingData.item as Record<string, unknown>)
50
+ : existingData;
51
+ const authorId = typeof existingItem?.authorId === "string" ? existingItem.authorId : "";
52
+ const denied = requireOwnerPerm(user, authorId, "content:publish_own", "content:publish_any");
53
+ if (denied) return denied;
54
+
55
+ const resolvedId = typeof existingItem?.id === "string" ? existingItem.id : id;
56
+ const body = await parseOptionalBody(request, contentPublishBody, {});
57
+ if (isParseError(body)) return body;
58
+
59
+ const actor = resolveActorIdentity({
60
+ user: user ? { id: user.id } : null,
61
+ tokenScopes: locals.tokenScopes,
62
+ authToken: locals.authToken,
63
+ });
64
+ const evaluator = new RiskPolicyEvaluator({
65
+ db: dineway.db,
66
+ handlers: dineway,
67
+ });
68
+ const reviewRequestId = body.reviewRequestId ?? body.review_request_id;
69
+ const decision = await evaluator.evaluateContentPublishReview({
70
+ actor,
71
+ collection,
72
+ id: resolvedId,
73
+ reviewRequestId,
74
+ });
75
+ if (!decision.allowed) {
76
+ return apiError("REVIEW_REQUEST_REQUIRED", decision.message, 409, {
77
+ reason: decision.reason,
78
+ target: decision.target ?? null,
79
+ });
80
+ }
81
+
82
+ const result = await dineway.handleContentPublish(collection, resolvedId);
83
+
84
+ if (!result.success) return unwrapResult(result);
85
+
86
+ if (cache.enabled) await cache.invalidate({ tags: [collection, resolvedId] });
87
+
88
+ await logContentActivity(dineway.db, locals, {
89
+ action: "published",
90
+ collection,
91
+ entryId: extractActivityItemId(result.data, resolvedId),
92
+ ...contentApiRouteSource("published"),
93
+ summary: `Published content in ${collection}`,
94
+ detail: {
95
+ reviewRequestId: decision.required ? decision.reviewRequest.id : null,
96
+ },
97
+ });
98
+
99
+ return unwrapResult(result);
100
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Restore content from trash endpoint - injected by Dineway integration
3
+ *
4
+ * POST /_dineway/api/content/{collection}/{id}/restore - Restore from trash
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { requireOwnerPerm } from "#api/authorize.js";
10
+ import { apiError, mapErrorStatus, unwrapResult } from "#api/error.js";
11
+ import { contentApiRouteSource, logContentActivity } from "#site-context/activity-events.js";
12
+
13
+ export const prerender = false;
14
+
15
+ export const POST: APIRoute = async ({ params, locals, cache }) => {
16
+ const { dineway, user } = locals;
17
+ const collection = params.collection!;
18
+ const id = params.id!;
19
+
20
+ if (!dineway?.handleContentRestore || !dineway?.handleContentGetIncludingTrashed) {
21
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
22
+ }
23
+
24
+ // Fetch item including trashed items to check ownership
25
+ const existing = await dineway.handleContentGetIncludingTrashed(collection, id);
26
+ if (!existing.success) {
27
+ return apiError(
28
+ existing.error?.code ?? "UNKNOWN_ERROR",
29
+ existing.error?.message ?? "Unknown error",
30
+ mapErrorStatus(existing.error?.code),
31
+ );
32
+ }
33
+ const existingData =
34
+ existing.data && typeof existing.data === "object"
35
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- handler returns unknown data; narrowed by typeof check above
36
+ (existing.data as Record<string, unknown>)
37
+ : undefined;
38
+ // Handler returns { item, _rev } — extract the item for ownership check
39
+ const existingItem =
40
+ existingData?.item && typeof existingData.item === "object"
41
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- narrowed by typeof check above
42
+ (existingData.item as Record<string, unknown>)
43
+ : existingData;
44
+ const authorId = typeof existingItem?.authorId === "string" ? existingItem.authorId : "";
45
+ const denied = requireOwnerPerm(user, authorId, "content:edit_own", "content:edit_any");
46
+ if (denied) return denied;
47
+ const resolvedId = typeof existingItem?.id === "string" ? existingItem.id : id;
48
+
49
+ const result = await dineway.handleContentRestore(collection, resolvedId);
50
+
51
+ if (!result.success) return unwrapResult(result);
52
+
53
+ if (cache.enabled) await cache.invalidate({ tags: [collection, resolvedId] });
54
+
55
+ await logContentActivity(dineway.db, locals, {
56
+ action: "restored",
57
+ collection,
58
+ entryId: resolvedId,
59
+ ...contentApiRouteSource("restored"),
60
+ summary: `Restored content in ${collection}`,
61
+ });
62
+
63
+ return unwrapResult(result);
64
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Revisions API endpoint - injected by Dineway integration
3
+ *
4
+ * GET /_dineway/api/content/{collection}/{id}/revisions - List revisions
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { requirePerm } from "#api/authorize.js";
10
+ import { apiError, unwrapResult } from "#api/error.js";
11
+
12
+ export const prerender = false;
13
+
14
+ export const GET: APIRoute = async ({ params, url, locals }) => {
15
+ const { dineway, user } = locals;
16
+ const denied = requirePerm(user, "content:read_drafts");
17
+ if (denied) return denied;
18
+ const collection = params.collection!;
19
+ const id = params.id!;
20
+
21
+ if (!dineway?.handleRevisionList) {
22
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
23
+ }
24
+
25
+ const limit = url.searchParams.get("limit");
26
+ const result = await dineway.handleRevisionList(collection, id, {
27
+ limit: limit ? parseInt(limit, 10) : undefined,
28
+ });
29
+
30
+ return unwrapResult(result);
31
+ };
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Schedule content for future publishing - injected by Dineway integration
3
+ *
4
+ * POST /_dineway/api/content/{collection}/{id}/schedule - Schedule for publishing
5
+ * DELETE /_dineway/api/content/{collection}/{id}/schedule - Unschedule (clear scheduled time)
6
+ */
7
+
8
+ import type { APIRoute } from "astro";
9
+
10
+ import { requireOwnerPerm } from "#api/authorize.js";
11
+ import { apiError, mapErrorStatus, unwrapResult } from "#api/error.js";
12
+ import { parseBody, isParseError } from "#api/parse.js";
13
+ import { contentScheduleBody } from "#api/schemas.js";
14
+ import {
15
+ contentApiRouteSource,
16
+ extractActivityItemId,
17
+ logContentActivity,
18
+ } from "#site-context/activity-events.js";
19
+
20
+ export const prerender = false;
21
+
22
+ /**
23
+ * Extract author ID from a content item response (shared by POST and DELETE).
24
+ */
25
+ function extractOwnership(data: unknown): { authorId: string; resolvedId: string | undefined } {
26
+ const obj =
27
+ data && typeof data === "object"
28
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- handler returns unknown; narrowed by typeof
29
+ (data as Record<string, unknown>)
30
+ : undefined;
31
+ const item =
32
+ obj?.item && typeof obj.item === "object"
33
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- narrowed by typeof
34
+ (obj.item as Record<string, unknown>)
35
+ : obj;
36
+ return {
37
+ authorId: typeof item?.authorId === "string" ? item.authorId : "",
38
+ resolvedId: typeof item?.id === "string" ? item.id : undefined,
39
+ };
40
+ }
41
+
42
+ export const POST: APIRoute = async ({ params, request, locals, cache }) => {
43
+ const { dineway, user } = locals;
44
+ const collection = params.collection!;
45
+ const id = params.id!;
46
+ const body = await parseBody(request, contentScheduleBody);
47
+ if (isParseError(body)) return body;
48
+
49
+ if (!dineway?.handleContentSchedule || !dineway?.handleContentGet) {
50
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
51
+ }
52
+
53
+ // Fetch item to check ownership
54
+ const existing = await dineway.handleContentGet(collection, id);
55
+ if (!existing.success) {
56
+ return apiError(
57
+ existing.error?.code ?? "UNKNOWN_ERROR",
58
+ existing.error?.message ?? "Unknown error",
59
+ mapErrorStatus(existing.error?.code),
60
+ );
61
+ }
62
+
63
+ const { authorId, resolvedId } = extractOwnership(existing.data);
64
+ const denied = requireOwnerPerm(user, authorId, "content:publish_own", "content:publish_any");
65
+ if (denied) return denied;
66
+
67
+ const result = await dineway.handleContentSchedule(
68
+ collection,
69
+ resolvedId ?? id,
70
+ body.scheduledAt,
71
+ );
72
+
73
+ if (!result.success) return unwrapResult(result);
74
+
75
+ if (cache.enabled) await cache.invalidate({ tags: [collection, resolvedId ?? id] });
76
+
77
+ await logContentActivity(dineway.db, locals, {
78
+ action: "scheduled",
79
+ collection,
80
+ entryId: extractActivityItemId(result.data, resolvedId ?? id),
81
+ ...contentApiRouteSource("scheduled"),
82
+ summary: `Scheduled content in ${collection}`,
83
+ detail: {
84
+ scheduledAt: body.scheduledAt,
85
+ },
86
+ });
87
+
88
+ return unwrapResult(result);
89
+ };
90
+
91
+ export const DELETE: APIRoute = async ({ params, locals, cache }) => {
92
+ const { dineway, user } = locals;
93
+ const collection = params.collection!;
94
+ const id = params.id!;
95
+
96
+ if (!dineway?.handleContentUnschedule || !dineway?.handleContentGet) {
97
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
98
+ }
99
+
100
+ // Fetch item to check ownership
101
+ const existing = await dineway.handleContentGet(collection, id);
102
+ if (!existing.success) {
103
+ return apiError(
104
+ existing.error?.code ?? "UNKNOWN_ERROR",
105
+ existing.error?.message ?? "Unknown error",
106
+ mapErrorStatus(existing.error?.code),
107
+ );
108
+ }
109
+
110
+ const { authorId, resolvedId } = extractOwnership(existing.data);
111
+ const denied = requireOwnerPerm(user, authorId, "content:publish_own", "content:publish_any");
112
+ if (denied) return denied;
113
+
114
+ const result = await dineway.handleContentUnschedule(collection, resolvedId ?? id);
115
+
116
+ if (!result.success) return unwrapResult(result);
117
+
118
+ if (cache.enabled) await cache.invalidate({ tags: [collection, resolvedId ?? id] });
119
+
120
+ await logContentActivity(dineway.db, locals, {
121
+ action: "unscheduled",
122
+ collection,
123
+ entryId: extractActivityItemId(result.data, resolvedId ?? id),
124
+ ...contentApiRouteSource("unscheduled"),
125
+ summary: `Unscheduled content in ${collection}`,
126
+ });
127
+
128
+ return unwrapResult(result);
129
+ };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Content-taxonomy association endpoint
3
+ *
4
+ * GET /_dineway/api/content/:collection/:id/terms/:taxonomy - Get terms for an entry
5
+ * POST /_dineway/api/content/:collection/:id/terms/:taxonomy - Set terms for an entry
6
+ */
7
+
8
+ import type { APIRoute } from "astro";
9
+
10
+ import { requirePerm, requireOwnerPerm } from "#api/authorize.js";
11
+ import { apiError, apiSuccess, handleError, requireDb } from "#api/error.js";
12
+ import { parseBody, isParseError } from "#api/parse.js";
13
+ import { contentTermsBody } from "#api/schemas.js";
14
+ import { TaxonomyRepository } from "#db/repositories/taxonomy.js";
15
+ import { invalidateTermCache } from "#taxonomies/index.js";
16
+
17
+ export const prerender = false;
18
+
19
+ /**
20
+ * Get terms assigned to an entry
21
+ */
22
+ export const GET: APIRoute = async ({ params, locals }) => {
23
+ const { dineway, user } = locals;
24
+ const { collection, id, taxonomy } = params;
25
+
26
+ const denied = requirePerm(user, "content:read");
27
+ if (denied) return denied;
28
+
29
+ if (!collection || !id || !taxonomy) {
30
+ return apiError("VALIDATION_ERROR", "Collection, id, and taxonomy required", 400);
31
+ }
32
+
33
+ const dbErr = requireDb(dineway?.db);
34
+ if (dbErr) return dbErr;
35
+
36
+ try {
37
+ const repo = new TaxonomyRepository(dineway!.db);
38
+ const terms = await repo.getTermsForEntry(collection, id, taxonomy);
39
+
40
+ return apiSuccess({
41
+ terms: terms.map((t) => ({
42
+ id: t.id,
43
+ name: t.name,
44
+ slug: t.slug,
45
+ label: t.label,
46
+ parentId: t.parentId,
47
+ })),
48
+ });
49
+ } catch (error) {
50
+ return handleError(error, "Failed to get entry terms", "TERMS_GET_ERROR");
51
+ }
52
+ };
53
+
54
+ /**
55
+ * Set terms for an entry (replaces existing)
56
+ */
57
+ export const POST: APIRoute = async ({ params, request, locals }) => {
58
+ const { dineway, user } = locals;
59
+ const { collection, id, taxonomy } = params;
60
+
61
+ if (!collection || !id || !taxonomy) {
62
+ return apiError("VALIDATION_ERROR", "Collection, id, and taxonomy required", 400);
63
+ }
64
+
65
+ const denied = requirePerm(user, "content:edit_own");
66
+ if (denied) return denied;
67
+
68
+ const dbErr = requireDb(dineway?.db);
69
+ if (dbErr) return dbErr;
70
+
71
+ if (!dineway!.handleContentGet) {
72
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
73
+ }
74
+
75
+ // Verify the content exists before modifying its terms
76
+ const existing = await dineway!.handleContentGet(collection, id);
77
+ if (!existing.success) {
78
+ return apiError(
79
+ existing.error?.code ?? "NOT_FOUND",
80
+ existing.error?.message ?? "Content not found",
81
+ existing.error?.code === "NOT_FOUND" ? 404 : 500,
82
+ );
83
+ }
84
+
85
+ // Check ownership for edit permission
86
+ const existingData =
87
+ existing.data && typeof existing.data === "object"
88
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- handler returns unknown data; narrowed by typeof check above
89
+ (existing.data as Record<string, unknown>)
90
+ : undefined;
91
+ // Handler returns { item, _rev } — extract the item for ownership check
92
+ const existingItem =
93
+ existingData?.item && typeof existingData.item === "object"
94
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- narrowed by typeof check above
95
+ (existingData.item as Record<string, unknown>)
96
+ : existingData;
97
+ const authorId = typeof existingItem?.authorId === "string" ? existingItem.authorId : "";
98
+ const editDenied = requireOwnerPerm(user, authorId, "content:edit_own", "content:edit_any");
99
+ if (editDenied) return editDenied;
100
+
101
+ try {
102
+ const body = await parseBody(request, contentTermsBody);
103
+ if (isParseError(body)) return body;
104
+ const { termIds } = body;
105
+
106
+ const repo = new TaxonomyRepository(dineway!.db);
107
+
108
+ // Verify all term IDs exist and belong to the correct taxonomy
109
+ for (const termId of termIds) {
110
+ const term = await repo.findById(termId);
111
+ if (!term) {
112
+ return apiError("NOT_FOUND", `Term ID '${termId}' not found`, 404);
113
+ }
114
+ if (term.name !== taxonomy) {
115
+ return apiError(
116
+ "VALIDATION_ERROR",
117
+ `Term ID '${termId}' does not belong to taxonomy '${taxonomy}'`,
118
+ 400,
119
+ );
120
+ }
121
+ }
122
+
123
+ // Set the terms (replaces existing)
124
+ await repo.setTermsForEntry(collection, id, taxonomy, termIds);
125
+
126
+ invalidateTermCache();
127
+
128
+ // Get the updated terms
129
+ const terms = await repo.getTermsForEntry(collection, id, taxonomy);
130
+
131
+ return apiSuccess({
132
+ terms: terms.map((t) => ({
133
+ id: t.id,
134
+ name: t.name,
135
+ slug: t.slug,
136
+ label: t.label,
137
+ parentId: t.parentId,
138
+ })),
139
+ });
140
+ } catch (error) {
141
+ return handleError(error, "Failed to set entry terms", "TERMS_SET_ERROR");
142
+ }
143
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Content translations endpoint
3
+ *
4
+ * GET /_dineway/api/content/{collection}/{id}/translations
5
+ *
6
+ * Returns all locale variants linked to the same translation group.
7
+ */
8
+
9
+ import { hasPermission } from "@dineway-ai/auth";
10
+ import type { APIRoute } from "astro";
11
+
12
+ import { requirePerm } from "#api/authorize.js";
13
+ import { apiError, unwrapResult } from "#api/error.js";
14
+
15
+ export const prerender = false;
16
+
17
+ function isPublishedTranslation(value: unknown): boolean {
18
+ return (
19
+ typeof value === "object" && value !== null && "status" in value && value.status === "published"
20
+ );
21
+ }
22
+
23
+ export const GET: APIRoute = async ({ params, locals }) => {
24
+ const { dineway, user } = locals;
25
+ const denied = requirePerm(user, "content:read");
26
+ if (denied) return denied;
27
+ const collection = params.collection!;
28
+ const id = params.id!;
29
+
30
+ if (!dineway?.handleContentTranslations) {
31
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
32
+ }
33
+
34
+ const result = await dineway.handleContentTranslations(collection, id);
35
+
36
+ if (result.success && !hasPermission(user, "content:read_drafts")) {
37
+ const data = result.data && typeof result.data === "object" ? result.data : {};
38
+ const translations =
39
+ "translations" in data && Array.isArray(data.translations) ? data.translations : [];
40
+ return unwrapResult({
41
+ success: true,
42
+ data: {
43
+ ...data,
44
+ translations: translations.filter(isPublishedTranslation),
45
+ },
46
+ });
47
+ }
48
+
49
+ return unwrapResult(result);
50
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Unpublish content - removes from public view, preserves draft
3
+ *
4
+ * POST /_dineway/api/content/{collection}/{id}/unpublish
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { requireOwnerPerm } from "#api/authorize.js";
10
+ import { apiError, mapErrorStatus, unwrapResult } from "#api/error.js";
11
+ import {
12
+ contentApiRouteSource,
13
+ extractActivityItemId,
14
+ logContentActivity,
15
+ } from "#site-context/activity-events.js";
16
+
17
+ export const prerender = false;
18
+
19
+ export const POST: APIRoute = async ({ params, locals, cache }) => {
20
+ const { dineway, user } = locals;
21
+ const collection = params.collection!;
22
+ const id = params.id!;
23
+
24
+ if (!dineway?.handleContentUnpublish || !dineway?.handleContentGet) {
25
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
26
+ }
27
+
28
+ // Fetch item to check ownership
29
+ const existing = await dineway.handleContentGet(collection, id);
30
+ if (!existing.success) {
31
+ return apiError(
32
+ existing.error?.code ?? "UNKNOWN_ERROR",
33
+ existing.error?.message ?? "Unknown error",
34
+ mapErrorStatus(existing.error?.code),
35
+ );
36
+ }
37
+
38
+ const existingData =
39
+ existing.data && typeof existing.data === "object"
40
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- handler returns unknown data; narrowed by typeof check above
41
+ (existing.data as Record<string, unknown>)
42
+ : undefined;
43
+ const existingItem =
44
+ existingData?.item && typeof existingData.item === "object"
45
+ ? // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- narrowed by typeof check above
46
+ (existingData.item as Record<string, unknown>)
47
+ : existingData;
48
+ const authorId = typeof existingItem?.authorId === "string" ? existingItem.authorId : "";
49
+ const denied = requireOwnerPerm(user, authorId, "content:publish_own", "content:publish_any");
50
+ if (denied) return denied;
51
+
52
+ const resolvedId = typeof existingItem?.id === "string" ? existingItem.id : id;
53
+
54
+ const result = await dineway.handleContentUnpublish(collection, resolvedId);
55
+
56
+ if (!result.success) return unwrapResult(result);
57
+
58
+ if (cache.enabled) await cache.invalidate({ tags: [collection, resolvedId] });
59
+
60
+ await logContentActivity(dineway.db, locals, {
61
+ action: "unpublished",
62
+ collection,
63
+ entryId: extractActivityItemId(result.data, resolvedId),
64
+ ...contentApiRouteSource("unpublished"),
65
+ summary: `Unpublished content in ${collection}`,
66
+ });
67
+
68
+ return unwrapResult(result);
69
+ };