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,122 @@
1
+ /**
2
+ * POST /_dineway/api/setup/admin/verify
3
+ *
4
+ * Complete admin creation by verifying the passkey registration
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ export const prerender = false;
10
+
11
+ import { Role, secureCompare } from "@dineway-ai/auth";
12
+ import { createKyselyAdapter } from "@dineway-ai/auth/adapters/kysely";
13
+ import { verifyRegistrationResponse, registerPasskey } from "@dineway-ai/auth/passkey";
14
+
15
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
16
+ import { isParseError, parseBody } from "#api/parse.js";
17
+ import { getPublicOrigin } from "#api/public-url.js";
18
+ import { setupAdminVerifyBody } from "#api/schemas.js";
19
+ import { createChallengeStore } from "#auth/challenge-store.js";
20
+ import { getPasskeyConfig } from "#auth/passkey-config.js";
21
+ import { SETUP_NONCE_COOKIE } from "#auth/setup-nonce.js";
22
+ import { OptionsRepository } from "#db/repositories/options.js";
23
+
24
+ export const POST: APIRoute = async ({ cookies, request, locals }) => {
25
+ const { dineway } = locals;
26
+
27
+ if (!dineway?.db) {
28
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
29
+ }
30
+
31
+ try {
32
+ // Check if setup is already complete
33
+ const options = new OptionsRepository(dineway.db);
34
+ const setupComplete = await options.get("dineway:setup_complete");
35
+
36
+ if (setupComplete === true || setupComplete === "true") {
37
+ return apiError("SETUP_COMPLETE", "Setup already complete", 400);
38
+ }
39
+
40
+ // Check if any users exist
41
+ const adapter = createKyselyAdapter(dineway.db);
42
+ const userCount = await adapter.countUsers();
43
+
44
+ if (userCount > 0) {
45
+ return apiError("ADMIN_EXISTS", "Admin user already exists", 400);
46
+ }
47
+
48
+ // Get setup state
49
+ const setupState = await options.get<{
50
+ step?: string;
51
+ email?: string;
52
+ name?: string | null;
53
+ nonce?: string;
54
+ }>("dineway:setup_state");
55
+
56
+ if (!setupState || setupState.step !== "admin") {
57
+ return apiError("INVALID_STATE", "Invalid setup state. Please restart setup.", 400);
58
+ }
59
+
60
+ const cookieNonce = cookies.get(SETUP_NONCE_COOKIE)?.value;
61
+ if (!setupState.nonce || !cookieNonce || !secureCompare(cookieNonce, setupState.nonce)) {
62
+ return apiError(
63
+ "INVALID_STATE",
64
+ "Setup session expired or tampered with. Please restart the admin step.",
65
+ 400,
66
+ );
67
+ }
68
+
69
+ if (!setupState.email) {
70
+ return apiError("INVALID_STATE", "Invalid setup state. Please restart setup.", 400);
71
+ }
72
+
73
+ // Parse request body
74
+ const body = await parseBody(request, setupAdminVerifyBody);
75
+ if (isParseError(body)) return body;
76
+
77
+ // Get passkey config
78
+ const url = new URL(request.url);
79
+ const siteName = (await options.get<string>("dineway:site_title")) ?? undefined;
80
+ const siteUrl = getPublicOrigin(url, dineway?.config);
81
+ const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl);
82
+
83
+ // Verify the registration response
84
+ const challengeStore = createChallengeStore(dineway.db);
85
+
86
+ const verified = await verifyRegistrationResponse(
87
+ passkeyConfig,
88
+ body.credential,
89
+ challengeStore,
90
+ );
91
+
92
+ // Create the admin user
93
+ const user = await adapter.createUser({
94
+ email: setupState.email,
95
+ name: setupState.name ?? null,
96
+ role: Role.ADMIN,
97
+ emailVerified: false, // No email verification for first user
98
+ });
99
+
100
+ // Register the passkey
101
+ await registerPasskey(adapter, user.id, verified, "Setup passkey");
102
+
103
+ // Mark setup as complete
104
+ await options.set("dineway:setup_complete", true);
105
+
106
+ // Clean up setup state and the session nonce cookie
107
+ await options.delete("dineway:setup_state");
108
+ cookies.delete(SETUP_NONCE_COOKIE, { path: "/_dineway/" });
109
+
110
+ return apiSuccess({
111
+ success: true,
112
+ user: {
113
+ id: user.id,
114
+ email: user.email,
115
+ name: user.name,
116
+ role: user.role,
117
+ },
118
+ });
119
+ } catch (error) {
120
+ return handleError(error, "Failed to verify admin setup", "SETUP_VERIFY_ERROR");
121
+ }
122
+ };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * POST /_dineway/api/setup/admin
3
+ *
4
+ * Step 3 of setup: Start admin creation by returning passkey registration options
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ export const prerender = false;
10
+
11
+ import { generateToken } from "@dineway-ai/auth";
12
+ import { createKyselyAdapter } from "@dineway-ai/auth/adapters/kysely";
13
+ import { generateRegistrationOptions } from "@dineway-ai/auth/passkey";
14
+
15
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
16
+ import { isParseError, parseBody } from "#api/parse.js";
17
+ import { getPublicOrigin } from "#api/public-url.js";
18
+ import { setupAdminBody } from "#api/schemas.js";
19
+ import { createChallengeStore } from "#auth/challenge-store.js";
20
+ import { getPasskeyConfig } from "#auth/passkey-config.js";
21
+ import { SETUP_NONCE_COOKIE, SETUP_NONCE_MAX_AGE_SECONDS } from "#auth/setup-nonce.js";
22
+ import { OptionsRepository } from "#db/repositories/options.js";
23
+
24
+ export const POST: APIRoute = async ({ cookies, request, locals }) => {
25
+ const { dineway } = locals;
26
+
27
+ if (!dineway?.db) {
28
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
29
+ }
30
+
31
+ try {
32
+ // Check if setup is already complete
33
+ const options = new OptionsRepository(dineway.db);
34
+ const setupComplete = await options.get("dineway:setup_complete");
35
+
36
+ if (setupComplete === true || setupComplete === "true") {
37
+ return apiError("SETUP_COMPLETE", "Setup already complete", 400);
38
+ }
39
+
40
+ // Check if any users exist
41
+ const adapter = createKyselyAdapter(dineway.db);
42
+ const userCount = await adapter.countUsers();
43
+
44
+ if (userCount > 0) {
45
+ return apiError("ADMIN_EXISTS", "Admin user already exists", 400);
46
+ }
47
+
48
+ // Parse request body
49
+ const body = await parseBody(request, setupAdminBody);
50
+ if (isParseError(body)) return body;
51
+
52
+ const nonce = generateToken();
53
+
54
+ // Get passkey config
55
+ const url = new URL(request.url);
56
+ const siteName = (await options.get<string>("dineway:site_title")) ?? undefined;
57
+ const siteUrl = getPublicOrigin(url, dineway?.config);
58
+ const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl);
59
+
60
+ // Generate registration options
61
+ const challengeStore = createChallengeStore(dineway.db);
62
+
63
+ // Create a temporary user object for registration options
64
+ // (not persisted until passkey is verified)
65
+ const tempUser = {
66
+ id: `setup-${Date.now()}`, // Temporary ID
67
+ email: body.email.toLowerCase(),
68
+ name: body.name || null,
69
+ };
70
+
71
+ const registrationOptions = await generateRegistrationOptions(
72
+ passkeyConfig,
73
+ tempUser,
74
+ [], // No existing credentials
75
+ challengeStore,
76
+ );
77
+
78
+ // Store the nonce with the setup state. The verify endpoint compares
79
+ // it to an HttpOnly cookie so the admin step stays bound to this browser.
80
+ await options.set("dineway:setup_state", {
81
+ step: "admin",
82
+ email: body.email.toLowerCase(),
83
+ name: body.name || null,
84
+ tempUserId: tempUser.id,
85
+ nonce,
86
+ });
87
+
88
+ const publicOrigin = new URL(siteUrl);
89
+ cookies.set(SETUP_NONCE_COOKIE, nonce, {
90
+ path: "/_dineway/",
91
+ httpOnly: true,
92
+ sameSite: "strict",
93
+ secure: publicOrigin.protocol === "https:",
94
+ maxAge: SETUP_NONCE_MAX_AGE_SECONDS,
95
+ });
96
+
97
+ return apiSuccess({
98
+ success: true,
99
+ options: registrationOptions,
100
+ });
101
+ } catch (error) {
102
+ return handleError(error, "Failed to create admin", "SETUP_ADMIN_ERROR");
103
+ }
104
+ };
@@ -0,0 +1,200 @@
1
+ /**
2
+ * POST /_dineway/api/setup/dev-bypass
3
+ * GET /_dineway/api/setup/dev-bypass
4
+ *
5
+ * Development-only endpoint to bypass the setup wizard.
6
+ * Runs migrations, creates a dev admin user, and marks setup complete.
7
+ *
8
+ * ONLY available when import.meta.env.DEV is true.
9
+ *
10
+ * Usage:
11
+ * - GET with redirect: /_dineway/api/setup/dev-bypass?redirect=/_dineway/admin
12
+ * - POST for API: Returns JSON with setup info
13
+ *
14
+ * For agent/browser testing, navigate to:
15
+ * /_dineway/api/setup/dev-bypass?redirect=/_dineway/admin
16
+ */
17
+
18
+ import type { APIRoute } from "astro";
19
+
20
+ export const prerender = false;
21
+
22
+ import { ulid } from "ulidx";
23
+
24
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
25
+ import { escapeHtml } from "#api/escape.js";
26
+ import { handleApiTokenCreate } from "#api/handlers/api-tokens.js";
27
+ import { getPublicOrigin } from "#api/public-url.js";
28
+ import { isSafeRedirect } from "#api/redirect.js";
29
+ import { runMigrations } from "#db/migrations/runner.js";
30
+ import { OptionsRepository } from "#db/repositories/options.js";
31
+ import { applySeed } from "#seed/apply.js";
32
+ import { loadSeed } from "#seed/load.js";
33
+ import { validateSeed } from "#seed/validate.js";
34
+
35
+ // RBAC role levels (matching @dineway-ai/auth)
36
+ const ROLE_ADMIN = 50;
37
+
38
+ const DEV_USER_EMAIL = "dev@dineway.local";
39
+ const DEV_USER_NAME = "Dev Admin";
40
+ const DEV_SITE_TITLE = "Dineway Dev Site";
41
+
42
+ async function handleDevBypass(context: Parameters<APIRoute>[0]): Promise<Response> {
43
+ // CRITICAL: Only allow in development mode
44
+ if (!import.meta.env.DEV) {
45
+ return apiError("FORBIDDEN", "Dev bypass is only available in development mode", 403);
46
+ }
47
+
48
+ const { locals, url, session } = context;
49
+ const { dineway } = locals;
50
+
51
+ if (!dineway?.db) {
52
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
53
+ }
54
+
55
+ try {
56
+ // Run migrations
57
+ const migrations = await runMigrations(dineway.db);
58
+ console.log("[setup-dev-bypass] Migrations applied:", migrations.applied);
59
+
60
+ // Apply seed (user seed or built-in default)
61
+ const seed = await loadSeed();
62
+ const validation = validateSeed(seed);
63
+ if (validation.valid) {
64
+ const seedResult = await applySeed(dineway.db, seed, {
65
+ includeContent: true,
66
+ onConflict: "skip",
67
+ storage: dineway.storage ?? undefined,
68
+ });
69
+ console.log(
70
+ `[setup-dev-bypass] Seed applied: ${seedResult.collections.created} collections, ${seedResult.fields.created} fields`,
71
+ );
72
+ }
73
+
74
+ const options = new OptionsRepository(dineway.db);
75
+
76
+ // Find or create dev user (direct DB access to avoid @dineway-ai/auth import issues in dev)
77
+ const existingUser = await dineway.db
78
+ .selectFrom("users")
79
+ .selectAll()
80
+ .where("email", "=", DEV_USER_EMAIL)
81
+ .executeTakeFirst();
82
+
83
+ let user: { id: string; email: string; name: string; role: number };
84
+ let userCreated = false;
85
+
86
+ if (!existingUser) {
87
+ const now = new Date().toISOString();
88
+ const newUser = {
89
+ id: ulid(),
90
+ email: DEV_USER_EMAIL,
91
+ name: DEV_USER_NAME,
92
+ role: ROLE_ADMIN,
93
+ email_verified: 1,
94
+ created_at: now,
95
+ updated_at: now,
96
+ };
97
+
98
+ await dineway.db.insertInto("users").values(newUser).execute();
99
+
100
+ user = {
101
+ id: newUser.id,
102
+ email: newUser.email,
103
+ name: newUser.name,
104
+ role: newUser.role,
105
+ };
106
+ userCreated = true;
107
+ console.log("[setup-dev-bypass] Created dev admin user:", user.email);
108
+ } else {
109
+ user = {
110
+ id: existingUser.id,
111
+ email: existingUser.email,
112
+ name: existingUser.name || DEV_USER_NAME,
113
+ role: existingUser.role,
114
+ };
115
+ }
116
+
117
+ // Set site title if not already set
118
+ const existingTitle = await options.get("dineway:site_title");
119
+ if (!existingTitle) {
120
+ await options.set("dineway:site_title", DEV_SITE_TITLE);
121
+ }
122
+
123
+ // Store canonical site URL (used by magic-link/recovery emails)
124
+ await options.set("dineway:site_url", getPublicOrigin(url, dineway?.config));
125
+
126
+ // Mark setup complete
127
+ await options.set("dineway:setup_complete", true);
128
+
129
+ // Create session
130
+ if (session) {
131
+ session.set("user", { id: user.id });
132
+ }
133
+
134
+ // Optionally create a PAT token (?token=1) for headless/CLI testing.
135
+ let token: string | undefined;
136
+ if (url.searchParams.has("token")) {
137
+ const result = await handleApiTokenCreate(dineway.db, user.id, {
138
+ name: "dev-bypass-token",
139
+ scopes: [
140
+ "content:read",
141
+ "content:write",
142
+ "media:read",
143
+ "media:write",
144
+ "schema:read",
145
+ "schema:write",
146
+ "admin",
147
+ ],
148
+ });
149
+ if (result.success) {
150
+ token = result.data.token;
151
+ }
152
+ }
153
+
154
+ // Check for redirect parameter
155
+ const redirect = url.searchParams.get("redirect");
156
+
157
+ if (redirect) {
158
+ // Validate redirect is a safe local path (prevent open redirect via //evil.com or /\evil.com)
159
+ if (!isSafeRedirect(redirect)) {
160
+ return apiError("INVALID_REDIRECT", "Redirect must be a local path", 400);
161
+ }
162
+
163
+ // Return an HTML page with meta-refresh redirect
164
+ // This ensures the session is fully saved before redirect
165
+ const safeRedirect = escapeHtml(redirect);
166
+ const html = `<!DOCTYPE html>
167
+ <html>
168
+ <head>
169
+ <meta http-equiv="refresh" content="0;url=${safeRedirect}">
170
+ </head>
171
+ <body>Redirecting...</body>
172
+ </html>`;
173
+ return new Response(html, {
174
+ status: 200,
175
+ headers: { "Content-Type": "text/html" },
176
+ });
177
+ }
178
+
179
+ // Return JSON response
180
+ return apiSuccess({
181
+ success: true,
182
+ message: "Dev setup complete",
183
+ migrations: migrations.applied,
184
+ userCreated,
185
+ user: {
186
+ id: user.id,
187
+ email: user.email,
188
+ name: user.name,
189
+ role: user.role,
190
+ },
191
+ ...(token ? { token } : {}),
192
+ });
193
+ } catch (error) {
194
+ return handleError(error, "Dev bypass failed", "DEV_BYPASS_ERROR");
195
+ }
196
+ }
197
+
198
+ // Support both GET and POST
199
+ export const GET: APIRoute = handleDevBypass;
200
+ export const POST: APIRoute = handleDevBypass;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * POST /_dineway/api/setup/dev-reset
3
+ *
4
+ * Development-only endpoint to reset setup state for testing.
5
+ * Clears the setup_complete flag and deletes all users,
6
+ * returning the site to the pre-setup state.
7
+ *
8
+ * ONLY available when import.meta.env.DEV is true.
9
+ */
10
+
11
+ import type { APIRoute } from "astro";
12
+
13
+ export const prerender = false;
14
+
15
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
16
+ import { OptionsRepository } from "#db/repositories/options.js";
17
+
18
+ export const POST: APIRoute = async ({ locals }) => {
19
+ if (!import.meta.env.DEV) {
20
+ return apiError("FORBIDDEN", "Dev reset is only available in development mode", 403);
21
+ }
22
+
23
+ const { dineway } = locals;
24
+
25
+ if (!dineway?.db) {
26
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
27
+ }
28
+
29
+ try {
30
+ const options = new OptionsRepository(dineway.db);
31
+
32
+ await options.delete("dineway:setup_complete");
33
+ await options.delete("dineway:setup_state");
34
+ await dineway.db.deleteFrom("users").execute();
35
+
36
+ return apiSuccess({ success: true });
37
+ } catch (error) {
38
+ return handleError(error, "Dev reset failed", "DEV_RESET_ERROR");
39
+ }
40
+ };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * POST /_dineway/api/setup
3
+ *
4
+ * Executes the setup wizard - applies seed file and marks setup complete
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ export const prerender = false;
10
+
11
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
12
+ import { isParseError, parseBody } from "#api/parse.js";
13
+ import { getPublicOrigin } from "#api/public-url.js";
14
+ import { setupBody } from "#api/schemas.js";
15
+ import { getAuthMode } from "#auth/mode.js";
16
+ import { runMigrations } from "#db/migrations/runner.js";
17
+ import { OptionsRepository } from "#db/repositories/options.js";
18
+ import { applySeed } from "#seed/apply.js";
19
+ import { loadSeed } from "#seed/load.js";
20
+ import { validateSeed } from "#seed/validate.js";
21
+
22
+ export const POST: APIRoute = async ({ request, url, locals }) => {
23
+ const { dineway } = locals;
24
+
25
+ if (!dineway?.db) {
26
+ return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
27
+ }
28
+
29
+ try {
30
+ // Guard: reject if setup has already been completed.
31
+ // The options table may not exist on first-ever setup (pre-migration),
32
+ // so a query failure means setup hasn't run yet — allow it to proceed.
33
+ try {
34
+ const options = new OptionsRepository(dineway.db);
35
+ const setupComplete = await options.get("dineway:setup_complete");
36
+
37
+ if (setupComplete === true || setupComplete === "true") {
38
+ return apiError("ALREADY_CONFIGURED", "Setup has already been completed", 409);
39
+ }
40
+ } catch {
41
+ // Options table doesn't exist yet — first-ever setup, allow it
42
+ }
43
+
44
+ // Parse request body
45
+ const body = await parseBody(request, setupBody);
46
+ if (isParseError(body)) return body;
47
+
48
+ // 1. Run core migrations
49
+ try {
50
+ await runMigrations(dineway.db);
51
+ } catch (error) {
52
+ return handleError(error, "Failed to run database migrations", "MIGRATION_ERROR");
53
+ }
54
+
55
+ // 2. Load seed file (user seed or built-in default)
56
+ const seed = await loadSeed();
57
+
58
+ // 3. Override seed settings with form values
59
+ seed.settings = {
60
+ ...seed.settings,
61
+ title: body.title,
62
+ tagline: body.tagline,
63
+ };
64
+
65
+ // 4. Apply seed
66
+ const validation = validateSeed(seed);
67
+ if (!validation.valid) {
68
+ return apiError("INVALID_SEED", `Invalid seed file: ${validation.errors.join(", ")}`, 400);
69
+ }
70
+
71
+ let result;
72
+ try {
73
+ result = await applySeed(dineway.db, seed, {
74
+ includeContent: body.includeContent,
75
+ onConflict: "skip",
76
+ storage: dineway.storage ?? undefined,
77
+ });
78
+ } catch (error) {
79
+ return handleError(error, "Failed to apply seed", "SEED_ERROR");
80
+ }
81
+
82
+ // 5. Store setup state
83
+ // In external auth mode, mark setup complete immediately (first user to login becomes admin)
84
+ // In passkey mode, setup_complete is set after admin user is created
85
+ const authMode = getAuthMode(dineway.config);
86
+ const useExternalAuth = authMode.type === "external";
87
+
88
+ try {
89
+ const options = new OptionsRepository(dineway.db);
90
+
91
+ // Store the canonical site URL from the setup request.
92
+ // Keep the first value so later unauthenticated setup calls cannot
93
+ // rewrite the public origin during the setup window.
94
+ const siteUrl = getPublicOrigin(url, dineway.config);
95
+ await options.setIfAbsent("dineway:site_url", siteUrl);
96
+
97
+ if (useExternalAuth) {
98
+ // External auth mode: mark setup complete now
99
+ // First user to log in via external provider will become admin
100
+ await options.set("dineway:setup_complete", true);
101
+ await options.set("dineway:site_title", body.title);
102
+ if (body.tagline) {
103
+ await options.set("dineway:site_tagline", body.tagline);
104
+ }
105
+ } else {
106
+ // Passkey mode: store state for next step (admin creation)
107
+ await options.set("dineway:setup_state", {
108
+ step: "site_complete",
109
+ title: body.title,
110
+ tagline: body.tagline,
111
+ });
112
+ }
113
+ } catch (error) {
114
+ console.error("Failed to save setup state:", error);
115
+ // Non-fatal - continue anyway
116
+ }
117
+
118
+ // 6. Return success with result
119
+ return apiSuccess({
120
+ success: true,
121
+ // In external auth mode, setup is complete - redirect to admin
122
+ setupComplete: useExternalAuth,
123
+ result,
124
+ });
125
+ } catch (error) {
126
+ return handleError(error, "Setup failed", "SETUP_ERROR");
127
+ }
128
+ };