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,111 @@
1
+ /**
2
+ * WordPress Plugin analyze endpoint
3
+ *
4
+ * POST /_dineway/api/import/wordpress-plugin/analyze
5
+ *
6
+ * Analyzes a WordPress site with Dineway Exporter plugin installed.
7
+ * Returns content counts, schema compatibility, etc.
8
+ */
9
+
10
+ import type { APIRoute } from "astro";
11
+ import { SchemaRegistry } from "dineway";
12
+
13
+ import { requirePerm } from "#api/authorize.js";
14
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
15
+ import { isParseError, parseBody } from "#api/parse.js";
16
+ import { wpPluginAnalyzeBody } from "#api/schemas.js";
17
+ import { getSource } from "#import/index.js";
18
+ import { resolveAndValidateExternalUrl, SsrfError } from "#import/ssrf.js";
19
+ import type { ImportAnalysis } from "#import/types.js";
20
+ import type { DinewayHandlers } from "#types";
21
+
22
+ export const prerender = false;
23
+
24
+ export interface WpPluginAnalyzeResponse {
25
+ success: boolean;
26
+ analysis?: ImportAnalysis;
27
+ error?: { message: string };
28
+ }
29
+
30
+ export const POST: APIRoute = async ({ request, locals }) => {
31
+ const { dineway, user } = locals;
32
+
33
+ const denied = requirePerm(user, "import:execute");
34
+ if (denied) return denied;
35
+
36
+ try {
37
+ const body = await parseBody(request, wpPluginAnalyzeBody);
38
+ if (isParseError(body)) return body;
39
+
40
+ // SSRF: reject internal/private network targets before import work starts.
41
+ try {
42
+ await resolveAndValidateExternalUrl(body.url);
43
+ } catch (e) {
44
+ const msg = e instanceof SsrfError ? e.message : "Invalid URL";
45
+ return apiError("SSRF_BLOCKED", msg, 400);
46
+ }
47
+
48
+ // Get the WordPress plugin source
49
+ const source = getSource("wordpress-plugin");
50
+ if (!source) {
51
+ return apiError("NOT_CONFIGURED", "WordPress plugin source not available", 500);
52
+ }
53
+
54
+ // Build context with existing collections info
55
+ const existingCollections = await fetchExistingCollections(dineway?.db);
56
+
57
+ // Analyze the site
58
+ const analysis = await source.analyze(
59
+ { type: "url", url: body.url, token: body.token },
60
+ {
61
+ db: dineway?.db,
62
+ getExistingCollections: async () => existingCollections,
63
+ },
64
+ );
65
+
66
+ return apiSuccess({
67
+ success: true,
68
+ analysis,
69
+ });
70
+ } catch (error) {
71
+ return handleError(error, "Failed to analyze WordPress site", "WP_PLUGIN_ANALYZE_ERROR");
72
+ }
73
+ };
74
+
75
+ /** Existing collection info from schema registry */
76
+ interface ExistingCollection {
77
+ slug: string;
78
+ fields: Map<string, { type: string }>;
79
+ }
80
+
81
+ /** Fetch collections and their fields from schema registry */
82
+ async function fetchExistingCollections(
83
+ db: DinewayHandlers["db"] | undefined,
84
+ ): Promise<Map<string, ExistingCollection>> {
85
+ const result = new Map<string, ExistingCollection>();
86
+
87
+ if (!db) return result;
88
+
89
+ try {
90
+ const registry = new SchemaRegistry(db);
91
+ const collections = await registry.listCollections();
92
+
93
+ for (const collection of collections) {
94
+ const fields = await registry.listFields(collection.id);
95
+ const fieldMap = new Map<string, { type: string }>();
96
+
97
+ for (const field of fields) {
98
+ fieldMap.set(field.slug, { type: field.type });
99
+ }
100
+
101
+ result.set(collection.slug, {
102
+ slug: collection.slug,
103
+ fields: fieldMap,
104
+ });
105
+ }
106
+ } catch (error) {
107
+ console.warn("Could not fetch schema registry:", error);
108
+ }
109
+
110
+ return result;
111
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * WordPress Application Password OAuth callback
3
+ *
4
+ * GET /_dineway/api/import/wordpress-plugin/callback
5
+ *
6
+ * WordPress redirects here after user approves the application password.
7
+ * We receive the credentials and redirect to the admin import UI with a token.
8
+ */
9
+
10
+ import type { APIRoute } from "astro";
11
+
12
+ import { encodeBase64 } from "#utils/base64.js";
13
+
14
+ export const prerender = false;
15
+
16
+ export const GET: APIRoute = async ({ url, cookies, redirect }) => {
17
+ // WordPress sends these params on success:
18
+ // - site_url: The WordPress site URL
19
+ // - user_login: The username
20
+ // - password: The newly created application password
21
+ //
22
+ // On rejection, it redirects to reject_url (if provided) or just doesn't include credentials
23
+
24
+ const siteUrl = url.searchParams.get("site_url");
25
+ const userLogin = url.searchParams.get("user_login");
26
+ const password = url.searchParams.get("password");
27
+
28
+ // Check if this is a rejection (no credentials)
29
+ if (!siteUrl || !userLogin || !password) {
30
+ return redirect("/_dineway/admin/import/wordpress?error=auth_rejected");
31
+ }
32
+
33
+ // Create the Basic Auth token
34
+ const token = encodeBase64(`${userLogin}:${password}`);
35
+
36
+ // Store credentials in a short-lived cookie (5 minutes)
37
+ // This allows the import UI to retrieve them
38
+ const authData = JSON.stringify({
39
+ siteUrl,
40
+ userLogin,
41
+ token,
42
+ timestamp: Date.now(),
43
+ });
44
+
45
+ // Base64 encode the auth data for cookie storage
46
+ const encodedAuth = encodeBase64(authData);
47
+
48
+ cookies.set("dineway_wp_auth", encodedAuth, {
49
+ path: "/_dineway/",
50
+ maxAge: 300, // 5 minutes
51
+ httpOnly: false, // Needs to be readable by JS
52
+ secure: url.protocol === "https:",
53
+ sameSite: "lax",
54
+ });
55
+
56
+ // Redirect to import UI - it will pick up the cookie
57
+ return redirect("/_dineway/admin/import/wordpress?auth=success");
58
+ };
@@ -0,0 +1,399 @@
1
+ /**
2
+ * WordPress Plugin execute import endpoint
3
+ *
4
+ * POST /_dineway/api/import/wordpress-plugin/execute
5
+ *
6
+ * Imports content from WordPress via Dineway Exporter plugin API.
7
+ */
8
+
9
+ import type { APIRoute } from "astro";
10
+ import { ContentRepository, SchemaRegistry } from "dineway";
11
+ import { z } from "zod";
12
+
13
+ import { requirePerm } from "#api/authorize.js";
14
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
15
+ import {
16
+ ensureWorkflowHitlRouteRequest,
17
+ hitlRequiredRouteError,
18
+ resolveHitlRouteActor,
19
+ } from "#api/hitl-route-helpers.js";
20
+ import { isParseError, parseBody } from "#api/parse.js";
21
+ import { wpPluginExecuteBody } from "#api/schemas.js";
22
+ import { BylineRepository } from "#db/repositories/byline.js";
23
+ import { getSource } from "#import/index.js";
24
+ import { resolveAndValidateExternalUrl, SsrfError } from "#import/ssrf.js";
25
+ import type { ImportConfig, ImportResult, NormalizedItem } from "#import/types.js";
26
+ import { resolveImportByline } from "#import/utils.js";
27
+ import type { FieldType } from "#schema/types.js";
28
+ import { RiskPolicyEvaluator, WordPressImportHitlPayloadBuilder } from "#site-context/index.js";
29
+ import type { DinewayHandlers, DinewayManifest } from "#types";
30
+ import { slugify } from "#utils/slugify.js";
31
+
32
+ export const prerender = false;
33
+
34
+ const wpPluginExecuteRouteBody = wpPluginExecuteBody.extend({
35
+ hitlRequestId: z.string().min(1).optional(),
36
+ });
37
+
38
+ export interface WpPluginImportConfig extends ImportConfig {
39
+ /** Author mappings (WP author login -> Dineway user ID) */
40
+ authorMappings?: Record<string, string | null>;
41
+ }
42
+
43
+ export interface WpPluginImportResponse {
44
+ success: boolean;
45
+ result?: ImportResult;
46
+ error?: { message: string };
47
+ }
48
+
49
+ export const POST: APIRoute = async ({ request, locals }) => {
50
+ const { dineway, dinewayManifest, user } = locals;
51
+
52
+ const denied = requirePerm(user, "import:execute");
53
+ if (denied) return denied;
54
+
55
+ if (!dineway?.handleContentCreate) {
56
+ return apiError("NOT_CONFIGURED", "Dineway is not configured", 500);
57
+ }
58
+
59
+ try {
60
+ const body = await parseBody(request, wpPluginExecuteRouteBody);
61
+ if (isParseError(body)) return body;
62
+
63
+ // SSRF: reject internal/private network targets before import work starts.
64
+ try {
65
+ await resolveAndValidateExternalUrl(body.url);
66
+ } catch (e) {
67
+ const msg = e instanceof SsrfError ? e.message : "Invalid URL";
68
+ return apiError("SSRF_BLOCKED", msg, 400);
69
+ }
70
+
71
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Zod schema output narrowed to WpPluginImportConfig
72
+ const config = body.config as unknown as WpPluginImportConfig;
73
+
74
+ // Get the WordPress plugin source
75
+ const source = getSource("wordpress-plugin");
76
+ if (!source) {
77
+ return apiError("NOT_CONFIGURED", "WordPress plugin source not available", 500);
78
+ }
79
+
80
+ // Build the list of post types to fetch
81
+ const postTypes = Object.entries(config.postTypeMappings)
82
+ .filter(([_, mapping]) => mapping.enabled)
83
+ .map(([postType]) => postType);
84
+
85
+ if (postTypes.length === 0) {
86
+ return apiError("VALIDATION_ERROR", "No post types selected for import", 400);
87
+ }
88
+
89
+ console.log("[WP Plugin Import] Starting import for:", body.url);
90
+ console.log("[WP Plugin Import] Post types:", postTypes);
91
+
92
+ const sourceInput = { type: "url", url: body.url, token: body.token } as const;
93
+ const actor = resolveHitlRouteActor(locals);
94
+ const evaluator = new RiskPolicyEvaluator({
95
+ db: dineway.db,
96
+ handlers: dineway,
97
+ });
98
+ let items: AsyncIterable<NormalizedItem> | Iterable<NormalizedItem>;
99
+
100
+ if (evaluator.requiresWorkflowHitl(actor.identity)) {
101
+ const fetchedItems = await collectPluginItems(
102
+ source.fetchContent(sourceInput, { postTypes, includeDrafts: true }),
103
+ );
104
+ const action = await new WordPressImportHitlPayloadBuilder().buildPluginExecuteRequest({
105
+ siteUrl: body.url,
106
+ config,
107
+ items: fetchedItems,
108
+ });
109
+ const decision = await evaluator.evaluateWorkflowHitl({
110
+ actor: actor.identity,
111
+ hitlRequestId: body.hitlRequestId,
112
+ action,
113
+ });
114
+ if (!decision.allowed) {
115
+ const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
116
+ return hitlRequiredRouteError(decision, ensured);
117
+ }
118
+ items = fetchedItems;
119
+ } else {
120
+ items = source.fetchContent(sourceInput, { postTypes, includeDrafts: true });
121
+ }
122
+
123
+ // Import content (including drafts since we have auth)
124
+ const result = await importContent(items, config, dineway, dinewayManifest);
125
+
126
+ console.log("[WP Plugin Import] Import result:", JSON.stringify(result, null, 2));
127
+
128
+ return apiSuccess({
129
+ success: true,
130
+ result,
131
+ });
132
+ } catch (error) {
133
+ return handleError(error, "Failed to import from WordPress", "WP_PLUGIN_IMPORT_ERROR");
134
+ }
135
+ };
136
+
137
+ /** Fields that should be auto-created if they don't exist */
138
+ const IMPORT_FIELDS: Array<{
139
+ slug: string;
140
+ label: string;
141
+ type: FieldType;
142
+ check: (item: NormalizedItem) => boolean;
143
+ }> = [
144
+ {
145
+ slug: "title",
146
+ label: "Title",
147
+ type: "string",
148
+ check: () => true,
149
+ },
150
+ {
151
+ slug: "content",
152
+ label: "Content",
153
+ type: "portableText",
154
+ check: () => true,
155
+ },
156
+ {
157
+ slug: "excerpt",
158
+ label: "Excerpt",
159
+ type: "text",
160
+ check: (item) => !!item.excerpt,
161
+ },
162
+ {
163
+ slug: "featured_image",
164
+ label: "Featured Image",
165
+ type: "image",
166
+ check: (item) => !!item.featuredImage,
167
+ },
168
+ ];
169
+
170
+ async function importContent(
171
+ items: AsyncIterable<NormalizedItem> | Iterable<NormalizedItem>,
172
+ config: WpPluginImportConfig,
173
+ dineway: DinewayHandlers,
174
+ manifest: DinewayManifest,
175
+ ): Promise<ImportResult> {
176
+ const result: ImportResult = {
177
+ success: true,
178
+ imported: 0,
179
+ skipped: 0,
180
+ errors: [],
181
+ byCollection: {},
182
+ };
183
+
184
+ // Create content repository for checking existing items
185
+ const contentRepo = new ContentRepository(dineway.db);
186
+ const bylineRepo = new BylineRepository(dineway.db);
187
+ const bylineCache = new Map<string, string>();
188
+ const schemaRegistry = new SchemaRegistry(dineway.db);
189
+
190
+ // Track which collections have had fields ensured
191
+ const ensuredCollections = new Set<string>();
192
+
193
+ // Track source translationGroup -> Dineway item ID for translation linking.
194
+ // Maps source-side translation group ID to the Dineway ID of the first item
195
+ // imported for that group (the default-locale item).
196
+ const translationGroupMap = new Map<string, string>();
197
+
198
+ for await (const item of items) {
199
+ console.log("[WP Plugin Import] Processing item:", {
200
+ sourceId: item.sourceId,
201
+ title: item.title,
202
+ postType: item.postType,
203
+ status: item.status,
204
+ contentBlocks: Array.isArray(item.content) ? item.content.length : 0,
205
+ featuredImage: item.featuredImage,
206
+ locale: item.locale,
207
+ translationGroup: item.translationGroup,
208
+ });
209
+
210
+ const mapping = config.postTypeMappings[item.postType];
211
+
212
+ // Skip if not mapped or disabled
213
+ if (!mapping || !mapping.enabled) {
214
+ result.skipped++;
215
+ continue;
216
+ }
217
+
218
+ const collection = mapping.collection;
219
+
220
+ // Check if collection exists in manifest
221
+ if (!manifest?.collections[collection]) {
222
+ result.errors.push({
223
+ title: item.title || "Untitled",
224
+ error: `Collection "${collection}" does not exist`,
225
+ });
226
+ continue;
227
+ }
228
+
229
+ try {
230
+ // Ensure required fields exist in the collection schema (once per collection)
231
+ if (!ensuredCollections.has(collection)) {
232
+ for (const field of IMPORT_FIELDS) {
233
+ if (field.check(item)) {
234
+ const existingField = await schemaRegistry.getField(collection, field.slug);
235
+ if (!existingField) {
236
+ console.log(
237
+ `[WP Plugin Import] Creating missing field "${field.slug}" in collection "${collection}"`,
238
+ );
239
+ try {
240
+ await schemaRegistry.createField(collection, {
241
+ slug: field.slug,
242
+ label: field.label,
243
+ type: field.type,
244
+ required: false,
245
+ });
246
+ } catch (e) {
247
+ // Field might already exist from concurrent creation
248
+ console.log(
249
+ `[WP Plugin Import] Field "${field.slug}" creation skipped:`,
250
+ e instanceof Error ? e.message : e,
251
+ );
252
+ }
253
+ }
254
+ }
255
+ }
256
+ ensuredCollections.add(collection);
257
+ }
258
+
259
+ // Generate slug from item slug or title
260
+ const slug = item.slug || slugify(item.title || `post-${item.sourceId}`);
261
+
262
+ // Check if already exists (idempotency) — locale-aware lookup
263
+ if (config.skipExisting) {
264
+ const existing = await contentRepo.findBySlug(collection, slug, item.locale);
265
+ if (existing) {
266
+ // Still track the translation group mapping for later items
267
+ if (item.translationGroup) {
268
+ translationGroupMap.set(item.translationGroup, existing.id);
269
+ }
270
+ result.skipped++;
271
+ continue;
272
+ }
273
+ }
274
+
275
+ // Map WordPress status to Dineway status
276
+ const status = mapStatus(item.status);
277
+
278
+ // Build data object - add all applicable fields
279
+ const data: Record<string, unknown> = {};
280
+
281
+ // Add standard fields
282
+ data.title = item.title || "Untitled";
283
+ data.content = item.content;
284
+
285
+ if (item.excerpt) {
286
+ data.excerpt = item.excerpt;
287
+ }
288
+ if (item.featuredImage) {
289
+ data.featured_image = item.featuredImage;
290
+ console.log("[WP Plugin Import] Adding featured_image:", item.featuredImage);
291
+ }
292
+
293
+ // Note: ACF/Yoast/RankMath fields are not added automatically
294
+ // They would need matching fields in the Dineway schema
295
+
296
+ // Resolve author ID from mappings
297
+ let authorId: string | undefined;
298
+ if (config.authorMappings && item.author) {
299
+ const mappedUserId = config.authorMappings[item.author];
300
+ if (mappedUserId !== undefined && mappedUserId !== null) {
301
+ authorId = mappedUserId;
302
+ }
303
+ }
304
+
305
+ const bylineId = await resolveImportByline(
306
+ item.author,
307
+ item.author, // display name fallback is the login
308
+ authorId,
309
+ bylineRepo,
310
+ bylineCache,
311
+ );
312
+
313
+ // Resolve translation link: if this item has a translationGroup and
314
+ // we've already imported another item in the same group, link them.
315
+ let translationOf: string | undefined;
316
+ if (item.translationGroup) {
317
+ const existingGroupItem = translationGroupMap.get(item.translationGroup);
318
+ if (existingGroupItem) {
319
+ translationOf = existingGroupItem;
320
+ }
321
+ }
322
+
323
+ // Preserve original dates from the source
324
+ const itemDateTime = item.date?.getTime();
325
+ const createdAt =
326
+ itemDateTime !== undefined && !Number.isNaN(itemDateTime)
327
+ ? item.date.toISOString()
328
+ : undefined;
329
+ const publishedAt = status === "published" && createdAt ? createdAt : undefined;
330
+
331
+ // Create the content item
332
+ const createResult = await dineway.handleContentCreate(collection, {
333
+ data,
334
+ slug,
335
+ status,
336
+ authorId,
337
+ bylines: bylineId ? [{ bylineId }] : undefined,
338
+ locale: item.locale,
339
+ translationOf,
340
+ createdAt,
341
+ publishedAt,
342
+ });
343
+
344
+ if (createResult.success) {
345
+ result.imported++;
346
+ result.byCollection[collection] = (result.byCollection[collection] || 0) + 1;
347
+
348
+ // Track translation group: first item in a group establishes the mapping
349
+ if (item.translationGroup && !translationGroupMap.has(item.translationGroup)) {
350
+ // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- handler success data includes id
351
+ const createdData = createResult.data as { id?: string } | undefined;
352
+ if (createdData?.id) {
353
+ translationGroupMap.set(item.translationGroup, createdData.id);
354
+ }
355
+ }
356
+ } else {
357
+ result.errors.push({
358
+ title: item.title || "Untitled",
359
+ error:
360
+ typeof createResult.error === "object" && createResult.error !== null
361
+ ? (createResult.error as { message?: string }).message || "Unknown error"
362
+ : String(createResult.error),
363
+ });
364
+ }
365
+ } catch (error) {
366
+ console.error(`Import error for "${item.title || "Untitled"}":`, error);
367
+ result.errors.push({
368
+ title: item.title || "Untitled",
369
+ error: error instanceof Error && error.message ? error.message : "Failed to import item",
370
+ });
371
+ }
372
+ }
373
+
374
+ result.success = result.errors.length === 0;
375
+ return result;
376
+ }
377
+
378
+ function mapStatus(wpStatus: string | undefined): string {
379
+ switch (wpStatus) {
380
+ case "publish":
381
+ return "published";
382
+ case "draft":
383
+ return "draft";
384
+ case "pending":
385
+ return "draft";
386
+ case "private":
387
+ return "draft";
388
+ default:
389
+ return "draft";
390
+ }
391
+ }
392
+
393
+ async function collectPluginItems(items: AsyncIterable<NormalizedItem>): Promise<NormalizedItem[]> {
394
+ const collected: NormalizedItem[] = [];
395
+ for await (const item of items) {
396
+ collected.push(item);
397
+ }
398
+ return collected;
399
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Admin manifest endpoint - injected by Dineway integration
3
+ *
4
+ * GET /_dineway/api/manifest
5
+ *
6
+ * Returns the admin manifest with collection definitions and plugin info.
7
+ * The manifest is generated from the user's live.config.ts at runtime.
8
+ */
9
+
10
+ import type { APIRoute } from "astro";
11
+
12
+ import { getAuthMode } from "#auth/mode.js";
13
+
14
+ import { experimentalSiteContextWorkflowsEnabled } from "../../../site-context/experimental-workflows.js";
15
+ import { COMMIT, VERSION } from "../../../version.js";
16
+ import { getStoredConfig } from "../../integration/runtime.js";
17
+ import type { DinewayManifest } from "../../types.js";
18
+
19
+ export const prerender = false;
20
+
21
+ export const GET: APIRoute = async ({ locals }) => {
22
+ const { dinewayManifest, dineway } = locals;
23
+
24
+ // Determine auth mode from config
25
+ const authMode = getAuthMode(dineway?.config);
26
+ const adminBranding = dinewayManifest?.admin ?? dineway?.config.admin ?? getStoredConfig()?.admin;
27
+
28
+ // Check if self-signup is enabled (any allowed domain with enabled = 1)
29
+ // Only relevant for passkey auth — external auth providers handle their own signup
30
+ let signupEnabled = false;
31
+ if (dineway?.db && authMode.type === "passkey") {
32
+ try {
33
+ const { sql } = await import("kysely");
34
+ const result = await sql<{ cnt: unknown }>`
35
+ SELECT COUNT(*) as cnt FROM allowed_domains WHERE enabled = 1
36
+ `.execute(dineway.db);
37
+ signupEnabled = Number(result.rows[0]?.cnt ?? 0) > 0;
38
+ } catch {
39
+ // Table may not exist yet, that's fine
40
+ }
41
+ }
42
+
43
+ const manifest: DinewayManifest = dinewayManifest
44
+ ? {
45
+ ...dinewayManifest,
46
+ features: {
47
+ ...dinewayManifest.features,
48
+ siteContextWorkflows: experimentalSiteContextWorkflowsEnabled(),
49
+ },
50
+ authMode: authMode.type === "external" ? authMode.providerType : "passkey",
51
+ signupEnabled,
52
+ admin: adminBranding,
53
+ }
54
+ : {
55
+ version: VERSION,
56
+ commit: COMMIT,
57
+ hash: "default",
58
+ collections: {},
59
+ plugins: {},
60
+ features: { siteContextWorkflows: experimentalSiteContextWorkflowsEnabled() },
61
+ taxonomies: [],
62
+ authMode: "passkey",
63
+ signupEnabled,
64
+ admin: adminBranding,
65
+ };
66
+
67
+ return Response.json(
68
+ { data: manifest },
69
+ {
70
+ headers: {
71
+ "Cache-Control": "private, no-store",
72
+ },
73
+ },
74
+ );
75
+ };