chore: format

This commit is contained in:
xiaojunnuo
2026-05-31 01:41:33 +08:00
parent acd440106b
commit 4b57a0d729
557 changed files with 12530 additions and 14039 deletions
@@ -9,7 +9,8 @@ import { AutoPrint } from "./auto-print.js";
@Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class AutoARegister { //这个A是必须,让他排在第一个 进行init,否则会被其他init模块抢先注册导致报错
export class AutoARegister {
//这个A是必须,让他排在第一个 进行init,否则会被其他init模块抢先注册导致报错
@Inject()
autoInitSite: AutoInitSite;
@@ -1,18 +1,18 @@
import { logger } from '@certd/basic';
import { SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { logger } from "@certd/basic";
import { SysSettingsService, SysSiteInfo } from "@certd/lib-server";
import { getPlusInfo, isPlus } from "@certd/plus-core";
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { Config, Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import dayjs from "dayjs";
import { Between } from "typeorm";
import { DomainService } from '../cert/service/domain-service.js';
import { Cron } from '../cron/cron.js';
import { DomainService } from "../cert/service/domain-service.js";
import { Cron } from "../cron/cron.js";
import { UserSiteMonitorSetting } from "../mine/service/models.js";
import { UserSettingsService } from "../mine/service/user-settings-service.js";
import { SiteInfoService } from '../monitor/index.js';
import { SiteInfoService } from "../monitor/index.js";
import { NotificationService } from "../pipeline/service/notification-service.js";
import { PipelineService } from '../pipeline/service/pipeline-service.js';
import { PipelineService } from "../pipeline/service/pipeline-service.js";
import { UserService } from "../sys/authority/service/user-service.js";
import { ProjectService } from '../sys/enterprise/service/project-service.js';
import { ProjectService } from "../sys/enterprise/service/project-service.js";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
@@ -20,13 +20,13 @@ export class AutoCron {
@Inject()
pipelineService: PipelineService;
@Config('cron.onlyAdminUser')
@Config("cron.onlyAdminUser")
private onlyAdminUser: boolean;
@Config('cron.immediateTriggerOnce')
@Config("cron.immediateTriggerOnce")
private immediateTriggerOnce = false;
@Config('cron.immediateTriggerSiteMonitor')
@Config("cron.immediateTriggerSiteMonitor")
private immediateTriggerSiteMonitor = false;
@Inject()
@@ -51,12 +51,10 @@ export class AutoCron {
@Inject()
projectService: ProjectService;
async init() {
logger.info('加载定时trigger开始');
logger.info("加载定时trigger开始");
await this.pipelineService.onStartup(this.immediateTriggerOnce, this.onlyAdminUser);
logger.info('加载定时trigger完成');
logger.info("加载定时trigger完成");
//
// const meta = getClassMetadata(CLASS_KEY, this.echoPlugin);
// console.log('meta', meta);
@@ -64,183 +62,180 @@ export class AutoCron {
// console.log('metas', metas);
await this.registerSiteMonitorCron();
await this.registerPlusExpireCheckCron();
await this.registerUserExpireCheckCron();
await this.registerDomainExpireCheckCron();
}
async registerSiteMonitorCron() {
//先注册公共job
logger.info(`注册公共站点证书检查定时任务`)
const randomMinute = Math.floor(Math.random() * 60)
logger.info(`注册公共站点证书检查定时任务`);
const randomMinute = Math.floor(Math.random() * 60);
this.cron.register({
name: 'siteMonitor',
name: "siteMonitor",
cron: `0 ${randomMinute} 0 * * *`,
job:async ()=>{
logger.info(`开始公共站点证书检查任务`)
await this.siteInfoService.triggerCommonJob()
logger.info(`公共站点证书检查任务完成`)
job: async () => {
logger.info(`开始公共站点证书检查任务`);
await this.siteInfoService.triggerCommonJob();
logger.info(`公共站点证书检查任务完成`);
},
});
logger.info(`注册公共站点证书检查定时任务完成`)
logger.info(`注册公共站点证书检查定时任务完成`);
//注册用户独立的检查时间
logger.info(`注册用户独立站点证书检查定时任务`)
logger.info(`注册用户独立站点证书检查定时任务`);
const monitorSettingList = await this.userSettingsService.list({
query:{
query: {
key: UserSiteMonitorSetting.__key__,
}
})
},
});
for (const item of monitorSettingList) {
const setting = item.setting ? JSON.parse(item.setting):{}
if(!setting?.cron){
continue
const setting = item.setting ? JSON.parse(item.setting) : {};
if (!setting?.cron) {
continue;
}
await this.siteInfoService.registerSiteMonitorJob(item.userId,item.projectId)
await this.siteInfoService.registerSiteMonitorJob(item.userId, item.projectId);
}
logger.info(`注册用户独立站点证书检查定时任务完成`)
logger.info(`注册用户独立站点证书检查定时任务完成`);
if (this.immediateTriggerSiteMonitor) {
logger.info(`立即触发一次公共站点证书检查任务`)
await this.siteInfoService.triggerCommonJob()
logger.info(`立即触发一次公共站点证书检查任务`);
await this.siteInfoService.triggerCommonJob();
}
}
registerPlusExpireCheckCron(){
registerPlusExpireCheckCron() {
// 添加plus即将到期检查任务
this.cron.register({
name: 'plus-expire-check',
name: "plus-expire-check",
cron: `0 10 9 * * *`, // 一天只能检查一次,否则会重复发送通知
job: async () => {
const plusInfo = getPlusInfo()
if (!plusInfo.originVipType || plusInfo.originVipType==="free" ) {
return
const plusInfo = getPlusInfo();
if (!plusInfo.originVipType || plusInfo.originVipType === "free") {
return;
}
let label ="专业版"
if( plusInfo.originVipType === 'comm'){
label = "商业版"
let label = "专业版";
if (plusInfo.originVipType === "comm") {
label = "商业版";
}
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo)
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
const appTitle = siteInfo.title || "certd"
const expiresDate = dayjs(plusInfo.expireTime).format("YYYY-MM-DD")
const appTitle = siteInfo.title || "certd";
const expiresDate = dayjs(plusInfo.expireTime).format("YYYY-MM-DD");
// plusInfo.expireTime= dayjs("2025-06-10").valueOf()
let expiresDays =Math.floor((plusInfo.expireTime - new Date().getTime())/ 1000 / 60 / 60 / 24)
let title = ""
let content =""
if(expiresDays === 20 ||expiresDays === 10 || expiresDays === 3 || expiresDays === 1 || expiresDays === 0){
title = `vip(${label})即将到期`
content = `您的${appTitle} vip (${label})剩余${expiresDays}天(${expiresDate})到期,请及时续期,以免影响业务`
}else if (expiresDays === -1 || expiresDays === -3 || expiresDays === -7) {
title = `vip(${label})已过期`
content = `您的${appTitle} vip (${label})已过期${Math.abs(expiresDays)}天(${expiresDate}),请尽快续期,以免影响业务`
const expiresDays = Math.floor((plusInfo.expireTime - new Date().getTime()) / 1000 / 60 / 60 / 24);
let title = "";
let content = "";
if (expiresDays === 20 || expiresDays === 10 || expiresDays === 3 || expiresDays === 1 || expiresDays === 0) {
title = `vip(${label})即将到期`;
content = `您的${appTitle} vip (${label})剩余${expiresDays}天(${expiresDate})到期,请及时续期,以免影响业务`;
} else if (expiresDays === -1 || expiresDays === -3 || expiresDays === -7) {
title = `vip(${label})已过期`;
content = `您的${appTitle} vip (${label})已过期${Math.abs(expiresDays)}天(${expiresDate}),请尽快续期,以免影响业务`;
}
if(title){
logger.warn(title)
logger.warn(content)
if (title) {
logger.warn(title);
logger.warn(content);
const url = await this.notificationService.getBindUrl("");
const adminUsers = await this.userService.getAdmins()
const adminUsers = await this.userService.getAdmins();
for (const adminUser of adminUsers) {
logger.info(`发送vip到期通知给管理员:${adminUser.username}`)
await this.notificationService.send({
useDefault: true,
logger: logger,
body:{
title,
content,
errorMessage:title,
url,
notificationType: "vipExpireRemind",
}
},adminUser.id)
logger.info(`发送vip到期通知给管理员:${adminUser.username}`);
await this.notificationService.send(
{
useDefault: true,
logger: logger,
body: {
title,
content,
errorMessage: title,
url,
notificationType: "vipExpireRemind",
},
},
adminUser.id
);
}
}
}
})
},
});
}
registerUserExpireCheckCron() {
// 添加plus即将到期检查任务
this.cron.register({
name: 'user-expire-check',
name: "user-expire-check",
cron: `0 20 9 * * *`, // 一天只能检查一次,否则会重复发送通知
job: async () => {
const getExpiresDaysUsers = async (days: number) => {
const targetDate = dayjs().add(days, 'day')
const startTime = targetDate.startOf('day').valueOf()
const endTime = targetDate.endOf('day').valueOf()
const targetDate = dayjs().add(days, "day");
const startTime = targetDate.startOf("day").valueOf();
const endTime = targetDate.endOf("day").valueOf();
return await this.userService.find({
where: {
validTime: Between(startTime, endTime),
status: 1
}
})
}
status: 1,
},
});
};
const notifyExpiresDaysUsers = async (days: number) => {
const list = await getExpiresDaysUsers(days)
const list = await getExpiresDaysUsers(days);
if (list.length === 0) {
return
return;
}
let title = `账号即将到期`
let content = `您的账号剩余${days}天到期,请及时续期,以免影响业务`
let title = `账号即将到期`;
let content = `您的账号剩余${days}天到期,请及时续期,以免影响业务`;
if (days <= 0) {
title = `账号已过期`
content = `您的账号已过期${Math.abs(days)}天,请尽快续期,以免影响业务`
title = `账号已过期`;
content = `您的账号已过期${Math.abs(days)}天,请尽快续期,以免影响业务`;
}
const url = await this.notificationService.getBindUrl("");
for (const user of list) {
logger.info(`发送到期通知给用户:${user.username}`)
await this.notificationService.send({
useDefault: true,
logger: logger,
body: {
title,
content,
errorMessage: title,
url,
notificationType: "userExpireRemind",
}
}, user.id)
logger.info(`发送到期通知给用户:${user.username}`);
await this.notificationService.send(
{
useDefault: true,
logger: logger,
body: {
title,
content,
errorMessage: title,
url,
notificationType: "userExpireRemind",
},
},
user.id
);
}
}
};
await notifyExpiresDaysUsers(7)
await notifyExpiresDaysUsers(3)
await notifyExpiresDaysUsers(1)
await notifyExpiresDaysUsers(0)
await notifyExpiresDaysUsers(-1)
await notifyExpiresDaysUsers(-3)
}
})
await notifyExpiresDaysUsers(7);
await notifyExpiresDaysUsers(3);
await notifyExpiresDaysUsers(1);
await notifyExpiresDaysUsers(0);
await notifyExpiresDaysUsers(-1);
await notifyExpiresDaysUsers(-3);
},
});
}
registerDomainExpireCheckCron(){
if (!isPlus()){
return
registerDomainExpireCheckCron() {
if (!isPlus()) {
return;
}
// 添加域名即将到期同步任务
const randomWeek = Math.floor(Math.random() * 7) + 1
const randomHour = Math.floor(Math.random() * 24)
const randomMinute = Math.floor(Math.random() * 60)
logger.info(`注册域名注册过期时间同步任务,每周${randomWeek} ${randomHour}:${randomMinute}检查一次`)
const randomWeek = Math.floor(Math.random() * 7) + 1;
const randomHour = Math.floor(Math.random() * 24);
const randomMinute = Math.floor(Math.random() * 60);
logger.info(`注册域名注册过期时间同步任务,每周${randomWeek} ${randomHour}:${randomMinute}检查一次`);
this.cron.register({
name: 'domain-expire-check',
name: "domain-expire-check",
cron: `0 ${randomMinute} ${randomHour} ? * ${randomWeek}`, // 每周随机一天检查一次
job: async () => {
await this.domainService.startSyncExpirationTask({})
}
})
await this.domainService.startSyncExpirationTask({});
},
});
}
}
@@ -1,9 +1,9 @@
import { logger } from '@certd/basic';
import { EncryptService, PlusService, SysInstallInfo, SysPrivateSettings, SysSettingsService } from '@certd/lib-server';
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import crypto from 'crypto';
import { nanoid } from 'nanoid';
import { UserService } from '../sys/authority/service/user-service.js';
import { logger } from "@certd/basic";
import { EncryptService, PlusService, SysInstallInfo, SysPrivateSettings, SysSettingsService } from "@certd/lib-server";
import { Config, Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import crypto from "crypto";
import { nanoid } from "nanoid";
import { UserService } from "../sys/authority/service/user-service.js";
import { SafeService } from "../sys/settings/safe-service.js";
@Provide()
@@ -12,7 +12,7 @@ export class AutoInitSite {
@Inject()
userService: UserService;
@Config('typeorm.dataSource.default.type')
@Config("typeorm.dataSource.default.type")
dbType: string;
@Inject()
@@ -24,10 +24,9 @@ export class AutoInitSite {
@Inject()
encryptService: EncryptService;
async init() {
logger.info('初始化站点开始');
logger.info("初始化站点开始");
await this.startOptimizeDb();
//安装信息
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
@@ -45,7 +44,7 @@ export class AutoInitSite {
if (!privateInfo.encryptSecret) {
const secretKey = crypto.randomBytes(32);
privateInfo.encryptSecret = secretKey.toString('base64');
privateInfo.encryptSecret = secretKey.toString("base64");
await this.sysSettingsService.saveSetting(privateInfo);
}
@@ -61,32 +60,32 @@ export class AutoInitSite {
try {
await this.plusService.verify();
} catch (e) {
logger.error('授权许可验证失败', e);
logger.error("授权许可验证失败", e);
}
//加载设置
await this.sysSettingsService.reloadSettings();
//加载站点隐藏配置
await this.safeService.reloadHiddenStatus(true)
logger.info('初始化站点完成');
await this.safeService.reloadHiddenStatus(true);
logger.info("初始化站点完成");
}
async startOptimizeDb() {
//优化数据库
//检查当前数据库类型为sqlite
if (this.dbType === 'better-sqlite3') {
const res = await this.userService.repository.query('PRAGMA auto_vacuum;');
if (this.dbType === "better-sqlite3") {
const res = await this.userService.repository.query("PRAGMA auto_vacuum;");
if (!(res && res.length > 0 && res[0].auto_vacuum > 0)) {
//未开启自动优化
await this.userService.repository.query('PRAGMA auto_vacuum = INCREMENTAL;');
logger.info('sqlite数据库自动优化已开启');
await this.userService.repository.query("PRAGMA auto_vacuum = INCREMENTAL;");
logger.info("sqlite数据库自动优化已开启");
}
const optimizeDb = async () => {
logger.info('sqlite数据库空间优化开始');
await this.userService.repository.query('VACUUM');
logger.info('sqlite数据库空间优化完成');
logger.info("sqlite数据库空间优化开始");
await this.userService.repository.query("VACUUM");
logger.info("sqlite数据库空间优化完成");
};
await optimizeDb();
setInterval(optimizeDb, 1000 * 60 * 60 * 24);
@@ -9,28 +9,26 @@ export class AutoLoadPlugins {
@Inject()
pluginService: PluginService;
async init() {
logger.info(`加载插件开始,加载模式:${process.env.certd_plugin_loadmode}`);
if (process.env.certd_plugin_loadmode === "metadata") {
await this.pluginService.registerFromLocal("./metadata")
}else{
await this.pluginService.registerFromLocal("./metadata");
} else {
// await import("../../plugins/index.js")
const fs = await import("fs");
const list = fs.readdirSync("./dist/plugins");
console.log("list", list);
for (const file of list) {
if (!file.includes(".")){
if (!file.includes(".")) {
logger.info(`加载插件文件:${file}`);
await import(`../../plugins/${file}/index.js`);
}
}
}
// await import("../../plugins/index.js")
await this.pluginService.registerFromDb()
await this.pluginService.registerFromDb();
await registerPaymentProviders();
logger.info(`加载插件完成,加载模式:${process.env.certd_plugin_loadmode}`);
}
}
@@ -1,6 +1,6 @@
import { logger, utils } from '@certd/basic';
import { UserSuiteService } from '@certd/commercial-core';
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { logger, utils } from "@certd/basic";
import { UserSuiteService } from "@certd/commercial-core";
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
@@ -12,8 +12,8 @@ export class AutoMitterRegister {
await this.registerOnNewUser();
}
async registerOnNewUser() {
utils.mitter.on('register', async (req: { userId: number }) => {
logger.info('register event', req.userId);
utils.mitter.on("register", async (req: { userId: number }) => {
logger.info("register event", req.userId);
await this.userSuiteService.presentGiftSuite(req.userId);
});
}
@@ -1,15 +1,15 @@
import { App, Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { getPlusInfo, isPlus } from '@certd/plus-core';
import { isDev, logger } from '@certd/basic';
import { App, Config, Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { getPlusInfo, isPlus } from "@certd/plus-core";
import { isDev, logger } from "@certd/basic";
import { SysInstallInfo, SysSettingsService } from '@certd/lib-server';
import { getVersion } from '../../utils/version.js';
import dayjs from 'dayjs';
import { Application } from '@midwayjs/koa';
import { httpsServer, HttpsServerOptions } from './https/server.js';
import { UserService } from '../sys/authority/service/user-service.js';
import { UserSettingsService } from '../mine/service/user-settings-service.js';
import { startProxyServer } from './proxy/server.js';
import { SysInstallInfo, SysSettingsService } from "@certd/lib-server";
import { getVersion } from "../../utils/version.js";
import dayjs from "dayjs";
import { Application } from "@midwayjs/koa";
import { httpsServer, HttpsServerOptions } from "./https/server.js";
import { UserService } from "../sys/authority/service/user-service.js";
import { UserSettingsService } from "../mine/service/user-settings-service.js";
import { startProxyServer } from "./proxy/server.js";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
@@ -20,18 +20,18 @@ export class AutoPrint {
@App()
app: Application;
@Config('https')
@Config("https")
httpsConfig: HttpsServerOptions;
@Config('koa')
@Config("koa")
koaConfig: any;
@Inject()
userService: UserService;
@Inject()
userSettingsService: UserSettingsService;
@Config('system.resetAdminPasswd')
@Config("system.resetAdminPasswd")
private resetAdminPasswd: boolean;
async init() {
@@ -43,31 +43,31 @@ export class AutoPrint {
this.startHeapLog();
}
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
logger.info('=========================================');
logger.info('当前站点ID:', installInfo.siteId);
logger.info("=========================================");
logger.info("当前站点ID:", installInfo.siteId);
const version = await getVersion();
logger.info(`当前版本:${version}`);
const plusInfo = getPlusInfo();
if (isPlus()) {
logger.info(`授权信息:${plusInfo.vipType},${plusInfo.expireTime === -1 ? '永久' : dayjs(plusInfo.expireTime).format('YYYY-MM-DD')}`);
logger.info(`授权信息:${plusInfo.vipType},${plusInfo.expireTime === -1 ? "永久" : dayjs(plusInfo.expireTime).format("YYYY-MM-DD")}`);
}
logger.info('Certd已启动');
logger.info('=========================================');
logger.info("Certd已启动");
logger.info("=========================================");
await this.resetPasswd();
}
async resetPasswd(){
async resetPasswd() {
if (this.resetAdminPasswd === true) {
logger.info('开始重置1号管理员用户的密码');
const newPasswd = '123456';
logger.info("开始重置1号管理员用户的密码");
const newPasswd = "123456";
await this.userService.resetPassword(1, newPasswd);
await this.userService.updateStatus(1, 1);
await this.userSettingsService.deleteWhere({
userId: 1,
key:"user.two.factor"
})
const publicSettings = await this.sysSettingsService.getPublicSettings()
publicSettings.captchaEnabled = false
key: "user.two.factor",
});
const publicSettings = await this.sysSettingsService.getPublicSettings();
publicSettings.captchaEnabled = false;
await this.sysSettingsService.savePublicSettings(publicSettings);
const user = await this.userService.info(1);
@@ -77,7 +77,7 @@ export class AutoPrint {
startHeapLog() {
function format(bytes: any) {
return (bytes / 1024 / 1024).toFixed(2) + ' MB';
return (bytes / 1024 / 1024).toFixed(2) + " MB";
}
function printHeapLog() {
const mu = process.memoryUsage();
@@ -89,7 +89,7 @@ export class AutoPrint {
startHttpsServer() {
if (!this.httpsConfig.enabled) {
logger.info('Https server is not enabled');
logger.info("Https server is not enabled");
return;
}
httpsServer.start({
@@ -100,6 +100,6 @@ export class AutoPrint {
}
startProxyServer() {
startProxyServer({port: 7003});
startProxyServer({ port: 7003 });
}
}
@@ -38,10 +38,10 @@ export class CertInfoWildcardDomainCountFix {
if (fixedCount > 0) {
logger.info(`已修复证书泛域名数量历史数据,数量=${fixedCount}`);
}
return true
return true;
} catch (e: any) {
logger.error("修复证书泛域名数量历史数据失败", e);
}
return false
return false;
}
}
@@ -63,7 +63,6 @@ export class CommonEabToAcmeAccountFix {
@Inject()
storageService: StorageService;
async init() {
try {
const certApplyConfig = await this.pluginConfigService.getPluginConfig({
@@ -56,12 +56,12 @@ export class GoogleCommonEabAccountKeyFix {
});
const googleCommonEabAccessId = certApplyConfig?.sysSetting?.input?.googleCommonEabAccessId;
if (!googleCommonEabAccessId) {
return true;
return true;
}
const eabAccess = await this.accessService.getAccessById(googleCommonEabAccessId, false);
if (eabAccess.accountKey) {
return true;
return true;
}
if (!eabAccess.kid) {
logger.info("公共Google EAB授权缺少KID,跳过历史ACME账号私钥修复");
@@ -82,7 +82,7 @@ export class GoogleCommonEabAccountKeyFix {
} catch (e: any) {
logger.error("修复公共Google EAB授权ACME账号私钥失败", e);
}
return false
return false;
}
async getLegacyGoogleAccountConfig(email?: string) {
@@ -74,7 +74,6 @@ export class LegacyAcmeAccountAccessFix {
@Inject()
accessService: AccessService;
async init() {
try {
const repository = this.storageService.getRepository();
@@ -41,7 +41,7 @@ export class OauthSubtypeBoundTypeFix {
await this.convertLegacyAddonLoginTypeToArray(addonEntity, legacyLoginType, manager);
}
});
return true
return true;
} catch (e: any) {
logger.error("修复OAuth subtype绑定历史数据失败", e);
}
@@ -14,7 +14,6 @@ export function fixSuiteContentWildcardDomainCount(contentValue?: string) {
return JSON.stringify(content);
}
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class SuiteContentWildcardDomainCountFix {
@@ -33,11 +32,11 @@ export class SuiteContentWildcardDomainCountFix {
if (fixedCount > 0) {
logger.info(`已修复套餐最大泛域名数量历史数据,数量=${fixedCount}`);
}
return true
return true;
} catch (e: any) {
logger.error("修复套餐最大泛域名数量历史数据失败", e);
}
return false
return false;
}
private async fixSuiteContentWildcardDomainCountByTable(entityManager: any, tableName: string) {
@@ -1,8 +1,8 @@
import { logger } from '@certd/basic';
import fs from 'fs';
import { logger } from "@certd/basic";
import fs from "fs";
// @ts-ignore
import forge from 'node-forge';
import path from 'path';
import forge from "node-forge";
import path from "path";
export function createSelfCertificate(opts: { crtPath: string; keyPath: string }) {
// 生成密钥对
@@ -11,14 +11,14 @@ export function createSelfCertificate(opts: { crtPath: string; keyPath: string }
// 创建自签名证书
const cert = forge.pki.createCertificate();
cert.publicKey = keypair.publicKey;
cert.serialNumber = '01';
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', // 或者你的域名
name: "commonName",
value: "self-certificate.certd", // 或者你的域名
},
];
cert.setSubject(attrs);
@@ -30,7 +30,7 @@ export function createSelfCertificate(opts: { crtPath: string; keyPath: string }
const pemKey = forge.pki.privateKeyToPem(keypair.privateKey);
// 写入文件
logger.info('生成自签名证书成功');
logger.info("生成自签名证书成功");
logger.info(`自签证书保存路径: ${opts.crtPath}`);
logger.info(`自签私钥保存路径: ${opts.keyPath}`);
const crtDir = path.dirname(opts.crtPath);
@@ -1,8 +1,8 @@
import https from 'node:https';
import fs from 'fs';
import { Application } from '@midwayjs/koa';
import { createSelfCertificate } from './self-certificate.js';
import {logger, safePromise} from '@certd/basic';
import https from "node:https";
import fs from "fs";
import { Application } from "@midwayjs/koa";
import { createSelfCertificate } from "./self-certificate.js";
import { logger, safePromise } from "@certd/basic";
export type HttpsServerOptions = {
enabled: boolean;
@@ -33,24 +33,24 @@ export class HttpsServer {
start(opts: HttpsServerOptions) {
if (!opts) {
logger.error('https配置不能为空');
logger.error("https配置不能为空");
return;
}
this.opts = opts;
logger.info('=========================================');
logger.info("=========================================");
if (!opts.key || !opts.cert) {
logger.error('证书路径未配置,无法启动https服务,请先配置:koa.https.key和koa.https.cert');
logger.error("证书路径未配置,无法启动https服务,请先配置:koa.https.key和koa.https.cert");
return;
}
if (!fs.existsSync(opts.key) || !fs.existsSync(opts.cert)) {
logger.info('证书文件不存在,将生成自签名证书');
logger.info("证书文件不存在,将生成自签名证书");
createSelfCertificate({
crtPath: opts.cert,
keyPath: opts.key,
});
}
logger.info('准备启动https服务');
logger.info("准备启动https服务");
const httpServer = https.createServer(
{
cert: fs.readFileSync(opts.cert),
@@ -59,7 +59,7 @@ export class HttpsServer {
opts.app.callback()
);
this.server = httpServer;
let hostname = opts.hostname || '::';
let hostname = opts.hostname || "::";
// A function that runs in the context of the http server
// and reports what type of server listens on which port
function listeningReporter() {
@@ -71,19 +71,18 @@ export class HttpsServer {
httpServer.listen(opts.port, hostname, listeningReporter);
return httpServer;
} catch (e) {
if ( e.message?.includes("address family not supported")) {
hostname = "0.0.0.0"
if (e.message?.includes("address family not supported")) {
hostname = "0.0.0.0";
logger.error(`${e.message},尝试监听${hostname}`, e);
try{
try {
httpServer.listen(opts.port, hostname, listeningReporter);
return httpServer;
}catch (e) {
logger.error('启动https服务失败', e);
} catch (e) {
logger.error("启动https服务失败", e);
}
}else{
logger.error('启动https服务失败', e);
} else {
logger.error("启动https服务失败", e);
}
}
}
}
@@ -1,13 +1,12 @@
// proxy-server.js
import http from 'http';
import https from 'https';
import url from 'url';
import net from 'net';
import { logger } from '@certd/basic';
import http from "http";
import https from "https";
import url from "url";
import net from "net";
import { logger } from "@certd/basic";
export function startProxyServer(opts:{port:number}) {
const {port} = opts;
export function startProxyServer(opts: { port: number }) {
const { port } = opts;
// 创建 HTTP 代理服务器
const proxyServer = http.createServer((clientReq, clientRes) => {
@@ -17,31 +16,31 @@ export function startProxyServer(opts:{port:number}) {
const parsedUrl = url.parse(clientReq.url);
const options = {
hostname: parsedUrl.hostname,
port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
port: parsedUrl.port || (parsedUrl.protocol === "https:" ? 443 : 80),
path: parsedUrl.path,
method: clientReq.method,
headers: clientReq.headers
headers: clientReq.headers,
};
// 根据协议选择不同的模块
const protocol = parsedUrl.protocol === 'https:' ? https : http;
const protocol = parsedUrl.protocol === "https:" ? https : http;
// 移除可能会引起问题的 headers
delete options.headers['proxy-connection'];
delete options.headers['connection'];
delete options.headers['keep-alive'];
delete options.headers["proxy-connection"];
delete options.headers["connection"];
delete options.headers["keep-alive"];
// 创建到目标服务器的请求
const proxyReq = protocol.request(options, (proxyRes) => {
const proxyReq = protocol.request(options, proxyRes => {
// 将目标服务器的响应返回给客户端
clientRes.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(clientRes);
});
proxyReq.on('error', (err) => {
logger.error('[proxy] 代理请求错误:', err);
proxyReq.on("error", err => {
logger.error("[proxy] 代理请求错误:", err);
clientRes.writeHead(500);
clientRes.end('代理服务器错误');
clientRes.end("代理服务器错误");
});
// 将客户端请求体转发到目标服务器
@@ -49,18 +48,16 @@ export function startProxyServer(opts:{port:number}) {
});
// 处理 CONNECT 方法(HTTPS 代理)
proxyServer.on('connect', (req, clientSocket, head) => {
proxyServer.on("connect", (req, clientSocket, head) => {
logger.log(`[proxy] HTTPS 连接请求: ${req.url}`);
const [hostname, port] = req.url.split(':');
const [hostname, port] = req.url.split(":");
const portNum = parseInt(port) || 443;
// 连接到目标服务器
const serverSocket = net.connect(portNum, hostname, () => {
// 告诉客户端连接已建立
clientSocket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: Node.js-Proxy\r\n' +
'\r\n');
clientSocket.write("HTTP/1.1 200 Connection Established\r\n" + "Proxy-agent: Node.js-Proxy\r\n" + "\r\n");
// 建立双向数据流
serverSocket.write(head);
@@ -68,25 +65,25 @@ export function startProxyServer(opts:{port:number}) {
clientSocket.pipe(serverSocket);
});
serverSocket.on('error', (err) => {
logger.error('[proxy] HTTPS 代理错误:', err);
serverSocket.on("error", err => {
logger.error("[proxy] HTTPS 代理错误:", err);
clientSocket.end();
});
clientSocket.on('error', (err) => {
logger.error('[proxy] 客户端 socket 错误:', err);
clientSocket.on("error", err => {
logger.error("[proxy] 客户端 socket 错误:", err);
serverSocket.end();
});
});
// 监听端口
proxyServer.listen(port, () => {
logger.info(`[proxy] 正向代理服务器运行在 http://0.0.0.0:${port}`);
logger.info(`[proxy] 正向代理服务器运行在 http://0.0.0.0:${port}`);
});
proxyServer.close(() => {
logger.info('[proxy] 正向代理服务器已关闭');
logger.info("[proxy] 正向代理服务器已关闭");
});
return proxyServer
}
return proxyServer;
}