@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
@@ -30,10 +30,10 @@ var POST = async ({ cookies }) => {
30
30
  });
31
31
  if (!response.ok) {
32
32
  console.warn(`[setzkasten] Deploy hook antwortete mit ${response.status}: ${url}`);
33
- return new Response(
34
- JSON.stringify({ ok: false, status: response.status }),
35
- { status: 200, headers: { "Content-Type": "application/json" } }
36
- );
33
+ return new Response(JSON.stringify({ ok: false, status: response.status }), {
34
+ status: 200,
35
+ headers: { "Content-Type": "application/json" }
36
+ });
37
37
  }
38
38
  return new Response(JSON.stringify({ ok: true }), {
39
39
  status: 200,
@@ -41,10 +41,10 @@ var POST = async ({ cookies }) => {
41
41
  });
42
42
  } catch (error) {
43
43
  console.error("[setzkasten] Deploy hook fehlgeschlagen:", error);
44
- return new Response(
45
- JSON.stringify({ ok: false, error: String(error) }),
46
- { status: 200, headers: { "Content-Type": "application/json" } }
47
- );
44
+ return new Response(JSON.stringify({ ok: false, error: String(error) }), {
45
+ status: 200,
46
+ headers: { "Content-Type": "application/json" }
47
+ });
48
48
  }
49
49
  };
50
50
  export {
@@ -1,5 +1,5 @@
1
- import { APIRoute } from 'astro';
2
1
  import { ContentEditorConfig } from '@setzkasten-cms/core';
2
+ import { APIRoute } from 'astro';
3
3
 
4
4
  declare const GET: APIRoute;
5
5
  declare const PUT: APIRoute;
@@ -3,11 +3,14 @@ import {
3
3
  PUT,
4
4
  readEditorsFile,
5
5
  readEditorsFileStatus
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
  export {
@@ -1,12 +1,25 @@
1
+ import {
2
+ parseSession
3
+ } from "../chunk-Q5HV47DW.js";
4
+ import "../chunk-QVCW6EF3.js";
5
+ import "../chunk-KENFINT4.js";
6
+ import {
7
+ resolveStorageConfigForRequest
8
+ } from "../chunk-6UIKVKED.js";
9
+ import "../chunk-ONP6BRZO.js";
10
+ import "../chunk-5PIMDP4N.js";
11
+ import "../chunk-45ARVNT3.js";
1
12
  import {
2
13
  resolveGitHubTokenForRequest
3
- } from "../chunk-NKDATSPA.js";
14
+ } from "../chunk-DP6RTINQ.js";
15
+ import "../chunk-TJNJKPUL.js";
16
+ import "../chunk-KH22FJO5.js";
4
17
 
5
18
  // src/api-routes/github-proxy.ts
6
19
  import { writeFile } from "fs/promises";
7
20
  import { join } from "path";
8
21
  var ALL = async ({ params, request, cookies }) => {
9
- const session = cookies.get("setzkasten_session")?.value;
22
+ const session = parseSession(cookies.get("setzkasten_session")?.value);
10
23
  if (!session) {
11
24
  return new Response("Unauthorized", { status: 401 });
12
25
  }
@@ -14,6 +27,17 @@ var ALL = async ({ params, request, cookies }) => {
14
27
  if (!githubPath) {
15
28
  return new Response("Missing path", { status: 400 });
16
29
  }
30
+ const storage = await resolveStorageConfigForRequest(request);
31
+ if (!storage) {
32
+ return new Response("Could not resolve owner/repo", { status: 400 });
33
+ }
34
+ const allowedPrefix = `repos/${storage.owner}/${storage.repo}/`;
35
+ const allowedExact = `repos/${storage.owner}/${storage.repo}`;
36
+ if (githubPath !== allowedExact && !githubPath.startsWith(allowedPrefix)) {
37
+ return new Response(`Forbidden: proxy is scoped to ${storage.owner}/${storage.repo}`, {
38
+ status: 403
39
+ });
40
+ }
17
41
  const tokenResult = await resolveGitHubTokenForRequest(request);
18
42
  if (!tokenResult.ok) {
19
43
  return new Response(tokenResult.error.message, { status: 500 });
@@ -38,11 +62,7 @@ var ALL = async ({ params, request, cookies }) => {
38
62
  });
39
63
  const responseHeaders = new Headers();
40
64
  responseHeaders.set("Content-Type", response.headers.get("content-type") ?? "application/json");
41
- const rateLimitHeaders = [
42
- "x-ratelimit-limit",
43
- "x-ratelimit-remaining",
44
- "x-ratelimit-reset"
45
- ];
65
+ const rateLimitHeaders = ["x-ratelimit-limit", "x-ratelimit-remaining", "x-ratelimit-reset"];
46
66
  for (const header of rateLimitHeaders) {
47
67
  const value = response.headers.get(header);
48
68
  if (value) responseHeaders.set(header, value);
@@ -59,7 +79,9 @@ var ALL = async ({ params, request, cookies }) => {
59
79
  const filePath = contentsMatch[1];
60
80
  const parsed = JSON.parse(body);
61
81
  if (parsed.content) {
62
- const decoded = Buffer.from(parsed.content.replace(/\s/g, ""), "base64").toString("utf-8");
82
+ const decoded = Buffer.from(parsed.content.replace(/\s/g, ""), "base64").toString(
83
+ "utf-8"
84
+ );
63
85
  await writeFile(join(repoRoot, filePath), decoded, "utf-8").catch(() => {
64
86
  });
65
87
  }
@@ -3,12 +3,15 @@ import {
3
3
  PUT,
4
4
  readGlobalConfig,
5
5
  writeGlobalConfig
6
- } from "../chunk-AM4DZXXM.js";
7
- import "../chunk-INIWFKQ3.js";
6
+ } from "../chunk-UJAFZEX2.js";
7
+ import "../chunk-Q5HV47DW.js";
8
+ import "../chunk-QVCW6EF3.js";
9
+ import "../chunk-KENFINT4.js";
8
10
  import "../chunk-6UIKVKED.js";
11
+ import "../chunk-ONP6BRZO.js";
9
12
  import "../chunk-5PIMDP4N.js";
10
13
  import "../chunk-45ARVNT3.js";
11
- import "../chunk-NKDATSPA.js";
14
+ import "../chunk-DP6RTINQ.js";
12
15
  import "../chunk-TJNJKPUL.js";
13
16
  import "../chunk-KH22FJO5.js";
14
17
  export {
@@ -1,17 +1,20 @@
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 "../chunk-5PIMDP4N.js";
9
12
  import {
10
13
  invalidateCache
11
14
  } from "../chunk-45ARVNT3.js";
12
15
  import {
13
16
  resolveGitHubTokenForRequest
14
- } from "../chunk-NKDATSPA.js";
17
+ } from "../chunk-DP6RTINQ.js";
15
18
  import "../chunk-TJNJKPUL.js";
16
19
  import {
17
20
  withTrailers
@@ -40,6 +43,14 @@ var POST = async ({ request, cookies }) => {
40
43
  return Response.json({ error: "Could not resolve owner/repo" }, { status: 400 });
41
44
  }
42
45
  const { owner, repo, branch } = storage;
46
+ const serverConfig = globalThis.__SETZKASTEN_CONFIG__;
47
+ const contentPath = serverConfig?.storage?.contentPath ?? "content";
48
+ if (!isPathInsideContent(path, contentPath)) {
49
+ return Response.json(
50
+ { error: `Rollback restricted to the content folder (${contentPath}/...)` },
51
+ { status: 400 }
52
+ );
53
+ }
43
54
  const headers = {
44
55
  Authorization: `Bearer ${tokenResult.value}`,
45
56
  Accept: "application/vnd.github+json",
@@ -57,10 +68,7 @@ var POST = async ({ request, cookies }) => {
57
68
  );
58
69
  }
59
70
  if (!versionRes.ok) {
60
- return Response.json(
61
- { error: `Failed to read version: ${versionRes.status}` },
62
- { status: 502 }
63
- );
71
+ return Response.json({ error: `Failed to read version: ${versionRes.status}` }, { status: 502 });
64
72
  }
65
73
  const versionData = await versionRes.json();
66
74
  const targetContent = versionData.encoding === "base64" ? Buffer.from(versionData.content, "base64").toString("utf-8") : versionData.content;
@@ -84,20 +92,18 @@ var POST = async ({ request, cookies }) => {
84
92
  }
85
93
  const shortSha = sha.slice(0, 7);
86
94
  const fileName = path.split("/").pop() ?? path;
87
- const message = withTrailers(
88
- `revert(${fileName}): rollback to ${shortSha}`,
89
- session.user.email
90
- );
95
+ const message = withTrailers(`revert(${fileName}): rollback to ${shortSha}`, session.user.email);
91
96
  const putBody = {
92
97
  message,
93
98
  content: Buffer.from(targetContent).toString("base64"),
94
99
  branch
95
100
  };
96
101
  if (currentSha) putBody.sha = currentSha;
97
- const putRes = await fetch(
98
- `https://api.github.com/repos/${owner}/${repo}/contents/${path}`,
99
- { method: "PUT", headers, body: JSON.stringify(putBody) }
100
- );
102
+ const putRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${path}`, {
103
+ method: "PUT",
104
+ headers,
105
+ body: JSON.stringify(putBody)
106
+ });
101
107
  if (!putRes.ok) {
102
108
  const text = await putRes.text();
103
109
  return Response.json({ error: `Rollback write failed: ${text}` }, { status: 502 });
@@ -106,6 +112,17 @@ var POST = async ({ request, cookies }) => {
106
112
  const putData = await putRes.json();
107
113
  return Response.json({ ok: true, commitSha: putData.commit.sha });
108
114
  };
115
+ function isPathInsideContent(target, base) {
116
+ if (typeof target !== "string" || target.length === 0) return false;
117
+ if (target.includes("\0") || target.includes("\\")) return false;
118
+ if (target.startsWith("/")) return false;
119
+ const segments = target.split("/");
120
+ for (const seg of segments) {
121
+ if (seg === "" || seg === "." || seg === "..") return false;
122
+ }
123
+ const normalizedBase = base.replace(/^\/+|\/+$/g, "");
124
+ return target === normalizedBase || target.startsWith(`${normalizedBase}/`);
125
+ }
109
126
  export {
110
127
  POST
111
128
  };
@@ -1,16 +1,19 @@
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 {
5
7
  resolveStorageConfigForRequest
6
8
  } from "../chunk-6UIKVKED.js";
9
+ import "../chunk-ONP6BRZO.js";
7
10
  import "../chunk-5PIMDP4N.js";
8
11
  import {
9
12
  cachedFetch
10
13
  } from "../chunk-45ARVNT3.js";
11
14
  import {
12
15
  resolveGitHubTokenForRequest
13
- } from "../chunk-NKDATSPA.js";
16
+ } from "../chunk-DP6RTINQ.js";
14
17
  import "../chunk-TJNJKPUL.js";
15
18
  import "../chunk-KH22FJO5.js";
16
19
 
@@ -32,9 +35,7 @@ var GET = async ({ request, url, cookies }) => {
32
35
  const { owner, repo } = storage;
33
36
  const cacheKey = `history-version:${owner}/${repo}:${path}:${sha}`;
34
37
  const result = await cachedFetch(cacheKey, 5 * 6e4, async () => {
35
- const u = new URL(
36
- `https://api.github.com/repos/${owner}/${repo}/contents/${path}`
37
- );
38
+ const u = new URL(`https://api.github.com/repos/${owner}/${repo}/contents/${path}`);
38
39
  u.searchParams.set("ref", sha);
39
40
  const res = await fetch(u, {
40
41
  headers: {
@@ -43,7 +44,8 @@ var GET = async ({ request, url, cookies }) => {
43
44
  "X-GitHub-Api-Version": "2022-11-28"
44
45
  }
45
46
  });
46
- if (res.status === 404) return { ok: false, status: 404, error: "File not found at given sha" };
47
+ if (res.status === 404)
48
+ return { ok: false, status: 404, error: "File not found at given sha" };
47
49
  if (!res.ok) return { ok: false, status: 502, error: `GitHub returned ${res.status}` };
48
50
  const data = await res.json();
49
51
  const raw = data.encoding === "base64" ? Buffer.from(data.content, "base64").toString("utf-8") : data.content;
@@ -1,16 +1,19 @@
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 {
5
7
  resolveStorageConfigForRequest
6
8
  } from "../chunk-6UIKVKED.js";
9
+ import "../chunk-ONP6BRZO.js";
7
10
  import "../chunk-5PIMDP4N.js";
8
11
  import {
9
12
  cachedFetch
10
13
  } from "../chunk-45ARVNT3.js";
11
14
  import {
12
15
  resolveGitHubTokenForRequest
13
- } from "../chunk-NKDATSPA.js";
16
+ } from "../chunk-DP6RTINQ.js";
14
17
  import "../chunk-TJNJKPUL.js";
15
18
  import "../chunk-KH22FJO5.js";
16
19
 
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-6UIKVKED.js";
5
5
  import {
6
6
  resolveGitHubTokenForRequest
7
- } from "../chunk-NKDATSPA.js";
7
+ } from "../chunk-DP6RTINQ.js";
8
8
 
9
9
  // src/api-routes/icons-local.ts
10
10
  import {
@@ -3,25 +3,33 @@ import {
3
3
  patchChildComponentForFieldPrefix,
4
4
  patchTemplateForFields,
5
5
  stripTemplateFallbacks
6
- } from "../chunk-Q3N336KR.js";
6
+ } from "../chunk-CDXCYYQR.js";
7
+ import {
8
+ requireAdmin
9
+ } from "../chunk-Q5HV47DW.js";
10
+ import "../chunk-QVCW6EF3.js";
11
+ import "../chunk-KENFINT4.js";
7
12
  import {
8
13
  prefixPath,
9
14
  resolveStorageConfigForRequest
10
15
  } from "../chunk-6UIKVKED.js";
16
+ import "../chunk-ONP6BRZO.js";
17
+ import "../chunk-5PIMDP4N.js";
18
+ import "../chunk-45ARVNT3.js";
11
19
  import {
12
20
  resolveGitHubTokenForRequest
13
- } from "../chunk-NKDATSPA.js";
21
+ } from "../chunk-DP6RTINQ.js";
22
+ import "../chunk-TJNJKPUL.js";
14
23
  import {
15
24
  withTrailers
16
25
  } from "../chunk-KH22FJO5.js";
17
26
 
18
27
  // src/api-routes/init-add-section.ts
28
+ import { isSafeKey } from "@setzkasten-cms/core";
19
29
  import { addSectionToConfig } from "@setzkasten-cms/core/init";
20
30
  var POST = async ({ request, cookies }) => {
21
- const session = cookies.get("setzkasten_session")?.value;
22
- if (!session) {
23
- return new Response("Unauthorized", { status: 401 });
24
- }
31
+ const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
32
+ if (denied) return denied;
25
33
  const tokenResult = await resolveGitHubTokenForRequest(request);
26
34
  if (!tokenResult.ok) {
27
35
  return new Response(tokenResult.error.message, { status: 500 });
@@ -31,7 +39,12 @@ var POST = async ({ request, cookies }) => {
31
39
  const body = await request.json();
32
40
  const storage = await resolveStorageConfigForRequest(request, body);
33
41
  if (!storage) {
34
- return Response.json({ error: "Could not resolve owner/repo. Set SETZKASTEN_OWNER and SETZKASTEN_REPO env vars." }, { status: 400 });
42
+ return Response.json(
43
+ {
44
+ error: "Could not resolve owner/repo. Set SETZKASTEN_OWNER and SETZKASTEN_REPO env vars."
45
+ },
46
+ { status: 400 }
47
+ );
35
48
  }
36
49
  const { owner, repo, branch, projectPrefix } = storage;
37
50
  const serverConfig = globalThis.__SETZKASTEN_CONFIG__;
@@ -40,6 +53,12 @@ var POST = async ({ request, cookies }) => {
40
53
  if (!section || !pageKey) {
41
54
  return Response.json({ error: "section and pageKey are required" }, { status: 400 });
42
55
  }
56
+ if (!isSafeKey(pageKey)) {
57
+ return Response.json({ error: "invalid pageKey" }, { status: 400 });
58
+ }
59
+ if (!isSafeKey(section.key)) {
60
+ return Response.json({ error: "invalid section key" }, { status: 400 });
61
+ }
43
62
  const headers = {
44
63
  Authorization: `Bearer ${githubToken}`,
45
64
  Accept: "application/vnd.github+json",
@@ -50,13 +69,24 @@ var POST = async ({ request, cookies }) => {
50
69
  const configPath = prefixPath("setzkasten.config.ts", projectPrefix);
51
70
  const existingConfig = await fetchFileContent(owner, repo, branch, configPath, githubToken);
52
71
  if (existingConfig) {
53
- const updatedConfig = addSectionToConfig(existingConfig, section.key, section, section.allFields);
72
+ const updatedConfig = addSectionToConfig(
73
+ existingConfig,
74
+ section.key,
75
+ section,
76
+ section.allFields
77
+ );
54
78
  if (updatedConfig) {
55
79
  filesToCommit.push({ path: configPath, content: updatedConfig });
56
80
  }
57
81
  }
58
82
  const sectionJsonPath = `${contentPath}/_sections/${section.key}.json`;
59
- const existingSectionJson = await fetchFileContent(owner, repo, branch, sectionJsonPath, githubToken);
83
+ const existingSectionJson = await fetchFileContent(
84
+ owner,
85
+ repo,
86
+ branch,
87
+ sectionJsonPath,
88
+ githubToken
89
+ );
60
90
  let sectionData = {};
61
91
  if (existingSectionJson) {
62
92
  try {
@@ -89,7 +119,13 @@ var POST = async ({ request, cookies }) => {
89
119
  });
90
120
  const configKey = "_" + pageKey.replace(/\//g, "_");
91
121
  const pageConfigPath = `${contentPath}/pages/${configKey}.json`;
92
- const existingPageConfig = await fetchFileContent(owner, repo, branch, pageConfigPath, githubToken);
122
+ const existingPageConfig = await fetchFileContent(
123
+ owner,
124
+ repo,
125
+ branch,
126
+ pageConfigPath,
127
+ githubToken
128
+ );
93
129
  let pageConfig;
94
130
  if (existingPageConfig) {
95
131
  pageConfig = JSON.parse(existingPageConfig);
@@ -163,7 +199,9 @@ import Page from '${importDepth}${relativePage}';
163
199
  for (const g of repeatedGroups) {
164
200
  const topField = fields.find((f) => f.key === g.fieldKey);
165
201
  if (!topField || !Array.isArray(topField.defaultValue)) continue;
166
- sectionData[g.fieldKey] = topField.defaultValue.filter((item) => item != null);
202
+ sectionData[g.fieldKey] = topField.defaultValue.filter(
203
+ (item) => item != null
204
+ );
167
205
  const jsonIdx = filesToCommit.findIndex((f) => f.path === sectionJsonPath);
168
206
  if (jsonIdx !== -1) {
169
207
  filesToCommit[jsonIdx].content = JSON.stringify(sectionData, null, 2);
@@ -171,10 +209,21 @@ import Page from '${importDepth}${relativePage}';
171
209
  }
172
210
  }
173
211
  } else if (section.componentPath) {
174
- const componentSource = await fetchFileContent(owner, repo, branch, section.componentPath, githubToken);
212
+ const componentSource = await fetchFileContent(
213
+ owner,
214
+ repo,
215
+ branch,
216
+ section.componentPath,
217
+ githubToken
218
+ );
175
219
  if (componentSource) {
176
220
  const repeatedGroups = section._analyzerResult?.repeatedGroups ?? [];
177
- const patchedSource = await patchTemplateForFields(componentSource, section.key, section.allFields ?? section.fields, repeatedGroups);
221
+ const patchedSource = await patchTemplateForFields(
222
+ componentSource,
223
+ section.key,
224
+ section.allFields ?? section.fields,
225
+ repeatedGroups
226
+ );
178
227
  if (patchedSource !== componentSource) {
179
228
  filesToCommit.push({ path: section.componentPath, content: patchedSource });
180
229
  }
@@ -182,7 +231,9 @@ import Page from '${importDepth}${relativePage}';
182
231
  for (const g of repeatedGroups) {
183
232
  const topField = fields.find((f) => f.key === g.fieldKey);
184
233
  if (!topField || !Array.isArray(topField.defaultValue)) continue;
185
- const items = topField.defaultValue.filter((item) => item != null);
234
+ const items = topField.defaultValue.filter(
235
+ (item) => item != null
236
+ );
186
237
  sectionData[g.fieldKey] = items;
187
238
  const jsonIdx = filesToCommit.findIndex((f) => f.path === sectionJsonPath);
188
239
  if (jsonIdx !== -1) {
@@ -195,7 +246,13 @@ import Page from '${importDepth}${relativePage}';
195
246
  const sectionDir = section.componentPath.replace(/\/[^/]+$/, "");
196
247
  const resolvedChildPath = resolveRelativePath(sectionDir, child.importPath);
197
248
  if (!resolvedChildPath) continue;
198
- const childSource = await fetchFileContent(owner, repo, branch, resolvedChildPath, githubToken);
249
+ const childSource = await fetchFileContent(
250
+ owner,
251
+ repo,
252
+ branch,
253
+ resolvedChildPath,
254
+ githubToken
255
+ );
199
256
  if (!childSource) continue;
200
257
  const patchedChild = patchChildComponentForFieldPrefix(childSource, child.innerFields);
201
258
  if (patchedChild !== childSource) {
@@ -207,7 +264,13 @@ import Page from '${importDepth}${relativePage}';
207
264
  const fullPagePath = prefixPath(body.pagePath, projectPrefix);
208
265
  const pageSource = await fetchFileContent(owner, repo, branch, fullPagePath, githubToken);
209
266
  if (pageSource) {
210
- const patched = patchPageFile(pageSource, section.key, section.componentName, section.componentPath, body.pagePath);
267
+ const patched = patchPageFile(
268
+ pageSource,
269
+ section.key,
270
+ section.componentName,
271
+ section.componentPath,
272
+ body.pagePath
273
+ );
211
274
  if (patched && patched !== pageSource) {
212
275
  filesToCommit.push({ path: fullPagePath, content: patched });
213
276
  }
@@ -216,7 +279,13 @@ import Page from '${importDepth}${relativePage}';
216
279
  const previewPath = prefixPath(`${pageDir}/sk-preview/[...page].astro`, projectPrefix);
217
280
  const previewSource = await fetchFileContent(owner, repo, branch, previewPath, githubToken);
218
281
  if (previewSource) {
219
- const patchedPreview = patchPageFile(previewSource, section.key, section.componentName, section.componentPath, `${pageDir}/sk-preview/[...page].astro`);
282
+ const patchedPreview = patchPageFile(
283
+ previewSource,
284
+ section.key,
285
+ section.componentName,
286
+ section.componentPath,
287
+ `${pageDir}/sk-preview/[...page].astro`
288
+ );
220
289
  if (patchedPreview && patchedPreview !== previewSource) {
221
290
  filesToCommit.push({ path: previewPath, content: patchedPreview });
222
291
  }
@@ -231,7 +300,9 @@ import Page from '${importDepth}${relativePage}';
231
300
  repo,
232
301
  branch,
233
302
  filesToCommit,
234
- withTrailers(existingSectionJson ? `content: update ${section.key} section \u2014 add new fields` : `content: add ${section.key} section to Setzkasten`),
303
+ withTrailers(
304
+ existingSectionJson ? `content: update ${section.key} section \u2014 add new fields` : `content: add ${section.key} section to Setzkasten`
305
+ ),
235
306
  headers
236
307
  );
237
308
  if (!commitResult.ok) {
@@ -379,37 +450,32 @@ async function batchCommit(owner, repo, branch, files, message, headers) {
379
450
  );
380
451
  if (!commitRes.ok) return { ok: false, error: `Failed to get commit: ${commitRes.status}` };
381
452
  const commitData = await commitRes.json();
382
- const treeRes = await fetch(
383
- `https://api.github.com/repos/${owner}/${repo}/git/trees`,
384
- {
385
- method: "POST",
386
- headers,
387
- body: JSON.stringify({
388
- base_tree: commitData.tree.sha,
389
- tree: files.map((f) => ({
390
- path: f.path,
391
- mode: "100644",
392
- type: "blob",
393
- content: f.content
394
- }))
395
- })
396
- }
397
- );
453
+ const treeRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/trees`, {
454
+ method: "POST",
455
+ headers,
456
+ body: JSON.stringify({
457
+ base_tree: commitData.tree.sha,
458
+ tree: files.map((f) => ({
459
+ path: f.path,
460
+ mode: "100644",
461
+ type: "blob",
462
+ content: f.content
463
+ }))
464
+ })
465
+ });
398
466
  if (!treeRes.ok) return { ok: false, error: `Failed to create tree: ${treeRes.status}` };
399
467
  const treeData = await treeRes.json();
400
- const newCommitRes = await fetch(
401
- `https://api.github.com/repos/${owner}/${repo}/git/commits`,
402
- {
403
- method: "POST",
404
- headers,
405
- body: JSON.stringify({
406
- tree: treeData.sha,
407
- parents: [headSha],
408
- message
409
- })
410
- }
411
- );
412
- if (!newCommitRes.ok) return { ok: false, error: `Failed to create commit: ${newCommitRes.status}` };
468
+ const newCommitRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/commits`, {
469
+ method: "POST",
470
+ headers,
471
+ body: JSON.stringify({
472
+ tree: treeData.sha,
473
+ parents: [headSha],
474
+ message
475
+ })
476
+ });
477
+ if (!newCommitRes.ok)
478
+ return { ok: false, error: `Failed to create commit: ${newCommitRes.status}` };
413
479
  const newCommitData = await newCommitRes.json();
414
480
  const updateRes = await fetch(
415
481
  `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,