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,50 @@
1
+ /**
2
+ * Search suggestions endpoint - Autocomplete
3
+ *
4
+ * GET /_dineway/api/search/suggest?q=hel&limit=5
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
10
+ import { isParseError, parseQuery } from "#api/parse.js";
11
+ import { searchSuggestQuery } from "#api/schemas.js";
12
+ import { getSuggestions } from "#search/index.js";
13
+
14
+ export const prerender = false;
15
+
16
+ /**
17
+ * Get search suggestions for autocomplete
18
+ *
19
+ * Query parameters:
20
+ * - q: Partial search query (required)
21
+ * - collections: Comma-separated list of collection slugs (optional)
22
+ * - limit: Maximum suggestions (optional, defaults to 5)
23
+ */
24
+ export const GET: APIRoute = async ({ url, locals }) => {
25
+ const { dineway } = locals;
26
+
27
+ if (!dineway?.db) {
28
+ return apiError("NOT_CONFIGURED", "Dineway is not configured", 500);
29
+ }
30
+
31
+ const query = parseQuery(url, searchSuggestQuery);
32
+ if (isParseError(query)) return query;
33
+
34
+ const collections = query.collections
35
+ ? query.collections.split(",").map((c: string) => c.trim())
36
+ : undefined;
37
+
38
+ try {
39
+ await dineway.ensureSearchHealthy?.();
40
+ const suggestions = await getSuggestions(dineway.db, query.q, {
41
+ collections,
42
+ locale: query.locale,
43
+ limit: query.limit,
44
+ });
45
+
46
+ return apiSuccess({ items: suggestions });
47
+ } catch (error) {
48
+ return handleError(error, "Failed to get suggestions", "SUGGESTION_ERROR");
49
+ }
50
+ };
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Section by slug endpoints
3
+ *
4
+ * GET /_dineway/api/sections/:slug - Get section
5
+ * PUT /_dineway/api/sections/:slug - Update section
6
+ * DELETE /_dineway/api/sections/:slug - Delete section
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
+ handleSectionDelete,
16
+ handleSectionGet,
17
+ handleSectionUpdate,
18
+ } from "#api/handlers/sections.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 { updateSectionBody } from "#api/schemas.js";
26
+ import {
27
+ activityChangedKeys,
28
+ logSectionActivity,
29
+ RiskPolicyEvaluator,
30
+ sectionApiRouteSource,
31
+ SectionHitlPayloadBuilder,
32
+ } from "#site-context/index.js";
33
+
34
+ export const prerender = false;
35
+
36
+ const updateSectionHitlBody = updateSectionBody.extend({
37
+ hitlRequestId: z.string().min(1).optional(),
38
+ });
39
+
40
+ const deleteSectionQuery = 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 { slug } = params;
48
+
49
+ const denied = requirePerm(user, "sections:read");
50
+ if (denied) return denied;
51
+
52
+ if (!slug) {
53
+ return apiError("VALIDATION_ERROR", "slug is required", 400);
54
+ }
55
+
56
+ try {
57
+ const result = await handleSectionGet(db, slug);
58
+ return unwrapResult(result);
59
+ } catch (error) {
60
+ return handleError(error, "Failed to fetch section", "SECTION_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 { slug } = params;
68
+
69
+ const denied = requirePerm(user, "sections:manage");
70
+ if (denied) return denied;
71
+
72
+ if (!slug) {
73
+ return apiError("VALIDATION_ERROR", "slug is required", 400);
74
+ }
75
+
76
+ try {
77
+ const body = await parseBody(request, updateSectionHitlBody);
78
+ if (isParseError(body)) return body;
79
+
80
+ const current = await new SectionHitlPayloadBuilder(db).loadSectionSnapshot(slug);
81
+ if (!current) {
82
+ return apiError("NOT_FOUND", `Section "${slug}" not found`, 404);
83
+ }
84
+
85
+ const { hitlRequestId, ...sectionInput } = body;
86
+ const actor = resolveHitlRouteActor(locals);
87
+ const evaluator = new RiskPolicyEvaluator({
88
+ db,
89
+ handlers: dineway,
90
+ });
91
+ let approvedHitlRequestId: string | null = null;
92
+
93
+ if (evaluator.requiresWorkflowHitl(actor.identity)) {
94
+ const action = await new SectionHitlPayloadBuilder(db).buildUpdateSectionRequest({
95
+ section: current,
96
+ ...sectionInput,
97
+ });
98
+ const decision = await evaluator.evaluateWorkflowHitl({
99
+ actor: actor.identity,
100
+ hitlRequestId,
101
+ action,
102
+ });
103
+ if (!decision.allowed) {
104
+ const ensured = await ensureWorkflowHitlRouteRequest(db, locals, decision.action);
105
+ return hitlRequiredRouteError(decision, ensured);
106
+ }
107
+ approvedHitlRequestId = decision.hitlRequest.id;
108
+ }
109
+
110
+ const result = await handleSectionUpdate(db, slug, sectionInput);
111
+ if (result.success) {
112
+ await logSectionActivity(db, locals, {
113
+ action: "updated",
114
+ sectionId: result.data.id,
115
+ sectionSlug: result.data.slug,
116
+ ...sectionApiRouteSource("updated"),
117
+ detail: {
118
+ changedKeys: activityChangedKeys(sectionInput),
119
+ source: current.source,
120
+ themeId: current.themeId,
121
+ previewMediaId:
122
+ sectionInput.previewMediaId !== undefined
123
+ ? sectionInput.previewMediaId
124
+ : current.previewMediaId,
125
+ hitlRequestId: approvedHitlRequestId,
126
+ },
127
+ });
128
+ }
129
+ return unwrapResult(result);
130
+ } catch (error) {
131
+ return handleError(error, "Failed to update section", "SECTION_UPDATE_ERROR");
132
+ }
133
+ };
134
+
135
+ export const DELETE: APIRoute = async ({ params, request, locals }) => {
136
+ const { dineway, user } = locals;
137
+ const db = dineway.db;
138
+ const { slug } = params;
139
+
140
+ const denied = requirePerm(user, "sections:manage");
141
+ if (denied) return denied;
142
+
143
+ if (!slug) {
144
+ return apiError("VALIDATION_ERROR", "slug is required", 400);
145
+ }
146
+
147
+ const query = parseQuery(new URL(request.url), deleteSectionQuery);
148
+ if (isParseError(query)) return query;
149
+
150
+ try {
151
+ const current = await new SectionHitlPayloadBuilder(db).loadSectionSnapshot(slug);
152
+ if (!current) {
153
+ return apiError("NOT_FOUND", `Section "${slug}" not found`, 404);
154
+ }
155
+
156
+ if (current.source === "theme") {
157
+ const result = await handleSectionDelete(db, slug);
158
+ return unwrapResult(result);
159
+ }
160
+
161
+ const actor = resolveHitlRouteActor(locals);
162
+ const evaluator = new RiskPolicyEvaluator({
163
+ db,
164
+ handlers: dineway,
165
+ });
166
+ let approvedHitlRequestId: string | null = null;
167
+
168
+ if (evaluator.requiresWorkflowHitl(actor.identity)) {
169
+ const action = await new SectionHitlPayloadBuilder(db).buildDeleteSectionRequest({
170
+ section: current,
171
+ });
172
+ const decision = await evaluator.evaluateWorkflowHitl({
173
+ actor: actor.identity,
174
+ hitlRequestId: query.hitlRequestId,
175
+ action,
176
+ });
177
+ if (!decision.allowed) {
178
+ const ensured = await ensureWorkflowHitlRouteRequest(db, locals, decision.action);
179
+ return hitlRequiredRouteError(decision, ensured);
180
+ }
181
+ approvedHitlRequestId = decision.hitlRequest.id;
182
+ }
183
+
184
+ const result = await handleSectionDelete(db, slug);
185
+ if (result.success) {
186
+ await logSectionActivity(db, locals, {
187
+ action: "deleted",
188
+ sectionId: current.id,
189
+ sectionSlug: current.slug,
190
+ ...sectionApiRouteSource("deleted"),
191
+ detail: {
192
+ source: current.source,
193
+ themeId: current.themeId,
194
+ previewMediaId: current.previewMediaId,
195
+ hitlRequestId: approvedHitlRequestId,
196
+ },
197
+ });
198
+ }
199
+ return unwrapResult(result);
200
+ } catch (error) {
201
+ return handleError(error, "Failed to delete section", "SECTION_DELETE_ERROR");
202
+ }
203
+ };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Sections list and create endpoints
3
+ *
4
+ * GET /_dineway/api/sections - List all sections (with filters)
5
+ * POST /_dineway/api/sections - Create section
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 { handleSectionCreate, handleSectionList } from "#api/handlers/sections.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 { createSectionBody, sectionsListQuery } from "#api/schemas.js";
21
+ import {
22
+ logSectionActivity,
23
+ RiskPolicyEvaluator,
24
+ sectionApiRouteSource,
25
+ SectionHitlPayloadBuilder,
26
+ } from "#site-context/index.js";
27
+
28
+ export const prerender = false;
29
+
30
+ const createSectionHitlBody = createSectionBody.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, "sections:read");
39
+ if (denied) return denied;
40
+
41
+ try {
42
+ const query = parseQuery(url, sectionsListQuery);
43
+ if (isParseError(query)) return query;
44
+
45
+ const result = await handleSectionList(db, query);
46
+ return unwrapResult(result);
47
+ } catch (error) {
48
+ return handleError(error, "Failed to fetch sections", "SECTION_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, "sections:manage");
57
+ if (denied) return denied;
58
+
59
+ try {
60
+ const body = await parseBody(request, createSectionHitlBody);
61
+ if (isParseError(body)) return body;
62
+
63
+ const { hitlRequestId, ...sectionInput } = body;
64
+ const actor = resolveHitlRouteActor(locals);
65
+ const evaluator = new RiskPolicyEvaluator({
66
+ db,
67
+ handlers: dineway,
68
+ });
69
+ let approvedHitlRequestId: string | null = null;
70
+
71
+ if (evaluator.requiresWorkflowHitl(actor.identity)) {
72
+ const action = await new SectionHitlPayloadBuilder(db).buildCreateSectionRequest(
73
+ sectionInput,
74
+ );
75
+ const decision = await evaluator.evaluateWorkflowHitl({
76
+ actor: actor.identity,
77
+ hitlRequestId,
78
+ action,
79
+ });
80
+ if (!decision.allowed) {
81
+ const ensured = await ensureWorkflowHitlRouteRequest(db, locals, decision.action);
82
+ return hitlRequiredRouteError(decision, ensured);
83
+ }
84
+ approvedHitlRequestId = decision.hitlRequest.id;
85
+ }
86
+
87
+ const result = await handleSectionCreate(db, sectionInput);
88
+ if (result.success) {
89
+ await logSectionActivity(db, locals, {
90
+ action: "created",
91
+ sectionId: result.data.id,
92
+ sectionSlug: result.data.slug,
93
+ ...sectionApiRouteSource("created"),
94
+ detail: {
95
+ title: result.data.title,
96
+ source: result.data.source,
97
+ themeId: result.data.themeId ?? null,
98
+ previewMediaId: sectionInput.previewMediaId ?? null,
99
+ hitlRequestId: approvedHitlRequestId,
100
+ },
101
+ });
102
+ }
103
+ return unwrapResult(result, 201);
104
+ } catch (error) {
105
+ return handleError(error, "Failed to create section", "SECTION_CREATE_ERROR");
106
+ }
107
+ };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Email Settings API endpoint
3
+ *
4
+ * GET /_dineway/api/settings/email — current provider, available providers, middleware
5
+ * POST /_dineway/api/settings/email/test — send a test email through the full pipeline
6
+ */
7
+
8
+ import { escapeHtml } from "@dineway-ai/auth";
9
+ import type { APIRoute } from "astro";
10
+ import { z } from "zod";
11
+
12
+ import { requirePerm } from "#api/authorize.js";
13
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
14
+ import { isParseError, parseBody } from "#api/parse.js";
15
+ import { OptionsRepository } from "#db/repositories/options.js";
16
+
17
+ export const prerender = false;
18
+
19
+ const EMAIL_DELIVER_HOOK = "email:deliver";
20
+ const EMAIL_BEFORE_SEND_HOOK = "email:beforeSend";
21
+ const EMAIL_AFTER_SEND_HOOK = "email:afterSend";
22
+
23
+ /**
24
+ * GET /_dineway/api/settings/email
25
+ *
26
+ * Returns the email configuration state:
27
+ * - Current provider selection
28
+ * - Available providers (plugins with email:deliver)
29
+ * - Active middleware (email:beforeSend / email:afterSend plugins)
30
+ * - Whether email is available
31
+ */
32
+ export const GET: APIRoute = async ({ locals }) => {
33
+ const { dineway, user } = locals;
34
+
35
+ if (!dineway?.db) {
36
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
37
+ }
38
+
39
+ const denied = requirePerm(user, "settings:manage");
40
+ if (denied) return denied;
41
+
42
+ try {
43
+ const pipeline = dineway.hooks;
44
+ const optionsRepo = new OptionsRepository(dineway.db);
45
+
46
+ // Get email:deliver providers and current selection
47
+ const providers = pipeline.getExclusiveHookProviders(EMAIL_DELIVER_HOOK);
48
+ const selectedProviderId = await optionsRepo.get<string>(
49
+ `dineway:exclusive_hook:${EMAIL_DELIVER_HOOK}`,
50
+ );
51
+
52
+ // Get middleware hooks (beforeSend / afterSend)
53
+ const beforeSendPlugins = pipeline
54
+ .getExclusiveHookProviders(EMAIL_BEFORE_SEND_HOOK)
55
+ .map((p) => p.pluginId);
56
+ const afterSendPlugins = pipeline
57
+ .getExclusiveHookProviders(EMAIL_AFTER_SEND_HOOK)
58
+ .map((p) => p.pluginId);
59
+
60
+ // Note: beforeSend/afterSend are NOT exclusive hooks, but getExclusiveHookProviders
61
+ // only finds exclusive ones. We need all hooks for those names.
62
+ // For now, report what we can from the exclusive hook system.
63
+ // Middleware is non-exclusive so we'd need a different query.
64
+ // TODO: Add getHookProviders() for non-exclusive hooks to the pipeline.
65
+
66
+ return apiSuccess({
67
+ available: dineway.email?.isAvailable() ?? false,
68
+ providers: providers.map((p) => ({
69
+ pluginId: p.pluginId,
70
+ })),
71
+ selectedProviderId: selectedProviderId ?? null,
72
+ middleware: {
73
+ beforeSend: beforeSendPlugins,
74
+ afterSend: afterSendPlugins,
75
+ },
76
+ });
77
+ } catch (error) {
78
+ return handleError(error, "Failed to get email settings", "EMAIL_SETTINGS_READ_ERROR");
79
+ }
80
+ };
81
+
82
+ /**
83
+ * POST /_dineway/api/settings/email/test
84
+ *
85
+ * Send a test email through the full pipeline.
86
+ * Validates the pipeline is configured and the provider works.
87
+ */
88
+ const testEmailBody = z.object({
89
+ to: z.string().email(),
90
+ });
91
+
92
+ export const POST: APIRoute = async ({ request, locals }) => {
93
+ const { dineway, user } = locals;
94
+
95
+ if (!dineway?.db) {
96
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
97
+ }
98
+
99
+ const denied = requirePerm(user, "settings:manage");
100
+ if (denied) return denied;
101
+
102
+ if (!dineway.email?.isAvailable()) {
103
+ return apiError(
104
+ "EMAIL_NOT_CONFIGURED",
105
+ "No email provider is configured. Install and activate an email provider plugin.",
106
+ 503,
107
+ );
108
+ }
109
+
110
+ try {
111
+ const body = await parseBody(request, testEmailBody);
112
+ if (isParseError(body)) return body;
113
+
114
+ const optionsRepo = new OptionsRepository(dineway.db);
115
+ const siteName = (await optionsRepo.get<string>("dineway:site_title")) ?? "Dineway";
116
+ const safeName = escapeHtml(siteName);
117
+
118
+ await dineway.email.send(
119
+ {
120
+ to: body.to,
121
+ subject: `Test email from ${siteName}`,
122
+ text: `This is a test email from ${siteName}.\n\nIf you received this, your email provider is working correctly.`,
123
+ html: `
124
+ <!DOCTYPE html>
125
+ <html>
126
+ <head>
127
+ <meta charset="utf-8">
128
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
129
+ </head>
130
+ <body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.5; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
131
+ <h1 style="font-size: 24px; margin-bottom: 20px;">Test Email</h1>
132
+ <p>This is a test email from <strong>${safeName}</strong>.</p>
133
+ <p>If you received this, your email provider is working correctly.</p>
134
+ <p style="color: #666; font-size: 14px; margin-top: 30px;">
135
+ Sent via the Dineway email pipeline.
136
+ </p>
137
+ </body>
138
+ </html>`,
139
+ },
140
+ "admin",
141
+ );
142
+
143
+ return apiSuccess({
144
+ success: true,
145
+ message: `Test email sent to ${body.to}`,
146
+ });
147
+ } catch (error) {
148
+ return handleError(error, "Failed to send test email", "EMAIL_TEST_ERROR");
149
+ }
150
+ };
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Site Settings API endpoint
3
+ *
4
+ * GET /_dineway/api/settings - Get all site settings
5
+ * POST /_dineway/api/settings - Update site settings
6
+ */
7
+
8
+ import type { APIRoute } from "astro";
9
+ import { z } from "zod";
10
+
11
+ import { requirePerm } from "#api/authorize.js";
12
+ import { apiError, handleError, unwrapResult } from "#api/error.js";
13
+ import { handleSettingsGet, handleSettingsUpdate } from "#api/handlers/settings.js";
14
+ import {
15
+ ensureWorkflowHitlRouteRequest,
16
+ hitlRequiredRouteError,
17
+ resolveHitlRouteActor,
18
+ } from "#api/hitl-route-helpers.js";
19
+ import { isParseError, parseBody } from "#api/parse.js";
20
+ import { settingsUpdateBody } from "#api/schemas.js";
21
+ import {
22
+ logSiteActivitySafely,
23
+ RiskPolicyEvaluator,
24
+ settingsApiRouteSource,
25
+ SettingsHitlPayloadBuilder,
26
+ } from "#site-context/index.js";
27
+
28
+ export const prerender = false;
29
+
30
+ const settingsHitlBody = settingsUpdateBody.extend({
31
+ hitlRequestId: z.string().min(1).optional(),
32
+ });
33
+
34
+ /**
35
+ * GET /_dineway/api/settings
36
+ *
37
+ * Returns all site settings as a JSON object.
38
+ * Unset values are undefined. Media references include resolved URLs.
39
+ */
40
+ export const GET: APIRoute = async ({ locals }) => {
41
+ const { dineway, user } = locals;
42
+
43
+ if (!dineway?.db) {
44
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
45
+ }
46
+
47
+ const denied = requirePerm(user, "settings:read");
48
+ if (denied) return denied;
49
+
50
+ try {
51
+ const result = await handleSettingsGet(dineway.db, dineway.storage);
52
+ return unwrapResult(result);
53
+ } catch (error) {
54
+ return handleError(error, "Failed to get settings", "SETTINGS_READ_ERROR");
55
+ }
56
+ };
57
+
58
+ /**
59
+ * POST /_dineway/api/settings
60
+ *
61
+ * Updates site settings. Accepts a partial settings object.
62
+ * Merges with existing settings and returns the updated settings.
63
+ */
64
+ export const POST: APIRoute = async ({ request, locals }) => {
65
+ const { dineway, user } = locals;
66
+
67
+ if (!dineway?.db) {
68
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
69
+ }
70
+
71
+ const denied = requirePerm(user, "settings:manage");
72
+ if (denied) return denied;
73
+
74
+ try {
75
+ const body = await parseBody(request, settingsHitlBody);
76
+ if (isParseError(body)) return body;
77
+
78
+ const { hitlRequestId, ...settingsInput } = body;
79
+ const actor = resolveHitlRouteActor(locals);
80
+ const action = await new SettingsHitlPayloadBuilder(dineway.db).buildUpdateSettingsRequest(
81
+ settingsInput,
82
+ );
83
+ const decision = await new RiskPolicyEvaluator({
84
+ db: dineway.db,
85
+ handlers: dineway,
86
+ }).evaluateWorkflowHitl({
87
+ actor: actor.identity,
88
+ hitlRequestId,
89
+ action,
90
+ });
91
+ if (!decision.allowed) {
92
+ const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
93
+ return hitlRequiredRouteError(decision, ensured);
94
+ }
95
+
96
+ const result = await handleSettingsUpdate(dineway.db, dineway.storage, settingsInput);
97
+ if (!result.success) return unwrapResult(result);
98
+
99
+ await logSiteActivitySafely(dineway.db, locals, {
100
+ actionType: "settings.updated",
101
+ subjectType: "site_settings",
102
+ subjectId: "site",
103
+ scope: "site",
104
+ ...settingsApiRouteSource("updated"),
105
+ summary: action.summary ?? "Updated site settings",
106
+ detail: {
107
+ changedKeys: Object.keys(settingsInput).toSorted(),
108
+ touchesSeo: Object.hasOwn(settingsInput, "seo"),
109
+ hitlRequestId: decision.required ? decision.hitlRequest.id : null,
110
+ },
111
+ });
112
+ return unwrapResult(result);
113
+ } catch (error) {
114
+ return handleError(error, "Failed to update settings", "SETTINGS_UPDATE_ERROR");
115
+ }
116
+ };