Files
indiekit-server/scripts/patch-store-github-update-fallback.mjs
Sven 53102a03b0
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m15s
fix: fall back to create when updateFile gets 404 from Gitea
Posts that exist in MongoDB but not in Gitea (e.g. due to a previous
failed write) caused HTTP 500 on re-publish: updateFile() tried to read
the file's SHA, got 404, and threw instead of creating. Now detects
Not Found and falls through to a create-style PUT (no sha field).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:37:53 +02:00

96 lines
3.1 KiB
JavaScript

/**
* Patch: make store-github updateFile fall back to create when the file doesn't exist.
*
* Problem: when a post was created in MongoDB but the Gitea write failed (e.g. due to a
* previous 401), IndieKit sees the post as existing and calls updateFile() on re-publish.
* updateFile() reads the file first to get its SHA, gets a 404, and throws instead of
* creating the file.
*
* Fix: if the read step returns "Not Found", proceed without a SHA. The Gitea Contents API
* (same as GitHub API) creates a new file when PUT is called without a sha field.
* Also fixes sha: false being sent (invalid) — now only sent when we actually have a SHA.
*/
import { readFile, writeFile, access } from "node:fs/promises";
const TARGET = "node_modules/@indiekit/store-github/index.js";
const MARKER = "// [patch] store-github-update-fallback";
const OLD_READ = ` let readResponse;
try {
debug(\`Try reading file \${filePath} in repo \${repo}, branch \${branch}\`);
readResponse = await this.#client(\`\${filePath}?ref=\${branch}\`);
} catch (error) {
const message = crudErrorMessage({
error,
operation: "read",
filePath,
repo,
branch,
});
debug(message);
throw new Error(message);
}
const { sha } = await readResponse.json();`;
const NEW_READ = ` let sha; ${MARKER}
try {
debug(\`Try reading file \${filePath} in repo \${repo}, branch \${branch}\`);
const readResponse = await this.#client(\`\${filePath}?ref=\${branch}\`);
const readJson = await readResponse.json();
sha = readJson.sha;
} catch (error) {
if (error.message && error.message.includes("Not Found")) {
// File doesn't exist yet — will create it via PUT without sha
debug(\`File \${filePath} not found in repo \${repo} — will create instead of update\`);
sha = undefined;
} else {
const message = crudErrorMessage({
error,
operation: "read",
filePath,
repo,
branch,
});
debug(message);
throw new Error(message);
}
}`;
const OLD_SHA = ` sha: sha || false,`;
const NEW_SHA = ` ...(sha ? { sha } : {}), ${MARKER}`;
async function exists(p) {
try { await access(p); return true; } catch { return false; }
}
if (!(await exists(TARGET))) {
console.log(`[postinstall] store-github-update-fallback: target not found, skipping`);
process.exit(0);
}
const source = await readFile(TARGET, "utf8");
if (source.includes(MARKER)) {
console.log("[postinstall] store-github-update-fallback: already patched");
process.exit(0);
}
if (!source.includes(OLD_READ)) {
console.warn("[postinstall] store-github-update-fallback: read snippet not found — skipping");
process.exit(0);
}
if (!source.includes(OLD_SHA)) {
console.warn("[postinstall] store-github-update-fallback: sha snippet not found — skipping");
process.exit(0);
}
const updated = source
.replace(OLD_READ, NEW_READ)
.replace(OLD_SHA, NEW_SHA);
await writeFile(TARGET, updated, "utf8");
console.log(`[postinstall] store-github-update-fallback: patched ${TARGET}`);