refactor(certificates): move to core package (#4686)

This commit is contained in:
Meier Lukas
2025-12-19 09:49:12 +01:00
committed by GitHub
parent 949a006b35
commit 2b971b9392
25 changed files with 241 additions and 132 deletions

View File

@@ -0,0 +1,74 @@
import { X509Certificate } from "node:crypto";
import fsSync from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import { rootCertificates } from "node:tls";
const getCertificateFolder = () => {
if (process.env.NODE_ENV !== "production") return process.env.LOCAL_CERTIFICATE_PATH;
return process.env.LOCAL_CERTIFICATE_PATH ?? path.join("/appdata", "trusted-certificates");
};
export const loadCustomRootCertificatesAsync = async () => {
const folder = getCertificateFolder();
if (!folder) {
return [];
}
if (!fsSync.existsSync(folder)) {
await fs.mkdir(folder, { recursive: true });
}
const dirContent = await fs.readdir(folder);
return await Promise.all(
dirContent
.filter((file) => file.endsWith(".crt") || file.endsWith(".pem"))
.map(async (file) => ({
content: await fs.readFile(path.join(folder, file), "utf8"),
fileName: file,
})),
);
};
export const getAllTrustedCertificatesAsync = async () => {
const customCertificates = await loadCustomRootCertificatesAsync();
return rootCertificates.concat(customCertificates.map((cert) => cert.content));
};
export const removeCustomRootCertificateAsync = async (fileName: string) => {
const folder = getCertificateFolder();
if (!folder) {
return null;
}
const existingFiles = await fs.readdir(folder, { withFileTypes: true });
if (!existingFiles.some((file) => file.isFile() && file.name === fileName)) {
throw new Error(`File ${fileName} does not exist`);
}
const fullPath = path.join(folder, fileName);
const content = await fs.readFile(fullPath, "utf8");
await fs.rm(fullPath);
try {
return new X509Certificate(content);
} catch {
return null;
}
};
export const addCustomRootCertificateAsync = async (fileName: string, content: string) => {
const folder = getCertificateFolder();
if (!folder) {
throw new Error(
"When you want to use custom certificates locally you need to set LOCAL_CERTIFICATE_PATH to an absolute path",
);
}
if (fileName.includes("/")) {
throw new Error("Invalid file name");
}
await fs.writeFile(path.join(folder, fileName), content);
};

View File

@@ -0,0 +1,15 @@
import { mysqlTable, primaryKey, text, varchar } from "drizzle-orm/mysql-core";
export const trustedCertificateHostnames = mysqlTable(
"trusted_certificate_hostname",
{
hostname: varchar({ length: 256 }).notNull(),
thumbprint: varchar({ length: 128 }).notNull(),
certificate: text().notNull(),
},
(table) => ({
compoundKey: primaryKey({
columns: [table.hostname, table.thumbprint],
}),
}),
);

View File

@@ -0,0 +1,15 @@
import { pgTable, primaryKey, text, varchar } from "drizzle-orm/pg-core";
export const trustedCertificateHostnames = pgTable(
"trusted_certificate_hostname",
{
hostname: varchar({ length: 256 }).notNull(),
thumbprint: varchar({ length: 128 }).notNull(),
certificate: text().notNull(),
},
(table) => ({
compoundKey: primaryKey({
columns: [table.hostname, table.thumbprint],
}),
}),
);

View File

@@ -0,0 +1,10 @@
import { createSchema } from "../../../db";
import * as mysql from "./mysql";
import * as postgresql from "./postgresql";
import * as sqlite from "./sqlite";
export const schema = createSchema({
"better-sqlite3": () => sqlite,
mysql2: () => mysql,
"node-postgres": () => postgresql,
});

View File

@@ -0,0 +1,15 @@
import { primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
export const trustedCertificateHostnames = sqliteTable(
"trusted_certificate_hostname",
{
hostname: text().notNull(),
thumbprint: text().notNull(),
certificate: text().notNull(),
},
(table) => ({
compoundKey: primaryKey({
columns: [table.hostname, table.thumbprint],
}),
}),
);

View File

@@ -0,0 +1,8 @@
import { createDb } from "../../db";
import { schema } from "./db/schema";
const db = createDb(schema);
export const getTrustedCertificateHostnamesAsync = async () => {
return await db.query.trustedCertificateHostnames.findMany();
};

View File

@@ -0,0 +1,7 @@
export { getTrustedCertificateHostnamesAsync } from "./hostnames";
export {
addCustomRootCertificateAsync,
removeCustomRootCertificateAsync,
getAllTrustedCertificatesAsync,
loadCustomRootCertificatesAsync,
} from "./files";