@setzkasten-cms/astro-admin 1.4.6 → 1.5.0

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 (166) hide show
  1. package/dist/api-routes/_auth-guard.d.ts +27 -3
  2. package/dist/api-routes/_auth-guard.js +5 -2
  3. package/dist/api-routes/_dev-session-secret.d.ts +8 -0
  4. package/dist/api-routes/_dev-session-secret.js +8 -0
  5. package/dist/api-routes/_github-token.js +1 -1
  6. package/dist/api-routes/_role-resolver.js +6 -3
  7. package/dist/api-routes/_session-secret.d.ts +19 -0
  8. package/dist/api-routes/_session-secret.js +7 -0
  9. package/dist/api-routes/_session-signing.d.ts +45 -0
  10. package/dist/api-routes/_session-signing.js +8 -0
  11. package/dist/api-routes/_webhook-dispatcher.js +4 -4
  12. package/dist/api-routes/asset-proxy.js +1 -1
  13. package/dist/api-routes/auth-callback.js +12 -5
  14. package/dist/api-routes/auth-logout.d.ts +4 -4
  15. package/dist/api-routes/auth-logout.js +8 -2
  16. package/dist/api-routes/auth-session.d.ts +6 -0
  17. package/dist/api-routes/auth-session.js +19 -19
  18. package/dist/api-routes/auth-setzkasten-login.js +14 -7
  19. package/dist/api-routes/catalog-add.js +59 -17
  20. package/dist/api-routes/catalog-export.js +14 -4
  21. package/dist/api-routes/config.d.ts +10 -3
  22. package/dist/api-routes/config.js +26 -4
  23. package/dist/api-routes/deploy-hook.js +8 -8
  24. package/dist/api-routes/editors.d.ts +1 -1
  25. package/dist/api-routes/editors.js +5 -2
  26. package/dist/api-routes/github-proxy.js +30 -8
  27. package/dist/api-routes/global-config.js +6 -3
  28. package/dist/api-routes/history-rollback.js +31 -14
  29. package/dist/api-routes/history-version.js +8 -6
  30. package/dist/api-routes/history.js +5 -2
  31. package/dist/api-routes/icons-local.js +1 -1
  32. package/dist/api-routes/init-add-section.js +113 -47
  33. package/dist/api-routes/init-apply.js +56 -42
  34. package/dist/api-routes/init-migrate.js +43 -36
  35. package/dist/api-routes/init-scan-page.d.ts +1 -1
  36. package/dist/api-routes/init-scan-page.js +59 -13
  37. package/dist/api-routes/init-scan.js +22 -7
  38. package/dist/api-routes/migrate-to-multi.js +5 -2
  39. package/dist/api-routes/pages.js +15 -4
  40. package/dist/api-routes/section-add.js +68 -16
  41. package/dist/api-routes/section-commit-pending.js +70 -22
  42. package/dist/api-routes/section-delete.js +49 -14
  43. package/dist/api-routes/section-duplicate.js +65 -16
  44. package/dist/api-routes/section-prepare-copy.js +15 -2
  45. package/dist/api-routes/section-prepare.js +25 -4
  46. package/dist/api-routes/setup-github-app-bounce.js +15 -1
  47. package/dist/api-routes/setup-github-app-branches.js +9 -6
  48. package/dist/api-routes/setup-github-app-callback.js +24 -1
  49. package/dist/api-routes/setup-github-app-credentials.d.ts +27 -0
  50. package/dist/api-routes/setup-github-app-credentials.js +43 -0
  51. package/dist/api-routes/setup-github-app-installed.js +22 -1
  52. package/dist/api-routes/setup-github-app-repos.js +5 -2
  53. package/dist/api-routes/setup-github-app.d.ts +4 -0
  54. package/dist/api-routes/setup-github-app.js +19 -2
  55. package/dist/api-routes/updater-register.js +7 -1
  56. package/dist/api-routes/webhooks-status.js +5 -2
  57. package/dist/api-routes/webhooks-test.js +9 -8
  58. package/dist/api-routes/webhooks.js +12 -14
  59. package/dist/api-routes/websites-add.js +5 -2
  60. package/dist/api-routes/websites-remove.js +5 -2
  61. package/dist/{chunk-ZQDGGWJP.js → chunk-5KMGSFCZ.js} +2 -2
  62. package/dist/{chunk-Q3N336KR.js → chunk-CDXCYYQR.js} +29 -24
  63. package/dist/{chunk-NKDATSPA.js → chunk-DP6RTINQ.js} +1 -1
  64. package/dist/chunk-KENFINT4.js +76 -0
  65. package/dist/chunk-ONP6BRZO.js +47 -0
  66. package/dist/{chunk-INIWFKQ3.js → chunk-Q5HV47DW.js} +33 -19
  67. package/dist/chunk-QVCW6EF3.js +26 -0
  68. package/dist/{chunk-TD76R3A6.js → chunk-UHI6323G.js} +293 -174
  69. package/dist/{chunk-AM4DZXXM.js → chunk-UJAFZEX2.js} +76 -9
  70. package/package.json +12 -6
  71. package/src/api-routes/__tests__/_session-signing.test.ts +114 -0
  72. package/src/api-routes/__tests__/_session-test-helper.ts +27 -0
  73. package/src/api-routes/__tests__/add-section-helpers.test.ts +59 -25
  74. package/src/api-routes/__tests__/auth-guard.test.ts +46 -7
  75. package/src/api-routes/__tests__/catalog-api.test.ts +14 -6
  76. package/src/api-routes/__tests__/commit-trailers.test.ts +5 -5
  77. package/src/api-routes/__tests__/deferred-operations.test.ts +9 -12
  78. package/src/api-routes/__tests__/deploy-hook.test.ts +3 -8
  79. package/src/api-routes/__tests__/feature-gate.test.ts +1 -1
  80. package/src/api-routes/__tests__/github-cache.test.ts +1 -1
  81. package/src/api-routes/__tests__/github-token.test.ts +1 -1
  82. package/src/api-routes/__tests__/global-config-theme.test.ts +4 -4
  83. package/src/api-routes/__tests__/history-rollback.test.ts +6 -3
  84. package/src/api-routes/__tests__/history.test.ts +9 -6
  85. package/src/api-routes/__tests__/init-scan-page-resolve-config.test.ts +11 -3
  86. package/src/api-routes/__tests__/migrate-to-multi.test.ts +5 -1
  87. package/src/api-routes/__tests__/pages-meta-store.test.ts +10 -5
  88. package/src/api-routes/__tests__/pages.test.ts +7 -2
  89. package/src/api-routes/__tests__/patch-page-file.test.ts +71 -19
  90. package/src/api-routes/__tests__/route-registry.test.ts +11 -18
  91. package/src/api-routes/__tests__/scan-page-helpers.test.ts +13 -10
  92. package/src/api-routes/__tests__/section-management.test.ts +28 -28
  93. package/src/api-routes/__tests__/setup-github-app-callback.test.ts +58 -16
  94. package/src/api-routes/__tests__/setup-github-app-repos.test.ts +4 -5
  95. package/src/api-routes/__tests__/setup-github-app.test.ts +27 -7
  96. package/src/api-routes/__tests__/storage-config-for-request.test.ts +83 -0
  97. package/src/api-routes/__tests__/updater-register.test.ts +230 -0
  98. package/src/api-routes/__tests__/webhook-signing.test.ts +1 -1
  99. package/src/api-routes/__tests__/webhooks.test.ts +19 -7
  100. package/src/api-routes/__tests__/websites-add.test.ts +2 -1
  101. package/src/api-routes/__tests__/websites-remove.test.ts +2 -1
  102. package/src/api-routes/_auth-guard.ts +47 -15
  103. package/src/api-routes/_commit-trailers.ts +3 -2
  104. package/src/api-routes/_dev-session-secret.ts +79 -0
  105. package/src/api-routes/_github-token.ts +1 -1
  106. package/src/api-routes/_pages-meta-store.ts +2 -2
  107. package/src/api-routes/_role-resolver.ts +7 -5
  108. package/src/api-routes/_session-secret.ts +46 -0
  109. package/src/api-routes/_session-signing.ts +135 -0
  110. package/src/api-routes/_vercel-origin.ts +2 -6
  111. package/src/api-routes/_webhook-dispatcher.ts +12 -16
  112. package/src/api-routes/_website-resolver.ts +3 -10
  113. package/src/api-routes/auth-callback.ts +9 -5
  114. package/src/api-routes/auth-login.ts +5 -3
  115. package/src/api-routes/auth-logout.ts +18 -1
  116. package/src/api-routes/auth-session.ts +13 -21
  117. package/src/api-routes/auth-setzkasten-login.ts +12 -9
  118. package/src/api-routes/catalog-add.ts +89 -31
  119. package/src/api-routes/catalog-export.ts +30 -10
  120. package/src/api-routes/config.ts +39 -6
  121. package/src/api-routes/deploy-hook.ts +13 -11
  122. package/src/api-routes/editors.ts +33 -22
  123. package/src/api-routes/github-proxy.ts +25 -11
  124. package/src/api-routes/global-config.ts +103 -18
  125. package/src/api-routes/history-rollback.ts +41 -14
  126. package/src/api-routes/history-version.ts +5 -6
  127. package/src/api-routes/history.ts +3 -3
  128. package/src/api-routes/icons-local.ts +2 -2
  129. package/src/api-routes/init-add-section.ts +174 -79
  130. package/src/api-routes/init-apply.ts +71 -56
  131. package/src/api-routes/init-migrate.ts +54 -48
  132. package/src/api-routes/init-scan-page.ts +77 -30
  133. package/src/api-routes/init-scan.ts +19 -11
  134. package/src/api-routes/pages.ts +16 -11
  135. package/src/api-routes/section-add.ts +98 -27
  136. package/src/api-routes/section-commit-pending.ts +87 -34
  137. package/src/api-routes/section-delete.ts +76 -27
  138. package/src/api-routes/section-duplicate.ts +95 -28
  139. package/src/api-routes/section-management.ts +3 -7
  140. package/src/api-routes/section-prepare-copy.ts +29 -8
  141. package/src/api-routes/section-prepare.ts +38 -10
  142. package/src/api-routes/setup-github-app-bounce.ts +7 -1
  143. package/src/api-routes/setup-github-app-branches.ts +6 -7
  144. package/src/api-routes/setup-github-app-callback.ts +18 -1
  145. package/src/api-routes/setup-github-app-credentials.ts +55 -0
  146. package/src/api-routes/setup-github-app-installed.ts +12 -1
  147. package/src/api-routes/setup-github-app-repos.ts +2 -3
  148. package/src/api-routes/setup-github-app.ts +14 -5
  149. package/src/api-routes/updater-check.ts +6 -4
  150. package/src/api-routes/updater-register.ts +34 -20
  151. package/src/api-routes/updater-transfer.ts +8 -6
  152. package/src/api-routes/updater-unbind.ts +14 -10
  153. package/src/api-routes/webhooks-test.ts +9 -11
  154. package/src/api-routes/webhooks.ts +15 -19
  155. package/src/init/__tests__/page-level.test.ts +279 -105
  156. package/src/init/__tests__/page-list-coverage.test.ts +70 -70
  157. package/src/init/__tests__/patcher-child-component.test.ts +12 -3
  158. package/src/init/__tests__/patcher-edge-cases.test.ts +47 -23
  159. package/src/init/__tests__/patcher-mixed-content-wrapper.test.ts +16 -6
  160. package/src/init/__tests__/patcher-page-mode.test.ts +30 -20
  161. package/src/init/__tests__/section-pipeline.test.ts +53 -19
  162. package/src/init/astro-config-patcher.ts +4 -18
  163. package/src/init/astro-detector.ts +2 -7
  164. package/src/init/astro-section-analyzer-v2.ts +475 -193
  165. package/src/init/field-label-enricher.ts +6 -6
  166. package/src/init/template-patcher-v2.ts +218 -97
@@ -0,0 +1,43 @@
1
+ import {
2
+ requireAdmin
3
+ } from "../chunk-Q5HV47DW.js";
4
+ import "../chunk-QVCW6EF3.js";
5
+ import "../chunk-KENFINT4.js";
6
+ import "../chunk-6UIKVKED.js";
7
+ import "../chunk-ONP6BRZO.js";
8
+ import "../chunk-5PIMDP4N.js";
9
+ import "../chunk-45ARVNT3.js";
10
+ import "../chunk-DP6RTINQ.js";
11
+ import "../chunk-TJNJKPUL.js";
12
+ import "../chunk-KH22FJO5.js";
13
+
14
+ // src/api-routes/setup-github-app-credentials.ts
15
+ var COOKIE_NAME = "sk_app_setup";
16
+ var GET = async ({ cookies }) => {
17
+ const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
18
+ if (denied) return denied;
19
+ const raw = cookies.get(COOKIE_NAME)?.value;
20
+ if (!raw) {
21
+ return Response.json({ available: false }, { status: 404 });
22
+ }
23
+ let parsed;
24
+ try {
25
+ parsed = JSON.parse(raw);
26
+ } catch {
27
+ return Response.json({ available: false, error: "malformed" }, { status: 500 });
28
+ }
29
+ if (!parsed || typeof parsed !== "object") {
30
+ return Response.json({ available: false }, { status: 404 });
31
+ }
32
+ return Response.json({ available: true, credentials: parsed });
33
+ };
34
+ var DELETE = async ({ cookies }) => {
35
+ const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
36
+ if (denied) return denied;
37
+ cookies.delete(COOKIE_NAME, { path: "/" });
38
+ return Response.json({ ok: true });
39
+ };
40
+ export {
41
+ DELETE,
42
+ GET
43
+ };
@@ -1,11 +1,25 @@
1
1
  import {
2
2
  getPublicOrigin
3
3
  } from "../chunk-5ZFTG4BW.js";
4
+ import {
5
+ requireAdmin
6
+ } from "../chunk-Q5HV47DW.js";
7
+ import "../chunk-QVCW6EF3.js";
8
+ import "../chunk-KENFINT4.js";
9
+ import "../chunk-6UIKVKED.js";
10
+ import "../chunk-ONP6BRZO.js";
11
+ import "../chunk-5PIMDP4N.js";
12
+ import "../chunk-45ARVNT3.js";
13
+ import "../chunk-DP6RTINQ.js";
14
+ import "../chunk-TJNJKPUL.js";
15
+ import "../chunk-KH22FJO5.js";
4
16
 
5
17
  // src/api-routes/setup-github-app-installed.ts
6
18
  var COOKIE_NAME = "sk_app_setup";
7
19
  var COOKIE_MAX_AGE = 600;
8
20
  var GET = async ({ url, request, cookies }) => {
21
+ const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
22
+ if (denied) return denied;
9
23
  const config = globalThis.__SETZKASTEN_CONFIG__;
10
24
  const adminPath = config?.adminPath ?? "/admin";
11
25
  const adminUrl = new URL(adminPath, getPublicOrigin(request));
@@ -20,7 +34,14 @@ var GET = async ({ url, request, cookies }) => {
20
34
  cookies.set(
21
35
  COOKIE_NAME,
22
36
  JSON.stringify({ ...data, installationId }),
23
- { httpOnly: false, sameSite: "lax", maxAge: COOKIE_MAX_AGE, path: "/" }
37
+ // C6: same httpOnly hardening as the callback route.
38
+ {
39
+ httpOnly: true,
40
+ secure: import.meta.env.PROD,
41
+ sameSite: "lax",
42
+ maxAge: COOKIE_MAX_AGE,
43
+ path: "/"
44
+ }
24
45
  );
25
46
  } catch {
26
47
  adminUrl.searchParams.set("github-app-error", "invalid_session");
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  requireAdmin
3
- } from "../chunk-INIWFKQ3.js";
3
+ } from "../chunk-Q5HV47DW.js";
4
+ import "../chunk-QVCW6EF3.js";
5
+ import "../chunk-KENFINT4.js";
4
6
  import "../chunk-6UIKVKED.js";
7
+ import "../chunk-ONP6BRZO.js";
5
8
  import "../chunk-5PIMDP4N.js";
6
9
  import "../chunk-45ARVNT3.js";
7
- import "../chunk-NKDATSPA.js";
10
+ import "../chunk-DP6RTINQ.js";
8
11
  import "../chunk-TJNJKPUL.js";
9
12
  import "../chunk-KH22FJO5.js";
10
13
 
@@ -8,6 +8,10 @@ import { APIRoute } from 'astro';
8
8
  *
9
9
  * Credentials werden NICHT persistiert – der Nutzer setzt die env vars manuell.
10
10
  * Der POST-Endpunkt validiert die Verbindung durch einen echten Token-Request.
11
+ *
12
+ * Both methods are admin-only. Pre-fix the GET let any unauthenticated
13
+ * visitor probe whether a deployment was configured (reconnaissance for
14
+ * targeting half-set-up instances).
11
15
  */
12
16
  declare const GET: APIRoute;
13
17
  declare const POST: APIRoute;
@@ -1,13 +1,30 @@
1
+ import {
2
+ requireAdmin
3
+ } from "../chunk-Q5HV47DW.js";
4
+ import "../chunk-QVCW6EF3.js";
5
+ import "../chunk-KENFINT4.js";
6
+ import "../chunk-6UIKVKED.js";
7
+ import "../chunk-ONP6BRZO.js";
8
+ import "../chunk-5PIMDP4N.js";
9
+ import "../chunk-45ARVNT3.js";
10
+ import "../chunk-DP6RTINQ.js";
11
+ import "../chunk-TJNJKPUL.js";
12
+ import "../chunk-KH22FJO5.js";
13
+
1
14
  // src/api-routes/setup-github-app.ts
2
15
  import { GitHubAppClient } from "@setzkasten-cms/github-adapter";
3
- var GET = async () => {
16
+ var GET = async ({ cookies }) => {
17
+ const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
18
+ if (denied) return denied;
4
19
  const appId = process.env.GITHUB_APP_ID;
5
20
  const privateKey = process.env.GITHUB_APP_PRIVATE_KEY;
6
21
  const installationId = process.env.GITHUB_APP_INSTALLATION_ID;
7
22
  const configured = Boolean(appId && privateKey && installationId);
8
23
  return Response.json({ configured, ...configured ? { appId } : {} });
9
24
  };
10
- var POST = async ({ request }) => {
25
+ var POST = async ({ request, cookies }) => {
26
+ const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
27
+ if (denied) return denied;
11
28
  let body;
12
29
  try {
13
30
  body = await request.json();
@@ -1,3 +1,7 @@
1
+ import {
2
+ listAllWebsites
3
+ } from "../chunk-V6IMPVF3.js";
4
+
1
5
  // src/api-routes/updater-register.ts
2
6
  var POST = async ({ cookies, request }) => {
3
7
  const session = cookies.get("setzkasten_session")?.value;
@@ -34,6 +38,8 @@ var POST = async ({ cookies, request }) => {
34
38
  }
35
39
  } catch {
36
40
  }
41
+ const websitesResult = await listAllWebsites();
42
+ const managedWebsites = websitesResult.ok ? websitesResult.value.map((w) => w.id) : [];
37
43
  try {
38
44
  const response = await fetch(`${updaterUrl}/api/register`, {
39
45
  method: "POST",
@@ -45,7 +51,7 @@ var POST = async ({ cookies, request }) => {
45
51
  licenseEmail,
46
52
  licenseKey,
47
53
  telemetryEnabled: true,
48
- managedWebsites: []
54
+ managedWebsites
49
55
  }),
50
56
  signal: AbortSignal.timeout(5e3)
51
57
  });
@@ -3,11 +3,14 @@ import {
3
3
  } from "../chunk-W3QHY5GW.js";
4
4
  import {
5
5
  requireAdmin
6
- } from "../chunk-INIWFKQ3.js";
6
+ } from "../chunk-Q5HV47DW.js";
7
+ import "../chunk-QVCW6EF3.js";
8
+ import "../chunk-KENFINT4.js";
7
9
  import "../chunk-6UIKVKED.js";
10
+ import "../chunk-ONP6BRZO.js";
8
11
  import "../chunk-5PIMDP4N.js";
9
12
  import "../chunk-45ARVNT3.js";
10
- import "../chunk-NKDATSPA.js";
13
+ import "../chunk-DP6RTINQ.js";
11
14
  import "../chunk-TJNJKPUL.js";
12
15
  import "../chunk-KH22FJO5.js";
13
16
 
@@ -1,30 +1,31 @@
1
- import {
2
- signPayload
3
- } from "../chunk-737TIZRU.js";
4
1
  import {
5
2
  recordWebhookFire
6
3
  } from "../chunk-W3QHY5GW.js";
4
+ import {
5
+ signPayload
6
+ } from "../chunk-737TIZRU.js";
7
7
  import {
8
8
  parseSession,
9
9
  requireAdmin
10
- } from "../chunk-INIWFKQ3.js";
10
+ } from "../chunk-Q5HV47DW.js";
11
+ import "../chunk-QVCW6EF3.js";
12
+ import "../chunk-KENFINT4.js";
11
13
  import {
12
14
  resolveStorageConfigForRequest
13
15
  } from "../chunk-6UIKVKED.js";
16
+ import "../chunk-ONP6BRZO.js";
14
17
  import {
15
18
  gateFeature
16
19
  } from "../chunk-5PIMDP4N.js";
17
20
  import "../chunk-45ARVNT3.js";
18
21
  import {
19
22
  resolveGitHubTokenForRequest
20
- } from "../chunk-NKDATSPA.js";
23
+ } from "../chunk-DP6RTINQ.js";
21
24
  import "../chunk-TJNJKPUL.js";
22
25
  import "../chunk-KH22FJO5.js";
23
26
 
24
27
  // src/api-routes/webhooks-test.ts
25
- import {
26
- parseWebhooksFile
27
- } from "@setzkasten-cms/core";
28
+ import { parseWebhooksFile } from "@setzkasten-cms/core";
28
29
  var WEBHOOKS_FILE = (contentPath) => `${contentPath}/_webhooks.json`;
29
30
  var TEST_TIMEOUT_MS = 1e4;
30
31
  var POST = async ({ request, cookies }) => {
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  parseSession,
3
3
  requireAdmin
4
- } from "../chunk-INIWFKQ3.js";
4
+ } from "../chunk-Q5HV47DW.js";
5
+ import "../chunk-QVCW6EF3.js";
6
+ import "../chunk-KENFINT4.js";
5
7
  import {
6
8
  resolveStorageConfigForRequest
7
9
  } from "../chunk-6UIKVKED.js";
10
+ import "../chunk-ONP6BRZO.js";
8
11
  import {
9
12
  gateFeature
10
13
  } from "../chunk-5PIMDP4N.js";
@@ -14,17 +17,14 @@ import {
14
17
  } from "../chunk-45ARVNT3.js";
15
18
  import {
16
19
  resolveGitHubTokenForRequest
17
- } from "../chunk-NKDATSPA.js";
20
+ } from "../chunk-DP6RTINQ.js";
18
21
  import "../chunk-TJNJKPUL.js";
19
22
  import {
20
23
  withTrailers
21
24
  } from "../chunk-KH22FJO5.js";
22
25
 
23
26
  // src/api-routes/webhooks.ts
24
- import {
25
- parseWebhooksFile,
26
- validateWebhookConfig
27
- } from "@setzkasten-cms/core";
27
+ import { parseWebhooksFile, validateWebhookConfig } from "@setzkasten-cms/core";
28
28
  var WEBHOOKS_FILE = (contentPath) => `${contentPath}/_webhooks.json`;
29
29
  var GET = async ({ request, cookies }) => {
30
30
  const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
@@ -82,10 +82,7 @@ var PUT = async ({ request, cookies }) => {
82
82
  for (let i = 0; i < body.webhooks.length; i++) {
83
83
  const result = validateWebhookConfig(body.webhooks[i]);
84
84
  if (!result.ok) {
85
- return Response.json(
86
- { error: result.error.message, index: i },
87
- { status: 400 }
88
- );
85
+ return Response.json({ error: result.error.message, index: i }, { status: 400 });
89
86
  }
90
87
  if (seenIds.has(result.value.id)) {
91
88
  return Response.json(
@@ -127,10 +124,11 @@ var PUT = async ({ request, cookies }) => {
127
124
  branch
128
125
  };
129
126
  if (currentSha) putBody.sha = currentSha;
130
- const putRes = await fetch(
131
- `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`,
132
- { method: "PUT", headers, body: JSON.stringify(putBody) }
133
- );
127
+ const putRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`, {
128
+ method: "PUT",
129
+ headers,
130
+ body: JSON.stringify(putBody)
131
+ });
134
132
  if (!putRes.ok) {
135
133
  const text = await putRes.text();
136
134
  return Response.json({ error: `Webhook write failed: ${text}` }, { status: 502 });
@@ -5,13 +5,16 @@ import {
5
5
  } from "../chunk-35S35OIV.js";
6
6
  import {
7
7
  requireAdmin
8
- } from "../chunk-INIWFKQ3.js";
8
+ } from "../chunk-Q5HV47DW.js";
9
+ import "../chunk-QVCW6EF3.js";
10
+ import "../chunk-KENFINT4.js";
9
11
  import "../chunk-6UIKVKED.js";
12
+ import "../chunk-ONP6BRZO.js";
10
13
  import "../chunk-5PIMDP4N.js";
11
14
  import "../chunk-45ARVNT3.js";
12
15
  import {
13
16
  resolveConfigRepoToken
14
- } from "../chunk-NKDATSPA.js";
17
+ } from "../chunk-DP6RTINQ.js";
15
18
  import {
16
19
  resolveLicenseTier
17
20
  } from "../chunk-TJNJKPUL.js";
@@ -5,13 +5,16 @@ import {
5
5
  } from "../chunk-35S35OIV.js";
6
6
  import {
7
7
  requireAdmin
8
- } from "../chunk-INIWFKQ3.js";
8
+ } from "../chunk-Q5HV47DW.js";
9
+ import "../chunk-QVCW6EF3.js";
10
+ import "../chunk-KENFINT4.js";
9
11
  import "../chunk-6UIKVKED.js";
12
+ import "../chunk-ONP6BRZO.js";
10
13
  import "../chunk-5PIMDP4N.js";
11
14
  import "../chunk-45ARVNT3.js";
12
15
  import {
13
16
  resolveConfigRepoToken
14
- } from "../chunk-NKDATSPA.js";
17
+ } from "../chunk-DP6RTINQ.js";
15
18
  import "../chunk-TJNJKPUL.js";
16
19
  import "../chunk-KH22FJO5.js";
17
20
 
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  readEditorsFileStatus
3
- } from "./chunk-INIWFKQ3.js";
3
+ } from "./chunk-Q5HV47DW.js";
4
4
  import {
5
5
  resolveStorageConfig
6
6
  } from "./chunk-6UIKVKED.js";
7
7
  import {
8
8
  resolveConfigRepoToken
9
- } from "./chunk-NKDATSPA.js";
9
+ } from "./chunk-DP6RTINQ.js";
10
10
 
11
11
  // src/api-routes/_role-resolver.ts
12
12
  import { resolveRoleForUser } from "@setzkasten-cms/core";
@@ -9,7 +9,8 @@ function findNormalizedText(haystack, needle, startFrom) {
9
9
  const escaped = normalized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/ /g, "\\s+");
10
10
  const regex = new RegExp(escaped);
11
11
  const match = regex.exec(haystack.slice(startFrom));
12
- if (match) return { start: startFrom + match.index, end: startFrom + match.index + match[0].length };
12
+ if (match)
13
+ return { start: startFrom + match.index, end: startFrom + match.index + match[0].length };
13
14
  return null;
14
15
  }
15
16
  async function patchTemplateForFields(source, sectionKey, fields, repeatedGroups = [], options) {
@@ -64,7 +65,13 @@ const { data: ${varName} } = Astro.props
64
65
  }
65
66
  }
66
67
  }
67
- const patched2 = await patchTemplateForFields(modifiedSource, sectionKey, fields, repeatedGroups, options);
68
+ const patched2 = await patchTemplateForFields(
69
+ modifiedSource,
70
+ sectionKey,
71
+ fields,
72
+ repeatedGroups,
73
+ options
74
+ );
68
75
  return removeOldVarDeclarations(patched2, fields, repeatedGroups, varName);
69
76
  }
70
77
  const existingBindings = /* @__PURE__ */ new Set();
@@ -579,7 +586,7 @@ function patchArrayField(source, sectionKey, field, varName, mapExpressions, edi
579
586
  insert: `(${varName}?.${field.key} ?? [])`
580
587
  });
581
588
  } else {
582
- const varMapRegex = new RegExp(`(\\w+)\\.map\\s*\\(`);
589
+ const varMapRegex = /(\w+)\.map\s*\(/;
583
590
  const varMapMatch = exprSource.match(varMapRegex);
584
591
  if (varMapMatch) {
585
592
  const varRef = varMapMatch[1];
@@ -802,7 +809,7 @@ function collectDynamicClassEdits(innerEdits, source, instances, tmpl, group) {
802
809
  if (!tmplPos) continue;
803
810
  const fieldTag = field.tag;
804
811
  let bestAttrIdx = -1;
805
- let bestDist = Infinity;
812
+ let bestDist = Number.POSITIVE_INFINITY;
806
813
  for (let ai = 0; ai < tmplAttrs.length; ai++) {
807
814
  const pathParts = tmplAttrs[ai].path.split("/");
808
815
  const leafTag = (pathParts[pathParts.length - 1] ?? "").replace(/:\d+$/, "");
@@ -1091,7 +1098,7 @@ ${indent}))}`;
1091
1098
  for (let i = instances.length - 1; i >= 1; i--) {
1092
1099
  const inst = instances[i];
1093
1100
  let deleteStart = inst.start;
1094
- let deleteEnd = inst.end;
1101
+ const deleteEnd = inst.end;
1095
1102
  while (deleteStart > 0 && /\s/.test(source[deleteStart - 1]) && source[deleteStart - 1] !== "\n") {
1096
1103
  deleteStart--;
1097
1104
  }
@@ -1351,7 +1358,8 @@ function removeOldVarDeclarations(source, fields, repeatedGroups, cmsVarName = "
1351
1358
  return source.slice(0, fmStart + 4) + frontmatter + source.slice(fmEnd);
1352
1359
  }
1353
1360
  function patchChildComponentForFieldPrefix(source, innerFields) {
1354
- if (source.includes("fieldPrefix?: string") || source.includes("fieldPrefix?:string")) return source;
1361
+ if (source.includes("fieldPrefix?: string") || source.includes("fieldPrefix?:string"))
1362
+ return source;
1355
1363
  const fmStart = source.indexOf("---");
1356
1364
  const fmEnd = source.indexOf("---", fmStart + 3);
1357
1365
  if (fmStart === -1 || fmEnd === -1) return source;
@@ -1365,15 +1373,12 @@ function patchChildComponentForFieldPrefix(source, innerFields) {
1365
1373
  const closeFm = result.indexOf("---", result.indexOf("---") + 3);
1366
1374
  result = result.slice(0, closeFm) + "interface Props { fieldPrefix?: string }\n" + result.slice(closeFm);
1367
1375
  }
1368
- result = result.replace(
1369
- /(\bconst\s*\{[^}]*)(}\s*=\s*Astro\.props)/,
1370
- (_m, before, after) => {
1371
- if (before.includes("fieldPrefix")) return _m;
1372
- const trimmed = before.trimEnd();
1373
- const sep = trimmed.endsWith(",") ? " " : ",\n ";
1374
- return `${trimmed}${sep}fieldPrefix${after}`;
1375
- }
1376
- );
1376
+ result = result.replace(/(\bconst\s*\{[^}]*)(}\s*=\s*Astro\.props)/, (_m, before, after) => {
1377
+ if (before.includes("fieldPrefix")) return _m;
1378
+ const trimmed = before.trimEnd();
1379
+ const sep = trimmed.endsWith(",") ? " " : ",\n ";
1380
+ return `${trimmed}${sep}fieldPrefix${after}`;
1381
+ });
1377
1382
  const closingFm = result.indexOf("---", result.indexOf("---") + 3);
1378
1383
  const frontmatterPart = result.slice(0, closingFm + 3);
1379
1384
  let templatePart = result.slice(closingFm + 3);
@@ -1399,10 +1404,7 @@ function patchChildComponentForFieldPrefix(source, innerFields) {
1399
1404
  });
1400
1405
  }
1401
1406
  for (const field of arrayFields) {
1402
- const mapParamRegex = new RegExp(
1403
- `(${field.key}\\.map\\s*\\(\\s*\\(\\s*)(\\w+)(\\s*\\))`,
1404
- "g"
1405
- );
1407
+ const mapParamRegex = new RegExp(`(${field.key}\\.map\\s*\\(\\s*\\(\\s*)(\\w+)(\\s*\\))`, "g");
1406
1408
  templatePart = templatePart.replace(
1407
1409
  new RegExp(`(<(?:ul|ol)[^>]*?)(\\/?>)([\\s\\S]*?${field.key}\\.map)`, "g"),
1408
1410
  (_m, ulOpen, ulClose, rest) => {
@@ -1419,11 +1421,14 @@ function patchChildComponentForFieldPrefix(source, innerFields) {
1419
1421
  `(${field.key}\\.map\\s*\\([^)]*\\)\\s*=>\\s*\\([\\s\\S]*?)(<(?:span|li|td)\\b)([^>]*?>)([^<]*\\{\\w+\\})`,
1420
1422
  "g"
1421
1423
  );
1422
- templatePart = templatePart.replace(mapBlockRegex, (_m, mapHead, tagOpen, tagClose, content) => {
1423
- if (tagClose.includes("data-sk-field")) return _m;
1424
- const attr = ` data-sk-field={fieldPrefix ? \`\${fieldPrefix}.${field.key}.\${_fi}\` : undefined}`;
1425
- return `${mapHead}${tagOpen}${tagClose.slice(0, -1)}${attr}>${content}`;
1426
- });
1424
+ templatePart = templatePart.replace(
1425
+ mapBlockRegex,
1426
+ (_m, mapHead, tagOpen, tagClose, content) => {
1427
+ if (tagClose.includes("data-sk-field")) return _m;
1428
+ const attr = ` data-sk-field={fieldPrefix ? \`\${fieldPrefix}.${field.key}.\${_fi}\` : undefined}`;
1429
+ return `${mapHead}${tagOpen}${tagClose.slice(0, -1)}${attr}>${content}`;
1430
+ }
1431
+ );
1427
1432
  }
1428
1433
  return frontmatterPart + templatePart;
1429
1434
  }
@@ -1,5 +1,5 @@
1
1
  // src/api-routes/_github-token.ts
2
- import { err, authError } from "@setzkasten-cms/core";
2
+ import { authError, err } from "@setzkasten-cms/core";
3
3
  import { GitHubAppClient } from "@setzkasten-cms/github-adapter";
4
4
  async function resolveConfigRepoToken() {
5
5
  const appId = process.env.GITHUB_APP_ID;
@@ -0,0 +1,76 @@
1
+ // src/api-routes/_session-signing.ts
2
+ import { createHmac, timingSafeEqual } from "crypto";
3
+ var SEPARATOR = ".";
4
+ function base64urlEncode(input) {
5
+ const buf = typeof input === "string" ? Buffer.from(input, "utf-8") : input;
6
+ return buf.toString("base64url");
7
+ }
8
+ function base64urlDecode(input) {
9
+ if (!/^[A-Za-z0-9_-]+$/.test(input)) return null;
10
+ try {
11
+ return Buffer.from(input, "base64url");
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+ function hmac(payload, secret) {
17
+ return createHmac("sha256", secret).update(payload).digest();
18
+ }
19
+ function signSession(session, secret) {
20
+ if (typeof secret !== "string" || secret.trim().length === 0) {
21
+ throw new Error("signSession: missing secret");
22
+ }
23
+ const json = JSON.stringify(session);
24
+ const payloadB64 = base64urlEncode(json);
25
+ const sigB64 = hmac(payloadB64, secret).toString("base64url");
26
+ return `${payloadB64}${SEPARATOR}${sigB64}`;
27
+ }
28
+ function verifySessionCookie(raw, secret, now = Date.now()) {
29
+ if (typeof raw !== "string" || raw.length === 0) {
30
+ return { ok: false, reason: "malformed" };
31
+ }
32
+ if (typeof secret !== "string" || secret.trim().length === 0) {
33
+ return { ok: false, reason: "malformed" };
34
+ }
35
+ const segments = raw.split(SEPARATOR);
36
+ if (segments.length !== 2) return { ok: false, reason: "malformed" };
37
+ const [payloadB64, sigB64] = segments;
38
+ if (!payloadB64 || !sigB64) return { ok: false, reason: "malformed" };
39
+ const expected = hmac(payloadB64, secret);
40
+ const actual = base64urlDecode(sigB64);
41
+ if (!actual) return { ok: false, reason: "malformed" };
42
+ if (actual.length !== expected.length) return { ok: false, reason: "bad-signature" };
43
+ if (!timingSafeEqual(actual, expected)) return { ok: false, reason: "bad-signature" };
44
+ const payloadBuf = base64urlDecode(payloadB64);
45
+ if (!payloadBuf) return { ok: false, reason: "malformed" };
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(payloadBuf.toString("utf-8"));
49
+ } catch {
50
+ return { ok: false, reason: "invalid-payload" };
51
+ }
52
+ if (!isAuthSessionShape(parsed)) {
53
+ return { ok: false, reason: "invalid-payload" };
54
+ }
55
+ if (typeof parsed.expiresAt !== "number" || !Number.isFinite(parsed.expiresAt)) {
56
+ return { ok: false, reason: "missing-expiry" };
57
+ }
58
+ if (parsed.expiresAt <= now) {
59
+ return { ok: false, reason: "expired" };
60
+ }
61
+ return { ok: true, value: parsed };
62
+ }
63
+ function isAuthSessionShape(value) {
64
+ if (!value || typeof value !== "object") return false;
65
+ const v = value;
66
+ if (!v.user || typeof v.user !== "object") return false;
67
+ const u = v.user;
68
+ if (typeof u.email !== "string" || u.email.length === 0) return false;
69
+ if (u.role !== "admin" && u.role !== "editor") return false;
70
+ return true;
71
+ }
72
+
73
+ export {
74
+ signSession,
75
+ verifySessionCookie
76
+ };
@@ -0,0 +1,47 @@
1
+ // src/api-routes/_dev-session-secret.ts
2
+ import { randomBytes } from "crypto";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
4
+ import { join } from "path";
5
+ var DEV_SECRET_DIR = ".setzkasten";
6
+ var DEV_SECRET_FILE = "dev-secret";
7
+ var cachedSecret = null;
8
+ function ensureDevSessionSecret() {
9
+ if (cachedSecret) return cachedSecret;
10
+ const dir = join(process.cwd(), DEV_SECRET_DIR);
11
+ const file = join(dir, DEV_SECRET_FILE);
12
+ if (existsSync(file)) {
13
+ const content = readFileSync(file, "utf-8").trim();
14
+ if (content.length >= 32) {
15
+ cachedSecret = content;
16
+ return content;
17
+ }
18
+ }
19
+ mkdirSync(dir, { recursive: true });
20
+ const fresh = randomBytes(32).toString("hex");
21
+ try {
22
+ writeFileSync(file, `${fresh}
23
+ `, { flag: "wx", mode: 384 });
24
+ } catch (cause) {
25
+ if (cause?.code === "EEXIST") {
26
+ const content = readFileSync(file, "utf-8").trim();
27
+ if (content.length >= 32) {
28
+ cachedSecret = content;
29
+ return content;
30
+ }
31
+ }
32
+ throw cause;
33
+ }
34
+ console.warn(
35
+ `[setzkasten] Generated dev session secret at ${file} (mode 0600). Set SETZKASTEN_SESSION_SECRET in production. Delete the file to rotate.`
36
+ );
37
+ cachedSecret = fresh;
38
+ return fresh;
39
+ }
40
+ function __setDevSessionSecretForTests(secret) {
41
+ cachedSecret = secret;
42
+ }
43
+
44
+ export {
45
+ ensureDevSessionSecret,
46
+ __setDevSessionSecretForTests
47
+ };