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,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /_dineway/api/setup/status
|
|
3
|
+
*
|
|
4
|
+
* Returns setup status and seed file information
|
|
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 { getAuthMode } from "#auth/mode.js";
|
|
13
|
+
import { loadUserSeed } from "#seed/load.js";
|
|
14
|
+
|
|
15
|
+
export const GET: APIRoute = async ({ locals }) => {
|
|
16
|
+
const { dineway } = locals;
|
|
17
|
+
|
|
18
|
+
if (!dineway?.db) {
|
|
19
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Check if setup is complete
|
|
24
|
+
const setupComplete = await dineway.db
|
|
25
|
+
.selectFrom("options")
|
|
26
|
+
.select("value")
|
|
27
|
+
.where("name", "=", "dineway:setup_complete")
|
|
28
|
+
.executeTakeFirst();
|
|
29
|
+
|
|
30
|
+
// Value is JSON-encoded, parse it. Accepts both boolean true and string "true"
|
|
31
|
+
const isComplete =
|
|
32
|
+
setupComplete &&
|
|
33
|
+
(() => {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(setupComplete.value);
|
|
36
|
+
return parsed === true || parsed === "true";
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
41
|
+
|
|
42
|
+
// Also check if users exist
|
|
43
|
+
let hasUsers = false;
|
|
44
|
+
try {
|
|
45
|
+
const userCount = await dineway.db
|
|
46
|
+
.selectFrom("users")
|
|
47
|
+
.select((eb) => eb.fn.countAll<number>().as("count"))
|
|
48
|
+
.executeTakeFirstOrThrow();
|
|
49
|
+
hasUsers = userCount.count > 0;
|
|
50
|
+
} catch {
|
|
51
|
+
// Users table might not exist yet
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Setup is complete only if flag is set AND users exist
|
|
55
|
+
if (isComplete && hasUsers) {
|
|
56
|
+
return apiSuccess({
|
|
57
|
+
needsSetup: false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Determine current step
|
|
62
|
+
// step: "start" | "site" | "admin" | "complete"
|
|
63
|
+
let step: "start" | "site" | "admin" = "start";
|
|
64
|
+
|
|
65
|
+
// Get setup state if it exists
|
|
66
|
+
const setupState = await dineway.db
|
|
67
|
+
.selectFrom("options")
|
|
68
|
+
.select("value")
|
|
69
|
+
.where("name", "=", "dineway:setup_state")
|
|
70
|
+
.executeTakeFirst();
|
|
71
|
+
|
|
72
|
+
if (setupState) {
|
|
73
|
+
try {
|
|
74
|
+
const state = JSON.parse(setupState.value);
|
|
75
|
+
if (state.step === "admin") {
|
|
76
|
+
step = "admin";
|
|
77
|
+
} else if (state.step === "site") {
|
|
78
|
+
step = "site";
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// Invalid state, stay at start
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If setup_complete but no users, jump to admin step
|
|
86
|
+
if (isComplete && !hasUsers) {
|
|
87
|
+
step = "admin";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check auth mode
|
|
91
|
+
const authMode = getAuthMode(dineway.config);
|
|
92
|
+
const useExternalAuth = authMode.type === "external";
|
|
93
|
+
|
|
94
|
+
// In external auth mode, setup is complete if flag is set (no users required initially)
|
|
95
|
+
if (useExternalAuth && isComplete) {
|
|
96
|
+
return apiSuccess({
|
|
97
|
+
needsSetup: false,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Setup needed - try to load seed file info
|
|
102
|
+
const seed = await loadUserSeed();
|
|
103
|
+
const seedInfo = seed
|
|
104
|
+
? {
|
|
105
|
+
name: seed.meta?.name || "Unknown Template",
|
|
106
|
+
description: seed.meta?.description || "",
|
|
107
|
+
collections: seed.collections?.length || 0,
|
|
108
|
+
hasContent: !!(seed.content && Object.keys(seed.content).length > 0),
|
|
109
|
+
}
|
|
110
|
+
: null;
|
|
111
|
+
|
|
112
|
+
return apiSuccess({
|
|
113
|
+
needsSetup: true,
|
|
114
|
+
step,
|
|
115
|
+
seedInfo,
|
|
116
|
+
// Tell the wizard which auth mode is active
|
|
117
|
+
authMode: useExternalAuth ? authMode.providerType : "passkey",
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return handleError(error, "Failed to check setup status", "SETUP_STATUS_ERROR");
|
|
121
|
+
}
|
|
122
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot endpoint — exports a portable database snapshot for preview mode.
|
|
3
|
+
*
|
|
4
|
+
* Security:
|
|
5
|
+
* - Authenticated users: requires content:read + schema:read permissions
|
|
6
|
+
* - Preview services: requires valid X-Preview-Signature header (HMAC-SHA256)
|
|
7
|
+
* - Excludes auth/user/session/token tables
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { APIRoute } from "astro";
|
|
11
|
+
|
|
12
|
+
import { requirePerm } from "#api/authorize.js";
|
|
13
|
+
import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
14
|
+
import {
|
|
15
|
+
generateSnapshot,
|
|
16
|
+
parsePreviewSignatureHeader,
|
|
17
|
+
verifyPreviewSignature,
|
|
18
|
+
} from "#api/handlers/snapshot.js";
|
|
19
|
+
import { getPublicOrigin } from "#api/public-url.js";
|
|
20
|
+
|
|
21
|
+
export const prerender = false;
|
|
22
|
+
|
|
23
|
+
export const GET: APIRoute = async ({ request, locals, url }) => {
|
|
24
|
+
const { dineway, user } = locals;
|
|
25
|
+
|
|
26
|
+
if (!dineway?.db) {
|
|
27
|
+
return apiError("NOT_CONFIGURED", "Dineway is not initialized", 500);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check for preview signature auth (used by preview sidecars)
|
|
31
|
+
const previewSig = request.headers.get("X-Preview-Signature");
|
|
32
|
+
let authorized = false;
|
|
33
|
+
|
|
34
|
+
if (previewSig) {
|
|
35
|
+
const secret = import.meta.env.DINEWAY_PREVIEW_SECRET || import.meta.env.PREVIEW_SECRET || "";
|
|
36
|
+
if (!secret) {
|
|
37
|
+
console.warn(
|
|
38
|
+
"[snapshot] X-Preview-Signature header present but no PREVIEW_SECRET configured",
|
|
39
|
+
);
|
|
40
|
+
} else {
|
|
41
|
+
const parsed = parsePreviewSignatureHeader(previewSig);
|
|
42
|
+
if (!parsed) {
|
|
43
|
+
console.warn("[snapshot] Failed to parse X-Preview-Signature header");
|
|
44
|
+
} else {
|
|
45
|
+
authorized = await verifyPreviewSignature(parsed.source, parsed.exp, parsed.sig, secret);
|
|
46
|
+
if (!authorized) {
|
|
47
|
+
console.warn("[snapshot] Preview signature verification failed", {
|
|
48
|
+
source: parsed.source,
|
|
49
|
+
exp: parsed.exp,
|
|
50
|
+
expired: parsed.exp < Date.now() / 1000,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!authorized) {
|
|
58
|
+
// Fall back to standard user auth
|
|
59
|
+
const contentDenied = requirePerm(user, "content:read");
|
|
60
|
+
if (contentDenied) return contentDenied;
|
|
61
|
+
const schemaDenied = requirePerm(user, "schema:read");
|
|
62
|
+
if (schemaDenied) return schemaDenied;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const includeDrafts = url.searchParams.get("drafts") === "true";
|
|
67
|
+
const snapshot = await generateSnapshot(dineway.db, {
|
|
68
|
+
includeDrafts,
|
|
69
|
+
origin: getPublicOrigin(url, dineway.config),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return apiSuccess(snapshot);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return handleError(error, "Failed to generate snapshot", "SNAPSHOT_ERROR");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single term endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/taxonomies/:name/terms/:slug - Get a single term
|
|
5
|
+
* PUT /_dineway/api/taxonomies/:name/terms/:slug - Update a term
|
|
6
|
+
* DELETE /_dineway/api/taxonomies/:name/terms/:slug - Delete a term
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { APIRoute } from "astro";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
import { requirePerm } from "#api/authorize.js";
|
|
13
|
+
import { apiError, handleError, requireDb, unwrapResult } from "#api/error.js";
|
|
14
|
+
import { handleTermDelete, handleTermGet, handleTermUpdate } from "#api/handlers/taxonomies.js";
|
|
15
|
+
import {
|
|
16
|
+
ensureWorkflowHitlRouteRequest,
|
|
17
|
+
hitlRequiredRouteError,
|
|
18
|
+
resolveHitlRouteActor,
|
|
19
|
+
} from "#api/hitl-route-helpers.js";
|
|
20
|
+
import { isParseError, parseBody, parseQuery } from "#api/parse.js";
|
|
21
|
+
import { updateTermBody } from "#api/schemas.js";
|
|
22
|
+
import {
|
|
23
|
+
activityChangedKeys,
|
|
24
|
+
logTaxonomyActivity,
|
|
25
|
+
RiskPolicyEvaluator,
|
|
26
|
+
taxonomyApiRouteSource,
|
|
27
|
+
TaxonomyHitlPayloadBuilder,
|
|
28
|
+
} from "#site-context/index.js";
|
|
29
|
+
|
|
30
|
+
export const prerender = false;
|
|
31
|
+
|
|
32
|
+
const updateTermHitlBody = updateTermBody.extend({
|
|
33
|
+
hitlRequestId: z.string().min(1).optional(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const deleteTermQuery = z.object({
|
|
37
|
+
hitlRequestId: z.string().min(1).optional(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get a single term
|
|
42
|
+
*/
|
|
43
|
+
export const GET: APIRoute = async ({ params, locals }) => {
|
|
44
|
+
const { dineway, user } = locals;
|
|
45
|
+
const { name, slug } = params;
|
|
46
|
+
|
|
47
|
+
if (!name || !slug) {
|
|
48
|
+
return apiError("VALIDATION_ERROR", "Taxonomy name and slug required", 400);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const dbErr = requireDb(dineway?.db);
|
|
52
|
+
if (dbErr) return dbErr;
|
|
53
|
+
|
|
54
|
+
const denied = requirePerm(user, "taxonomies:read");
|
|
55
|
+
if (denied) return denied;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const result = await handleTermGet(dineway.db, name, slug);
|
|
59
|
+
return unwrapResult(result);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return handleError(error, "Failed to get term", "TERM_GET_ERROR");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Update a term
|
|
67
|
+
*/
|
|
68
|
+
export const PUT: APIRoute = async ({ params, request, locals }) => {
|
|
69
|
+
const { dineway, user } = locals;
|
|
70
|
+
const { name, slug } = params;
|
|
71
|
+
|
|
72
|
+
if (!name || !slug) {
|
|
73
|
+
return apiError("VALIDATION_ERROR", "Taxonomy name and slug required", 400);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const dbErr = requireDb(dineway?.db);
|
|
77
|
+
if (dbErr) return dbErr;
|
|
78
|
+
|
|
79
|
+
const denied = requirePerm(user, "taxonomies:manage");
|
|
80
|
+
if (denied) return denied;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const body = await parseBody(request, updateTermHitlBody);
|
|
84
|
+
if (isParseError(body)) return body;
|
|
85
|
+
|
|
86
|
+
const current = await handleTermGet(dineway.db, name, slug);
|
|
87
|
+
if (!current.success) return unwrapResult(current);
|
|
88
|
+
|
|
89
|
+
const { hitlRequestId, ...termInput } = body;
|
|
90
|
+
const actor = resolveHitlRouteActor(locals);
|
|
91
|
+
const evaluator = new RiskPolicyEvaluator({
|
|
92
|
+
db: dineway.db,
|
|
93
|
+
handlers: dineway,
|
|
94
|
+
});
|
|
95
|
+
let approvedHitlRequestId: string | null = null;
|
|
96
|
+
|
|
97
|
+
if (evaluator.requiresWorkflowHitl(actor.identity)) {
|
|
98
|
+
const payloadBuilder = new TaxonomyHitlPayloadBuilder(dineway.db);
|
|
99
|
+
const taxonomy = await payloadBuilder.loadTaxonomyDefinition(name);
|
|
100
|
+
if (!taxonomy) {
|
|
101
|
+
return apiError("NOT_FOUND", `Taxonomy '${name}' not found`, 404);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const action = await payloadBuilder.buildUpdateTermRequest({
|
|
105
|
+
taxonomy,
|
|
106
|
+
currentTerm: {
|
|
107
|
+
id: current.data.term.id,
|
|
108
|
+
name: current.data.term.name,
|
|
109
|
+
slug: current.data.term.slug,
|
|
110
|
+
label: current.data.term.label,
|
|
111
|
+
parentId: current.data.term.parentId,
|
|
112
|
+
description: current.data.term.description ?? null,
|
|
113
|
+
},
|
|
114
|
+
...termInput,
|
|
115
|
+
});
|
|
116
|
+
const decision = await evaluator.evaluateWorkflowHitl({
|
|
117
|
+
actor: actor.identity,
|
|
118
|
+
hitlRequestId,
|
|
119
|
+
action,
|
|
120
|
+
});
|
|
121
|
+
if (!decision.allowed) {
|
|
122
|
+
const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
|
|
123
|
+
return hitlRequiredRouteError(decision, ensured);
|
|
124
|
+
}
|
|
125
|
+
approvedHitlRequestId = decision.hitlRequest.id;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const result = await handleTermUpdate(dineway.db, name, slug, termInput);
|
|
129
|
+
if (result.success) {
|
|
130
|
+
await logTaxonomyActivity(dineway.db, locals, {
|
|
131
|
+
action: "term_updated",
|
|
132
|
+
taxonomyName: name,
|
|
133
|
+
termId: result.data.term.id,
|
|
134
|
+
termSlug: result.data.term.slug,
|
|
135
|
+
...taxonomyApiRouteSource("term_updated"),
|
|
136
|
+
detail: {
|
|
137
|
+
changedKeys: activityChangedKeys(termInput),
|
|
138
|
+
label: result.data.term.label,
|
|
139
|
+
parentId: result.data.term.parentId,
|
|
140
|
+
description: result.data.term.description ?? null,
|
|
141
|
+
hitlRequestId: approvedHitlRequestId,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return unwrapResult(result);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return handleError(error, "Failed to update term", "TERM_UPDATE_ERROR");
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Delete a term
|
|
153
|
+
*/
|
|
154
|
+
export const DELETE: APIRoute = async ({ params, request, locals }) => {
|
|
155
|
+
const { dineway, user } = locals;
|
|
156
|
+
const { name, slug } = params;
|
|
157
|
+
|
|
158
|
+
if (!name || !slug) {
|
|
159
|
+
return apiError("VALIDATION_ERROR", "Taxonomy name and slug required", 400);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const dbErr = requireDb(dineway?.db);
|
|
163
|
+
if (dbErr) return dbErr;
|
|
164
|
+
|
|
165
|
+
const denied = requirePerm(user, "taxonomies:manage");
|
|
166
|
+
if (denied) return denied;
|
|
167
|
+
|
|
168
|
+
const query = parseQuery(new URL(request.url), deleteTermQuery);
|
|
169
|
+
if (isParseError(query)) return query;
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const current = await handleTermGet(dineway.db, name, slug);
|
|
173
|
+
if (!current.success) return unwrapResult(current);
|
|
174
|
+
|
|
175
|
+
const actor = resolveHitlRouteActor(locals);
|
|
176
|
+
const evaluator = new RiskPolicyEvaluator({
|
|
177
|
+
db: dineway.db,
|
|
178
|
+
handlers: dineway,
|
|
179
|
+
});
|
|
180
|
+
let approvedHitlRequestId: string | null = null;
|
|
181
|
+
|
|
182
|
+
if (evaluator.requiresWorkflowHitl(actor.identity)) {
|
|
183
|
+
const payloadBuilder = new TaxonomyHitlPayloadBuilder(dineway.db);
|
|
184
|
+
const taxonomy = await payloadBuilder.loadTaxonomyDefinition(name);
|
|
185
|
+
if (!taxonomy) {
|
|
186
|
+
return apiError("NOT_FOUND", `Taxonomy '${name}' not found`, 404);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const action = await payloadBuilder.buildDeleteTermRequest({
|
|
190
|
+
taxonomy,
|
|
191
|
+
currentTerm: {
|
|
192
|
+
id: current.data.term.id,
|
|
193
|
+
name: current.data.term.name,
|
|
194
|
+
slug: current.data.term.slug,
|
|
195
|
+
label: current.data.term.label,
|
|
196
|
+
parentId: current.data.term.parentId,
|
|
197
|
+
description: current.data.term.description ?? null,
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
const decision = await evaluator.evaluateWorkflowHitl({
|
|
201
|
+
actor: actor.identity,
|
|
202
|
+
hitlRequestId: query.hitlRequestId,
|
|
203
|
+
action,
|
|
204
|
+
});
|
|
205
|
+
if (!decision.allowed) {
|
|
206
|
+
const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
|
|
207
|
+
return hitlRequiredRouteError(decision, ensured);
|
|
208
|
+
}
|
|
209
|
+
approvedHitlRequestId = decision.hitlRequest.id;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const result = await handleTermDelete(dineway.db, name, slug);
|
|
213
|
+
if (result.success) {
|
|
214
|
+
await logTaxonomyActivity(dineway.db, locals, {
|
|
215
|
+
action: "term_deleted",
|
|
216
|
+
taxonomyName: name,
|
|
217
|
+
termId: current.data.term.id,
|
|
218
|
+
termSlug: current.data.term.slug,
|
|
219
|
+
...taxonomyApiRouteSource("term_deleted"),
|
|
220
|
+
detail: {
|
|
221
|
+
label: current.data.term.label,
|
|
222
|
+
parentId: current.data.term.parentId,
|
|
223
|
+
description: current.data.term.description ?? null,
|
|
224
|
+
hitlRequestId: approvedHitlRequestId,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return unwrapResult(result);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
return handleError(error, "Failed to delete term", "TERM_DELETE_ERROR");
|
|
231
|
+
}
|
|
232
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taxonomy terms list and create endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/taxonomies/:name/terms - List all terms (tree for hierarchical)
|
|
5
|
+
* POST /_dineway/api/taxonomies/:name/terms - Create a new term
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { APIRoute } from "astro";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
import { requirePerm } from "#api/authorize.js";
|
|
12
|
+
import { apiError, handleError, requireDb, unwrapResult } from "#api/error.js";
|
|
13
|
+
import { handleTermCreate, handleTermList } from "#api/handlers/taxonomies.js";
|
|
14
|
+
import {
|
|
15
|
+
ensureWorkflowHitlRouteRequest,
|
|
16
|
+
hitlRequiredRouteError,
|
|
17
|
+
resolveHitlRouteActor,
|
|
18
|
+
} from "#api/hitl-route-helpers.js";
|
|
19
|
+
import { isParseError, parseBody } from "#api/parse.js";
|
|
20
|
+
import { createTermBody } from "#api/schemas.js";
|
|
21
|
+
import {
|
|
22
|
+
logTaxonomyActivity,
|
|
23
|
+
RiskPolicyEvaluator,
|
|
24
|
+
taxonomyApiRouteSource,
|
|
25
|
+
TaxonomyHitlPayloadBuilder,
|
|
26
|
+
} from "#site-context/index.js";
|
|
27
|
+
|
|
28
|
+
export const prerender = false;
|
|
29
|
+
|
|
30
|
+
const createTermHitlBody = createTermBody.extend({
|
|
31
|
+
hitlRequestId: z.string().min(1).optional(),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* List all terms for a taxonomy
|
|
36
|
+
*/
|
|
37
|
+
export const GET: APIRoute = async ({ params, locals }) => {
|
|
38
|
+
const { dineway, user } = locals;
|
|
39
|
+
const { name } = params;
|
|
40
|
+
|
|
41
|
+
if (!name) {
|
|
42
|
+
return apiError("VALIDATION_ERROR", "Taxonomy name required", 400);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const dbErr = requireDb(dineway?.db);
|
|
46
|
+
if (dbErr) return dbErr;
|
|
47
|
+
|
|
48
|
+
const denied = requirePerm(user, "taxonomies:read");
|
|
49
|
+
if (denied) return denied;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const result = await handleTermList(dineway.db, name);
|
|
53
|
+
return unwrapResult(result);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return handleError(error, "Failed to list terms", "TERM_LIST_ERROR");
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a new term
|
|
61
|
+
*/
|
|
62
|
+
export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
63
|
+
const { dineway, user } = locals;
|
|
64
|
+
const { name } = params;
|
|
65
|
+
|
|
66
|
+
if (!name) {
|
|
67
|
+
return apiError("VALIDATION_ERROR", "Taxonomy name required", 400);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const dbErr = requireDb(dineway?.db);
|
|
71
|
+
if (dbErr) return dbErr;
|
|
72
|
+
|
|
73
|
+
const denied = requirePerm(user, "taxonomies:manage");
|
|
74
|
+
if (denied) return denied;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const body = await parseBody(request, createTermHitlBody);
|
|
78
|
+
if (isParseError(body)) return body;
|
|
79
|
+
|
|
80
|
+
const { hitlRequestId, ...termInput } = body;
|
|
81
|
+
const actor = resolveHitlRouteActor(locals);
|
|
82
|
+
const evaluator = new RiskPolicyEvaluator({
|
|
83
|
+
db: dineway.db,
|
|
84
|
+
handlers: dineway,
|
|
85
|
+
});
|
|
86
|
+
let approvedHitlRequestId: string | null = null;
|
|
87
|
+
|
|
88
|
+
if (evaluator.requiresWorkflowHitl(actor.identity)) {
|
|
89
|
+
const payloadBuilder = new TaxonomyHitlPayloadBuilder(dineway.db);
|
|
90
|
+
const taxonomy = await payloadBuilder.loadTaxonomyDefinition(name);
|
|
91
|
+
if (!taxonomy) {
|
|
92
|
+
return apiError("NOT_FOUND", `Taxonomy '${name}' not found`, 404);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const action = await payloadBuilder.buildCreateTermRequest({
|
|
96
|
+
taxonomy,
|
|
97
|
+
...termInput,
|
|
98
|
+
});
|
|
99
|
+
const decision = await evaluator.evaluateWorkflowHitl({
|
|
100
|
+
actor: actor.identity,
|
|
101
|
+
hitlRequestId,
|
|
102
|
+
action,
|
|
103
|
+
});
|
|
104
|
+
if (!decision.allowed) {
|
|
105
|
+
const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
|
|
106
|
+
return hitlRequiredRouteError(decision, ensured);
|
|
107
|
+
}
|
|
108
|
+
approvedHitlRequestId = decision.hitlRequest.id;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const result = await handleTermCreate(dineway.db, name, termInput);
|
|
112
|
+
if (result.success) {
|
|
113
|
+
await logTaxonomyActivity(dineway.db, locals, {
|
|
114
|
+
action: "term_created",
|
|
115
|
+
taxonomyName: name,
|
|
116
|
+
termId: result.data.term.id,
|
|
117
|
+
termSlug: result.data.term.slug,
|
|
118
|
+
...taxonomyApiRouteSource("term_created"),
|
|
119
|
+
detail: {
|
|
120
|
+
label: result.data.term.label,
|
|
121
|
+
parentId: result.data.term.parentId,
|
|
122
|
+
description: result.data.term.description ?? null,
|
|
123
|
+
hitlRequestId: approvedHitlRequestId,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return unwrapResult(result, 201);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return handleError(error, "Failed to create term", "TERM_CREATE_ERROR");
|
|
130
|
+
}
|
|
131
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taxonomy definitions endpoint
|
|
3
|
+
*
|
|
4
|
+
* GET /_dineway/api/taxonomies - List all taxonomy definitions
|
|
5
|
+
* POST /_dineway/api/taxonomies - Create a custom taxonomy definition
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { APIRoute } from "astro";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
import { requirePerm } from "#api/authorize.js";
|
|
12
|
+
import { handleError, requireDb, unwrapResult } from "#api/error.js";
|
|
13
|
+
import { handleTaxonomyCreate, handleTaxonomyList } from "#api/handlers/taxonomies.js";
|
|
14
|
+
import {
|
|
15
|
+
ensureWorkflowHitlRouteRequest,
|
|
16
|
+
hitlRequiredRouteError,
|
|
17
|
+
resolveHitlRouteActor,
|
|
18
|
+
} from "#api/hitl-route-helpers.js";
|
|
19
|
+
import { isParseError, parseBody } from "#api/parse.js";
|
|
20
|
+
import { createTaxonomyDefBody } from "#api/schemas.js";
|
|
21
|
+
import {
|
|
22
|
+
logTaxonomyActivity,
|
|
23
|
+
RiskPolicyEvaluator,
|
|
24
|
+
taxonomyApiRouteSource,
|
|
25
|
+
TaxonomyHitlPayloadBuilder,
|
|
26
|
+
} from "#site-context/index.js";
|
|
27
|
+
|
|
28
|
+
export const prerender = false;
|
|
29
|
+
|
|
30
|
+
const createTaxonomyHitlBody = createTaxonomyDefBody.extend({
|
|
31
|
+
hitlRequestId: z.string().min(1).optional(),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* List taxonomy definitions
|
|
36
|
+
*/
|
|
37
|
+
export const GET: APIRoute = async ({ locals }) => {
|
|
38
|
+
const { dineway, user } = locals;
|
|
39
|
+
|
|
40
|
+
const dbErr = requireDb(dineway?.db);
|
|
41
|
+
if (dbErr) return dbErr;
|
|
42
|
+
|
|
43
|
+
const denied = requirePerm(user, "taxonomies:read");
|
|
44
|
+
if (denied) return denied;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const result = await handleTaxonomyList(dineway.db);
|
|
48
|
+
return unwrapResult(result);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return handleError(error, "Failed to list taxonomies", "TAXONOMY_LIST_ERROR");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a custom taxonomy definition
|
|
56
|
+
*/
|
|
57
|
+
export const POST: APIRoute = async ({ request, locals }) => {
|
|
58
|
+
const { dineway, user } = locals;
|
|
59
|
+
|
|
60
|
+
const dbErr = requireDb(dineway?.db);
|
|
61
|
+
if (dbErr) return dbErr;
|
|
62
|
+
|
|
63
|
+
const denied = requirePerm(user, "taxonomies:manage");
|
|
64
|
+
if (denied) return denied;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const body = await parseBody(request, createTaxonomyHitlBody);
|
|
68
|
+
if (isParseError(body)) return body;
|
|
69
|
+
|
|
70
|
+
const { hitlRequestId, ...taxonomyInput } = body;
|
|
71
|
+
const actor = resolveHitlRouteActor(locals);
|
|
72
|
+
const evaluator = new RiskPolicyEvaluator({
|
|
73
|
+
db: dineway.db,
|
|
74
|
+
handlers: dineway,
|
|
75
|
+
});
|
|
76
|
+
let approvedHitlRequestId: string | null = null;
|
|
77
|
+
|
|
78
|
+
if (evaluator.requiresWorkflowHitl(actor.identity)) {
|
|
79
|
+
const action = await new TaxonomyHitlPayloadBuilder(dineway.db).buildCreateTaxonomyRequest(
|
|
80
|
+
taxonomyInput,
|
|
81
|
+
);
|
|
82
|
+
const decision = await evaluator.evaluateWorkflowHitl({
|
|
83
|
+
actor: actor.identity,
|
|
84
|
+
hitlRequestId,
|
|
85
|
+
action,
|
|
86
|
+
});
|
|
87
|
+
if (!decision.allowed) {
|
|
88
|
+
const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
|
|
89
|
+
return hitlRequiredRouteError(decision, ensured);
|
|
90
|
+
}
|
|
91
|
+
approvedHitlRequestId = decision.hitlRequest.id;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const result = await handleTaxonomyCreate(dineway.db, taxonomyInput);
|
|
95
|
+
if (result.success) dineway.invalidateManifest();
|
|
96
|
+
if (result.success) {
|
|
97
|
+
await logTaxonomyActivity(dineway.db, locals, {
|
|
98
|
+
action: "created",
|
|
99
|
+
taxonomyId: result.data.taxonomy.id,
|
|
100
|
+
taxonomyName: result.data.taxonomy.name,
|
|
101
|
+
...taxonomyApiRouteSource("created"),
|
|
102
|
+
detail: {
|
|
103
|
+
label: result.data.taxonomy.label,
|
|
104
|
+
hierarchical: result.data.taxonomy.hierarchical,
|
|
105
|
+
collections: result.data.taxonomy.collections,
|
|
106
|
+
hitlRequestId: approvedHitlRequestId,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return unwrapResult(result, 201);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return handleError(error, "Failed to create taxonomy", "TAXONOMY_CREATE_ERROR");
|
|
113
|
+
}
|
|
114
|
+
};
|