Initial working state: CL3.5 complete (Plesk sync + suspend/unsuspend)

This commit is contained in:
2026-03-05 06:47:43 +00:00
commit a36d55eae5
53 changed files with 9925 additions and 0 deletions

116
lib/crypto.ts Normal file
View File

@@ -0,0 +1,116 @@
import {
createCipheriv,
createDecipheriv,
createHash,
randomBytes,
} from "crypto";
import { env } from "@/lib/env";
const ALGO = "aes-256-gcm";
function deriveKey(secret: string) {
return createHash("sha256").update(secret).digest();
}
export function encryptSecret(value: string) {
if (!env.encryptionKey) {
throw new Error("ENCRYPTION_KEY is not configured");
}
const iv = randomBytes(12);
const key = deriveKey(env.encryptionKey);
const cipher = createCipheriv(ALGO, key, iv);
const encrypted = Buffer.concat([
cipher.update(value, "utf8"),
cipher.final(),
]);
const tag = cipher.getAuthTag();
return `${iv.toString("base64")}:${tag.toString("base64")}:${encrypted.toString("base64")}`;
}
export function decryptSecret(payload: string) {
if (!env.encryptionKey) {
throw new Error("ENCRYPTION_KEY is not configured");
}
const [ivEncoded, tagEncoded, valueEncoded] = payload.split(":");
if (!ivEncoded || !tagEncoded || !valueEncoded) {
throw new Error("Invalid encrypted payload");
}
const key = deriveKey(env.encryptionKey);
const decipher = createDecipheriv(
ALGO,
key,
Buffer.from(ivEncoded, "base64"),
);
decipher.setAuthTag(Buffer.from(tagEncoded, "base64"));
const decrypted = Buffer.concat([
decipher.update(Buffer.from(valueEncoded, "base64")),
decipher.final(),
]);
return decrypted.toString("utf8");
}
function derivePleskKey() {
if (!env.pleskCredEncKey) {
throw new Error("PLESK_CRED_ENC_KEY is not configured");
}
const raw = env.pleskCredEncKey.trim();
if (/^[0-9a-fA-F]{64}$/.test(raw)) {
return Buffer.from(raw, "hex");
}
const b64 = Buffer.from(raw, "base64");
if (b64.length === 32) {
return b64;
}
throw new Error("PLESK_CRED_ENC_KEY must be 32-byte base64 or 64-char hex");
}
export function encryptPleskSecret(value: string, keyId = "v1") {
const iv = randomBytes(12);
const key = derivePleskKey();
const cipher = createCipheriv(ALGO, key, iv);
const encrypted = Buffer.concat([
cipher.update(value, "utf8"),
cipher.final(),
]);
const tag = cipher.getAuthTag();
return {
encryptedSecret: `${iv.toString("base64")}:${tag.toString("base64")}:${encrypted.toString("base64")}`,
secretKid: keyId,
};
}
export function decryptPleskSecret(payload: string) {
const [ivEncoded, tagEncoded, valueEncoded] = payload.split(":");
if (!ivEncoded || !tagEncoded || !valueEncoded) {
throw new Error("Invalid encrypted payload");
}
const key = derivePleskKey();
const decipher = createDecipheriv(
ALGO,
key,
Buffer.from(ivEncoded, "base64"),
);
decipher.setAuthTag(Buffer.from(tagEncoded, "base64"));
const decrypted = Buffer.concat([
decipher.update(Buffer.from(valueEncoded, "base64")),
decipher.final(),
]);
return decrypted.toString("utf8");
}