@setzkasten-cms/astro-admin 1.4.2 → 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.
- package/dist/api-routes/_auth-guard.d.ts +27 -3
- package/dist/api-routes/_auth-guard.js +5 -2
- package/dist/api-routes/_dev-session-secret.d.ts +8 -0
- package/dist/api-routes/_dev-session-secret.js +8 -0
- package/dist/api-routes/_github-token.js +1 -1
- package/dist/api-routes/_role-resolver.js +6 -3
- package/dist/api-routes/_session-secret.d.ts +19 -0
- package/dist/api-routes/_session-secret.js +7 -0
- package/dist/api-routes/_session-signing.d.ts +45 -0
- package/dist/api-routes/_session-signing.js +8 -0
- package/dist/api-routes/_webhook-dispatcher.js +4 -4
- package/dist/api-routes/asset-proxy.js +1 -1
- package/dist/api-routes/auth-callback.js +12 -5
- package/dist/api-routes/auth-logout.d.ts +4 -4
- package/dist/api-routes/auth-logout.js +8 -2
- package/dist/api-routes/auth-session.d.ts +6 -0
- package/dist/api-routes/auth-session.js +19 -19
- package/dist/api-routes/auth-setzkasten-login.js +14 -7
- package/dist/api-routes/catalog-add.js +59 -17
- package/dist/api-routes/catalog-export.js +14 -4
- package/dist/api-routes/config.d.ts +10 -3
- package/dist/api-routes/config.js +26 -4
- package/dist/api-routes/deploy-hook.js +8 -8
- package/dist/api-routes/editors.d.ts +1 -1
- package/dist/api-routes/editors.js +5 -2
- package/dist/api-routes/github-proxy.js +30 -8
- package/dist/api-routes/global-config.js +6 -3
- package/dist/api-routes/history-rollback.js +31 -14
- package/dist/api-routes/history-version.js +8 -6
- package/dist/api-routes/history.js +5 -2
- package/dist/api-routes/icons-local.js +1 -1
- package/dist/api-routes/init-add-section.js +150 -48
- package/dist/api-routes/init-apply.js +56 -42
- package/dist/api-routes/init-migrate.js +43 -36
- package/dist/api-routes/init-scan-page.d.ts +1 -1
- package/dist/api-routes/init-scan-page.js +59 -13
- package/dist/api-routes/init-scan.js +22 -7
- package/dist/api-routes/migrate-to-multi.js +5 -2
- package/dist/api-routes/pages.js +15 -4
- package/dist/api-routes/section-add.js +68 -16
- package/dist/api-routes/section-commit-pending.js +70 -22
- package/dist/api-routes/section-delete.js +49 -14
- package/dist/api-routes/section-duplicate.js +65 -16
- package/dist/api-routes/section-prepare-copy.js +15 -2
- package/dist/api-routes/section-prepare.js +25 -4
- package/dist/api-routes/setup-github-app-bounce.js +15 -1
- package/dist/api-routes/setup-github-app-branches.js +9 -6
- package/dist/api-routes/setup-github-app-callback.js +24 -1
- package/dist/api-routes/setup-github-app-credentials.d.ts +27 -0
- package/dist/api-routes/setup-github-app-credentials.js +43 -0
- package/dist/api-routes/setup-github-app-installed.js +22 -1
- package/dist/api-routes/setup-github-app-repos.js +5 -2
- package/dist/api-routes/setup-github-app.d.ts +4 -0
- package/dist/api-routes/setup-github-app.js +19 -2
- package/dist/api-routes/updater-register.js +7 -1
- package/dist/api-routes/webhooks-status.js +5 -2
- package/dist/api-routes/webhooks-test.js +9 -8
- package/dist/api-routes/webhooks.js +12 -14
- package/dist/api-routes/websites-add.js +5 -2
- package/dist/api-routes/websites-remove.js +5 -2
- package/dist/{chunk-ZQDGGWJP.js → chunk-5KMGSFCZ.js} +2 -2
- package/dist/{chunk-RHJONMLK.js → chunk-CDXCYYQR.js} +222 -5
- package/dist/{chunk-NKDATSPA.js → chunk-DP6RTINQ.js} +1 -1
- package/dist/chunk-KENFINT4.js +76 -0
- package/dist/chunk-ONP6BRZO.js +47 -0
- package/dist/{chunk-INIWFKQ3.js → chunk-Q5HV47DW.js} +33 -19
- package/dist/chunk-QVCW6EF3.js +26 -0
- package/dist/{chunk-K22A4ZBS.js → chunk-UHI6323G.js} +293 -174
- package/dist/{chunk-AM4DZXXM.js → chunk-UJAFZEX2.js} +76 -9
- package/package.json +12 -6
- package/src/api-routes/__tests__/_session-signing.test.ts +114 -0
- package/src/api-routes/__tests__/_session-test-helper.ts +27 -0
- package/src/api-routes/__tests__/add-section-helpers.test.ts +91 -97
- package/src/api-routes/__tests__/auth-guard.test.ts +46 -7
- package/src/api-routes/__tests__/catalog-api.test.ts +14 -6
- package/src/api-routes/__tests__/commit-trailers.test.ts +5 -5
- package/src/api-routes/__tests__/deferred-operations.test.ts +9 -12
- package/src/api-routes/__tests__/deploy-hook.test.ts +3 -8
- package/src/api-routes/__tests__/feature-gate.test.ts +1 -1
- package/src/api-routes/__tests__/github-cache.test.ts +1 -1
- package/src/api-routes/__tests__/github-token.test.ts +1 -1
- package/src/api-routes/__tests__/global-config-theme.test.ts +4 -4
- package/src/api-routes/__tests__/history-rollback.test.ts +6 -3
- package/src/api-routes/__tests__/history.test.ts +9 -6
- package/src/api-routes/__tests__/init-scan-page-resolve-config.test.ts +11 -3
- package/src/api-routes/__tests__/migrate-to-multi.test.ts +5 -1
- package/src/api-routes/__tests__/pages-meta-store.test.ts +10 -5
- package/src/api-routes/__tests__/pages.test.ts +7 -2
- package/src/api-routes/__tests__/patch-page-file.test.ts +71 -19
- package/src/api-routes/__tests__/route-registry.test.ts +11 -18
- package/src/api-routes/__tests__/scan-page-helpers.test.ts +13 -10
- package/src/api-routes/__tests__/section-management.test.ts +28 -28
- package/src/api-routes/__tests__/setup-github-app-callback.test.ts +58 -16
- package/src/api-routes/__tests__/setup-github-app-repos.test.ts +4 -5
- package/src/api-routes/__tests__/setup-github-app.test.ts +27 -7
- package/src/api-routes/__tests__/storage-config-for-request.test.ts +83 -0
- package/src/api-routes/__tests__/updater-register.test.ts +230 -0
- package/src/api-routes/__tests__/webhook-signing.test.ts +1 -1
- package/src/api-routes/__tests__/webhooks.test.ts +19 -7
- package/src/api-routes/__tests__/websites-add.test.ts +2 -1
- package/src/api-routes/__tests__/websites-remove.test.ts +2 -1
- package/src/api-routes/_auth-guard.ts +47 -15
- package/src/api-routes/_commit-trailers.ts +3 -2
- package/src/api-routes/_dev-session-secret.ts +79 -0
- package/src/api-routes/_github-token.ts +1 -1
- package/src/api-routes/_pages-meta-store.ts +2 -2
- package/src/api-routes/_role-resolver.ts +7 -5
- package/src/api-routes/_session-secret.ts +46 -0
- package/src/api-routes/_session-signing.ts +135 -0
- package/src/api-routes/_vercel-origin.ts +2 -6
- package/src/api-routes/_webhook-dispatcher.ts +12 -16
- package/src/api-routes/_website-resolver.ts +3 -10
- package/src/api-routes/auth-callback.ts +9 -5
- package/src/api-routes/auth-login.ts +5 -3
- package/src/api-routes/auth-logout.ts +18 -1
- package/src/api-routes/auth-session.ts +13 -21
- package/src/api-routes/auth-setzkasten-login.ts +12 -9
- package/src/api-routes/catalog-add.ts +89 -31
- package/src/api-routes/catalog-export.ts +30 -10
- package/src/api-routes/config.ts +39 -6
- package/src/api-routes/deploy-hook.ts +13 -11
- package/src/api-routes/editors.ts +33 -22
- package/src/api-routes/github-proxy.ts +25 -11
- package/src/api-routes/global-config.ts +103 -18
- package/src/api-routes/history-rollback.ts +41 -14
- package/src/api-routes/history-version.ts +5 -6
- package/src/api-routes/history.ts +3 -3
- package/src/api-routes/icons-local.ts +2 -2
- package/src/api-routes/init-add-section.ts +218 -88
- package/src/api-routes/init-apply.ts +71 -56
- package/src/api-routes/init-migrate.ts +54 -48
- package/src/api-routes/init-scan-page.ts +77 -30
- package/src/api-routes/init-scan.ts +19 -11
- package/src/api-routes/pages.ts +16 -11
- package/src/api-routes/section-add.ts +98 -27
- package/src/api-routes/section-commit-pending.ts +87 -34
- package/src/api-routes/section-delete.ts +76 -27
- package/src/api-routes/section-duplicate.ts +95 -28
- package/src/api-routes/section-management.ts +3 -7
- package/src/api-routes/section-prepare-copy.ts +29 -8
- package/src/api-routes/section-prepare.ts +38 -10
- package/src/api-routes/setup-github-app-bounce.ts +7 -1
- package/src/api-routes/setup-github-app-branches.ts +6 -7
- package/src/api-routes/setup-github-app-callback.ts +18 -1
- package/src/api-routes/setup-github-app-credentials.ts +55 -0
- package/src/api-routes/setup-github-app-installed.ts +12 -1
- package/src/api-routes/setup-github-app-repos.ts +2 -3
- package/src/api-routes/setup-github-app.ts +14 -5
- package/src/api-routes/updater-check.ts +6 -4
- package/src/api-routes/updater-register.ts +34 -20
- package/src/api-routes/updater-transfer.ts +8 -6
- package/src/api-routes/updater-unbind.ts +14 -10
- package/src/api-routes/webhooks-test.ts +9 -11
- package/src/api-routes/webhooks.ts +15 -19
- package/src/init/__tests__/page-level.test.ts +279 -105
- package/src/init/__tests__/page-list-coverage.test.ts +70 -70
- package/src/init/__tests__/patcher-child-component.test.ts +126 -0
- package/src/init/__tests__/patcher-edge-cases.test.ts +47 -23
- package/src/init/__tests__/patcher-mixed-content-wrapper.test.ts +16 -6
- package/src/init/__tests__/patcher-page-mode.test.ts +30 -20
- package/src/init/__tests__/section-pipeline.test.ts +102 -16
- package/src/init/astro-config-patcher.ts +4 -18
- package/src/init/astro-detector.ts +2 -7
- package/src/init/astro-section-analyzer-v2.ts +475 -193
- package/src/init/field-label-enricher.ts +6 -6
- package/src/init/template-patcher-v2.ts +490 -56
|
@@ -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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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 {
|
|
@@ -3,11 +3,14 @@ import {
|
|
|
3
3
|
PUT,
|
|
4
4
|
readEditorsFile,
|
|
5
5
|
readEditorsFileStatus
|
|
6
|
-
} from "../chunk-
|
|
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-
|
|
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-
|
|
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(
|
|
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-
|
|
7
|
-
import "../chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
99
|
-
|
|
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-
|
|
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-
|
|
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)
|
|
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-
|
|
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-
|
|
16
|
+
} from "../chunk-DP6RTINQ.js";
|
|
14
17
|
import "../chunk-TJNJKPUL.js";
|
|
15
18
|
import "../chunk-KH22FJO5.js";
|
|
16
19
|
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
|
+
detectChildImports,
|
|
3
|
+
patchChildComponentForFieldPrefix,
|
|
2
4
|
patchTemplateForFields,
|
|
3
5
|
stripTemplateFallbacks
|
|
4
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-CDXCYYQR.js";
|
|
7
|
+
import {
|
|
8
|
+
requireAdmin
|
|
9
|
+
} from "../chunk-Q5HV47DW.js";
|
|
10
|
+
import "../chunk-QVCW6EF3.js";
|
|
11
|
+
import "../chunk-KENFINT4.js";
|
|
5
12
|
import {
|
|
6
13
|
prefixPath,
|
|
7
14
|
resolveStorageConfigForRequest
|
|
8
15
|
} from "../chunk-6UIKVKED.js";
|
|
16
|
+
import "../chunk-ONP6BRZO.js";
|
|
17
|
+
import "../chunk-5PIMDP4N.js";
|
|
18
|
+
import "../chunk-45ARVNT3.js";
|
|
9
19
|
import {
|
|
10
20
|
resolveGitHubTokenForRequest
|
|
11
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-DP6RTINQ.js";
|
|
22
|
+
import "../chunk-TJNJKPUL.js";
|
|
12
23
|
import {
|
|
13
24
|
withTrailers
|
|
14
25
|
} from "../chunk-KH22FJO5.js";
|
|
15
26
|
|
|
16
27
|
// src/api-routes/init-add-section.ts
|
|
28
|
+
import { isSafeKey } from "@setzkasten-cms/core";
|
|
17
29
|
import { addSectionToConfig } from "@setzkasten-cms/core/init";
|
|
18
30
|
var POST = async ({ request, cookies }) => {
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
21
|
-
return new Response("Unauthorized", { status: 401 });
|
|
22
|
-
}
|
|
31
|
+
const denied = requireAdmin(cookies.get("setzkasten_session")?.value);
|
|
32
|
+
if (denied) return denied;
|
|
23
33
|
const tokenResult = await resolveGitHubTokenForRequest(request);
|
|
24
34
|
if (!tokenResult.ok) {
|
|
25
35
|
return new Response(tokenResult.error.message, { status: 500 });
|
|
@@ -29,7 +39,12 @@ var POST = async ({ request, cookies }) => {
|
|
|
29
39
|
const body = await request.json();
|
|
30
40
|
const storage = await resolveStorageConfigForRequest(request, body);
|
|
31
41
|
if (!storage) {
|
|
32
|
-
return Response.json(
|
|
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
|
+
);
|
|
33
48
|
}
|
|
34
49
|
const { owner, repo, branch, projectPrefix } = storage;
|
|
35
50
|
const serverConfig = globalThis.__SETZKASTEN_CONFIG__;
|
|
@@ -38,6 +53,12 @@ var POST = async ({ request, cookies }) => {
|
|
|
38
53
|
if (!section || !pageKey) {
|
|
39
54
|
return Response.json({ error: "section and pageKey are required" }, { status: 400 });
|
|
40
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
|
+
}
|
|
41
62
|
const headers = {
|
|
42
63
|
Authorization: `Bearer ${githubToken}`,
|
|
43
64
|
Accept: "application/vnd.github+json",
|
|
@@ -48,13 +69,24 @@ var POST = async ({ request, cookies }) => {
|
|
|
48
69
|
const configPath = prefixPath("setzkasten.config.ts", projectPrefix);
|
|
49
70
|
const existingConfig = await fetchFileContent(owner, repo, branch, configPath, githubToken);
|
|
50
71
|
if (existingConfig) {
|
|
51
|
-
const updatedConfig = addSectionToConfig(
|
|
72
|
+
const updatedConfig = addSectionToConfig(
|
|
73
|
+
existingConfig,
|
|
74
|
+
section.key,
|
|
75
|
+
section,
|
|
76
|
+
section.allFields
|
|
77
|
+
);
|
|
52
78
|
if (updatedConfig) {
|
|
53
79
|
filesToCommit.push({ path: configPath, content: updatedConfig });
|
|
54
80
|
}
|
|
55
81
|
}
|
|
56
82
|
const sectionJsonPath = `${contentPath}/_sections/${section.key}.json`;
|
|
57
|
-
const existingSectionJson = await fetchFileContent(
|
|
83
|
+
const existingSectionJson = await fetchFileContent(
|
|
84
|
+
owner,
|
|
85
|
+
repo,
|
|
86
|
+
branch,
|
|
87
|
+
sectionJsonPath,
|
|
88
|
+
githubToken
|
|
89
|
+
);
|
|
58
90
|
let sectionData = {};
|
|
59
91
|
if (existingSectionJson) {
|
|
60
92
|
try {
|
|
@@ -87,7 +119,13 @@ var POST = async ({ request, cookies }) => {
|
|
|
87
119
|
});
|
|
88
120
|
const configKey = "_" + pageKey.replace(/\//g, "_");
|
|
89
121
|
const pageConfigPath = `${contentPath}/pages/${configKey}.json`;
|
|
90
|
-
const existingPageConfig = await fetchFileContent(
|
|
122
|
+
const existingPageConfig = await fetchFileContent(
|
|
123
|
+
owner,
|
|
124
|
+
repo,
|
|
125
|
+
branch,
|
|
126
|
+
pageConfigPath,
|
|
127
|
+
githubToken
|
|
128
|
+
);
|
|
91
129
|
let pageConfig;
|
|
92
130
|
if (existingPageConfig) {
|
|
93
131
|
pageConfig = JSON.parse(existingPageConfig);
|
|
@@ -146,8 +184,14 @@ var POST = async ({ request, cookies }) => {
|
|
|
146
184
|
}
|
|
147
185
|
if (patchedSource !== pageSource) {
|
|
148
186
|
filesToCommit.push({ path: fullPagePath, content: patchedSource });
|
|
149
|
-
const previewCopySource = patchedSource.replace(/\bexport\s+const\s+prerender\s*=\s*true\s*;?\s*\n?/, "").replace(/(from\s+')(\.\.\/)/g, "$1../$2").replace(/(from\s+")(\.\.\/)/g, "$1../$2");
|
|
150
187
|
const relativePage = resolvedPagePath.replace(/^src\/pages\//, "");
|
|
188
|
+
const importDepth = "../".repeat(relativePage.split("/").length);
|
|
189
|
+
const previewCopySource = `---
|
|
190
|
+
export const prerender = false;
|
|
191
|
+
import Page from '${importDepth}${relativePage}';
|
|
192
|
+
---
|
|
193
|
+
<Page />
|
|
194
|
+
`;
|
|
151
195
|
const previewCopyPath = prefixPath(`src/pages/sk-preview/${relativePage}`, projectPrefix);
|
|
152
196
|
filesToCommit.push({ path: previewCopyPath, content: previewCopySource });
|
|
153
197
|
}
|
|
@@ -155,7 +199,9 @@ var POST = async ({ request, cookies }) => {
|
|
|
155
199
|
for (const g of repeatedGroups) {
|
|
156
200
|
const topField = fields.find((f) => f.key === g.fieldKey);
|
|
157
201
|
if (!topField || !Array.isArray(topField.defaultValue)) continue;
|
|
158
|
-
sectionData[g.fieldKey] = topField.defaultValue.filter(
|
|
202
|
+
sectionData[g.fieldKey] = topField.defaultValue.filter(
|
|
203
|
+
(item) => item != null
|
|
204
|
+
);
|
|
159
205
|
const jsonIdx = filesToCommit.findIndex((f) => f.path === sectionJsonPath);
|
|
160
206
|
if (jsonIdx !== -1) {
|
|
161
207
|
filesToCommit[jsonIdx].content = JSON.stringify(sectionData, null, 2);
|
|
@@ -163,10 +209,21 @@ var POST = async ({ request, cookies }) => {
|
|
|
163
209
|
}
|
|
164
210
|
}
|
|
165
211
|
} else if (section.componentPath) {
|
|
166
|
-
const componentSource = await fetchFileContent(
|
|
212
|
+
const componentSource = await fetchFileContent(
|
|
213
|
+
owner,
|
|
214
|
+
repo,
|
|
215
|
+
branch,
|
|
216
|
+
section.componentPath,
|
|
217
|
+
githubToken
|
|
218
|
+
);
|
|
167
219
|
if (componentSource) {
|
|
168
220
|
const repeatedGroups = section._analyzerResult?.repeatedGroups ?? [];
|
|
169
|
-
const patchedSource = await patchTemplateForFields(
|
|
221
|
+
const patchedSource = await patchTemplateForFields(
|
|
222
|
+
componentSource,
|
|
223
|
+
section.key,
|
|
224
|
+
section.allFields ?? section.fields,
|
|
225
|
+
repeatedGroups
|
|
226
|
+
);
|
|
170
227
|
if (patchedSource !== componentSource) {
|
|
171
228
|
filesToCommit.push({ path: section.componentPath, content: patchedSource });
|
|
172
229
|
}
|
|
@@ -174,19 +231,46 @@ var POST = async ({ request, cookies }) => {
|
|
|
174
231
|
for (const g of repeatedGroups) {
|
|
175
232
|
const topField = fields.find((f) => f.key === g.fieldKey);
|
|
176
233
|
if (!topField || !Array.isArray(topField.defaultValue)) continue;
|
|
177
|
-
const items = topField.defaultValue.filter(
|
|
234
|
+
const items = topField.defaultValue.filter(
|
|
235
|
+
(item) => item != null
|
|
236
|
+
);
|
|
178
237
|
sectionData[g.fieldKey] = items;
|
|
179
238
|
const jsonIdx = filesToCommit.findIndex((f) => f.path === sectionJsonPath);
|
|
180
239
|
if (jsonIdx !== -1) {
|
|
181
240
|
filesToCommit[jsonIdx].content = JSON.stringify(sectionData, null, 2);
|
|
182
241
|
}
|
|
183
242
|
}
|
|
243
|
+
const allFields = section.allFields ?? section.fields;
|
|
244
|
+
const childPatches = detectChildImports(patchedSource, allFields);
|
|
245
|
+
for (const child of childPatches) {
|
|
246
|
+
const sectionDir = section.componentPath.replace(/\/[^/]+$/, "");
|
|
247
|
+
const resolvedChildPath = resolveRelativePath(sectionDir, child.importPath);
|
|
248
|
+
if (!resolvedChildPath) continue;
|
|
249
|
+
const childSource = await fetchFileContent(
|
|
250
|
+
owner,
|
|
251
|
+
repo,
|
|
252
|
+
branch,
|
|
253
|
+
resolvedChildPath,
|
|
254
|
+
githubToken
|
|
255
|
+
);
|
|
256
|
+
if (!childSource) continue;
|
|
257
|
+
const patchedChild = patchChildComponentForFieldPrefix(childSource, child.innerFields);
|
|
258
|
+
if (patchedChild !== childSource) {
|
|
259
|
+
filesToCommit.push({ path: resolvedChildPath, content: patchedChild });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
184
262
|
}
|
|
185
263
|
if (body.pagePath && section.componentName) {
|
|
186
264
|
const fullPagePath = prefixPath(body.pagePath, projectPrefix);
|
|
187
265
|
const pageSource = await fetchFileContent(owner, repo, branch, fullPagePath, githubToken);
|
|
188
266
|
if (pageSource) {
|
|
189
|
-
const patched = patchPageFile(
|
|
267
|
+
const patched = patchPageFile(
|
|
268
|
+
pageSource,
|
|
269
|
+
section.key,
|
|
270
|
+
section.componentName,
|
|
271
|
+
section.componentPath,
|
|
272
|
+
body.pagePath
|
|
273
|
+
);
|
|
190
274
|
if (patched && patched !== pageSource) {
|
|
191
275
|
filesToCommit.push({ path: fullPagePath, content: patched });
|
|
192
276
|
}
|
|
@@ -195,7 +279,13 @@ var POST = async ({ request, cookies }) => {
|
|
|
195
279
|
const previewPath = prefixPath(`${pageDir}/sk-preview/[...page].astro`, projectPrefix);
|
|
196
280
|
const previewSource = await fetchFileContent(owner, repo, branch, previewPath, githubToken);
|
|
197
281
|
if (previewSource) {
|
|
198
|
-
const patchedPreview = patchPageFile(
|
|
282
|
+
const patchedPreview = patchPageFile(
|
|
283
|
+
previewSource,
|
|
284
|
+
section.key,
|
|
285
|
+
section.componentName,
|
|
286
|
+
section.componentPath,
|
|
287
|
+
`${pageDir}/sk-preview/[...page].astro`
|
|
288
|
+
);
|
|
199
289
|
if (patchedPreview && patchedPreview !== previewSource) {
|
|
200
290
|
filesToCommit.push({ path: previewPath, content: patchedPreview });
|
|
201
291
|
}
|
|
@@ -210,7 +300,9 @@ var POST = async ({ request, cookies }) => {
|
|
|
210
300
|
repo,
|
|
211
301
|
branch,
|
|
212
302
|
filesToCommit,
|
|
213
|
-
withTrailers(
|
|
303
|
+
withTrailers(
|
|
304
|
+
existingSectionJson ? `content: update ${section.key} section \u2014 add new fields` : `content: add ${section.key} section to Setzkasten`
|
|
305
|
+
),
|
|
214
306
|
headers
|
|
215
307
|
);
|
|
216
308
|
if (!commitResult.ok) {
|
|
@@ -288,7 +380,7 @@ function patchPageFile(source, sectionKey, componentName, componentPath, pagePat
|
|
|
288
380
|
if (lastEntryMatch && lastEntryMatch.index !== void 0) {
|
|
289
381
|
const insertPos = registryMatch.index + registryMatch[0].indexOf(registryContent) + lastEntryMatch.index + lastEntryMatch[0].length;
|
|
290
382
|
const newEntry = `
|
|
291
|
-
|
|
383
|
+
'${sectionKey}': ${componentName},`;
|
|
292
384
|
patched = patched.slice(0, insertPos) + newEntry + patched.slice(insertPos);
|
|
293
385
|
}
|
|
294
386
|
}
|
|
@@ -309,6 +401,21 @@ function calculateRelativePath(fromDir, toPath) {
|
|
|
309
401
|
if (ups === 0) return "./" + remaining;
|
|
310
402
|
return "../".repeat(ups) + remaining;
|
|
311
403
|
}
|
|
404
|
+
function resolveRelativePath(baseDir, relativePath) {
|
|
405
|
+
if (relativePath.startsWith("/")) return relativePath.replace(/^\//, "");
|
|
406
|
+
const parts = [...baseDir.split("/").filter(Boolean)];
|
|
407
|
+
for (const segment of relativePath.split("/")) {
|
|
408
|
+
if (segment === ".") continue;
|
|
409
|
+
if (segment === "..") {
|
|
410
|
+
parts.pop();
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
parts.push(segment);
|
|
414
|
+
}
|
|
415
|
+
const resolved = parts.join("/");
|
|
416
|
+
if (resolved.startsWith("../") || resolved.startsWith("/")) return null;
|
|
417
|
+
return resolved;
|
|
418
|
+
}
|
|
312
419
|
async function fetchFileContent(owner, repo, branch, path, token) {
|
|
313
420
|
try {
|
|
314
421
|
const response = await fetch(
|
|
@@ -343,37 +450,32 @@ async function batchCommit(owner, repo, branch, files, message, headers) {
|
|
|
343
450
|
);
|
|
344
451
|
if (!commitRes.ok) return { ok: false, error: `Failed to get commit: ${commitRes.status}` };
|
|
345
452
|
const commitData = await commitRes.json();
|
|
346
|
-
const treeRes = await fetch(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
})
|
|
360
|
-
}
|
|
361
|
-
);
|
|
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
|
+
});
|
|
362
466
|
if (!treeRes.ok) return { ok: false, error: `Failed to create tree: ${treeRes.status}` };
|
|
363
467
|
const treeData = await treeRes.json();
|
|
364
|
-
const newCommitRes = await fetch(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
);
|
|
376
|
-
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}` };
|
|
377
479
|
const newCommitData = await newCommitRes.json();
|
|
378
480
|
const updateRes = await fetch(
|
|
379
481
|
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
|