diff --git a/packages/ui/certd-client/vite.config.ts b/packages/ui/certd-client/vite.config.ts index bba9ba3e..5cf79419 100644 --- a/packages/ui/certd-client/vite.config.ts +++ b/packages/ui/certd-client/vite.config.ts @@ -16,6 +16,7 @@ process.env.VITE_APP_VERSION = require("./package.json").version; process.env.VITE_APP_BUILD_TIME = require("dayjs")().format("YYYY-M-D HH:mm:ss"); import { theme } from "ant-design-vue"; +import * as https from "node:https"; const { defaultAlgorithm, defaultSeed } = theme; @@ -103,7 +104,9 @@ export default ({ command, mode }) => { // with options "/api": { //配套后端 https://github.com/fast-crud/fs-server-js - target: "http://127.0.0.1:7001" + target: "https://127.0.0.1:7002", + //忽略证书 + agent: new https.Agent({ rejectUnauthorized: false }) } } } diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index d2314097..3bc69a18 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -74,6 +74,7 @@ "md5": "^2.3.0", "mwtsc": "^1.4.0", "nanoid": "^5.0.7", + "node-forge": "^1.3.1", "nodemailer": "^6.9.3", "pg": "^8.12.0", "psl": "^1.9.0", diff --git a/packages/ui/certd-server/src/config/config.default.ts b/packages/ui/certd-server/src/config/config.default.ts index 352075c3..167598d0 100644 --- a/packages/ui/certd-server/src/config/config.default.ts +++ b/packages/ui/certd-server/src/config/config.default.ts @@ -24,6 +24,12 @@ const development = { koa: { port: 7001, }, + https: { + enabled: true, + port: 7002, + key: './data/ssl/cert.key', + cert: './data/ssl/cert.crt', + }, staticFile: { usePrecompiledGzip: true, buffer: true, diff --git a/packages/ui/certd-server/src/configuration.ts b/packages/ui/certd-server/src/configuration.ts index f90f3a88..49ca8e1d 100644 --- a/packages/ui/certd-server/src/configuration.ts +++ b/packages/ui/certd-server/src/configuration.ts @@ -1,5 +1,6 @@ import { App, Configuration } from '@midwayjs/core'; import * as koa from '@midwayjs/koa'; +import { IMidwayKoaContext, NextFunction } from '@midwayjs/koa'; import * as orm from '@midwayjs/typeorm'; import * as cache from '@midwayjs/cache'; import * as validate from '@midwayjs/validate'; @@ -18,7 +19,7 @@ import * as libServer from '@certd/lib-server'; import * as commercial from '@certd/commercial-core'; import * as upload from '@midwayjs/upload'; import { setLogger } from '@certd/acme-client'; -import { IMidwayKoaContext, NextFunction } from '@midwayjs/koa'; + process.on('uncaughtException', error => { console.error('未捕获的异常:', error); // 在这里可以添加日志记录、发送错误通知等操作 diff --git a/packages/ui/certd-server/src/modules/auto/auto-z.ts b/packages/ui/certd-server/src/modules/auto/auto-z.ts index aa983450..9a222cfa 100644 --- a/packages/ui/certd-server/src/modules/auto/auto-z.ts +++ b/packages/ui/certd-server/src/modules/auto/auto-z.ts @@ -1,8 +1,10 @@ -import { Autoload, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core'; +import { App, Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core'; import { getPlusInfo, isPlus, logger } from '@certd/pipeline'; import { SysInstallInfo, SysSettingsService } from '@certd/lib-server'; import { getVersion } from '../../utils/version.js'; import dayjs from 'dayjs'; +import { Application } from '@midwayjs/koa'; +import { HttpsServerOptions, startHttpsServer } from './https/server.js'; @Autoload() @Scope(ScopeEnum.Singleton) @@ -10,8 +12,17 @@ export class AutoZPrint { @Inject() sysSettingsService: SysSettingsService; + @App() + app: Application; + + @Config('https') + httpsConfig: HttpsServerOptions; + @Init() async init() { + //监听https + this.startHttpsServer(); + const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo); logger.info('========================================='); logger.info('当前站点ID:', installInfo.siteId); @@ -24,4 +35,15 @@ export class AutoZPrint { logger.info('Certd已启动'); logger.info('========================================='); } + + async startHttpsServer() { + if (!this.httpsConfig.enabled) { + logger.info('Https server is not enabled'); + return; + } + await startHttpsServer({ + ...this.httpsConfig, + app: this.app, + }); + } } diff --git a/packages/ui/certd-server/src/modules/auto/https/self-certificate.ts b/packages/ui/certd-server/src/modules/auto/https/self-certificate.ts new file mode 100644 index 00000000..038f6e1b --- /dev/null +++ b/packages/ui/certd-server/src/modules/auto/https/self-certificate.ts @@ -0,0 +1,44 @@ +import { logger } from '@certd/pipeline'; +import fs from 'fs'; +// @ts-ignore +import forge from 'node-forge'; + +export function createSelfCertificate(opts: { crtPath: string; keyPath: string }) { + // 生成密钥对 + const keypair = forge.pki.rsa.generateKeyPair(2048); + + // 创建自签名证书 + const cert = forge.pki.createCertificate(); + cert.publicKey = keypair.publicKey; + cert.serialNumber = '01'; + cert.validFrom = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(); // 1天前 + cert.validTo = new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10).toISOString(); // 10年后 + // 创建主题 + const attrs = [ + { + name: 'commonName', + value: 'self-certificate.certd', // 或者你的域名 + }, + ]; + cert.setSubject(attrs); + cert.setIssuer(attrs); + cert.sign(keypair.privateKey, forge.md.sha256.create()); + + // 导出证书和私钥 + const pemCert = forge.pki.certificateToPem(cert); + const pemKey = forge.pki.privateKeyToPem(keypair.privateKey); + + // 写入文件 + logger.info('生成自签名证书成功'); + logger.info(`自签证书保存路径: ${opts.crtPath}`); + logger.info(`自签私钥保存路径: ${opts.keyPath}`); + fs.writeFileSync(opts.crtPath, pemCert); + fs.writeFileSync(opts.keyPath, pemKey); + + return { + crtPath: opts.crtPath, + keyPath: opts.keyPath, + crt: pemCert, + key: pemKey, + }; +} diff --git a/packages/ui/certd-server/src/modules/auto/https/server.ts b/packages/ui/certd-server/src/modules/auto/https/server.ts new file mode 100644 index 00000000..e6894600 --- /dev/null +++ b/packages/ui/certd-server/src/modules/auto/https/server.ts @@ -0,0 +1,52 @@ +import https from 'node:https'; +import fs from 'fs'; +import { Application } from '@midwayjs/koa'; +import { createSelfCertificate } from './self-certificate.js'; +import { logger } from '@certd/pipeline'; + +export type HttpsServerOptions = { + enabled: boolean; + app?: Application; + port: number; + key: string; + cert: string; +}; + +export async function startHttpsServer(opts: HttpsServerOptions) { + // const httpsServer = https.createServer({ + // key: fs.readFileSync(path.join(__dirname, '../ssl/2_certd.cn.key')), + // cert + + if (!opts.key || !opts.cert) { + logger.error('证书路径未配置,无法启动https服务,请先配置:koa.https.key和koa.https.cert'); + return; + } + + if (!fs.existsSync(opts.key) || !fs.existsSync(opts.cert)) { + logger.info('证书文件不存在,将生成自签名证书'); + createSelfCertificate({ + crtPath: opts.cert, + keyPath: opts.key, + }); + } + logger.info('准备启动https服务'); + const httpServer = https.createServer( + { + cert: fs.readFileSync(opts.cert), + key: fs.readFileSync(opts.key), + }, + opts.app.callback() + ); + const hostname = '0.0.0.0'; + // A function that runs in the context of the http server + // and reports what type of server listens on which port + function listeningReporter() { + // `this` refers to the http server here + logger.info(`Https server is listening on https://${hostname}:${opts.port}`); + } + try { + httpServer.listen(opts.port, hostname, listeningReporter); + } catch (e) { + logger.error('启动https服务失败', e); + } +}