131 lines
3.0 KiB
JavaScript
131 lines
3.0 KiB
JavaScript
import { generateKeyPairSync } from "node:crypto";
|
|
|
|
import { MongoClient } from "mongodb";
|
|
|
|
import config from "../indiekit.config.mjs";
|
|
|
|
const strictMode = process.env.REQUIRE_MONGO !== "0";
|
|
const mongodbUrl = config.application?.mongodbUrl;
|
|
|
|
function hasPublicPem(value) {
|
|
return (
|
|
typeof value === "string" &&
|
|
value.includes("-----BEGIN PUBLIC KEY-----") &&
|
|
value.includes("-----END PUBLIC KEY-----")
|
|
);
|
|
}
|
|
|
|
function hasPrivatePem(value) {
|
|
return (
|
|
typeof value === "string" &&
|
|
value.includes("-----BEGIN PRIVATE KEY-----") &&
|
|
value.includes("-----END PRIVATE KEY-----")
|
|
);
|
|
}
|
|
|
|
function hasValidRsaPem(doc) {
|
|
return hasPublicPem(doc?.publicKeyPem) && hasPrivatePem(doc?.privateKeyPem);
|
|
}
|
|
|
|
function createRsaPemPair() {
|
|
const { publicKey, privateKey } = generateKeyPairSync("rsa", {
|
|
modulusLength: 2048,
|
|
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
});
|
|
|
|
return { publicKeyPem: publicKey, privateKeyPem: privateKey };
|
|
}
|
|
|
|
if (!mongodbUrl) {
|
|
console.warn(
|
|
"[preflight] ActivityPub RSA key sync skipped: MongoDB URL is not configured.",
|
|
);
|
|
process.exit(0);
|
|
}
|
|
|
|
const client = new MongoClient(mongodbUrl, { connectTimeoutMS: 5000 });
|
|
|
|
try {
|
|
await client.connect();
|
|
|
|
const apKeys = client.db().collection("ap_keys");
|
|
const now = new Date().toISOString();
|
|
const typedRsaDoc = await apKeys.findOne({ type: "rsa" });
|
|
|
|
if (hasValidRsaPem(typedRsaDoc)) {
|
|
console.log("[preflight] ActivityPub RSA key pair already present");
|
|
process.exit(0);
|
|
}
|
|
|
|
if (typedRsaDoc) {
|
|
const rsaPair = createRsaPemPair();
|
|
|
|
await apKeys.updateOne(
|
|
{ _id: typedRsaDoc._id },
|
|
{
|
|
$set: {
|
|
type: "rsa",
|
|
...rsaPair,
|
|
updatedAt: now,
|
|
},
|
|
},
|
|
);
|
|
|
|
console.log(
|
|
"[preflight] Repaired ActivityPub RSA key pair in existing type='rsa' document",
|
|
);
|
|
process.exit(0);
|
|
}
|
|
|
|
const legacyPemDoc = await apKeys.findOne({
|
|
publicKeyPem: { $exists: true },
|
|
privateKeyPem: { $exists: true },
|
|
});
|
|
|
|
if (hasValidRsaPem(legacyPemDoc)) {
|
|
if (legacyPemDoc.type !== "rsa") {
|
|
await apKeys.updateOne(
|
|
{ _id: legacyPemDoc._id },
|
|
{
|
|
$set: {
|
|
type: "rsa",
|
|
updatedAt: now,
|
|
},
|
|
},
|
|
);
|
|
|
|
console.log("[preflight] Marked existing ActivityPub PEM key as type='rsa'");
|
|
} else {
|
|
console.log("[preflight] ActivityPub legacy RSA PEM key already usable");
|
|
}
|
|
|
|
process.exit(0);
|
|
}
|
|
|
|
const rsaPair = createRsaPemPair();
|
|
|
|
await apKeys.insertOne({
|
|
type: "rsa",
|
|
...rsaPair,
|
|
createdAt: now,
|
|
});
|
|
|
|
console.log("[preflight] Generated and stored ActivityPub RSA key pair");
|
|
} catch (error) {
|
|
const message = `[preflight] ActivityPub RSA key sync failed: ${error.message}`;
|
|
|
|
if (strictMode) {
|
|
console.error(message);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.warn(`${message} Continuing because strict mode is disabled.`);
|
|
} finally {
|
|
try {
|
|
await client.close();
|
|
} catch {
|
|
// no-op
|
|
}
|
|
}
|