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,109 @@
1
+ /**
2
+ * Plugin API routes - dynamic handler for plugin-defined endpoints
3
+ *
4
+ * Routes are mounted at /_dineway/api/plugins/{pluginId}/*
5
+ * Plugins register routes like "POST /do-something" which becomes
6
+ * POST /_dineway/api/plugins/{pluginId}/do-something
7
+ *
8
+ * Routes marked as `public: true` skip authentication and CSRF checks.
9
+ * Private routes (the default) require authentication and appropriate permissions.
10
+ */
11
+
12
+ import type { APIRoute } from "astro";
13
+
14
+ import { requirePerm } from "#api/authorize.js";
15
+ import { apiError, apiSuccess } from "#api/error.js";
16
+ import { requireScope } from "#auth/scopes.js";
17
+
18
+ export const prerender = false;
19
+
20
+ /**
21
+ * Handle all methods by matching against plugin-defined routes
22
+ */
23
+ const handleRequest: APIRoute = async ({ params, request, locals }) => {
24
+ const { dineway, user } = locals;
25
+ const pluginId = params.pluginId!;
26
+ const path = params.path || "";
27
+ const method = request.method.toUpperCase();
28
+
29
+ if (!dineway?.handlePluginApiRoute) {
30
+ return apiError("NOT_CONFIGURED", "Dineway is not configured", 500);
31
+ }
32
+
33
+ // Resolve route metadata to decide auth before dispatch
34
+ const routeMeta = dineway.getPluginRouteMeta(pluginId, `/${path}`);
35
+
36
+ if (!routeMeta) {
37
+ return apiError("NOT_FOUND", "Plugin route not found", 404);
38
+ }
39
+
40
+ // Public routes skip auth, CSRF, and scope checks entirely
41
+ if (!routeMeta.public) {
42
+ const isStateChangingMethod = !["GET", "HEAD", "OPTIONS"].includes(method);
43
+
44
+ // Private routes require authentication and permission checks
45
+ const permission = isStateChangingMethod ? "plugins:manage" : "plugins:read";
46
+ const denied = requirePerm(user, permission);
47
+ if (denied) return denied;
48
+
49
+ // Token scope enforcement — plugin routes require "admin" scope.
50
+ // Session auth is implicitly full-access (requireScope returns null).
51
+ const scopeError = requireScope(locals, "admin");
52
+ if (scopeError) return scopeError;
53
+
54
+ // Generic private plugin routes can carry arbitrary plugin-defined payloads,
55
+ // including secret-bearing config. Until route metadata can describe a
56
+ // redacted review payload plus immutable reviewed target, API-token writes
57
+ // stay blocked instead of being routed through generic HITL.
58
+ if (isStateChangingMethod && locals.authToken?.type === "api_token") {
59
+ return apiError(
60
+ "HITL_UNSUPPORTED",
61
+ "Private plugin routes do not yet support API-token writes without a reviewed redaction contract",
62
+ 409,
63
+ {
64
+ reason: "review_contract_missing",
65
+ pluginId,
66
+ path: `/${path}`,
67
+ },
68
+ );
69
+ }
70
+
71
+ // CSRF protection for state-changing requests on private routes.
72
+ // Plugin routes use soft auth in the middleware (user resolved but not required),
73
+ // so the middleware's CSRF check doesn't run. We enforce it here for private routes.
74
+ // Token-authed requests (which set tokenScopes) are exempt — tokens aren't
75
+ // ambient credentials like cookies.
76
+ if (
77
+ isStateChangingMethod &&
78
+ !locals.tokenScopes &&
79
+ request.headers.get("X-Dineway-Request") !== "1"
80
+ ) {
81
+ return apiError("CSRF_REJECTED", "Missing required header", 403);
82
+ }
83
+ }
84
+
85
+ const result = await dineway.handlePluginApiRoute(pluginId, method, `/${path}`, request);
86
+
87
+ if (!result.success) {
88
+ const code = result.error?.code ?? "PLUGIN_ERROR";
89
+ // Pass through messages from known plugin errors (PluginRouteError),
90
+ // but mask internal errors (unhandled exceptions) to avoid leaking
91
+ // database errors, file paths, etc. from sandboxed plugins.
92
+ const message =
93
+ code === "INTERNAL_ERROR"
94
+ ? "Plugin route error"
95
+ : (result.error?.message ?? "Plugin route error");
96
+ // PluginRouteError status is returned at the top level of the result
97
+ const status = (result as { status?: number }).status ?? (code === "NOT_FOUND" ? 404 : 400);
98
+ return apiError(code, message, status);
99
+ }
100
+
101
+ return apiSuccess(result.data);
102
+ };
103
+
104
+ // Export handlers for all HTTP methods
105
+ export const GET = handleRequest;
106
+ export const POST = handleRequest;
107
+ export const PUT = handleRequest;
108
+ export const PATCH = handleRequest;
109
+ export const DELETE = handleRequest;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * 404 log list and management endpoints
3
+ *
4
+ * GET /_dineway/api/redirects/404s - List 404 log entries
5
+ * DELETE /_dineway/api/redirects/404s - Clear all 404 log entries
6
+ * POST /_dineway/api/redirects/404s - Prune 404 log entries older than date
7
+ */
8
+
9
+ import type { APIRoute } from "astro";
10
+
11
+ import { requirePerm } from "#api/authorize.js";
12
+ import { handleError, unwrapResult } from "#api/error.js";
13
+ import {
14
+ handleNotFoundClear,
15
+ handleNotFoundList,
16
+ handleNotFoundPrune,
17
+ } from "#api/handlers/redirects.js";
18
+ import { isParseError, parseBody, parseQuery } from "#api/parse.js";
19
+ import { notFoundListQuery, notFoundPruneBody } from "#api/schemas.js";
20
+
21
+ export const prerender = false;
22
+
23
+ export const GET: APIRoute = async ({ url, locals }) => {
24
+ const { dineway, user } = locals;
25
+ const db = dineway.db;
26
+
27
+ const denied = requirePerm(user, "redirects:read");
28
+ if (denied) return denied;
29
+
30
+ try {
31
+ const query = parseQuery(url, notFoundListQuery);
32
+ if (isParseError(query)) return query;
33
+
34
+ const result = await handleNotFoundList(db, query);
35
+ return unwrapResult(result);
36
+ } catch (error) {
37
+ return handleError(error, "Failed to fetch 404 log", "NOT_FOUND_LIST_ERROR");
38
+ }
39
+ };
40
+
41
+ export const DELETE: APIRoute = async ({ locals }) => {
42
+ const { dineway, user } = locals;
43
+ const db = dineway.db;
44
+
45
+ const denied = requirePerm(user, "redirects:manage");
46
+ if (denied) return denied;
47
+
48
+ try {
49
+ const result = await handleNotFoundClear(db);
50
+ return unwrapResult(result);
51
+ } catch (error) {
52
+ return handleError(error, "Failed to clear 404 log", "NOT_FOUND_CLEAR_ERROR");
53
+ }
54
+ };
55
+
56
+ export const POST: APIRoute = async ({ request, locals }) => {
57
+ const { dineway, user } = locals;
58
+ const db = dineway.db;
59
+
60
+ const denied = requirePerm(user, "redirects:manage");
61
+ if (denied) return denied;
62
+
63
+ try {
64
+ const body = await parseBody(request, notFoundPruneBody);
65
+ if (isParseError(body)) return body;
66
+
67
+ const result = await handleNotFoundPrune(db, body.olderThan);
68
+ return unwrapResult(result);
69
+ } catch (error) {
70
+ return handleError(error, "Failed to prune 404 log", "NOT_FOUND_PRUNE_ERROR");
71
+ }
72
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 404 summary endpoint
3
+ *
4
+ * GET /_dineway/api/redirects/404s/summary - Get 404 summary grouped by path
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { requirePerm } from "#api/authorize.js";
10
+ import { handleError, unwrapResult } from "#api/error.js";
11
+ import { handleNotFoundSummary } from "#api/handlers/redirects.js";
12
+ import { isParseError, parseQuery } from "#api/parse.js";
13
+ import { notFoundSummaryQuery } from "#api/schemas.js";
14
+
15
+ export const prerender = false;
16
+
17
+ export const GET: APIRoute = async ({ url, locals }) => {
18
+ const { dineway, user } = locals;
19
+ const db = dineway.db;
20
+
21
+ const denied = requirePerm(user, "redirects:read");
22
+ if (denied) return denied;
23
+
24
+ try {
25
+ const query = parseQuery(url, notFoundSummaryQuery);
26
+ if (isParseError(query)) return query;
27
+
28
+ const result = await handleNotFoundSummary(db, query.limit);
29
+ return unwrapResult(result);
30
+ } catch (error) {
31
+ return handleError(error, "Failed to fetch 404 summary", "NOT_FOUND_SUMMARY_ERROR");
32
+ }
33
+ };
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Redirect by ID endpoints
3
+ *
4
+ * GET /_dineway/api/redirects/:id - Get redirect
5
+ * PUT /_dineway/api/redirects/:id - Update redirect
6
+ * DELETE /_dineway/api/redirects/:id - Delete redirect
7
+ */
8
+
9
+ import type { APIRoute } from "astro";
10
+ import { z } from "zod";
11
+
12
+ import { requirePerm } from "#api/authorize.js";
13
+ import { apiError, handleError, unwrapResult } from "#api/error.js";
14
+ import {
15
+ handleRedirectDelete,
16
+ handleRedirectGet,
17
+ handleRedirectUpdate,
18
+ } from "#api/handlers/redirects.js";
19
+ import {
20
+ ensureWorkflowHitlRouteRequest,
21
+ hitlRequiredRouteError,
22
+ resolveHitlRouteActor,
23
+ } from "#api/hitl-route-helpers.js";
24
+ import { isParseError, parseBody, parseQuery } from "#api/parse.js";
25
+ import { updateRedirectBody } from "#api/schemas.js";
26
+ import {
27
+ activityChangedKeys,
28
+ logRedirectActivity,
29
+ redirectApiRouteSource,
30
+ RedirectHitlPayloadBuilder,
31
+ RiskPolicyEvaluator,
32
+ } from "#site-context/index.js";
33
+
34
+ export const prerender = false;
35
+
36
+ const updateRedirectHitlBody = updateRedirectBody.extend({
37
+ hitlRequestId: z.string().min(1).optional(),
38
+ });
39
+
40
+ const deleteRedirectQuery = z.object({
41
+ hitlRequestId: z.string().min(1).optional(),
42
+ });
43
+
44
+ export const GET: APIRoute = async ({ params, locals }) => {
45
+ const { dineway, user } = locals;
46
+ const db = dineway.db;
47
+ const { id } = params;
48
+
49
+ const denied = requirePerm(user, "redirects:read");
50
+ if (denied) return denied;
51
+
52
+ if (!id) {
53
+ return apiError("VALIDATION_ERROR", "id is required", 400);
54
+ }
55
+
56
+ try {
57
+ const result = await handleRedirectGet(db, id);
58
+ return unwrapResult(result);
59
+ } catch (error) {
60
+ return handleError(error, "Failed to fetch redirect", "REDIRECT_GET_ERROR");
61
+ }
62
+ };
63
+
64
+ export const PUT: APIRoute = async ({ params, request, locals }) => {
65
+ const { dineway, user } = locals;
66
+ const db = dineway.db;
67
+ const { id } = params;
68
+
69
+ const denied = requirePerm(user, "redirects:manage");
70
+ if (denied) return denied;
71
+
72
+ if (!id) {
73
+ return apiError("VALIDATION_ERROR", "id is required", 400);
74
+ }
75
+
76
+ try {
77
+ const body = await parseBody(request, updateRedirectHitlBody);
78
+ if (isParseError(body)) return body;
79
+
80
+ const current = await handleRedirectGet(db, id);
81
+ if (!current.success) return unwrapResult(current);
82
+
83
+ const { hitlRequestId, ...redirectInput } = body;
84
+ const actor = resolveHitlRouteActor(locals);
85
+ const action = await new RedirectHitlPayloadBuilder().buildUpdateRedirectRequest({
86
+ redirect: current.data,
87
+ ...redirectInput,
88
+ });
89
+ const decision = await new RiskPolicyEvaluator({
90
+ db,
91
+ handlers: dineway,
92
+ }).evaluateWorkflowHitl({
93
+ actor: actor.identity,
94
+ hitlRequestId,
95
+ action,
96
+ });
97
+ if (!decision.allowed) {
98
+ const ensured = await ensureWorkflowHitlRouteRequest(db, locals, decision.action);
99
+ return hitlRequiredRouteError(decision, ensured);
100
+ }
101
+
102
+ const result = await handleRedirectUpdate(db, id, redirectInput);
103
+ if (!result.success) return unwrapResult(result);
104
+
105
+ await logRedirectActivity(db, locals, {
106
+ action: "updated",
107
+ redirectId: result.data.id,
108
+ source: result.data.source,
109
+ destination: result.data.destination,
110
+ ...redirectApiRouteSource("updated"),
111
+ summary: `Updated redirect ${result.data.source} -> ${result.data.destination}`,
112
+ detail: {
113
+ changedKeys: activityChangedKeys(redirectInput),
114
+ type: result.data.type,
115
+ enabled: result.data.enabled,
116
+ groupName: result.data.groupName,
117
+ hitlRequestId: decision.required ? decision.hitlRequest.id : null,
118
+ },
119
+ });
120
+ return unwrapResult(result);
121
+ } catch (error) {
122
+ return handleError(error, "Failed to update redirect", "REDIRECT_UPDATE_ERROR");
123
+ }
124
+ };
125
+
126
+ export const DELETE: APIRoute = async ({ params, request, locals }) => {
127
+ const { dineway, user } = locals;
128
+ const db = dineway.db;
129
+ const { id } = params;
130
+
131
+ const denied = requirePerm(user, "redirects:manage");
132
+ if (denied) return denied;
133
+
134
+ if (!id) {
135
+ return apiError("VALIDATION_ERROR", "id is required", 400);
136
+ }
137
+
138
+ const query = parseQuery(new URL(request.url), deleteRedirectQuery);
139
+ if (isParseError(query)) return query;
140
+
141
+ try {
142
+ const current = await handleRedirectGet(db, id);
143
+ if (!current.success) return unwrapResult(current);
144
+
145
+ const actor = resolveHitlRouteActor(locals);
146
+ const action = await new RedirectHitlPayloadBuilder().buildDeleteRedirectRequest({
147
+ redirect: current.data,
148
+ });
149
+ const decision = await new RiskPolicyEvaluator({
150
+ db,
151
+ handlers: dineway,
152
+ }).evaluateWorkflowHitl({
153
+ actor: actor.identity,
154
+ hitlRequestId: query.hitlRequestId,
155
+ action,
156
+ });
157
+ if (!decision.allowed) {
158
+ const ensured = await ensureWorkflowHitlRouteRequest(db, locals, decision.action);
159
+ return hitlRequiredRouteError(decision, ensured);
160
+ }
161
+
162
+ const result = await handleRedirectDelete(db, id);
163
+ if (!result.success) return unwrapResult(result);
164
+
165
+ await logRedirectActivity(db, locals, {
166
+ action: "deleted",
167
+ redirectId: current.data.id,
168
+ source: current.data.source,
169
+ destination: current.data.destination,
170
+ ...redirectApiRouteSource("deleted"),
171
+ summary: `Deleted redirect ${current.data.source} -> ${current.data.destination}`,
172
+ detail: {
173
+ type: current.data.type,
174
+ enabled: current.data.enabled,
175
+ groupName: current.data.groupName,
176
+ hitlRequestId: decision.required ? decision.hitlRequest.id : null,
177
+ },
178
+ });
179
+ return unwrapResult(result);
180
+ } catch (error) {
181
+ return handleError(error, "Failed to delete redirect", "REDIRECT_DELETE_ERROR");
182
+ }
183
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Redirects list and create endpoints
3
+ *
4
+ * GET /_dineway/api/redirects - List redirects (with filters)
5
+ * POST /_dineway/api/redirects - Create redirect
6
+ */
7
+
8
+ import type { APIRoute } from "astro";
9
+ import { z } from "zod";
10
+
11
+ import { requirePerm } from "#api/authorize.js";
12
+ import { handleError, unwrapResult } from "#api/error.js";
13
+ import { handleRedirectCreate, handleRedirectList } from "#api/handlers/redirects.js";
14
+ import {
15
+ ensureWorkflowHitlRouteRequest,
16
+ hitlRequiredRouteError,
17
+ resolveHitlRouteActor,
18
+ } from "#api/hitl-route-helpers.js";
19
+ import { isParseError, parseBody, parseQuery } from "#api/parse.js";
20
+ import { createRedirectBody, redirectsListQuery } from "#api/schemas.js";
21
+ import {
22
+ logRedirectActivity,
23
+ redirectApiRouteSource,
24
+ RedirectHitlPayloadBuilder,
25
+ RiskPolicyEvaluator,
26
+ } from "#site-context/index.js";
27
+
28
+ export const prerender = false;
29
+
30
+ const createRedirectHitlBody = createRedirectBody.extend({
31
+ hitlRequestId: z.string().min(1).optional(),
32
+ });
33
+
34
+ export const GET: APIRoute = async ({ url, locals }) => {
35
+ const { dineway, user } = locals;
36
+ const db = dineway.db;
37
+
38
+ const denied = requirePerm(user, "redirects:read");
39
+ if (denied) return denied;
40
+
41
+ try {
42
+ const query = parseQuery(url, redirectsListQuery);
43
+ if (isParseError(query)) return query;
44
+
45
+ const result = await handleRedirectList(db, query);
46
+ return unwrapResult(result);
47
+ } catch (error) {
48
+ return handleError(error, "Failed to fetch redirects", "REDIRECT_LIST_ERROR");
49
+ }
50
+ };
51
+
52
+ export const POST: APIRoute = async ({ request, locals }) => {
53
+ const { dineway, user } = locals;
54
+ const db = dineway.db;
55
+
56
+ const denied = requirePerm(user, "redirects:manage");
57
+ if (denied) return denied;
58
+
59
+ try {
60
+ const body = await parseBody(request, createRedirectHitlBody);
61
+ if (isParseError(body)) return body;
62
+
63
+ const { hitlRequestId, ...redirectInput } = body;
64
+ const actor = resolveHitlRouteActor(locals);
65
+ const action = await new RedirectHitlPayloadBuilder().buildCreateRedirectRequest(redirectInput);
66
+ const decision = await new RiskPolicyEvaluator({
67
+ db,
68
+ handlers: dineway,
69
+ }).evaluateWorkflowHitl({
70
+ actor: actor.identity,
71
+ hitlRequestId,
72
+ action,
73
+ });
74
+ if (!decision.allowed) {
75
+ const ensured = await ensureWorkflowHitlRouteRequest(db, locals, decision.action);
76
+ return hitlRequiredRouteError(decision, ensured);
77
+ }
78
+
79
+ const result = await handleRedirectCreate(db, redirectInput);
80
+ if (!result.success) return unwrapResult(result, 201);
81
+
82
+ await logRedirectActivity(db, locals, {
83
+ action: "created",
84
+ redirectId: result.data.id,
85
+ source: result.data.source,
86
+ destination: result.data.destination,
87
+ ...redirectApiRouteSource("created"),
88
+ summary: `Created redirect ${result.data.source} -> ${result.data.destination}`,
89
+ detail: {
90
+ type: result.data.type,
91
+ enabled: result.data.enabled,
92
+ groupName: result.data.groupName,
93
+ hitlRequestId: decision.required ? decision.hitlRequest.id : null,
94
+ },
95
+ });
96
+ return unwrapResult(result, 201);
97
+ } catch (error) {
98
+ return handleError(error, "Failed to create redirect", "REDIRECT_CREATE_ERROR");
99
+ }
100
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Single revision endpoint - injected by Dineway integration
3
+ *
4
+ * GET /_dineway/api/revisions/{revisionId} - Get revision details
5
+ * POST /_dineway/api/revisions/{revisionId}/restore - Restore revision
6
+ */
7
+
8
+ import type { APIRoute } from "astro";
9
+
10
+ import { requirePerm } from "#api/authorize.js";
11
+ import { apiError, unwrapResult } from "#api/error.js";
12
+
13
+ export const prerender = false;
14
+
15
+ export const GET: APIRoute = async ({ params, locals }) => {
16
+ const { dineway, user } = locals;
17
+ const revisionId = params.revisionId!;
18
+
19
+ const denied = requirePerm(user, "content:read");
20
+ if (denied) return denied;
21
+
22
+ if (!dineway?.handleRevisionGet) {
23
+ return apiError("NOT_CONFIGURED", "Dineway is not configured", 500);
24
+ }
25
+
26
+ const result = await dineway.handleRevisionGet(revisionId);
27
+
28
+ return unwrapResult(result);
29
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Restore revision endpoint - injected by Dineway integration
3
+ *
4
+ * POST /_dineway/api/revisions/{revisionId}/restore - Restore revision
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
+
12
+ export const prerender = false;
13
+
14
+ export const POST: APIRoute = async ({ params, locals }) => {
15
+ const { dineway, user } = locals;
16
+ const revisionId = params.revisionId!;
17
+
18
+ if (
19
+ !dineway?.handleRevisionRestore ||
20
+ !dineway?.handleRevisionGet ||
21
+ !dineway?.handleContentGet
22
+ ) {
23
+ return apiError("NOT_CONFIGURED", "Dineway is not configured", 500);
24
+ }
25
+
26
+ // Fetch the revision to discover which content entry it belongs to
27
+ const revision = await dineway.handleRevisionGet(revisionId);
28
+ if (!revision.success) {
29
+ return apiError(
30
+ revision.error?.code ?? "UNKNOWN_ERROR",
31
+ revision.error?.message ?? "Revision not found",
32
+ mapErrorStatus(revision.error?.code),
33
+ );
34
+ }
35
+
36
+ const collection = revision.data?.item?.collection;
37
+ const entryId = revision.data?.item?.entryId;
38
+
39
+ if (!collection || !entryId) {
40
+ return apiError("INVALID_REVISION", "Revision is missing collection or entry reference", 400);
41
+ }
42
+
43
+ // Fetch the content entry to check ownership
44
+ const existing = await dineway.handleContentGet(collection, entryId);
45
+ if (!existing.success) {
46
+ return apiError(
47
+ existing.error?.code ?? "UNKNOWN_ERROR",
48
+ existing.error?.message ?? "Content not found",
49
+ mapErrorStatus(existing.error?.code),
50
+ );
51
+ }
52
+
53
+ const authorId = existing.data?.item?.authorId ?? "";
54
+
55
+ // Check ownership: authors can only restore their own content, editors+ can restore any
56
+ const denied = requireOwnerPerm(user, authorId, "content:edit_own", "content:edit_any");
57
+ if (denied) return denied;
58
+
59
+ const result = await dineway.handleRevisionRestore(revisionId, user!.id);
60
+
61
+ return unwrapResult(result);
62
+ };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Schema field CRUD endpoints
3
+ *
4
+ * GET /_dineway/api/schema/collections/{slug}/fields/{fieldSlug} - Get field
5
+ * PUT /_dineway/api/schema/collections/{slug}/fields/{fieldSlug} - Update field
6
+ * DELETE /_dineway/api/schema/collections/{slug}/fields/{fieldSlug} - Delete field
7
+ */
8
+
9
+ import type { APIRoute } from "astro";
10
+
11
+ import { requirePerm } from "#api/authorize.js";
12
+ import { requireDb, unwrapResult } from "#api/error.js";
13
+ import {
14
+ handleSchemaFieldGet,
15
+ handleSchemaFieldUpdate,
16
+ handleSchemaFieldDelete,
17
+ } from "#api/index.js";
18
+ import { parseBody, isParseError } from "#api/parse.js";
19
+ import { updateFieldBody } from "#api/schemas.js";
20
+ import type { UpdateFieldInput } from "#schema/types.js";
21
+ import {
22
+ activityChangedKeys,
23
+ logSchemaActivity,
24
+ schemaApiRouteSource,
25
+ } from "#site-context/activity-events.js";
26
+
27
+ export const prerender = false;
28
+
29
+ export const GET: APIRoute = async ({ params, locals }) => {
30
+ const { dineway, user } = locals;
31
+ const collectionSlug = params.slug!;
32
+ const fieldSlug = params.fieldSlug!;
33
+
34
+ const dbErr = requireDb(dineway?.db);
35
+ if (dbErr) return dbErr;
36
+
37
+ const denied = requirePerm(user, "schema:read");
38
+ if (denied) return denied;
39
+
40
+ const result = await handleSchemaFieldGet(dineway.db, collectionSlug, fieldSlug);
41
+ return unwrapResult(result);
42
+ };
43
+
44
+ export const PUT: APIRoute = async ({ params, request, locals }) => {
45
+ const { dineway, user } = locals;
46
+ const collectionSlug = params.slug!;
47
+ const fieldSlug = params.fieldSlug!;
48
+
49
+ const dbErr = requireDb(dineway?.db);
50
+ if (dbErr) return dbErr;
51
+
52
+ const denied = requirePerm(user, "schema:manage");
53
+ if (denied) return denied;
54
+
55
+ const body = await parseBody(request, updateFieldBody);
56
+ if (isParseError(body)) return body;
57
+
58
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- body is Zod-validated via parseBody(request, updateFieldBody) above
59
+ const result = await handleSchemaFieldUpdate(
60
+ dineway.db,
61
+ collectionSlug,
62
+ fieldSlug,
63
+ body as UpdateFieldInput,
64
+ );
65
+ if (result.success) dineway.invalidateManifest();
66
+ if (result.success) {
67
+ await logSchemaActivity(dineway.db, locals, {
68
+ action: "field_updated",
69
+ collection: collectionSlug,
70
+ field: fieldSlug,
71
+ ...schemaApiRouteSource("field_updated"),
72
+ summary: `Updated field ${fieldSlug} in ${collectionSlug}`,
73
+ detail: {
74
+ changedFields: activityChangedKeys(body),
75
+ },
76
+ });
77
+ }
78
+ return unwrapResult(result);
79
+ };
80
+
81
+ export const DELETE: APIRoute = async ({ params, locals }) => {
82
+ const { dineway, user } = locals;
83
+ const collectionSlug = params.slug!;
84
+ const fieldSlug = params.fieldSlug!;
85
+
86
+ const dbErr = requireDb(dineway?.db);
87
+ if (dbErr) return dbErr;
88
+
89
+ const denied = requirePerm(user, "schema:manage");
90
+ if (denied) return denied;
91
+
92
+ const result = await handleSchemaFieldDelete(dineway.db, collectionSlug, fieldSlug);
93
+ if (result.success) dineway.invalidateManifest();
94
+ if (result.success) {
95
+ await logSchemaActivity(dineway.db, locals, {
96
+ action: "field_deleted",
97
+ collection: collectionSlug,
98
+ field: fieldSlug,
99
+ ...schemaApiRouteSource("field_deleted"),
100
+ summary: `Deleted field ${fieldSlug} from ${collectionSlug}`,
101
+ });
102
+ }
103
+ return unwrapResult(result);
104
+ };