@setzkasten-cms/astro-admin 1.4.0 → 1.4.1
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 +47 -0
- package/dist/api-routes/_auth-guard.js +18 -0
- package/dist/api-routes/_commit-trailers.d.ts +8 -0
- package/dist/api-routes/_commit-trailers.js +8 -0
- package/dist/api-routes/_feature-gate.d.ts +23 -0
- package/dist/api-routes/_feature-gate.js +7 -0
- package/dist/api-routes/_github-cache.d.ts +4 -0
- package/dist/api-routes/_github-cache.js +8 -0
- package/dist/api-routes/_github-token.d.ts +27 -0
- package/dist/api-routes/_github-token.js +8 -0
- package/dist/api-routes/_license-tier.d.ts +22 -0
- package/dist/api-routes/_license-tier.js +6 -0
- package/dist/api-routes/_pages-meta-store.d.ts +32 -0
- package/dist/api-routes/_pages-meta-store.js +9 -0
- package/dist/api-routes/_role-resolver.d.ts +15 -0
- package/dist/api-routes/_role-resolver.js +13 -0
- package/dist/api-routes/_session-cookie.d.ts +18 -0
- package/dist/api-routes/_session-cookie.js +6 -0
- package/dist/api-routes/_storage-config.d.ts +60 -0
- package/dist/api-routes/_storage-config.js +10 -0
- package/dist/api-routes/_vercel-origin.d.ts +16 -0
- package/dist/api-routes/_vercel-origin.js +6 -0
- package/dist/api-routes/_webhook-dispatcher.d.ts +13 -0
- package/dist/api-routes/_webhook-dispatcher.js +97 -0
- package/dist/api-routes/_webhook-signing.d.ts +11 -0
- package/dist/api-routes/_webhook-signing.js +6 -0
- package/dist/api-routes/_webhook-status-store.d.ts +19 -0
- package/dist/api-routes/_webhook-status-store.js +10 -0
- package/dist/api-routes/_website-resolver.d.ts +49 -0
- package/dist/api-routes/_website-resolver.js +14 -0
- package/dist/api-routes/_websites-store.d.ts +30 -0
- package/dist/api-routes/_websites-store.js +11 -0
- package/dist/api-routes/asset-proxy.d.ts +12 -0
- package/dist/api-routes/asset-proxy.js +67 -0
- package/dist/api-routes/auth-callback.d.ts +9 -0
- package/dist/api-routes/auth-callback.js +68 -0
- package/dist/api-routes/auth-login.d.ts +11 -0
- package/dist/api-routes/auth-login.js +27 -0
- package/dist/api-routes/auth-logout.d.ts +10 -0
- package/dist/api-routes/auth-logout.js +13 -0
- package/dist/api-routes/auth-session.d.ts +9 -0
- package/dist/api-routes/auth-session.js +31 -0
- package/dist/api-routes/auth-setzkasten-login.d.ts +18 -0
- package/dist/api-routes/auth-setzkasten-login.js +74 -0
- package/dist/api-routes/catalog-add.d.ts +14 -0
- package/dist/api-routes/catalog-add.js +153 -0
- package/dist/api-routes/catalog-export.d.ts +13 -0
- package/dist/api-routes/catalog-export.js +71 -0
- package/dist/api-routes/catalog-helpers.d.ts +41 -0
- package/dist/api-routes/catalog-helpers.js +11 -0
- package/dist/api-routes/catalog-list.d.ts +11 -0
- package/dist/api-routes/catalog-list.js +12 -0
- package/dist/api-routes/config.d.ts +12 -0
- package/dist/api-routes/config.js +43 -0
- package/dist/api-routes/deploy-hook.d.ts +14 -0
- package/dist/api-routes/deploy-hook.js +52 -0
- package/dist/api-routes/editors.d.ts +29 -0
- package/dist/api-routes/editors.js +18 -0
- package/dist/api-routes/github-proxy.d.ts +12 -0
- package/dist/api-routes/github-proxy.js +82 -0
- package/dist/api-routes/global-config.d.ts +20 -0
- package/dist/api-routes/global-config.js +19 -0
- package/dist/api-routes/history-rollback.d.ts +22 -0
- package/dist/api-routes/history-rollback.js +111 -0
- package/dist/api-routes/history-version.d.ts +11 -0
- package/dist/api-routes/history-version.js +57 -0
- package/dist/api-routes/history.d.ts +13 -0
- package/dist/api-routes/history.js +85 -0
- package/dist/api-routes/icons-local.d.ts +28 -0
- package/dist/api-routes/icons-local.js +115 -0
- package/dist/api-routes/init-add-section.d.ts +23 -0
- package/dist/api-routes/init-add-section.js +396 -0
- package/dist/api-routes/init-apply.d.ts +11 -0
- package/dist/api-routes/init-apply.js +266 -0
- package/dist/api-routes/init-migrate.d.ts +16 -0
- package/dist/api-routes/init-migrate.js +205 -0
- package/dist/api-routes/init-scan-page.d.ts +39 -0
- package/dist/api-routes/init-scan-page.js +260 -0
- package/dist/api-routes/init-scan.d.ts +11 -0
- package/dist/api-routes/init-scan.js +128 -0
- package/dist/api-routes/migrate-to-multi.d.ts +26 -0
- package/dist/api-routes/migrate-to-multi.js +188 -0
- package/dist/api-routes/pages.d.ts +39 -0
- package/dist/api-routes/pages.js +88 -0
- package/dist/api-routes/section-add.d.ts +18 -0
- package/dist/api-routes/section-add.js +173 -0
- package/dist/api-routes/section-commit-pending.d.ts +18 -0
- package/dist/api-routes/section-commit-pending.js +207 -0
- package/dist/api-routes/section-delete.d.ts +15 -0
- package/dist/api-routes/section-delete.js +149 -0
- package/dist/api-routes/section-duplicate.d.ts +15 -0
- package/dist/api-routes/section-duplicate.js +143 -0
- package/dist/api-routes/section-management.d.ts +41 -0
- package/dist/api-routes/section-management.js +14 -0
- package/dist/api-routes/section-prepare-copy.d.ts +25 -0
- package/dist/api-routes/section-prepare-copy.js +69 -0
- package/dist/api-routes/section-prepare.d.ts +18 -0
- package/dist/api-routes/section-prepare.js +104 -0
- package/dist/api-routes/setup-github-app-bounce.d.ts +13 -0
- package/dist/api-routes/setup-github-app-bounce.js +45 -0
- package/dist/api-routes/setup-github-app-branches.d.ts +14 -0
- package/dist/api-routes/setup-github-app-branches.js +58 -0
- package/dist/api-routes/setup-github-app-callback.d.ts +15 -0
- package/dist/api-routes/setup-github-app-callback.js +45 -0
- package/dist/api-routes/setup-github-app-installed.d.ts +15 -0
- package/dist/api-routes/setup-github-app-installed.js +33 -0
- package/dist/api-routes/setup-github-app-repos.d.ts +17 -0
- package/dist/api-routes/setup-github-app-repos.js +41 -0
- package/dist/api-routes/setup-github-app.d.ts +15 -0
- package/dist/api-routes/setup-github-app.js +41 -0
- package/dist/api-routes/updater-check.d.ts +10 -0
- package/dist/api-routes/updater-check.js +37 -0
- package/dist/api-routes/updater-register.d.ts +14 -0
- package/dist/api-routes/updater-register.js +71 -0
- package/dist/api-routes/updater-transfer.d.ts +11 -0
- package/dist/api-routes/updater-transfer.js +37 -0
- package/dist/api-routes/updater-unbind.d.ts +17 -0
- package/dist/api-routes/updater-unbind.js +35 -0
- package/dist/api-routes/webhooks-status.d.ts +12 -0
- package/dist/api-routes/webhooks-status.js +22 -0
- package/dist/api-routes/webhooks-test.d.ts +13 -0
- package/dist/api-routes/webhooks-test.js +124 -0
- package/dist/api-routes/webhooks.d.ts +6 -0
- package/dist/api-routes/webhooks.js +148 -0
- package/dist/api-routes/websites-add.d.ts +15 -0
- package/dist/api-routes/websites-add.js +92 -0
- package/dist/api-routes/websites-list.d.ts +12 -0
- package/dist/api-routes/websites-list.js +35 -0
- package/dist/api-routes/websites-remove.d.ts +15 -0
- package/dist/api-routes/websites-remove.js +69 -0
- package/dist/chunk-35S35OIV.js +80 -0
- package/dist/chunk-45ARVNT3.js +25 -0
- package/dist/chunk-5PIMDP4N.js +25 -0
- package/dist/chunk-5ZFTG4BW.js +10 -0
- package/dist/chunk-6UIKVKED.js +51 -0
- package/dist/chunk-737TIZRU.js +9 -0
- package/dist/chunk-AM4DZXXM.js +120 -0
- package/dist/chunk-FXNOTESI.js +87 -0
- package/dist/chunk-GHNK2GFE.js +48 -0
- package/dist/chunk-GRG3LNKH.js +37 -0
- package/dist/chunk-INIWFKQ3.js +236 -0
- package/dist/chunk-JHY6XTLL.js +24 -0
- package/dist/chunk-K22A4ZBS.js +1574 -0
- package/dist/chunk-KH22FJO5.js +17 -0
- package/dist/chunk-NKDATSPA.js +43 -0
- package/dist/chunk-RHJONMLK.js +1267 -0
- package/dist/chunk-TJNJKPUL.js +11 -0
- package/dist/chunk-V6IMPVF3.js +120 -0
- package/dist/chunk-W3QHY5GW.js +19 -0
- package/dist/chunk-ZQDGGWJP.js +43 -0
- package/package.json +249 -53
- package/src/api-routes/__tests__/route-registry.test.ts +7 -1
- package/tsconfig.json +0 -9
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import {
|
|
2
|
+
patchTemplateForFields
|
|
3
|
+
} from "../chunk-RHJONMLK.js";
|
|
4
|
+
import {
|
|
5
|
+
resolveGitHubTokenForRequest
|
|
6
|
+
} from "../chunk-NKDATSPA.js";
|
|
7
|
+
import {
|
|
8
|
+
withTrailers
|
|
9
|
+
} from "../chunk-KH22FJO5.js";
|
|
10
|
+
|
|
11
|
+
// src/api-routes/init-apply.ts
|
|
12
|
+
import { generateConfigFile } from "@setzkasten-cms/core/init";
|
|
13
|
+
|
|
14
|
+
// src/init/astro-config-patcher.ts
|
|
15
|
+
function patchAstroConfig(source) {
|
|
16
|
+
if (source.includes("@setzkasten-cms/astro") || source.includes("setzkasten(")) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
let result = source;
|
|
20
|
+
const importLine = "import setzkasten from '@setzkasten-cms/astro'\n";
|
|
21
|
+
const lastImportIndex = findLastImportEnd(result);
|
|
22
|
+
if (lastImportIndex >= 0) {
|
|
23
|
+
result = result.slice(0, lastImportIndex) + "\n" + importLine + result.slice(lastImportIndex);
|
|
24
|
+
} else {
|
|
25
|
+
result = importLine + "\n" + result;
|
|
26
|
+
}
|
|
27
|
+
result = addToIntegrations(result);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
function findLastImportEnd(source) {
|
|
31
|
+
const lines = source.split("\n");
|
|
32
|
+
let lastImportLineEnd = -1;
|
|
33
|
+
let pos = 0;
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
pos += line.length + 1;
|
|
36
|
+
if (line.trimStart().startsWith("import ")) {
|
|
37
|
+
lastImportLineEnd = pos;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return lastImportLineEnd;
|
|
41
|
+
}
|
|
42
|
+
function addToIntegrations(source) {
|
|
43
|
+
const integrationsMatch = source.match(/integrations\s*:\s*\[/);
|
|
44
|
+
if (integrationsMatch && integrationsMatch.index !== void 0) {
|
|
45
|
+
const insertPos = integrationsMatch.index + integrationsMatch[0].length;
|
|
46
|
+
const after = source.slice(insertPos).trimStart();
|
|
47
|
+
if (after.startsWith("]")) {
|
|
48
|
+
return source.slice(0, insertPos) + "setzkasten()" + source.slice(insertPos);
|
|
49
|
+
}
|
|
50
|
+
return source.slice(0, insertPos) + "\n setzkasten(),\n " + source.slice(insertPos);
|
|
51
|
+
}
|
|
52
|
+
const defineConfigMatch = source.match(/defineConfig\s*\(\s*\{/);
|
|
53
|
+
if (defineConfigMatch && defineConfigMatch.index !== void 0) {
|
|
54
|
+
const insertPos = defineConfigMatch.index + defineConfigMatch[0].length;
|
|
55
|
+
return source.slice(0, insertPos) + "\n integrations: [setzkasten()],\n" + source.slice(insertPos);
|
|
56
|
+
}
|
|
57
|
+
return source;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/api-routes/init-apply.ts
|
|
61
|
+
var POST = async ({ request, cookies }) => {
|
|
62
|
+
const session = cookies.get("setzkasten_session")?.value;
|
|
63
|
+
if (!session) {
|
|
64
|
+
return new Response("Unauthorized", { status: 401 });
|
|
65
|
+
}
|
|
66
|
+
const tokenResult = await resolveGitHubTokenForRequest(request);
|
|
67
|
+
if (!tokenResult.ok) {
|
|
68
|
+
return new Response(tokenResult.error.message, { status: 500 });
|
|
69
|
+
}
|
|
70
|
+
const githubToken = tokenResult.value;
|
|
71
|
+
try {
|
|
72
|
+
const body = await request.json();
|
|
73
|
+
const { owner, repo, branch = "main", projectRoot, astroConfigPath, sections, pages } = body;
|
|
74
|
+
const contentPath = body.contentPath ?? "content";
|
|
75
|
+
const filesToCommit = [];
|
|
76
|
+
const configInput = {
|
|
77
|
+
gitRepo: `${owner}/${repo}`,
|
|
78
|
+
productKey: "website",
|
|
79
|
+
sections
|
|
80
|
+
};
|
|
81
|
+
const configContent = generateConfigFile(configInput);
|
|
82
|
+
const configPath = projectRoot ? `${projectRoot}/setzkasten.config.ts` : "setzkasten.config.ts";
|
|
83
|
+
filesToCommit.push({ path: configPath, content: configContent });
|
|
84
|
+
if (astroConfigPath) {
|
|
85
|
+
const astroConfigSource = await fetchFileContent(owner, repo, branch, astroConfigPath, githubToken);
|
|
86
|
+
if (astroConfigSource) {
|
|
87
|
+
const patched = patchAstroConfig(astroConfigSource);
|
|
88
|
+
if (patched) {
|
|
89
|
+
filesToCommit.push({ path: astroConfigPath, content: patched });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const section of sections) {
|
|
94
|
+
const sectionData = {};
|
|
95
|
+
for (const field of section.fields) {
|
|
96
|
+
sectionData[field.key] = getDefaultValue(field.type);
|
|
97
|
+
}
|
|
98
|
+
const sectionPath = `${contentPath}/_sections/${section.key}.json`;
|
|
99
|
+
filesToCommit.push({
|
|
100
|
+
path: sectionPath,
|
|
101
|
+
content: JSON.stringify(sectionData, null, 2)
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
for (const section of sections) {
|
|
105
|
+
if (!section.componentPath) continue;
|
|
106
|
+
const componentSource = await fetchFileContent(owner, repo, branch, section.componentPath, githubToken);
|
|
107
|
+
if (!componentSource) continue;
|
|
108
|
+
const patched = await patchTemplateForFields(componentSource, section.key, section.fields);
|
|
109
|
+
if (patched !== componentSource) {
|
|
110
|
+
filesToCommit.push({ path: section.componentPath, content: patched });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const page of pages) {
|
|
114
|
+
const pageConfig = {
|
|
115
|
+
sections: page.sectionKeys.map((key, index) => ({
|
|
116
|
+
key,
|
|
117
|
+
enabled: true,
|
|
118
|
+
order: index
|
|
119
|
+
}))
|
|
120
|
+
};
|
|
121
|
+
const configKey = "_" + page.pageKey.replace(/\//g, "_");
|
|
122
|
+
const pagePath = `${contentPath}/pages/${configKey}.json`;
|
|
123
|
+
filesToCommit.push({
|
|
124
|
+
path: pagePath,
|
|
125
|
+
content: JSON.stringify(pageConfig, null, 2)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const commitResult = await batchCommit(
|
|
129
|
+
owner,
|
|
130
|
+
repo,
|
|
131
|
+
branch,
|
|
132
|
+
filesToCommit,
|
|
133
|
+
withTrailers("feat: initialize Setzkasten CMS"),
|
|
134
|
+
githubToken
|
|
135
|
+
);
|
|
136
|
+
if (!commitResult.ok) {
|
|
137
|
+
return Response.json(
|
|
138
|
+
{ error: commitResult.error },
|
|
139
|
+
{ status: 500 }
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return Response.json({
|
|
143
|
+
success: true,
|
|
144
|
+
commitSha: commitResult.sha,
|
|
145
|
+
filesWritten: filesToCommit.map((f) => f.path)
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error("[setzkasten] Init apply error:", error);
|
|
149
|
+
return Response.json(
|
|
150
|
+
{ error: error instanceof Error ? error.message : "Apply failed" },
|
|
151
|
+
{ status: 500 }
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
function getDefaultValue(fieldType) {
|
|
156
|
+
switch (fieldType) {
|
|
157
|
+
case "text":
|
|
158
|
+
return "";
|
|
159
|
+
case "number":
|
|
160
|
+
return 0;
|
|
161
|
+
case "boolean":
|
|
162
|
+
return false;
|
|
163
|
+
case "image":
|
|
164
|
+
return { path: "", alt: "" };
|
|
165
|
+
case "array":
|
|
166
|
+
return [];
|
|
167
|
+
case "color":
|
|
168
|
+
return "#000000";
|
|
169
|
+
case "date":
|
|
170
|
+
return "";
|
|
171
|
+
case "icon":
|
|
172
|
+
return "";
|
|
173
|
+
default:
|
|
174
|
+
return "";
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function fetchFileContent(owner, repo, branch, path, token) {
|
|
178
|
+
try {
|
|
179
|
+
const response = await fetch(
|
|
180
|
+
`https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`,
|
|
181
|
+
{
|
|
182
|
+
headers: {
|
|
183
|
+
Authorization: `Bearer ${token}`,
|
|
184
|
+
Accept: "application/vnd.github+json",
|
|
185
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
if (!response.ok) return null;
|
|
190
|
+
const data = await response.json();
|
|
191
|
+
return data.encoding === "base64" ? Buffer.from(data.content, "base64").toString("utf-8") : data.content;
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function batchCommit(owner, repo, branch, files, message, token) {
|
|
197
|
+
const headers = {
|
|
198
|
+
Authorization: `Bearer ${token}`,
|
|
199
|
+
Accept: "application/vnd.github+json",
|
|
200
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
201
|
+
"Content-Type": "application/json"
|
|
202
|
+
};
|
|
203
|
+
try {
|
|
204
|
+
const refRes = await fetch(
|
|
205
|
+
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
|
|
206
|
+
{ headers }
|
|
207
|
+
);
|
|
208
|
+
if (!refRes.ok) return { ok: false, error: `Failed to get HEAD: ${refRes.status}` };
|
|
209
|
+
const refData = await refRes.json();
|
|
210
|
+
const headSha = refData.object.sha;
|
|
211
|
+
const commitRes = await fetch(
|
|
212
|
+
`https://api.github.com/repos/${owner}/${repo}/git/commits/${headSha}`,
|
|
213
|
+
{ headers }
|
|
214
|
+
);
|
|
215
|
+
if (!commitRes.ok) return { ok: false, error: `Failed to get commit: ${commitRes.status}` };
|
|
216
|
+
const commitData = await commitRes.json();
|
|
217
|
+
const baseTreeSha = commitData.tree.sha;
|
|
218
|
+
const treeRes = await fetch(
|
|
219
|
+
`https://api.github.com/repos/${owner}/${repo}/git/trees`,
|
|
220
|
+
{
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers,
|
|
223
|
+
body: JSON.stringify({
|
|
224
|
+
base_tree: baseTreeSha,
|
|
225
|
+
tree: files.map((f) => ({
|
|
226
|
+
path: f.path,
|
|
227
|
+
mode: "100644",
|
|
228
|
+
type: "blob",
|
|
229
|
+
content: f.content
|
|
230
|
+
}))
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
if (!treeRes.ok) return { ok: false, error: `Failed to create tree: ${treeRes.status}` };
|
|
235
|
+
const treeData = await treeRes.json();
|
|
236
|
+
const newCommitRes = await fetch(
|
|
237
|
+
`https://api.github.com/repos/${owner}/${repo}/git/commits`,
|
|
238
|
+
{
|
|
239
|
+
method: "POST",
|
|
240
|
+
headers,
|
|
241
|
+
body: JSON.stringify({
|
|
242
|
+
tree: treeData.sha,
|
|
243
|
+
parents: [headSha],
|
|
244
|
+
message
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
if (!newCommitRes.ok) return { ok: false, error: `Failed to create commit: ${newCommitRes.status}` };
|
|
249
|
+
const newCommitData = await newCommitRes.json();
|
|
250
|
+
const updateRes = await fetch(
|
|
251
|
+
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
|
|
252
|
+
{
|
|
253
|
+
method: "PATCH",
|
|
254
|
+
headers,
|
|
255
|
+
body: JSON.stringify({ sha: newCommitData.sha })
|
|
256
|
+
}
|
|
257
|
+
);
|
|
258
|
+
if (!updateRes.ok) return { ok: false, error: `Failed to update ref: ${updateRes.status}` };
|
|
259
|
+
return { ok: true, sha: newCommitData.sha };
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return { ok: false, error: error instanceof Error ? error.message : "Commit failed" };
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
POST
|
|
266
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { APIRoute } from 'astro';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST /api/setzkasten/init/migrate
|
|
5
|
+
*
|
|
6
|
+
* Adds `data-sk-field` attributes to an existing Astro section component.
|
|
7
|
+
* Handles two cases:
|
|
8
|
+
* 1. Fields using `getSection()` / `Astro.props` variable patterns
|
|
9
|
+
* 2. Hardcoded text matching content JSON values (for adopted fields without bindings)
|
|
10
|
+
*
|
|
11
|
+
* Body: { owner, repo, branch?, sectionKey, componentPath }
|
|
12
|
+
* Returns: { commitSha, patchedSource, originalSource }
|
|
13
|
+
*/
|
|
14
|
+
declare const POST: APIRoute;
|
|
15
|
+
|
|
16
|
+
export { POST };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
patchTemplateForFields
|
|
3
|
+
} from "../chunk-RHJONMLK.js";
|
|
4
|
+
import {
|
|
5
|
+
prefixPath,
|
|
6
|
+
resolveStorageConfigForRequest
|
|
7
|
+
} from "../chunk-6UIKVKED.js";
|
|
8
|
+
import {
|
|
9
|
+
resolveGitHubTokenForRequest
|
|
10
|
+
} from "../chunk-NKDATSPA.js";
|
|
11
|
+
import {
|
|
12
|
+
withTrailers
|
|
13
|
+
} from "../chunk-KH22FJO5.js";
|
|
14
|
+
|
|
15
|
+
// src/api-routes/init-migrate.ts
|
|
16
|
+
var POST = async ({ request, cookies }) => {
|
|
17
|
+
const session = cookies.get("setzkasten_session")?.value;
|
|
18
|
+
if (!session) {
|
|
19
|
+
return new Response("Unauthorized", { status: 401 });
|
|
20
|
+
}
|
|
21
|
+
const tokenResult = await resolveGitHubTokenForRequest(request);
|
|
22
|
+
if (!tokenResult.ok) {
|
|
23
|
+
return new Response(tokenResult.error.message, { status: 500 });
|
|
24
|
+
}
|
|
25
|
+
const githubToken = tokenResult.value;
|
|
26
|
+
try {
|
|
27
|
+
const body = await request.json();
|
|
28
|
+
const storage = await resolveStorageConfigForRequest(request, body);
|
|
29
|
+
if (!storage) {
|
|
30
|
+
return Response.json({ error: "Could not resolve owner/repo. Set SETZKASTEN_OWNER and SETZKASTEN_REPO env vars." }, { status: 400 });
|
|
31
|
+
}
|
|
32
|
+
const { owner, repo, branch, projectPrefix } = storage;
|
|
33
|
+
const { sectionKey } = body;
|
|
34
|
+
const componentPath = prefixPath(
|
|
35
|
+
body.componentPath || deriveComponentPath(sectionKey),
|
|
36
|
+
projectPrefix
|
|
37
|
+
);
|
|
38
|
+
if (!sectionKey) {
|
|
39
|
+
return Response.json({ error: "sectionKey is required" }, { status: 400 });
|
|
40
|
+
}
|
|
41
|
+
const headers = {
|
|
42
|
+
Authorization: `Bearer ${githubToken}`,
|
|
43
|
+
Accept: "application/vnd.github+json",
|
|
44
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
45
|
+
"Content-Type": "application/json"
|
|
46
|
+
};
|
|
47
|
+
const source = await fetchFileContent(owner, repo, branch, componentPath, githubToken);
|
|
48
|
+
if (!source) {
|
|
49
|
+
return Response.json({ error: "Could not read component source" }, { status: 404 });
|
|
50
|
+
}
|
|
51
|
+
const serverConfig = globalThis.__SETZKASTEN_CONFIG__;
|
|
52
|
+
const contentPath = serverConfig?.storage?.contentPath || "content";
|
|
53
|
+
const sectionJsonPath = `${contentPath}/_sections/${sectionKey}.json`;
|
|
54
|
+
const sectionJson = await fetchFileContent(owner, repo, branch, sectionJsonPath, githubToken);
|
|
55
|
+
const fullConfig = globalThis.__SETZKASTEN_FULL_CONFIG__;
|
|
56
|
+
const fieldDefs = getFieldDefs(fullConfig, sectionKey);
|
|
57
|
+
const fields = [];
|
|
58
|
+
let contentData = {};
|
|
59
|
+
if (sectionJson) {
|
|
60
|
+
try {
|
|
61
|
+
contentData = JSON.parse(sectionJson);
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const [key, type] of fieldDefs) {
|
|
66
|
+
fields.push({ key, type, defaultValue: contentData[key] });
|
|
67
|
+
}
|
|
68
|
+
for (const [key, value] of Object.entries(contentData)) {
|
|
69
|
+
if (!fieldDefs.has(key)) {
|
|
70
|
+
fields.push({ key, type: "text", defaultValue: value });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const patched = await patchTemplateForFields(source, sectionKey, fields);
|
|
74
|
+
if (patched === source) {
|
|
75
|
+
return Response.json({
|
|
76
|
+
success: true,
|
|
77
|
+
noChanges: true,
|
|
78
|
+
message: "No patchable patterns found in template"
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const commitResult = await batchCommit(
|
|
82
|
+
owner,
|
|
83
|
+
repo,
|
|
84
|
+
branch,
|
|
85
|
+
[{ path: componentPath, content: patched }],
|
|
86
|
+
withTrailers(`chore: add live-preview bindings to ${sectionKey} section`),
|
|
87
|
+
headers
|
|
88
|
+
);
|
|
89
|
+
if (!commitResult.ok) {
|
|
90
|
+
return Response.json({ error: commitResult.error }, { status: 500 });
|
|
91
|
+
}
|
|
92
|
+
return Response.json({
|
|
93
|
+
success: true,
|
|
94
|
+
commitSha: commitResult.sha,
|
|
95
|
+
patchedSource: patched,
|
|
96
|
+
originalSource: source
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error("[setzkasten] migrate error:", error);
|
|
100
|
+
return Response.json(
|
|
101
|
+
{ error: error instanceof Error ? error.message : "Migration failed" },
|
|
102
|
+
{ status: 500 }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function getFieldDefs(config, sectionKey) {
|
|
107
|
+
const defs = /* @__PURE__ */ new Map();
|
|
108
|
+
if (!config) return defs;
|
|
109
|
+
for (const product of Object.values(config.products)) {
|
|
110
|
+
const section = product.sections[sectionKey];
|
|
111
|
+
if (section) {
|
|
112
|
+
for (const [key, field] of Object.entries(section.fields)) {
|
|
113
|
+
defs.set(key, field.type || "text");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return defs;
|
|
118
|
+
}
|
|
119
|
+
function deriveComponentPath(sectionKey) {
|
|
120
|
+
const componentName = sectionKey.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Section";
|
|
121
|
+
return `src/components/sections/${componentName}.astro`;
|
|
122
|
+
}
|
|
123
|
+
async function fetchFileContent(owner, repo, branch, path, token) {
|
|
124
|
+
try {
|
|
125
|
+
const response = await fetch(
|
|
126
|
+
`https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`,
|
|
127
|
+
{
|
|
128
|
+
headers: {
|
|
129
|
+
Authorization: `Bearer ${token}`,
|
|
130
|
+
Accept: "application/vnd.github+json",
|
|
131
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
if (!response.ok) return null;
|
|
136
|
+
const data = await response.json();
|
|
137
|
+
return data.encoding === "base64" ? Buffer.from(data.content, "base64").toString("utf-8") : data.content;
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function batchCommit(owner, repo, branch, files, message, headers) {
|
|
143
|
+
try {
|
|
144
|
+
const refRes = await fetch(
|
|
145
|
+
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
|
|
146
|
+
{ headers }
|
|
147
|
+
);
|
|
148
|
+
if (!refRes.ok) return { ok: false, error: `Failed to get HEAD: ${refRes.status}` };
|
|
149
|
+
const refData = await refRes.json();
|
|
150
|
+
const headSha = refData.object.sha;
|
|
151
|
+
const commitRes = await fetch(
|
|
152
|
+
`https://api.github.com/repos/${owner}/${repo}/git/commits/${headSha}`,
|
|
153
|
+
{ headers }
|
|
154
|
+
);
|
|
155
|
+
if (!commitRes.ok) return { ok: false, error: `Failed to get commit: ${commitRes.status}` };
|
|
156
|
+
const commitData = await commitRes.json();
|
|
157
|
+
const treeRes = await fetch(
|
|
158
|
+
`https://api.github.com/repos/${owner}/${repo}/git/trees`,
|
|
159
|
+
{
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers,
|
|
162
|
+
body: JSON.stringify({
|
|
163
|
+
base_tree: commitData.tree.sha,
|
|
164
|
+
tree: files.map((f) => ({
|
|
165
|
+
path: f.path,
|
|
166
|
+
mode: "100644",
|
|
167
|
+
type: "blob",
|
|
168
|
+
content: f.content
|
|
169
|
+
}))
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
if (!treeRes.ok) return { ok: false, error: `Failed to create tree: ${treeRes.status}` };
|
|
174
|
+
const treeData = await treeRes.json();
|
|
175
|
+
const newCommitRes = await fetch(
|
|
176
|
+
`https://api.github.com/repos/${owner}/${repo}/git/commits`,
|
|
177
|
+
{
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers,
|
|
180
|
+
body: JSON.stringify({
|
|
181
|
+
tree: treeData.sha,
|
|
182
|
+
parents: [headSha],
|
|
183
|
+
message
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
if (!newCommitRes.ok) return { ok: false, error: `Failed to create commit: ${newCommitRes.status}` };
|
|
188
|
+
const newCommitData = await newCommitRes.json();
|
|
189
|
+
const updateRes = await fetch(
|
|
190
|
+
`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
|
|
191
|
+
{
|
|
192
|
+
method: "PATCH",
|
|
193
|
+
headers,
|
|
194
|
+
body: JSON.stringify({ sha: newCommitData.sha })
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
if (!updateRes.ok) return { ok: false, error: `Failed to update ref: ${updateRes.status}` };
|
|
198
|
+
return { ok: true, sha: newCommitData.sha };
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return { ok: false, error: error instanceof Error ? error.message : "Commit failed" };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export {
|
|
204
|
+
POST
|
|
205
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { APIRoute } from 'astro';
|
|
2
|
+
import { SetzKastenConfig } from '@setzkasten-cms/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the full Setzkasten config.
|
|
6
|
+
* Reads the Vite build-time constant first; falls back to globalThis for
|
|
7
|
+
* local dev / test environments where the define is not applied.
|
|
8
|
+
*
|
|
9
|
+
* Without the build-time fallback, cold-start Vercel function invocations of
|
|
10
|
+
* this API route see managedSections={} and offer every adopted section
|
|
11
|
+
* (including _layout_header / _layout_footer) for re-adoption.
|
|
12
|
+
*/
|
|
13
|
+
declare function resolveFullConfig(): SetzKastenConfig | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* POST /api/setzkasten/init/scan-page
|
|
16
|
+
*
|
|
17
|
+
* Scans a single page's Astro template for section imports and identifies:
|
|
18
|
+
* 1. Unmanaged sections — not yet in the Setzkasten schema
|
|
19
|
+
* 2. Missing fields on managed sections — template fields not in the schema
|
|
20
|
+
*
|
|
21
|
+
* Body: { owner, repo, branch?, pagePath, projectRoot? }
|
|
22
|
+
* Returns: { unmanagedSections: InferredSection[], managedUpdates: ManagedUpdate[] }
|
|
23
|
+
*/
|
|
24
|
+
declare const POST: APIRoute;
|
|
25
|
+
interface LayoutRegion {
|
|
26
|
+
/** Region name: 'header' or 'footer' */
|
|
27
|
+
name: string;
|
|
28
|
+
/** HTML tag used to identify this region */
|
|
29
|
+
tag: string;
|
|
30
|
+
/** Extracted HTML content */
|
|
31
|
+
html: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Extract header and footer regions from a layout file's template.
|
|
35
|
+
* Looks for top-level <header>...</header> and <footer>...</footer> blocks.
|
|
36
|
+
*/
|
|
37
|
+
declare function extractLayoutRegions(layoutSource: string): LayoutRegion[];
|
|
38
|
+
|
|
39
|
+
export { POST, extractLayoutRegions, resolveFullConfig };
|