dineway 0.1.4 → 0.1.6

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 (193) 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-BApX1xhM.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-hmtC3Cmv.mjs +6 -0
  77. package/package.json +49 -38
  78. package/src/astro/routes/admin.astro +25 -9
  79. package/src/astro/routes/api/admin/api-tokens/[id].ts +4 -0
  80. package/src/astro/routes/api/admin/api-tokens/index.ts +24 -2
  81. package/src/astro/routes/api/admin/briefing.ts +76 -0
  82. package/src/astro/routes/api/admin/bylines/[id]/index.ts +3 -0
  83. package/src/astro/routes/api/admin/bylines/index.ts +2 -0
  84. package/src/astro/routes/api/admin/context/[id]/history.ts +35 -0
  85. package/src/astro/routes/api/admin/context/[id]/index.ts +35 -0
  86. package/src/astro/routes/api/admin/context/[id]/review.ts +57 -0
  87. package/src/astro/routes/api/admin/context/[id]/supersede.ts +58 -0
  88. package/src/astro/routes/api/admin/context/diff.ts +35 -0
  89. package/src/astro/routes/api/admin/context/index.ts +69 -0
  90. package/src/astro/routes/api/admin/context/stale.ts +35 -0
  91. package/src/astro/routes/api/admin/hitl-requests/[id]/index.ts +38 -0
  92. package/src/astro/routes/api/admin/hitl-requests/[id]/resolve.ts +54 -0
  93. package/src/astro/routes/api/admin/hitl-requests/index.ts +38 -0
  94. package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +58 -17
  95. package/src/astro/routes/api/admin/oauth-clients/[id].ts +28 -1
  96. package/src/astro/routes/api/admin/oauth-clients/index.ts +25 -1
  97. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +54 -2
  98. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +54 -2
  99. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +51 -1
  100. package/src/astro/routes/api/admin/plugins/[id]/update.ts +98 -3
  101. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +72 -1
  102. package/src/astro/routes/api/admin/review-requests/[id]/index.ts +35 -0
  103. package/src/astro/routes/api/admin/review-requests/[id]/resolve.ts +52 -0
  104. package/src/astro/routes/api/admin/review-requests/index.ts +35 -0
  105. package/src/astro/routes/api/admin/users/[id]/disable.ts +26 -23
  106. package/src/astro/routes/api/admin/users/[id]/index.ts +41 -21
  107. package/src/astro/routes/api/auth/invite/register-options.ts +73 -0
  108. package/src/astro/routes/api/auth/magic-link/send.ts +2 -1
  109. package/src/astro/routes/api/auth/passkey/options.ts +2 -1
  110. package/src/astro/routes/api/auth/passkey/verify.ts +5 -1
  111. package/src/astro/routes/api/auth/signup/request.ts +20 -8
  112. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +3 -4
  113. package/src/astro/routes/api/content/[collection]/[id]/compare.ts +1 -1
  114. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +16 -2
  115. package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +16 -0
  116. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +9 -0
  117. package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +1 -1
  118. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +45 -1
  119. package/src/astro/routes/api/content/[collection]/[id]/restore.ts +12 -2
  120. package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +1 -1
  121. package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +24 -0
  122. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +3 -0
  123. package/src/astro/routes/api/content/[collection]/[id]/translations.ts +20 -0
  124. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +13 -0
  125. package/src/astro/routes/api/content/[collection]/[id].ts +36 -0
  126. package/src/astro/routes/api/content/[collection]/index.ts +48 -4
  127. package/src/astro/routes/api/content/[collection]/trash.ts +1 -1
  128. package/src/astro/routes/api/health.ts +54 -0
  129. package/src/astro/routes/api/import/wordpress/analyze.ts +2 -10
  130. package/src/astro/routes/api/import/wordpress/execute.ts +40 -6
  131. package/src/astro/routes/api/import/wordpress/prepare.ts +36 -5
  132. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +33 -1
  133. package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +3 -3
  134. package/src/astro/routes/api/import/wordpress-plugin/execute.ts +57 -15
  135. package/src/astro/routes/api/manifest.ts +13 -1
  136. package/src/astro/routes/api/mcp.ts +1 -0
  137. package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +7 -2
  138. package/src/astro/routes/api/media/upload-url.ts +11 -2
  139. package/src/astro/routes/api/media.ts +9 -7
  140. package/src/astro/routes/api/menus/[name]/items.ts +124 -5
  141. package/src/astro/routes/api/menus/[name]/reorder.ts +47 -1
  142. package/src/astro/routes/api/menus/[name].ts +84 -4
  143. package/src/astro/routes/api/menus/index.ts +46 -2
  144. package/src/astro/routes/api/oauth/authorize.ts +21 -8
  145. package/src/astro/routes/api/oauth/device/code.ts +2 -1
  146. package/src/astro/routes/api/oauth/device/token.ts +2 -1
  147. package/src/astro/routes/api/oauth/register.ts +182 -0
  148. package/src/astro/routes/api/oauth/token.ts +18 -7
  149. package/src/astro/routes/api/openapi.json.ts +3 -2
  150. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +21 -4
  151. package/src/astro/routes/api/redirects/[id].ts +103 -4
  152. package/src/astro/routes/api/redirects/index.ts +50 -2
  153. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +28 -0
  154. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +15 -0
  155. package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +13 -0
  156. package/src/astro/routes/api/schema/collections/[slug]/index.ts +27 -0
  157. package/src/astro/routes/api/schema/collections/index.ts +14 -0
  158. package/src/astro/routes/api/search/index.ts +1 -0
  159. package/src/astro/routes/api/search/suggest.ts +1 -0
  160. package/src/astro/routes/api/sections/[slug].ts +123 -4
  161. package/src/astro/routes/api/sections/index.ts +57 -2
  162. package/src/astro/routes/api/settings.ts +51 -2
  163. package/src/astro/routes/api/setup/admin-verify.ts +25 -5
  164. package/src/astro/routes/api/setup/admin.ts +16 -8
  165. package/src/astro/routes/api/setup/index.ts +3 -2
  166. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +141 -4
  167. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +64 -2
  168. package/src/astro/routes/api/taxonomies/index.ts +57 -2
  169. package/src/astro/routes/api/well-known/auth.ts +3 -1
  170. package/src/astro/routes/api/well-known/oauth-authorization-server.ts +8 -5
  171. package/src/astro/routes/api/well-known/oauth-protected-resource.ts +3 -2
  172. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +58 -16
  173. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +124 -38
  174. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +66 -20
  175. package/src/astro/routes/api/widget-areas/[name].ts +55 -7
  176. package/src/astro/routes/api/widget-areas/index.ts +56 -6
  177. package/src/components/DinewayHead.astro +15 -7
  178. package/src/components/DinewayMedia.astro +1 -1
  179. package/src/components/InlinePortableTextEditor.tsx +1 -1
  180. package/src/components/Table.astro +68 -41
  181. package/src/components/index.ts +2 -12
  182. package/src/components/marks.ts +19 -0
  183. package/LICENSE +0 -9
  184. /package/dist/{adapters-BlzWJG82.d.mts → adapters-C2ypTrZZ.d.mts} +0 -0
  185. /package/dist/{config-Cq8H0SfX.mjs → config-BXwuX8Bx.mjs} +0 -0
  186. /package/dist/{load-C6FCD1FU.mjs → load-Coc9HpHH.mjs} +0 -0
  187. /package/dist/{manifest-schema-CTSEyIJ3.mjs → manifest-schema-D1MSVnoI.mjs} +0 -0
  188. /package/dist/{mode-BlyYtIFO.mjs → mode-47goXBBK.mjs} +0 -0
  189. /package/dist/{tokens-4vgYuXsZ.mjs → tokens-CJz9ubV6.mjs} +0 -0
  190. /package/dist/{transport-C5FYnid7.mjs → transport-DB5eDN4x.mjs} +0 -0
  191. /package/dist/{transport-gIL-e43D.d.mts → transport-Wge_IzKl.d.mts} +0 -0
  192. /package/dist/{types-CLLdsG3g.d.mts → types-BzcUjoqg.d.mts} +0 -0
  193. /package/dist/{types-DShnjzb6.mjs → types-griIBQOQ.mjs} +0 -0
@@ -10,6 +10,7 @@ import { Role } from "@dineway-ai/auth";
10
10
  import type { APIRoute } from "astro";
11
11
  import { z } from "zod";
12
12
 
13
+ import { rejectApiTokenAuthControlWrite } from "#api/auth-control-guard.js";
13
14
  import { apiError, handleError, unwrapResult } from "#api/error.js";
14
15
  import {
15
16
  handleOAuthClientDelete,
@@ -17,6 +18,11 @@ import {
17
18
  handleOAuthClientUpdate,
18
19
  } from "#api/handlers/oauth-clients.js";
19
20
  import { isParseError, parseBody } from "#api/parse.js";
21
+ import { ALL_VALID_SCOPES } from "#auth/api-tokens.js";
22
+ import {
23
+ disabledExperimentalSiteContextWorkflowScopes,
24
+ getExperimentalSiteContextWorkflowScopesDisabledMessage,
25
+ } from "#site-context/experimental-workflows.js";
20
26
 
21
27
  export const prerender = false;
22
28
 
@@ -26,7 +32,7 @@ const updateClientSchema = z.object({
26
32
  .array(z.string().url("Each redirect URI must be a valid URL"))
27
33
  .min(1, "At least one redirect URI is required")
28
34
  .optional(),
29
- scopes: z.array(z.string()).nullable().optional(),
35
+ scopes: z.array(z.enum(ALL_VALID_SCOPES)).nullable().optional(),
30
36
  });
31
37
 
32
38
  /**
@@ -43,6 +49,9 @@ export const GET: APIRoute = async ({ params, locals }) => {
43
49
  return apiError("FORBIDDEN", "Admin privileges required", 403);
44
50
  }
45
51
 
52
+ const credentialGuard = rejectApiTokenAuthControlWrite(locals, "oauth_clients");
53
+ if (credentialGuard) return credentialGuard;
54
+
46
55
  const clientId = params.id;
47
56
  if (!clientId) {
48
57
  return apiError("VALIDATION_ERROR", "Client ID is required", 400);
@@ -66,6 +75,9 @@ export const PUT: APIRoute = async ({ params, request, locals }) => {
66
75
  return apiError("FORBIDDEN", "Admin privileges required", 403);
67
76
  }
68
77
 
78
+ const credentialGuard = rejectApiTokenAuthControlWrite(locals, "oauth_clients");
79
+ if (credentialGuard) return credentialGuard;
80
+
69
81
  const clientId = params.id;
70
82
  if (!clientId) {
71
83
  return apiError("VALIDATION_ERROR", "Client ID is required", 400);
@@ -74,6 +86,21 @@ export const PUT: APIRoute = async ({ params, request, locals }) => {
74
86
  try {
75
87
  const body = await parseBody(request, updateClientSchema);
76
88
  if (isParseError(body)) return body;
89
+ const requestedWorkflowScopes = disabledExperimentalSiteContextWorkflowScopes(
90
+ body.scopes ?? [],
91
+ );
92
+ if (requestedWorkflowScopes.length > 0) {
93
+ return apiError(
94
+ "NOT_IMPLEMENTED",
95
+ getExperimentalSiteContextWorkflowScopesDisabledMessage(),
96
+ 501,
97
+ {
98
+ status: "workflow_oauth_client_scopes_disabled",
99
+ reason: "workflow_oauth_client_scopes_disabled",
100
+ scopes: requestedWorkflowScopes,
101
+ },
102
+ );
103
+ }
77
104
 
78
105
  const result = await handleOAuthClientUpdate(dineway.db, clientId, body);
79
106
  return unwrapResult(result);
@@ -9,9 +9,15 @@ import { Role } from "@dineway-ai/auth";
9
9
  import type { APIRoute } from "astro";
10
10
  import { z } from "zod";
11
11
 
12
+ import { rejectApiTokenAuthControlWrite } from "#api/auth-control-guard.js";
12
13
  import { apiError, handleError, unwrapResult } from "#api/error.js";
13
14
  import { handleOAuthClientCreate, handleOAuthClientList } from "#api/handlers/oauth-clients.js";
14
15
  import { isParseError, parseBody } from "#api/parse.js";
16
+ import { ALL_VALID_SCOPES } from "#auth/api-tokens.js";
17
+ import {
18
+ disabledExperimentalSiteContextWorkflowScopes,
19
+ getExperimentalSiteContextWorkflowScopesDisabledMessage,
20
+ } from "#site-context/experimental-workflows.js";
15
21
 
16
22
  export const prerender = false;
17
23
 
@@ -24,7 +30,7 @@ const createClientSchema = z.object({
24
30
  redirectUris: z
25
31
  .array(z.string().url("Each redirect URI must be a valid URL"))
26
32
  .min(1, "At least one redirect URI is required"),
27
- scopes: z.array(z.string()).optional(),
33
+ scopes: z.array(z.enum(ALL_VALID_SCOPES)).optional(),
28
34
  });
29
35
 
30
36
  /**
@@ -59,9 +65,27 @@ export const POST: APIRoute = async ({ request, locals }) => {
59
65
  return apiError("FORBIDDEN", "Admin privileges required", 403);
60
66
  }
61
67
 
68
+ const credentialGuard = rejectApiTokenAuthControlWrite(locals, "oauth_clients");
69
+ if (credentialGuard) return credentialGuard;
70
+
62
71
  try {
63
72
  const body = await parseBody(request, createClientSchema);
64
73
  if (isParseError(body)) return body;
74
+ const requestedWorkflowScopes = disabledExperimentalSiteContextWorkflowScopes(
75
+ body.scopes ?? [],
76
+ );
77
+ if (requestedWorkflowScopes.length > 0) {
78
+ return apiError(
79
+ "NOT_IMPLEMENTED",
80
+ getExperimentalSiteContextWorkflowScopesDisabledMessage(),
81
+ 501,
82
+ {
83
+ status: "workflow_oauth_client_scopes_disabled",
84
+ reason: "workflow_oauth_client_scopes_disabled",
85
+ scopes: requestedWorkflowScopes,
86
+ },
87
+ );
88
+ }
65
89
 
66
90
  const result = await handleOAuthClientCreate(dineway.db, body);
67
91
  return unwrapResult(result, 201);
@@ -5,15 +5,32 @@
5
5
  */
6
6
 
7
7
  import type { APIRoute } from "astro";
8
+ import { z } from "zod";
8
9
 
9
10
  import { requirePerm } from "#api/authorize.js";
10
11
  import { apiError, unwrapResult } from "#api/error.js";
11
- import { handlePluginDisable } from "#api/index.js";
12
+ import {
13
+ ensureWorkflowHitlRouteRequest,
14
+ hitlRequiredRouteError,
15
+ resolveHitlRouteActor,
16
+ } from "#api/hitl-route-helpers.js";
17
+ import { handlePluginDisable, handlePluginGet } from "#api/index.js";
18
+ import { isParseError, parseOptionalBody } from "#api/parse.js";
12
19
  import { setCronTasksEnabled } from "#plugins/cron.js";
20
+ import {
21
+ logPluginActivity,
22
+ pluginApiRouteSource,
23
+ PluginHitlPayloadBuilder,
24
+ RiskPolicyEvaluator,
25
+ } from "#site-context/index.js";
13
26
 
14
27
  export const prerender = false;
15
28
 
16
- export const POST: APIRoute = async ({ params, locals }) => {
29
+ const hitlBodySchema = z.object({
30
+ hitlRequestId: z.string().min(1).optional(),
31
+ });
32
+
33
+ export const POST: APIRoute = async ({ params, request, locals }) => {
17
34
  const { dineway, user } = locals;
18
35
  const { id } = params;
19
36
 
@@ -28,12 +45,47 @@ export const POST: APIRoute = async ({ params, locals }) => {
28
45
  return apiError("INVALID_REQUEST", "Plugin ID required", 400);
29
46
  }
30
47
 
48
+ const body = await parseOptionalBody(request, hitlBodySchema, {});
49
+ if (isParseError(body)) return body;
50
+
51
+ const current = await handlePluginGet(
52
+ dineway.db,
53
+ dineway.configuredPlugins,
54
+ id,
55
+ dineway.config.marketplace,
56
+ );
57
+ if (!current.success) return unwrapResult(current);
58
+
59
+ const actor = resolveHitlRouteActor(locals);
60
+ const action = await new PluginHitlPayloadBuilder().buildDisableRequest(current.data.item);
61
+ const decision = await new RiskPolicyEvaluator({
62
+ db: dineway.db,
63
+ handlers: dineway,
64
+ }).evaluateWorkflowHitl({
65
+ actor: actor.identity,
66
+ hitlRequestId: body.hitlRequestId,
67
+ action,
68
+ });
69
+ if (!decision.allowed) {
70
+ const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
71
+ return hitlRequiredRouteError(decision, ensured);
72
+ }
73
+
31
74
  const result = await handlePluginDisable(dineway.db, dineway.configuredPlugins, id);
32
75
 
33
76
  if (!result.success) return unwrapResult(result);
34
77
 
35
78
  await dineway.setPluginStatus(id, "inactive");
36
79
  await setCronTasksEnabled(dineway.db, id, false);
80
+ await logPluginActivity(dineway.db, locals, {
81
+ action: "disabled",
82
+ pluginId: id,
83
+ ...pluginApiRouteSource("disabled"),
84
+ summary: `Disabled plugin ${id}`,
85
+ detail: {
86
+ hitlRequestId: decision.required ? decision.hitlRequest.id : null,
87
+ },
88
+ });
37
89
 
38
90
  return unwrapResult(result);
39
91
  };
@@ -5,15 +5,32 @@
5
5
  */
6
6
 
7
7
  import type { APIRoute } from "astro";
8
+ import { z } from "zod";
8
9
 
9
10
  import { requirePerm } from "#api/authorize.js";
10
11
  import { apiError, unwrapResult } from "#api/error.js";
11
- import { handlePluginEnable } from "#api/index.js";
12
+ import {
13
+ ensureWorkflowHitlRouteRequest,
14
+ hitlRequiredRouteError,
15
+ resolveHitlRouteActor,
16
+ } from "#api/hitl-route-helpers.js";
17
+ import { handlePluginEnable, handlePluginGet } from "#api/index.js";
18
+ import { isParseError, parseOptionalBody } from "#api/parse.js";
12
19
  import { setCronTasksEnabled } from "#plugins/cron.js";
20
+ import {
21
+ logPluginActivity,
22
+ pluginApiRouteSource,
23
+ PluginHitlPayloadBuilder,
24
+ RiskPolicyEvaluator,
25
+ } from "#site-context/index.js";
13
26
 
14
27
  export const prerender = false;
15
28
 
16
- export const POST: APIRoute = async ({ params, locals }) => {
29
+ const hitlBodySchema = z.object({
30
+ hitlRequestId: z.string().min(1).optional(),
31
+ });
32
+
33
+ export const POST: APIRoute = async ({ params, request, locals }) => {
17
34
  const { dineway, user } = locals;
18
35
  const { id } = params;
19
36
 
@@ -28,12 +45,47 @@ export const POST: APIRoute = async ({ params, locals }) => {
28
45
  return apiError("INVALID_REQUEST", "Plugin ID required", 400);
29
46
  }
30
47
 
48
+ const body = await parseOptionalBody(request, hitlBodySchema, {});
49
+ if (isParseError(body)) return body;
50
+
51
+ const current = await handlePluginGet(
52
+ dineway.db,
53
+ dineway.configuredPlugins,
54
+ id,
55
+ dineway.config.marketplace,
56
+ );
57
+ if (!current.success) return unwrapResult(current);
58
+
59
+ const actor = resolveHitlRouteActor(locals);
60
+ const action = await new PluginHitlPayloadBuilder().buildEnableRequest(current.data.item);
61
+ const decision = await new RiskPolicyEvaluator({
62
+ db: dineway.db,
63
+ handlers: dineway,
64
+ }).evaluateWorkflowHitl({
65
+ actor: actor.identity,
66
+ hitlRequestId: body.hitlRequestId,
67
+ action,
68
+ });
69
+ if (!decision.allowed) {
70
+ const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
71
+ return hitlRequiredRouteError(decision, ensured);
72
+ }
73
+
31
74
  const result = await handlePluginEnable(dineway.db, dineway.configuredPlugins, id);
32
75
 
33
76
  if (!result.success) return unwrapResult(result);
34
77
 
35
78
  await dineway.setPluginStatus(id, "active");
36
79
  await setCronTasksEnabled(dineway.db, id, true);
80
+ await logPluginActivity(dineway.db, locals, {
81
+ action: "enabled",
82
+ pluginId: id,
83
+ ...pluginApiRouteSource("enabled"),
84
+ summary: `Enabled plugin ${id}`,
85
+ detail: {
86
+ hitlRequestId: decision.required ? decision.hitlRequest.id : null,
87
+ },
88
+ });
37
89
 
38
90
  return unwrapResult(result);
39
91
  };
@@ -9,13 +9,25 @@ import { z } from "zod";
9
9
 
10
10
  import { requirePerm } from "#api/authorize.js";
11
11
  import { apiError, unwrapResult } from "#api/error.js";
12
- import { handleMarketplaceUninstall } from "#api/index.js";
12
+ import {
13
+ ensureWorkflowHitlRouteRequest,
14
+ hitlRequiredRouteError,
15
+ resolveHitlRouteActor,
16
+ } from "#api/hitl-route-helpers.js";
17
+ import { handleMarketplaceUninstall, handlePluginList } from "#api/index.js";
13
18
  import { isParseError, parseOptionalBody } from "#api/parse.js";
19
+ import {
20
+ logPluginActivity,
21
+ pluginApiRouteSource,
22
+ PluginHitlPayloadBuilder,
23
+ RiskPolicyEvaluator,
24
+ } from "#site-context/index.js";
14
25
 
15
26
  export const prerender = false;
16
27
 
17
28
  const uninstallBodySchema = z.object({
18
29
  deleteData: z.boolean().optional(),
30
+ hitlRequestId: z.string().min(1).optional(),
19
31
  });
20
32
 
21
33
  export const POST: APIRoute = async ({ params, request, locals }) => {
@@ -36,6 +48,34 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
36
48
  const body = await parseOptionalBody(request, uninstallBodySchema, {});
37
49
  if (isParseError(body)) return body;
38
50
 
51
+ const actor = resolveHitlRouteActor(locals);
52
+ let approvedHitlRequestId: string | null = null;
53
+ const current = await handlePluginList(
54
+ dineway.db,
55
+ dineway.configuredPlugins,
56
+ dineway.config.marketplace,
57
+ );
58
+ if (!current.success) return unwrapResult(current);
59
+ const plugin = current.data.items.find((item) => item.id === id && item.source === "marketplace");
60
+ if (plugin) {
61
+ const action = await new PluginHitlPayloadBuilder().buildUninstallRequest(plugin, {
62
+ deleteData: body.deleteData ?? false,
63
+ });
64
+ const decision = await new RiskPolicyEvaluator({
65
+ db: dineway.db,
66
+ handlers: dineway,
67
+ }).evaluateWorkflowHitl({
68
+ actor: actor.identity,
69
+ hitlRequestId: body.hitlRequestId,
70
+ action,
71
+ });
72
+ if (!decision.allowed) {
73
+ const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
74
+ return hitlRequiredRouteError(decision, ensured);
75
+ }
76
+ approvedHitlRequestId = decision.required ? decision.hitlRequest.id : null;
77
+ }
78
+
39
79
  const result = await handleMarketplaceUninstall(dineway.db, dineway.storage, id, {
40
80
  deleteData: body.deleteData ?? false,
41
81
  });
@@ -43,6 +83,16 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
43
83
  if (!result.success) return unwrapResult(result);
44
84
 
45
85
  await dineway.syncMarketplacePlugins();
86
+ await logPluginActivity(dineway.db, locals, {
87
+ action: "uninstalled",
88
+ pluginId: id,
89
+ ...pluginApiRouteSource("uninstalled"),
90
+ summary: `Uninstalled plugin ${id}`,
91
+ detail: {
92
+ deleteData: body.deleteData ?? false,
93
+ hitlRequestId: approvedHitlRequestId,
94
+ },
95
+ });
46
96
 
47
97
  return unwrapResult(result);
48
98
  };
@@ -9,8 +9,19 @@ import { z } from "zod";
9
9
 
10
10
  import { requirePerm } from "#api/authorize.js";
11
11
  import { apiError, unwrapResult } from "#api/error.js";
12
- import { handleMarketplaceUpdate } from "#api/index.js";
12
+ import {
13
+ ensureWorkflowHitlRouteRequest,
14
+ hitlRequiredRouteError,
15
+ resolveHitlRouteActor,
16
+ } from "#api/hitl-route-helpers.js";
17
+ import { handleMarketplaceUpdate, handleMarketplaceUpdatePreview } from "#api/index.js";
13
18
  import { isParseError, parseOptionalBody } from "#api/parse.js";
19
+ import {
20
+ logPluginActivity,
21
+ pluginApiRouteSource,
22
+ PluginHitlPayloadBuilder,
23
+ RiskPolicyEvaluator,
24
+ } from "#site-context/index.js";
14
25
 
15
26
  export const prerender = false;
16
27
 
@@ -18,6 +29,7 @@ const updateBodySchema = z.object({
18
29
  version: z.string().min(1).optional(),
19
30
  confirmCapabilityChanges: z.boolean().optional(),
20
31
  confirmRouteVisibilityChanges: z.boolean().optional(),
32
+ hitlRequestId: z.string().min(1).optional(),
21
33
  });
22
34
 
23
35
  export const POST: APIRoute = async ({ params, request, locals }) => {
@@ -37,6 +49,68 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
37
49
 
38
50
  const body = await parseOptionalBody(request, updateBodySchema, {});
39
51
  if (isParseError(body)) return body;
52
+ const actor = resolveHitlRouteActor(locals);
53
+ const evaluator = new RiskPolicyEvaluator({
54
+ db: dineway.db,
55
+ handlers: dineway,
56
+ });
57
+ let approvedHitlRequestId: string | null = null;
58
+ let previewDiffs: { hasCapabilityChanges: boolean; hasNewPublicRoutes: boolean } | null = null;
59
+
60
+ if (evaluator.requiresWorkflowHitl(actor.identity)) {
61
+ if (!body.version) {
62
+ return apiError(
63
+ "VALIDATION_ERROR",
64
+ "API-token marketplace updates require an explicit version so the reviewed target is immutable.",
65
+ 400,
66
+ );
67
+ }
68
+
69
+ const preview = await handleMarketplaceUpdatePreview(
70
+ dineway.db,
71
+ dineway.storage,
72
+ dineway.config.marketplace,
73
+ id,
74
+ { version: body.version },
75
+ );
76
+ if (!preview.success) return unwrapResult(preview);
77
+
78
+ const action = await new PluginHitlPayloadBuilder().buildMarketplaceUpdateRequest({
79
+ id: preview.data.pluginId,
80
+ name: preview.data.name,
81
+ description: preview.data.description,
82
+ authorName: preview.data.authorName,
83
+ version: preview.data.version,
84
+ minDinewayVersion: preview.data.minDinewayVersion,
85
+ bundleSize: preview.data.bundleSize,
86
+ checksum: preview.data.checksum,
87
+ changelog: preview.data.changelog,
88
+ capabilities: preview.data.capabilities,
89
+ status: preview.data.status,
90
+ auditVerdict: preview.data.auditVerdict,
91
+ imageAuditVerdict: preview.data.imageAuditVerdict,
92
+ publishedAt: preview.data.publishedAt,
93
+ oldVersion: preview.data.oldVersion,
94
+ capabilityChanges: preview.data.capabilityChanges,
95
+ routeVisibilityChanges: preview.data.routeVisibilityChanges,
96
+ });
97
+ const decision = await evaluator.evaluateWorkflowHitl({
98
+ actor: actor.identity,
99
+ hitlRequestId: body.hitlRequestId,
100
+ action,
101
+ });
102
+ if (!decision.allowed) {
103
+ const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
104
+ return hitlRequiredRouteError(decision, ensured);
105
+ }
106
+ approvedHitlRequestId = decision.hitlRequest.id;
107
+ previewDiffs = {
108
+ hasCapabilityChanges:
109
+ preview.data.capabilityChanges.added.length > 0 ||
110
+ preview.data.capabilityChanges.removed.length > 0,
111
+ hasNewPublicRoutes: preview.data.routeVisibilityChanges.newlyPublic.length > 0,
112
+ };
113
+ }
40
114
 
41
115
  const result = await handleMarketplaceUpdate(
42
116
  dineway.db,
@@ -46,14 +120,35 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
46
120
  id,
47
121
  {
48
122
  version: body.version,
49
- confirmCapabilityChanges: body.confirmCapabilityChanges,
50
- confirmRouteVisibilityChanges: body.confirmRouteVisibilityChanges,
123
+ confirmCapabilityChanges: previewDiffs
124
+ ? previewDiffs.hasCapabilityChanges || body.confirmCapabilityChanges === true
125
+ : body.confirmCapabilityChanges,
126
+ confirmRouteVisibilityChanges: previewDiffs
127
+ ? previewDiffs.hasNewPublicRoutes || body.confirmRouteVisibilityChanges === true
128
+ : body.confirmRouteVisibilityChanges,
51
129
  },
52
130
  );
53
131
 
54
132
  if (!result.success) return unwrapResult(result);
55
133
 
56
134
  await dineway.syncMarketplacePlugins();
135
+ await logPluginActivity(dineway.db, locals, {
136
+ action: "updated",
137
+ pluginId: id,
138
+ ...pluginApiRouteSource("updated"),
139
+ summary: `Updated plugin ${id}`,
140
+ detail: {
141
+ version: result.data.newVersion,
142
+ oldVersion: result.data.oldVersion,
143
+ confirmedCapabilityChanges: previewDiffs
144
+ ? previewDiffs.hasCapabilityChanges
145
+ : (body.confirmCapabilityChanges ?? false),
146
+ confirmedRouteVisibilityChanges: previewDiffs
147
+ ? previewDiffs.hasNewPublicRoutes
148
+ : (body.confirmRouteVisibilityChanges ?? false),
149
+ hitlRequestId: approvedHitlRequestId,
150
+ },
151
+ });
57
152
 
58
153
  return unwrapResult(result);
59
154
  };
@@ -9,13 +9,25 @@ import { z } from "zod";
9
9
 
10
10
  import { requirePerm } from "#api/authorize.js";
11
11
  import { apiError, handleError, unwrapResult } from "#api/error.js";
12
- import { handleMarketplaceInstall } from "#api/index.js";
12
+ import {
13
+ ensureWorkflowHitlRouteRequest,
14
+ hitlRequiredRouteError,
15
+ resolveHitlRouteActor,
16
+ } from "#api/hitl-route-helpers.js";
17
+ import { handleMarketplaceInstall, handleMarketplaceInstallPreview } from "#api/index.js";
13
18
  import { isParseError, parseOptionalBody } from "#api/parse.js";
19
+ import {
20
+ logPluginActivity,
21
+ pluginApiRouteSource,
22
+ PluginHitlPayloadBuilder,
23
+ RiskPolicyEvaluator,
24
+ } from "#site-context/index.js";
14
25
 
15
26
  export const prerender = false;
16
27
 
17
28
  const installBodySchema = z.object({
18
29
  version: z.string().min(1).optional(),
30
+ hitlRequestId: z.string().min(1).optional(),
19
31
  });
20
32
 
21
33
  export const POST: APIRoute = async ({ params, request, locals }) => {
@@ -36,6 +48,55 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
36
48
 
37
49
  const body = await parseOptionalBody(request, installBodySchema, {});
38
50
  if (isParseError(body)) return body;
51
+ const actor = resolveHitlRouteActor(locals);
52
+ const evaluator = new RiskPolicyEvaluator({
53
+ db: dineway.db,
54
+ handlers: dineway,
55
+ });
56
+ let approvedHitlRequestId: string | null = null;
57
+
58
+ if (evaluator.requiresWorkflowHitl(actor.identity)) {
59
+ if (!body.version) {
60
+ return apiError(
61
+ "VALIDATION_ERROR",
62
+ "API-token marketplace installs require an explicit version so the reviewed target is immutable.",
63
+ 400,
64
+ );
65
+ }
66
+
67
+ const preview = await handleMarketplaceInstallPreview(dineway.config.marketplace, id, {
68
+ version: body.version,
69
+ siteOrigin: new URL(request.url).origin,
70
+ });
71
+ if (!preview.success) return unwrapResult(preview);
72
+
73
+ const action = await new PluginHitlPayloadBuilder().buildInstallRequest({
74
+ id: preview.data.pluginId,
75
+ name: preview.data.name,
76
+ description: preview.data.description,
77
+ authorName: preview.data.authorName,
78
+ version: preview.data.version,
79
+ minDinewayVersion: preview.data.minDinewayVersion,
80
+ bundleSize: preview.data.bundleSize,
81
+ checksum: preview.data.checksum,
82
+ changelog: preview.data.changelog,
83
+ capabilities: preview.data.capabilities,
84
+ status: preview.data.status,
85
+ auditVerdict: preview.data.auditVerdict,
86
+ imageAuditVerdict: preview.data.imageAuditVerdict,
87
+ publishedAt: preview.data.publishedAt,
88
+ });
89
+ const decision = await evaluator.evaluateWorkflowHitl({
90
+ actor: actor.identity,
91
+ hitlRequestId: body.hitlRequestId,
92
+ action,
93
+ });
94
+ if (!decision.allowed) {
95
+ const ensured = await ensureWorkflowHitlRouteRequest(dineway.db, locals, decision.action);
96
+ return hitlRequiredRouteError(decision, ensured);
97
+ }
98
+ approvedHitlRequestId = decision.hitlRequest.id;
99
+ }
39
100
 
40
101
  const configuredPluginIds = new Set<string>(
41
102
  dineway.configuredPlugins.map((p: { id: string }) => p.id),
@@ -55,6 +116,16 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
55
116
  if (!result.success) return unwrapResult(result);
56
117
 
57
118
  await dineway.syncMarketplacePlugins();
119
+ await logPluginActivity(dineway.db, locals, {
120
+ action: "installed",
121
+ pluginId: id,
122
+ ...pluginApiRouteSource("installed"),
123
+ summary: `Installed plugin ${id}`,
124
+ detail: {
125
+ version: result.data.version,
126
+ hitlRequestId: approvedHitlRequestId,
127
+ },
128
+ });
58
129
 
59
130
  return unwrapResult(result, 201);
60
131
  } catch (error) {
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Admin review request detail
3
+ *
4
+ * GET /_dineway/api/admin/review-requests/:id - Get a review request
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { requirePerm } from "#api/authorize.js";
10
+ import { apiError, handleError, requireDb, unwrapResult } from "#api/error.js";
11
+ import { handleReviewRequestGet } from "#api/handlers/review-requests.js";
12
+
13
+ export const prerender = false;
14
+
15
+ export const GET: APIRoute = async ({ params, locals }) => {
16
+ const { dineway, user } = locals;
17
+ const { id } = params;
18
+
19
+ if (!id) {
20
+ return apiError("VALIDATION_ERROR", "Review request ID required", 400);
21
+ }
22
+
23
+ const dbErr = requireDb(dineway?.db);
24
+ if (dbErr) return dbErr;
25
+
26
+ const denied = requirePerm(user, "content:edit_any");
27
+ if (denied) return denied;
28
+
29
+ try {
30
+ const result = await handleReviewRequestGet(dineway.db, id);
31
+ return unwrapResult(result);
32
+ } catch (error) {
33
+ return handleError(error, "Failed to fetch review request", "REVIEW_REQUEST_GET_ERROR");
34
+ }
35
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Admin review request resolution
3
+ *
4
+ * POST /_dineway/api/admin/review-requests/:id/resolve - Approve or reject a review request
5
+ */
6
+
7
+ import type { APIRoute } from "astro";
8
+
9
+ import { requirePerm } from "#api/authorize.js";
10
+ import { apiError, handleError, requireDb, unwrapResult } from "#api/error.js";
11
+ import { handleReviewRequestResolve } from "#api/handlers/review-requests.js";
12
+ import { isParseError, parseBody } from "#api/parse.js";
13
+ import { logReviewRouteActivity, resolveReviewRouteActor } from "#api/review-route-helpers.js";
14
+ import { reviewRequestResolveBody } from "#api/schemas.js";
15
+
16
+ export const prerender = false;
17
+
18
+ export const POST: APIRoute = async ({ params, request, locals }) => {
19
+ const { dineway, user } = locals;
20
+ const { id } = params;
21
+
22
+ if (!id) {
23
+ return apiError("VALIDATION_ERROR", "Review request ID required", 400);
24
+ }
25
+
26
+ const dbErr = requireDb(dineway?.db);
27
+ if (dbErr) return dbErr;
28
+
29
+ const denied = requirePerm(user, "content:edit_any");
30
+ if (denied) return denied;
31
+
32
+ try {
33
+ const body = await parseBody(request, reviewRequestResolveBody);
34
+ if (isParseError(body)) return body;
35
+
36
+ const result = await handleReviewRequestResolve(dineway.db, id, {
37
+ decision: body.decision,
38
+ reviewNote: body.reviewNote,
39
+ actor: resolveReviewRouteActor(locals),
40
+ });
41
+ if (!result.success) return unwrapResult(result);
42
+
43
+ await logReviewRouteActivity(dineway.db, locals, {
44
+ actionType: body.decision === "approved" ? "review.approved" : "review.rejected",
45
+ item: result.data.item,
46
+ });
47
+
48
+ return unwrapResult(result);
49
+ } catch (error) {
50
+ return handleError(error, "Failed to resolve review request", "REVIEW_REQUEST_RESOLVE_ERROR");
51
+ }
52
+ };