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.
- package/README.md +6 -3
- package/dist/{apply-CAPvMfoU.mjs → apply-iVSqz2qs.mjs} +132 -39
- package/dist/astro/index.d.mts +18 -9
- package/dist/astro/index.mjs +238 -16
- package/dist/astro/middleware/auth.d.mts +16 -5
- package/dist/astro/middleware/auth.mjs +74 -37
- package/dist/astro/middleware/redirect.mjs +24 -8
- package/dist/astro/middleware/request-context.mjs +18 -5
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.mjs +411 -169
- package/dist/astro/types.d.mts +25 -8
- package/dist/{byline-DeWCMU_i.mjs → byline-OhH2dlRu.mjs} +6 -21
- package/dist/{bylines-DyqBV9EQ.mjs → bylines-BGpD9_hy.mjs} +16 -6
- package/dist/cache-BdSY-gQN.mjs +42 -0
- package/dist/chunks--4F8ddV4.mjs +18 -0
- package/dist/cli/index.mjs +935 -15
- package/dist/client/external-auth-headers.d.mts +1 -1
- package/dist/client/index.d.mts +11 -3
- package/dist/client/index.mjs +4 -3
- package/dist/{connection-C9pxzuag.mjs → connection-BCNICDWN.mjs} +22 -5
- package/dist/{content-zSgdNmnt.mjs → content-DWi4d0rT.mjs} +41 -2
- package/dist/database/instrumentation.d.mts +34 -0
- package/dist/database/instrumentation.mjs +53 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +2 -2
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/libsql.mjs +11 -5
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.mjs +7 -1
- package/dist/db-errors-CEqD7qH9.mjs +23 -0
- package/dist/{default-WYlzADZL.mjs → default-VjJyuuG9.mjs} +2 -0
- package/dist/{dialect-helpers-B9uSp2GJ.mjs → dialect-helpers-DhTzaUxP.mjs} +3 -0
- package/dist/{error-DrxtnGPg.mjs → error-BmL6QipT.mjs} +7 -3
- package/dist/{index-C-jx21qs.d.mts → index-yvc6E_17.d.mts} +157 -30
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +24 -22
- package/dist/{loader-qKmo0wAY.mjs → loader-sMG4TZ-u.mjs} +9 -3
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/page/index.d.mts +10 -2
- package/dist/page/index.mjs +22 -1
- package/dist/patterns-CrCYkMBb.mjs +92 -0
- package/dist/{placeholder-bOx1xCTY.d.mts → placeholder--wOi4TbO.d.mts} +1 -1
- package/dist/{placeholder-B3knXwNc.mjs → placeholder-Cp8g5Emj.mjs} +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-BiaPl_g2.mjs → query-kDmwCsHh.mjs} +118 -50
- package/dist/{redirect-JPqLAbxa.mjs → redirect-DnEWAkVg.mjs} +43 -99
- package/dist/{registry-DSd1GWB8.mjs → registry-C0zjeB9P.mjs} +191 -123
- package/dist/request-cache-Dk5qPSOx.mjs +66 -0
- package/dist/request-context.d.mts +4 -16
- package/dist/{runner-B5l1JfOj.d.mts → runner-CFI6B6J2.d.mts} +1 -1
- package/dist/{runner-BGUGywgG.mjs → runner-DWZm2KQm.mjs} +589 -137
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +2 -2
- package/dist/{search-BNruJHDL.mjs → search-ByRGV2pq.mjs} +570 -424
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +11 -10
- package/dist/seo/index.d.mts +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +11 -3
- package/dist/storage/s3.mjs +78 -15
- package/dist/taxonomies-1s5PaS_8.mjs +266 -0
- package/dist/transaction-Cn2rjY78.mjs +27 -0
- package/dist/{types-BgQeVaPj.d.mts → types-BuMDPy5C.d.mts} +52 -3
- package/dist/{types-DuNbGKjF.mjs → types-COeOq9nK.mjs} +6 -1
- package/dist/{types-ju-_ORz7.d.mts → types-CWbdtiux.d.mts} +13 -5
- package/dist/{types-D38djUXv.d.mts → types-Cj0KMIZV.d.mts} +16 -3
- package/dist/{types-DkvMXalq.d.mts → types-DOrVigru.d.mts} +159 -0
- package/dist/{validate-CXnRKfJK.mjs → validate-BZ5wnLLp.mjs} +2 -1
- package/dist/{validate-DVKJJ-M_.d.mts → validate-IPf8n4Fj.d.mts} +4 -51
- package/dist/{validate-CqRJb_xU.mjs → validate-VPnKoIzW.mjs} +10 -10
- package/dist/version-BKXPsfmJ.mjs +6 -0
- package/package.json +53 -39
- package/src/astro/routes/PluginRegistry.tsx +21 -0
- package/src/astro/routes/admin.astro +99 -0
- package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
- package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
- package/src/astro/routes/api/admin/api-tokens/[id].ts +44 -0
- package/src/astro/routes/api/admin/api-tokens/index.ts +90 -0
- package/src/astro/routes/api/admin/briefing.ts +76 -0
- package/src/astro/routes/api/admin/bylines/[id]/index.ts +90 -0
- package/src/astro/routes/api/admin/bylines/index.ts +74 -0
- package/src/astro/routes/api/admin/comments/[id]/status.ts +120 -0
- package/src/astro/routes/api/admin/comments/[id].ts +64 -0
- package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
- package/src/astro/routes/api/admin/comments/counts.ts +30 -0
- package/src/astro/routes/api/admin/comments/index.ts +46 -0
- package/src/astro/routes/api/admin/context/[id]/history.ts +35 -0
- package/src/astro/routes/api/admin/context/[id]/index.ts +35 -0
- package/src/astro/routes/api/admin/context/[id]/review.ts +57 -0
- package/src/astro/routes/api/admin/context/[id]/supersede.ts +58 -0
- package/src/astro/routes/api/admin/context/diff.ts +35 -0
- package/src/astro/routes/api/admin/context/index.ts +69 -0
- package/src/astro/routes/api/admin/context/stale.ts +35 -0
- package/src/astro/routes/api/admin/hitl-requests/[id]/index.ts +38 -0
- package/src/astro/routes/api/admin/hitl-requests/[id]/resolve.ts +54 -0
- package/src/astro/routes/api/admin/hitl-requests/index.ts +38 -0
- package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +132 -0
- package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
- package/src/astro/routes/api/admin/oauth-clients/[id].ts +137 -0
- package/src/astro/routes/api/admin/oauth-clients/index.ts +95 -0
- package/src/astro/routes/api/admin/plugins/[id]/disable.ts +91 -0
- package/src/astro/routes/api/admin/plugins/[id]/enable.ts +91 -0
- package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +98 -0
- package/src/astro/routes/api/admin/plugins/[id]/update.ts +154 -0
- package/src/astro/routes/api/admin/plugins/index.ts +32 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +62 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +135 -0
- package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
- package/src/astro/routes/api/admin/review-requests/[id]/index.ts +35 -0
- package/src/astro/routes/api/admin/review-requests/[id]/resolve.ts +52 -0
- package/src/astro/routes/api/admin/review-requests/index.ts +35 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +62 -0
- package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
- package/src/astro/routes/api/admin/users/[id]/disable.ts +72 -0
- package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
- package/src/astro/routes/api/admin/users/[id]/index.ts +166 -0
- package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
- package/src/astro/routes/api/admin/users/index.ts +66 -0
- package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
- package/src/astro/routes/api/auth/invite/accept.ts +52 -0
- package/src/astro/routes/api/auth/invite/complete.ts +86 -0
- package/src/astro/routes/api/auth/invite/index.ts +99 -0
- package/src/astro/routes/api/auth/invite/register-options.ts +73 -0
- package/src/astro/routes/api/auth/logout.ts +40 -0
- package/src/astro/routes/api/auth/magic-link/send.ts +90 -0
- package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
- package/src/astro/routes/api/auth/me.ts +60 -0
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +221 -0
- package/src/astro/routes/api/auth/oauth/[provider].ts +120 -0
- package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
- package/src/astro/routes/api/auth/passkey/index.ts +54 -0
- package/src/astro/routes/api/auth/passkey/options.ts +85 -0
- package/src/astro/routes/api/auth/passkey/register/options.ts +88 -0
- package/src/astro/routes/api/auth/passkey/register/verify.ts +119 -0
- package/src/astro/routes/api/auth/passkey/verify.ts +72 -0
- package/src/astro/routes/api/auth/signup/complete.ts +87 -0
- package/src/astro/routes/api/auth/signup/request.ts +89 -0
- package/src/astro/routes/api/auth/signup/verify.ts +53 -0
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +310 -0
- package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +68 -0
- package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +77 -0
- package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +42 -0
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +100 -0
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +64 -0
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +129 -0
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +143 -0
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +50 -0
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +69 -0
- package/src/astro/routes/api/content/[collection]/[id].ts +173 -0
- package/src/astro/routes/api/content/[collection]/index.ts +103 -0
- package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
- package/src/astro/routes/api/dashboard.ts +32 -0
- package/src/astro/routes/api/dev/emails.ts +36 -0
- package/src/astro/routes/api/health.ts +54 -0
- package/src/astro/routes/api/import/probe.ts +47 -0
- package/src/astro/routes/api/import/wordpress/analyze.ts +523 -0
- package/src/astro/routes/api/import/wordpress/execute.ts +330 -0
- package/src/astro/routes/api/import/wordpress/media.ts +338 -0
- package/src/astro/routes/api/import/wordpress/prepare.ts +212 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +425 -0
- package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
- package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +399 -0
- package/src/astro/routes/api/manifest.ts +75 -0
- package/src/astro/routes/api/mcp.ts +125 -0
- package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
- package/src/astro/routes/api/media/[id].ts +145 -0
- package/src/astro/routes/api/media/file/[...key].ts +79 -0
- package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +91 -0
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
- package/src/astro/routes/api/media/providers/index.ts +30 -0
- package/src/astro/routes/api/media/upload-url.ts +146 -0
- package/src/astro/routes/api/media.ts +204 -0
- package/src/astro/routes/api/menus/[name]/items.ts +206 -0
- package/src/astro/routes/api/menus/[name]/reorder.ts +79 -0
- package/src/astro/routes/api/menus/[name].ts +145 -0
- package/src/astro/routes/api/menus/index.ts +91 -0
- package/src/astro/routes/api/oauth/authorize.ts +430 -0
- package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
- package/src/astro/routes/api/oauth/device/code.ts +56 -0
- package/src/astro/routes/api/oauth/device/token.ts +70 -0
- package/src/astro/routes/api/oauth/register.ts +182 -0
- package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
- package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
- package/src/astro/routes/api/oauth/token.ts +195 -0
- package/src/astro/routes/api/openapi.json.ts +33 -0
- package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +109 -0
- package/src/astro/routes/api/redirects/404s/index.ts +72 -0
- package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
- package/src/astro/routes/api/redirects/[id].ts +183 -0
- package/src/astro/routes/api/redirects/index.ts +100 -0
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
- package/src/astro/routes/api/revisions/[revisionId]/restore.ts +62 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +104 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +67 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +45 -0
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +107 -0
- package/src/astro/routes/api/schema/collections/index.ts +61 -0
- package/src/astro/routes/api/schema/index.ts +109 -0
- package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
- package/src/astro/routes/api/schema/orphans/index.ts +26 -0
- package/src/astro/routes/api/search/enable.ts +64 -0
- package/src/astro/routes/api/search/index.ts +52 -0
- package/src/astro/routes/api/search/rebuild.ts +72 -0
- package/src/astro/routes/api/search/stats.ts +35 -0
- package/src/astro/routes/api/search/suggest.ts +50 -0
- package/src/astro/routes/api/sections/[slug].ts +203 -0
- package/src/astro/routes/api/sections/index.ts +107 -0
- package/src/astro/routes/api/settings/email.ts +150 -0
- package/src/astro/routes/api/settings.ts +116 -0
- package/src/astro/routes/api/setup/admin-verify.ts +122 -0
- package/src/astro/routes/api/setup/admin.ts +104 -0
- package/src/astro/routes/api/setup/dev-bypass.ts +200 -0
- package/src/astro/routes/api/setup/dev-reset.ts +40 -0
- package/src/astro/routes/api/setup/index.ts +128 -0
- package/src/astro/routes/api/setup/status.ts +122 -0
- package/src/astro/routes/api/snapshot.ts +76 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +232 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +131 -0
- package/src/astro/routes/api/taxonomies/index.ts +114 -0
- package/src/astro/routes/api/themes/preview.ts +78 -0
- package/src/astro/routes/api/typegen.ts +114 -0
- package/src/astro/routes/api/well-known/auth.ts +71 -0
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +48 -0
- package/src/astro/routes/api/well-known/oauth-protected-resource.ts +39 -0
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +114 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +213 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +126 -0
- package/src/astro/routes/api/widget-areas/[name].ts +135 -0
- package/src/astro/routes/api/widget-areas/index.ts +149 -0
- package/src/astro/routes/api/widget-components.ts +22 -0
- package/src/astro/routes/robots.txt.ts +81 -0
- package/src/astro/routes/sitemap-[collection].xml.ts +104 -0
- package/src/astro/routes/sitemap.xml.ts +92 -0
- package/src/components/Break.astro +45 -0
- package/src/components/Button.astro +71 -0
- package/src/components/Buttons.astro +49 -0
- package/src/components/Code.astro +59 -0
- package/src/components/Columns.astro +59 -0
- package/src/components/CommentForm.astro +315 -0
- package/src/components/Comments.astro +232 -0
- package/src/components/Cover.astro +128 -0
- package/src/components/DinewayBodyEnd.astro +32 -0
- package/src/components/DinewayBodyStart.astro +32 -0
- package/src/components/DinewayHead.astro +61 -0
- package/src/components/DinewayImage.astro +178 -0
- package/src/components/DinewayMedia.astro +167 -0
- package/src/components/Embed.astro +128 -0
- package/src/components/File.astro +122 -0
- package/src/components/Gallery.astro +93 -0
- package/src/components/HtmlBlock.astro +33 -0
- package/src/components/Image.astro +178 -0
- package/src/components/InlineEditor.astro +27 -0
- package/src/components/InlinePortableTextEditor.tsx +1937 -0
- package/src/components/LiveSearch.astro +614 -0
- package/src/components/PortableText.astro +51 -0
- package/src/components/Pullquote.astro +51 -0
- package/src/components/Table.astro +135 -0
- package/src/components/WidgetArea.astro +22 -0
- package/src/components/WidgetRenderer.astro +72 -0
- package/src/components/index.ts +106 -0
- package/src/components/marks/Link.astro +31 -0
- package/src/components/marks/StrikeThrough.astro +7 -0
- package/src/components/marks/Subscript.astro +7 -0
- package/src/components/marks/Superscript.astro +7 -0
- package/src/components/marks/Underline.astro +7 -0
- package/src/components/marks.ts +19 -0
- package/src/components/widgets/Archives.astro +65 -0
- package/src/components/widgets/Categories.astro +35 -0
- package/src/components/widgets/RecentPosts.astro +51 -0
- package/src/components/widgets/Search.astro +18 -0
- package/src/components/widgets/Tags.astro +38 -0
- package/src/ui.ts +75 -0
- package/LICENSE +0 -9
- /package/dist/{adapters-BlzWJG82.d.mts → adapters-C2ypTrZZ.d.mts} +0 -0
- /package/dist/{config-Cq8H0SfX.mjs → config-BXwuX8Bx.mjs} +0 -0
- /package/dist/{load-C6FCD1FU.mjs → load-Coc9HpHH.mjs} +0 -0
- /package/dist/{manifest-schema-CTSEyIJ3.mjs → manifest-schema-D1MSVnoI.mjs} +0 -0
- /package/dist/{mode-BlyYtIFO.mjs → mode-47goXBBK.mjs} +0 -0
- /package/dist/{tokens-4vgYuXsZ.mjs → tokens-CJz9ubV6.mjs} +0 -0
- /package/dist/{transport-C5FYnid7.mjs → transport-DB5eDN4x.mjs} +0 -0
- /package/dist/{transport-gIL-e43D.d.mts → transport-Wge_IzKl.d.mts} +0 -0
- /package/dist/{types-CLLdsG3g.d.mts → types-BzcUjoqg.d.mts} +0 -0
- /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
|
+
};
|