inital commit
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import { spawn } from "child_process";
|
||||
import { existsSync, mkdirSync } from "fs";
|
||||
import { dirname } from "path";
|
||||
|
||||
interface CertOptions {
|
||||
certPath: string;
|
||||
keyPath: string;
|
||||
commonName?: string;
|
||||
daysValid?: number;
|
||||
additionalIPs?: string[];
|
||||
}
|
||||
|
||||
export function certificatesExist(certPath: string, keyPath: string): boolean {
|
||||
return existsSync(certPath) && existsSync(keyPath);
|
||||
}
|
||||
|
||||
export async function generateSelfSignedCert(
|
||||
options: CertOptions,
|
||||
): Promise<void> {
|
||||
const {
|
||||
certPath,
|
||||
keyPath,
|
||||
commonName = "localhost",
|
||||
daysValid = 3650,
|
||||
additionalIPs = [],
|
||||
} = options;
|
||||
|
||||
const certDir = dirname(certPath);
|
||||
if (!existsSync(certDir)) {
|
||||
mkdirSync(certDir, { recursive: true });
|
||||
}
|
||||
|
||||
console.log("[TLS] Generating self-signed certificate...");
|
||||
console.log(`[TLS] Common Name: ${commonName}`);
|
||||
console.log(`[TLS] Valid for: ${daysValid} days`);
|
||||
|
||||
const sanConfig = `
|
||||
[req]
|
||||
default_bits = 2048
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
distinguished_name = dn
|
||||
req_extensions = req_ext
|
||||
x509_extensions = v3_ca
|
||||
|
||||
[dn]
|
||||
C=US
|
||||
ST=State
|
||||
L=City
|
||||
O=Overlord
|
||||
OU=IT
|
||||
CN=${commonName}
|
||||
|
||||
[req_ext]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[v3_ca]
|
||||
subjectAltName = @alt_names
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = ${commonName}
|
||||
DNS.2 = localhost
|
||||
DNS.3 = *.local
|
||||
IP.1 = 127.0.0.1
|
||||
IP.2 = ::1
|
||||
${additionalIPs.map((ip, i) => `IP.${i + 3} = ${ip}`).join("\n")}
|
||||
`;
|
||||
|
||||
const configPath = `${certDir}/openssl.cnf`;
|
||||
|
||||
try {
|
||||
await Bun.write(configPath, sanConfig);
|
||||
|
||||
await execCommand("openssl", [
|
||||
"req",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:2048",
|
||||
"-nodes",
|
||||
"-keyout",
|
||||
keyPath,
|
||||
"-out",
|
||||
certPath,
|
||||
"-days",
|
||||
daysValid.toString(),
|
||||
"-config",
|
||||
configPath,
|
||||
]);
|
||||
|
||||
try {
|
||||
await Bun.file(configPath)
|
||||
.text()
|
||||
.then(() => {
|
||||
spawn("rm", ["-f", configPath]);
|
||||
});
|
||||
} catch {}
|
||||
|
||||
console.log(`[TLS] ✓ Certificate generated successfully`);
|
||||
console.log(`[TLS] Certificate: ${certPath}`);
|
||||
console.log(`[TLS] Private Key: ${keyPath}`);
|
||||
} catch (error) {
|
||||
console.error("[TLS] Failed to generate certificate:", error);
|
||||
throw new Error(`Certificate generation failed: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
function execCommand(command: string, args: string[]): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = spawn(command, args);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
proc.stdout?.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
proc.stderr?.on("data", (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
proc.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
resolve(stdout);
|
||||
} else {
|
||||
reject(new Error(`Command failed with code ${code}: ${stderr}`));
|
||||
}
|
||||
});
|
||||
|
||||
proc.on("error", (err) => {
|
||||
reject(new Error(`Failed to execute ${command}: ${err.message}`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function isOpenSSLAvailable(): Promise<boolean> {
|
||||
try {
|
||||
await execCommand("openssl", ["version"]);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocalIPs(): string[] {
|
||||
try {
|
||||
const os = require("os");
|
||||
const interfaces = os.networkInterfaces();
|
||||
const ips: string[] = [];
|
||||
|
||||
for (const name of Object.keys(interfaces)) {
|
||||
for (const iface of interfaces[name]) {
|
||||
if (iface.family === "IPv4" && !iface.internal) {
|
||||
ips.push(iface.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ips;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user