mirror of
https://github.com/certd/certd.git
synced 2026-07-06 12:07:32 +08:00
chore: format
This commit is contained in:
@@ -1,64 +1,64 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { PipelineEntity } from '../../pipeline/entity/pipeline.js';
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
|
||||
|
||||
@Entity('cd_cert_info')
|
||||
@Entity("cd_cert_info")
|
||||
export class CertInfoEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
@Column({ name: "user_id", comment: "用户id" })
|
||||
userId: number;
|
||||
|
||||
@Column({ name: 'domain', comment: '主域名' })
|
||||
@Column({ name: "domain", comment: "主域名" })
|
||||
domain: string;
|
||||
|
||||
@Column({ name: 'domains', comment: '域名' })
|
||||
@Column({ name: "domains", comment: "域名" })
|
||||
domains: string;
|
||||
|
||||
@Column({ name: 'domain_count', comment: '域名数量' })
|
||||
@Column({ name: "domain_count", comment: "域名数量" })
|
||||
domainCount: number;
|
||||
|
||||
@Column({ name: 'wildcard_domain_count', comment: '泛域名数量', default: 0 })
|
||||
@Column({ name: "wildcard_domain_count", comment: "泛域名数量", default: 0 })
|
||||
wildcardDomainCount: number;
|
||||
|
||||
@Column({ name: 'pipeline_id', comment: '关联流水线id' })
|
||||
@Column({ name: "pipeline_id", comment: "关联流水线id" })
|
||||
pipelineId: number;
|
||||
|
||||
@Column({ name: 'apply_time', comment: '申请时间' })
|
||||
@Column({ name: "apply_time", comment: "申请时间" })
|
||||
applyTime: number;
|
||||
|
||||
@Column({ name: 'from_type', comment: '来源' })
|
||||
@Column({ name: "from_type", comment: "来源" })
|
||||
fromType: string;
|
||||
|
||||
@Column({ name: 'cert_provider', comment: '证书颁发机构' })
|
||||
@Column({ name: "cert_provider", comment: "证书颁发机构" })
|
||||
certProvider: string;
|
||||
|
||||
@Column({ name: 'effective_time', comment: '生效时间' })
|
||||
@Column({ name: "effective_time", comment: "生效时间" })
|
||||
effectiveTime: number;
|
||||
|
||||
@Column({ name: 'expires_time', comment: '过期时间' })
|
||||
@Column({ name: "expires_time", comment: "过期时间" })
|
||||
expiresTime: number;
|
||||
|
||||
@Column({ name: 'cert_info', comment: '证书详情' })
|
||||
@Column({ name: "cert_info", comment: "证书详情" })
|
||||
certInfo: string;
|
||||
|
||||
@Column({ name: 'cert_file', comment: '证书下载' })
|
||||
@Column({ name: "cert_file", comment: "证书下载" })
|
||||
certFile: string;
|
||||
|
||||
@Column({ name: 'project_id', comment: '项目id' })
|
||||
@Column({ name: "project_id", comment: "项目id" })
|
||||
projectId: number;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
name: "create_time",
|
||||
comment: "创建时间",
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
createTime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
name: "update_time",
|
||||
comment: "修改时间",
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
updateTime: Date;
|
||||
|
||||
|
||||
@@ -1,50 +1,49 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { PipelineEntity } from '../../pipeline/entity/pipeline.js';
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
|
||||
|
||||
@Entity('cd_job_history')
|
||||
@Entity("cd_job_history")
|
||||
export class JobHistoryEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
@Column({ name: "user_id", comment: "用户id" })
|
||||
userId: number;
|
||||
|
||||
@Column({ name: 'project_id', comment: '项目id' })
|
||||
@Column({ name: "project_id", comment: "项目id" })
|
||||
projectId: number;
|
||||
|
||||
|
||||
@Column({ name: 'type', comment: '类型' })
|
||||
@Column({ name: "type", comment: "类型" })
|
||||
type: string;
|
||||
|
||||
@Column({ name: 'title', comment: '标题' })
|
||||
@Column({ name: "title", comment: "标题" })
|
||||
title: string;
|
||||
|
||||
@Column({ name: 'content', comment: '内容' })
|
||||
@Column({ name: "content", comment: "内容" })
|
||||
content: string;
|
||||
|
||||
@Column({ name: 'related_id', comment: '关联id' })
|
||||
@Column({ name: "related_id", comment: "关联id" })
|
||||
relatedId: string;
|
||||
|
||||
@Column({ name: 'result', comment: '结果' })
|
||||
@Column({ name: "result", comment: "结果" })
|
||||
result: string;
|
||||
|
||||
@Column({ name: 'start_at', comment: '开始时间' })
|
||||
@Column({ name: "start_at", comment: "开始时间" })
|
||||
startAt: number;
|
||||
|
||||
@Column({ name: 'end_at', comment: '结束时间' })
|
||||
@Column({ name: "end_at", comment: "结束时间" })
|
||||
endAt: number;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
name: "create_time",
|
||||
comment: "创建时间",
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
createTime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
name: "update_time",
|
||||
comment: "修改时间",
|
||||
default: () => "CURRENT_TIMESTAMP",
|
||||
})
|
||||
updateTime: Date;
|
||||
|
||||
|
||||
@@ -1,84 +1,82 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
|
||||
/**
|
||||
*/
|
||||
@Entity('cd_site_info')
|
||||
@Entity("cd_site_info")
|
||||
export class SiteInfoEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
@Column({ name: "user_id", comment: "用户id" })
|
||||
userId: number;
|
||||
@Column({ name: 'name', comment: '站点名称', length: 100 })
|
||||
@Column({ name: "name", comment: "站点名称", length: 100 })
|
||||
name: string;
|
||||
@Column({ name: 'domain', comment: '域名', length: 100 })
|
||||
@Column({ name: "domain", comment: "域名", length: 100 })
|
||||
domain: string;
|
||||
|
||||
@Column({ name: 'https_port', comment: '端口' })
|
||||
@Column({ name: "https_port", comment: "端口" })
|
||||
httpsPort: number;
|
||||
|
||||
@Column({ name: 'cert_domains', comment: '证书域名', length: 4096 })
|
||||
@Column({ name: "cert_domains", comment: "证书域名", length: 4096 })
|
||||
certDomains: string;
|
||||
@Column({ name: 'cert_info', comment: '证书详情', length: 4096 })
|
||||
@Column({ name: "cert_info", comment: "证书详情", length: 4096 })
|
||||
certInfo: string;
|
||||
@Column({ name: 'cert_status', comment: '证书状态', length: 100 })
|
||||
@Column({ name: "cert_status", comment: "证书状态", length: 100 })
|
||||
certStatus: string;
|
||||
|
||||
@Column({ name: 'cert_provider', comment: '证书颁发机构', length: 100 })
|
||||
@Column({ name: "cert_provider", comment: "证书颁发机构", length: 100 })
|
||||
certProvider: string;
|
||||
|
||||
@Column({ name: 'cert_effective_time', comment: '证书生效时间' })
|
||||
@Column({ name: "cert_effective_time", comment: "证书生效时间" })
|
||||
certEffectiveTime: number;
|
||||
@Column({ name: 'cert_expires_time', comment: '证书到期时间' })
|
||||
@Column({ name: "cert_expires_time", comment: "证书到期时间" })
|
||||
certExpiresTime: number;
|
||||
@Column({ name: 'last_check_time', comment: '上次检查时间' })
|
||||
@Column({ name: "last_check_time", comment: "上次检查时间" })
|
||||
lastCheckTime: number;
|
||||
@Column({ name: 'check_status', comment: '检查状态' })
|
||||
@Column({ name: "check_status", comment: "检查状态" })
|
||||
checkStatus: string;
|
||||
@Column({ name: 'error', comment: '错误信息' })
|
||||
@Column({ name: "error", comment: "错误信息" })
|
||||
error: string;
|
||||
@Column({ name: 'pipeline_id', comment: '关联流水线id' })
|
||||
@Column({ name: "pipeline_id", comment: "关联流水线id" })
|
||||
pipelineId: number;
|
||||
|
||||
@Column({ name: 'cert_info_id', comment: '证书id' })
|
||||
@Column({ name: "cert_info_id", comment: "证书id" })
|
||||
certInfoId: number;
|
||||
|
||||
|
||||
@Column({ name: 'ip_check', comment: '是否检查IP' })
|
||||
@Column({ name: "ip_check", comment: "是否检查IP" })
|
||||
ipCheck: boolean;
|
||||
|
||||
@Column({ name: 'ip_sync_auto', comment: '是否自动同步IP' })
|
||||
@Column({ name: "ip_sync_auto", comment: "是否自动同步IP" })
|
||||
ipSyncAuto: boolean;
|
||||
|
||||
@Column({ name: 'ip_sync_mode', comment: 'IP同步模式' })
|
||||
@Column({ name: "ip_sync_mode", comment: "IP同步模式" })
|
||||
ipSyncMode: string;
|
||||
|
||||
@Column({ name: 'ip_ignore_coherence', comment: '忽略证书一致性' })
|
||||
@Column({ name: "ip_ignore_coherence", comment: "忽略证书一致性" })
|
||||
ipIgnoreCoherence: boolean;
|
||||
|
||||
@Column({ name: 'ip_count', comment: 'ip数量' })
|
||||
ipCount: number
|
||||
@Column({ name: "ip_count", comment: "ip数量" })
|
||||
ipCount: number;
|
||||
|
||||
@Column({ name: 'ip_error_count', comment: 'ip异常数量' })
|
||||
ipErrorCount: number
|
||||
@Column({ name: "ip_error_count", comment: "ip异常数量" })
|
||||
ipErrorCount: number;
|
||||
|
||||
|
||||
@Column({ name: 'disabled', comment: '禁用启用' })
|
||||
@Column({ name: "disabled", comment: "禁用启用" })
|
||||
disabled: boolean;
|
||||
|
||||
@Column({ name: 'remark', comment: '备注', length: 512 })
|
||||
@Column({ name: "remark", comment: "备注", length: 512 })
|
||||
remark: string;
|
||||
|
||||
@Column({ name: 'group_id', comment: '分组id' })
|
||||
@Column({ name: "group_id", comment: "分组id" })
|
||||
groupId: number;
|
||||
|
||||
@Column({ name: 'ip_address', comment: 'IP地址', length: 128 })
|
||||
@Column({ name: "ip_address", comment: "IP地址", length: 128 })
|
||||
ipAddress: string;
|
||||
|
||||
@Column({ name: 'project_id', comment: '项目id' })
|
||||
@Column({ name: "project_id", comment: "项目id" })
|
||||
projectId: number;
|
||||
|
||||
@Column({ name: 'create_time', comment: '创建时间', default: () => 'CURRENT_TIMESTAMP' })
|
||||
@Column({ name: "create_time", comment: "创建时间", default: () => "CURRENT_TIMESTAMP" })
|
||||
createTime: Date;
|
||||
@Column({ name: 'update_time', comment: '修改时间', default: () => 'CURRENT_TIMESTAMP' })
|
||||
@Column({ name: "update_time", comment: "修改时间", default: () => "CURRENT_TIMESTAMP" })
|
||||
updateTime: Date;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||
|
||||
/**
|
||||
*/
|
||||
@Entity('cd_site_ip')
|
||||
@Entity("cd_site_ip")
|
||||
export class SiteIpEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column({ name: 'user_id', comment: '用户id' })
|
||||
@Column({ name: "user_id", comment: "用户id" })
|
||||
userId: number;
|
||||
@Column({ name: 'site_id', comment: '站点id' })
|
||||
@Column({ name: "site_id", comment: "站点id" })
|
||||
siteId: number;
|
||||
@Column({ name: 'ip_address', comment: 'IP', length: 100 })
|
||||
@Column({ name: "ip_address", comment: "IP", length: 100 })
|
||||
ipAddress: string;
|
||||
|
||||
@Column({ name: 'cert_domains', comment: '证书域名', length: 4096 })
|
||||
@Column({ name: "cert_domains", comment: "证书域名", length: 4096 })
|
||||
certDomains: string;
|
||||
@Column({ name: 'cert_status', comment: '证书状态', length: 100 })
|
||||
@Column({ name: "cert_status", comment: "证书状态", length: 100 })
|
||||
certStatus: string;
|
||||
@Column({ name: 'cert_provider', comment: '证书颁发机构', length: 100 })
|
||||
@Column({ name: "cert_provider", comment: "证书颁发机构", length: 100 })
|
||||
certProvider: string;
|
||||
@Column({ name: 'cert_expires_time', comment: '证书到期时间' })
|
||||
@Column({ name: "cert_expires_time", comment: "证书到期时间" })
|
||||
certExpiresTime: number;
|
||||
@Column({ name: 'last_check_time', comment: '上次检查时间' })
|
||||
@Column({ name: "last_check_time", comment: "上次检查时间" })
|
||||
lastCheckTime: number;
|
||||
@Column({ name: 'check_status', comment: '检查状态' })
|
||||
@Column({ name: "check_status", comment: "检查状态" })
|
||||
checkStatus: string;
|
||||
@Column({ name: 'error', comment: '错误信息' })
|
||||
@Column({ name: "error", comment: "错误信息" })
|
||||
error: string;
|
||||
@Column({ name: 'from', comment: '来源' })
|
||||
from: string
|
||||
@Column({ name: 'remark', comment: '备注' })
|
||||
@Column({ name: "from", comment: "来源" })
|
||||
from: string;
|
||||
@Column({ name: "remark", comment: "备注" })
|
||||
remark: string;
|
||||
@Column({ name: "disabled", comment: "禁用启用" })
|
||||
disabled: boolean;
|
||||
@Column({ name: 'project_id', comment: '项目id' })
|
||||
@Column({ name: "project_id", comment: "项目id" })
|
||||
projectId: number;
|
||||
|
||||
@Column({ name: 'create_time', comment: '创建时间', default: () => 'CURRENT_TIMESTAMP' })
|
||||
@Column({ name: "create_time", comment: "创建时间", default: () => "CURRENT_TIMESTAMP" })
|
||||
createTime: Date;
|
||||
@Column({ name: 'update_time', comment: '修改时间', default: () => 'CURRENT_TIMESTAMP' })
|
||||
@Column({ name: "update_time", comment: "修改时间", default: () => "CURRENT_TIMESTAMP" })
|
||||
updateTime: Date;
|
||||
}
|
||||
|
||||
@@ -10,25 +10,23 @@ import { CertInfoService } from "../service/cert-info-service.js";
|
||||
import { DomainService } from "../../cert/service/domain-service.js";
|
||||
import { DomainVerifierGetter } from "../../pipeline/service/getter/domain-verifier-getter.js";
|
||||
|
||||
|
||||
@Provide("CertInfoFacade")
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class CertInfoFacade {
|
||||
|
||||
export class CertInfoFacade {
|
||||
@Inject()
|
||||
pipelineService: PipelineService;
|
||||
|
||||
@Inject()
|
||||
certInfoService: CertInfoService ;
|
||||
certInfoService: CertInfoService;
|
||||
|
||||
@Inject()
|
||||
domainService: DomainService
|
||||
domainService: DomainService;
|
||||
|
||||
@Inject()
|
||||
userSettingsService : UserSettingsService
|
||||
userSettingsService: UserSettingsService;
|
||||
|
||||
async getCertInfo(req: { domains?: string; certId?: number; userId: number, projectId:number, autoApply?:boolean,format?:string }) {
|
||||
const { domains, certId, userId,projectId } = req;
|
||||
async getCertInfo(req: { domains?: string; certId?: number; userId: number; projectId: number; autoApply?: boolean; format?: string }) {
|
||||
const { domains, certId, userId, projectId } = req;
|
||||
if (certId) {
|
||||
return await this.certInfoService.getCertInfoById({ id: certId, userId, projectId });
|
||||
}
|
||||
@@ -38,41 +36,38 @@ export class CertInfoFacade {
|
||||
message: "参数错误,certId和domains必须传一个",
|
||||
});
|
||||
}
|
||||
const domainArr = domains.split(',');
|
||||
const domainArr = domains.split(",");
|
||||
|
||||
const matchedList = await this.certInfoService.getMatchCertList({domains:domainArr,userId,projectId})
|
||||
const matchedList = await this.certInfoService.getMatchCertList({ domains: domainArr, userId, projectId });
|
||||
|
||||
if (matchedList.length === 0 ) {
|
||||
if(req.autoApply === true){
|
||||
if (matchedList.length === 0) {
|
||||
if (req.autoApply === true) {
|
||||
//自动申请,先创建自动申请流水线
|
||||
const pipeline:PipelineEntity = await this.createAutoPipeline({domains:domainArr,userId,projectId})
|
||||
await this.triggerApplyPipeline({pipelineId:pipeline.id})
|
||||
}else{
|
||||
const pipeline: PipelineEntity = await this.createAutoPipeline({ domains: domainArr, userId, projectId });
|
||||
await this.triggerApplyPipeline({ pipelineId: pipeline.id });
|
||||
} else {
|
||||
throw new CodeException({
|
||||
...Constants.res.openCertNotFound,
|
||||
message:"在证书仓库中没有找到匹配域名的证书,请先创建证书流水线,或传入autoApply参数,自动创建"
|
||||
message: "在证书仓库中没有找到匹配域名的证书,请先创建证书流水线,或传入autoApply参数,自动创建",
|
||||
});
|
||||
}
|
||||
}
|
||||
let matched = this.getMinixMatched(matchedList);
|
||||
const matched = this.getMinixMatched(matchedList);
|
||||
if (!matched) {
|
||||
if(req.autoApply === true){
|
||||
if (req.autoApply === true) {
|
||||
//如果没有找到有效期内的证书,则自动触发一次申请
|
||||
const first = matchedList[0]
|
||||
await this.triggerApplyPipeline({pipelineId:first.pipelineId})
|
||||
return
|
||||
}else{
|
||||
const first = matchedList[0];
|
||||
await this.triggerApplyPipeline({ pipelineId: first.pipelineId });
|
||||
return;
|
||||
} else {
|
||||
throw new CodeException({
|
||||
...Constants.res.openCertNotFound,
|
||||
message:"证书已过期,请触发流水线申请,或者传入autoApply参数,自动触发"
|
||||
message: "证书已过期,请触发流水线申请,或者传入autoApply参数,自动触发",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return await this.certInfoService.getCertInfoById({ id: matched.id, userId: userId,projectId,format:req.format });
|
||||
|
||||
|
||||
|
||||
return await this.certInfoService.getCertInfoById({ id: matched.id, userId: userId, projectId, format: req.format });
|
||||
}
|
||||
|
||||
public getMinixMatched(matchedList: CertInfoEntity[]) {
|
||||
@@ -103,59 +98,55 @@ export class CertInfoFacade {
|
||||
return matched;
|
||||
}
|
||||
|
||||
async createAutoPipeline(req:{domains:string[],userId:number,projectId:number}){
|
||||
async createAutoPipeline(req: { domains: string[]; userId: number; projectId: number }) {
|
||||
const verifierGetter = new DomainVerifierGetter(req.userId, req.projectId, this.domainService);
|
||||
|
||||
const verifierGetter = new DomainVerifierGetter(req.userId, req.projectId, this.domainService)
|
||||
|
||||
const allDomains = []
|
||||
const allDomains = [];
|
||||
for (const item of req.domains) {
|
||||
allDomains.push(item.replaceAll("*.",""))
|
||||
allDomains.push(item.replaceAll("*.", ""));
|
||||
}
|
||||
const verifiers = await verifierGetter.getVerifiers(allDomains)
|
||||
const verifiers = await verifierGetter.getVerifiers(allDomains);
|
||||
for (const item of allDomains) {
|
||||
if (!verifiers[item]){
|
||||
if (!verifiers[item]) {
|
||||
throw new CodeException({
|
||||
...Constants.res.openDomainNoVerifier,
|
||||
message:`域名${item}没有配置校验方式,请先在域名管理页面配置`,
|
||||
data:{
|
||||
domain:item
|
||||
}
|
||||
message: `域名${item}没有配置校验方式,请先在域名管理页面配置`,
|
||||
data: {
|
||||
domain: item,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const userEmailSetting = await this.userSettingsService.getSetting<UserEmailSetting>(req.userId,null, UserEmailSetting)
|
||||
if(!userEmailSetting.list){
|
||||
throw new CodeException(Constants.res.openEmailNotFound)
|
||||
const userEmailSetting = await this.userSettingsService.getSetting<UserEmailSetting>(req.userId, null, UserEmailSetting);
|
||||
if (!userEmailSetting.list) {
|
||||
throw new CodeException(Constants.res.openEmailNotFound);
|
||||
}
|
||||
const email = userEmailSetting.list[0]
|
||||
const email = userEmailSetting.list[0];
|
||||
|
||||
return await this.pipelineService.createAutoPipeline({
|
||||
domains: req.domains,
|
||||
email,
|
||||
projectId: req.projectId,
|
||||
userId: req.userId,
|
||||
from: "OpenAPI"
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
async triggerApplyPipeline(req:{pipelineId:number}){
|
||||
//查询流水线状态
|
||||
const status = await this.pipelineService.getStatus(req.pipelineId)
|
||||
if (status != 'running') {
|
||||
await this.pipelineService.trigger(req.pipelineId)
|
||||
await utils.sleep(1000)
|
||||
}
|
||||
const certInfo = await this.certInfoService.getByPipelineId(req.pipelineId)
|
||||
throw new CodeException({
|
||||
...Constants.res.openCertApplying,
|
||||
data:{
|
||||
pipelineId:req.pipelineId,
|
||||
certId:certInfo?.id
|
||||
}
|
||||
from: "OpenAPI",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async triggerApplyPipeline(req: { pipelineId: number }) {
|
||||
//查询流水线状态
|
||||
const status = await this.pipelineService.getStatus(req.pipelineId);
|
||||
if (status != "running") {
|
||||
await this.pipelineService.trigger(req.pipelineId);
|
||||
await utils.sleep(1000);
|
||||
}
|
||||
const certInfo = await this.certInfoService.getByPipelineId(req.pipelineId);
|
||||
throw new CodeException({
|
||||
...Constants.res.openCertApplying,
|
||||
data: {
|
||||
pipelineId: req.pipelineId,
|
||||
certId: certInfo?.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './entity/site-info.js';
|
||||
export * from './entity/cert-info.js';
|
||||
export * from "./entity/site-info.js";
|
||||
export * from "./entity/cert-info.js";
|
||||
|
||||
export * from './service/cert-info-service.js';
|
||||
export * from './service/site-info-service.js';
|
||||
export * from "./service/cert-info-service.js";
|
||||
export * from "./service/site-info-service.js";
|
||||
|
||||
@@ -12,10 +12,9 @@ export type UploadCertReq = {
|
||||
fromType?: string;
|
||||
userId?: number;
|
||||
projectId?: number;
|
||||
file?:any
|
||||
file?: any;
|
||||
};
|
||||
|
||||
|
||||
@Provide("CertInfoService")
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
@@ -32,19 +31,19 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
}
|
||||
|
||||
async getUserDomainCount(userId: number) {
|
||||
if (userId==null) {
|
||||
throw new Error('userId is required');
|
||||
if (userId == null) {
|
||||
throw new Error("userId is required");
|
||||
}
|
||||
return await this.repository.sum('domainCount', {
|
||||
return await this.repository.sum("domainCount", {
|
||||
userId,
|
||||
});
|
||||
}
|
||||
|
||||
async getUserWildcardDomainCount(userId: number) {
|
||||
if (userId == null) {
|
||||
throw new Error('userId is required');
|
||||
throw new Error("userId is required");
|
||||
}
|
||||
return await this.repository.sum('wildcardDomainCount', {
|
||||
return await this.repository.sum("wildcardDomainCount", {
|
||||
userId,
|
||||
});
|
||||
}
|
||||
@@ -56,7 +55,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
return domains.filter(domain => domain?.trim().toLowerCase().startsWith("*.")).length;
|
||||
}
|
||||
|
||||
async updateDomains(pipelineId: number, userId: number, projectId: number, domains: string[],fromType?:string) {
|
||||
async updateDomains(pipelineId: number, userId: number, projectId: number, domains: string[], fromType?: string) {
|
||||
const found = await this.repository.findOne({
|
||||
where: {
|
||||
pipelineId,
|
||||
@@ -73,26 +72,26 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
bean.pipelineId = pipelineId;
|
||||
bean.userId = userId;
|
||||
bean.projectId = projectId;
|
||||
bean.fromType = fromType
|
||||
bean.fromType = fromType;
|
||||
if (!domains || domains.length === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!domains || domains.length === 0) {
|
||||
bean.domain = '';
|
||||
bean.domains = '';
|
||||
bean.domain = "";
|
||||
bean.domains = "";
|
||||
bean.domainCount = 0;
|
||||
bean.wildcardDomainCount = 0;
|
||||
} else {
|
||||
bean.domain = domains[0];
|
||||
bean.domains = domains.join(',');
|
||||
bean.domains = domains.join(",");
|
||||
bean.domainCount = domains.length;
|
||||
bean.wildcardDomainCount = this.countWildcardDomains(domains);
|
||||
}
|
||||
|
||||
await this.addOrUpdate(bean);
|
||||
return bean.id
|
||||
return bean.id;
|
||||
}
|
||||
|
||||
async deleteByPipelineId(id: number) {
|
||||
@@ -104,12 +103,12 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
});
|
||||
}
|
||||
|
||||
async getMatchCertList(params: { domains: string[]; userId: number,projectId?:number }) {
|
||||
const { domains, userId,projectId } = params;
|
||||
async getMatchCertList(params: { domains: string[]; userId: number; projectId?: number }) {
|
||||
const { domains, userId, projectId } = params;
|
||||
if (!domains) {
|
||||
throw new CodeException({
|
||||
...Constants.res.openCertNotFound,
|
||||
message:"域名不能为空"
|
||||
message: "域名不能为空",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -117,27 +116,27 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
select: {
|
||||
id: true,
|
||||
domains: true,
|
||||
expiresTime:true,
|
||||
pipelineId:true,
|
||||
expiresTime: true,
|
||||
pipelineId: true,
|
||||
},
|
||||
where: {
|
||||
userId,
|
||||
projectId,
|
||||
},
|
||||
order: {
|
||||
id: 'DESC',
|
||||
id: "DESC",
|
||||
},
|
||||
});
|
||||
//遍历查找
|
||||
return list.filter(item => {
|
||||
const itemDomains = item.domains.split(',');
|
||||
const itemDomains = item.domains.split(",");
|
||||
return utils.domain.match(domains, itemDomains);
|
||||
});
|
||||
}
|
||||
|
||||
async getCertInfoById(req: { id: number; userId: number,projectId:number,format?:string }) {
|
||||
async getCertInfoById(req: { id: number; userId: number; projectId: number; format?: string }) {
|
||||
const entity = await this.info(req.id);
|
||||
if (!entity || entity.userId !== req.userId ) {
|
||||
if (!entity || entity.userId !== req.userId) {
|
||||
throw new CodeException(Constants.res.openCertNotFound);
|
||||
}
|
||||
if (req.projectId && entity.projectId !== req.projectId) {
|
||||
@@ -153,13 +152,13 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
...certReader.toCertInfo(req.format),
|
||||
detail: {
|
||||
id: entity.id,
|
||||
domains: entity.domains.split(','),
|
||||
domains: entity.domains.split(","),
|
||||
notAfter: certReader.expires,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async updateCertByPipelineId(pipelineId: number, cert: CertInfo,file?:string,fromType = 'pipeline') {
|
||||
async updateCertByPipelineId(pipelineId: number, cert: CertInfo, file?: string, fromType = "pipeline") {
|
||||
const found = await this.repository.findOne({
|
||||
where: {
|
||||
pipelineId,
|
||||
@@ -169,14 +168,14 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
id: found?.id,
|
||||
certReader: new CertReader(cert),
|
||||
fromType,
|
||||
file
|
||||
file,
|
||||
});
|
||||
return bean;
|
||||
}
|
||||
|
||||
private async updateCert(req: UploadCertReq) {
|
||||
const bean = new CertInfoEntity();
|
||||
const { id, fromType,userId, certReader } = req;
|
||||
const { id, fromType, userId, certReader } = req;
|
||||
if (id) {
|
||||
bean.id = id;
|
||||
} else {
|
||||
@@ -186,7 +185,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
bean.certInfo = JSON.stringify(certInfo);
|
||||
bean.applyTime = new Date().getTime();
|
||||
const domains = certReader.detail.domains.altNames;
|
||||
bean.domains = domains.join(',');
|
||||
bean.domains = domains.join(",");
|
||||
bean.domain = domains[0];
|
||||
bean.domainCount = domains.length;
|
||||
bean.wildcardDomainCount = this.countWildcardDomains(domains);
|
||||
@@ -195,8 +194,8 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
bean.certProvider = certReader.detail.issuer.commonName;
|
||||
bean.userId = userId;
|
||||
bean.projectId = req.projectId;
|
||||
if(req.file){
|
||||
bean.certFile = req.file
|
||||
if (req.file) {
|
||||
bean.certFile = req.file;
|
||||
}
|
||||
await this.addOrUpdate(bean);
|
||||
return bean;
|
||||
@@ -210,7 +209,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
});
|
||||
}
|
||||
|
||||
async count({ userId,projectId }: { userId: number,projectId?:number }) {
|
||||
async count({ userId, projectId }: { userId: number; projectId?: number }) {
|
||||
const total = await this.repository.count({
|
||||
where: {
|
||||
userId,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import {LocalCache, logger} from '@certd/basic';
|
||||
import dnsSdk, {AnyRecord} from 'dns'
|
||||
import {LookupAddress} from "node:dns";
|
||||
import { LocalCache, logger } from "@certd/basic";
|
||||
import dnsSdk, { AnyRecord } from "dns";
|
||||
import { LookupAddress } from "node:dns";
|
||||
|
||||
const dns = dnsSdk.promises
|
||||
const dns = dnsSdk.promises;
|
||||
|
||||
export class DnsCustom{
|
||||
export class DnsCustom {
|
||||
private resolver: dnsSdk.promises.Resolver;
|
||||
// private cache = new LRUCache<string, any>({
|
||||
// max: 1000,
|
||||
// ttl: 1000 * 60 * 5,
|
||||
// });
|
||||
|
||||
constructor(dnsServers:string[]) {
|
||||
constructor(dnsServers: string[]) {
|
||||
const resolver = new dns.Resolver();
|
||||
resolver.setServers(dnsServers);
|
||||
this.resolver = resolver;
|
||||
@@ -27,103 +27,100 @@ export class DnsCustom{
|
||||
// this.cache.set(cacheKey,res)
|
||||
// return res
|
||||
// }
|
||||
async lookup(hostname:string,options?:{ family: any, hints: number, all: boolean }):Promise<LookupAddress[]>{
|
||||
async lookup(hostname: string, options?: { family: any; hints: number; all: boolean }): Promise<LookupAddress[]> {
|
||||
// { family: undefined, hints: 0, all: true }
|
||||
let v4:LookupAddress[] = []
|
||||
let v6:LookupAddress[] = []
|
||||
let errors = []
|
||||
const queryV6 = async ()=>{
|
||||
try{
|
||||
const list = await this.resolver.resolve6(hostname)
|
||||
let v4: LookupAddress[] = [];
|
||||
let v6: LookupAddress[] = [];
|
||||
const errors = [];
|
||||
const queryV6 = async () => {
|
||||
try {
|
||||
const list = await this.resolver.resolve6(hostname);
|
||||
if (list && list.length > 0) {
|
||||
v6 = list.map(item=>{
|
||||
v6 = list.map(item => {
|
||||
return {
|
||||
address: item,
|
||||
family: 6
|
||||
}
|
||||
})
|
||||
family: 6,
|
||||
};
|
||||
});
|
||||
}
|
||||
}catch (e) {
|
||||
logger.warn("query v6 error",e)
|
||||
errors.push(e)
|
||||
} catch (e) {
|
||||
logger.warn("query v6 error", e);
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
const queryV4 = async ()=>{
|
||||
try{
|
||||
const list =await this.resolver.resolve4(hostname)
|
||||
};
|
||||
const queryV4 = async () => {
|
||||
try {
|
||||
const list = await this.resolver.resolve4(hostname);
|
||||
if (list && list.length > 0) {
|
||||
v4 = list.map(item=>{
|
||||
v4 = list.map(item => {
|
||||
return {
|
||||
address: item,
|
||||
family: 4
|
||||
}
|
||||
})
|
||||
family: 4,
|
||||
};
|
||||
});
|
||||
}
|
||||
}catch (e) {
|
||||
logger.warn("query v4 error",e)
|
||||
errors.push(e)
|
||||
} catch (e) {
|
||||
logger.warn("query v4 error", e);
|
||||
errors.push(e);
|
||||
}
|
||||
};
|
||||
|
||||
const queries: Promise<any>[] = [];
|
||||
|
||||
const { family, all } = options;
|
||||
if (all) {
|
||||
queries.push(queryV6());
|
||||
queries.push(queryV4());
|
||||
} else {
|
||||
if (family === 6) {
|
||||
queries.push(queryV6());
|
||||
}
|
||||
if (family === 4) {
|
||||
queries.push(queryV4());
|
||||
}
|
||||
}
|
||||
|
||||
const queries:Promise<any>[] = []
|
||||
|
||||
|
||||
const {family, all} = options
|
||||
if (all){
|
||||
queries.push(queryV6())
|
||||
queries.push(queryV4())
|
||||
}else{
|
||||
if(family === 6 ){
|
||||
queries.push(queryV6())
|
||||
}
|
||||
if(family === 4 ){
|
||||
queries.push(queryV4())
|
||||
}
|
||||
}
|
||||
await Promise.all(queries)
|
||||
const res = [...v4,...v6]
|
||||
if(res.length === 0){
|
||||
if (errors.length > 0){
|
||||
const e = new Error(errors[0])
|
||||
await Promise.all(queries);
|
||||
const res = [...v4, ...v6];
|
||||
if (res.length === 0) {
|
||||
if (errors.length > 0) {
|
||||
const e = new Error(errors[0]);
|
||||
// @ts-ignore
|
||||
e.errors = errors
|
||||
throw e
|
||||
e.errors = errors;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return res
|
||||
return res;
|
||||
}
|
||||
|
||||
async resolve4(hostname:string):Promise<string[]>{
|
||||
return await this.resolver.resolve4(hostname)
|
||||
async resolve4(hostname: string): Promise<string[]> {
|
||||
return await this.resolver.resolve4(hostname);
|
||||
}
|
||||
async resolve6(hostname:string):Promise<string[]>{
|
||||
return await this.resolver.resolve6(hostname)
|
||||
async resolve6(hostname: string): Promise<string[]> {
|
||||
return await this.resolver.resolve6(hostname);
|
||||
}
|
||||
async resolveAny(hostname:string):Promise<AnyRecord[]>{
|
||||
return await this.resolver.resolveAny(hostname)
|
||||
async resolveAny(hostname: string): Promise<AnyRecord[]> {
|
||||
return await this.resolver.resolveAny(hostname);
|
||||
}
|
||||
|
||||
async resolveCname(hostname:string):Promise<string[]>{
|
||||
return await this.resolver.resolveCname(hostname)
|
||||
async resolveCname(hostname: string): Promise<string[]> {
|
||||
return await this.resolver.resolveCname(hostname);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class DnsContainer{
|
||||
bucket: LocalCache<DnsCustom> = new LocalCache()
|
||||
export class DnsContainer {
|
||||
bucket: LocalCache<DnsCustom> = new LocalCache();
|
||||
|
||||
constructor() {}
|
||||
getDns(server:string[]){
|
||||
const key = server.join(',')
|
||||
let dns = this.bucket.get(key)
|
||||
if (dns){
|
||||
return dns
|
||||
getDns(server: string[]) {
|
||||
const key = server.join(",");
|
||||
let dns = this.bucket.get(key);
|
||||
if (dns) {
|
||||
return dns;
|
||||
}
|
||||
dns = new DnsCustom(server)
|
||||
this.bucket.set(key,dns)
|
||||
return dns
|
||||
dns = new DnsCustom(server);
|
||||
this.bucket.set(key, dns);
|
||||
return dns;
|
||||
}
|
||||
}
|
||||
|
||||
export const dnsContainer = new DnsContainer()
|
||||
export const dnsContainer = new DnsContainer();
|
||||
|
||||
@@ -5,14 +5,12 @@ import { Repository } from "typeorm";
|
||||
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
|
||||
import { JobHistoryEntity } from "../entity/job-history.js";
|
||||
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class JobHistoryService extends BaseService<JobHistoryEntity> {
|
||||
@InjectEntityModel(JobHistoryEntity)
|
||||
repository: Repository<JobHistoryEntity>;
|
||||
|
||||
|
||||
@Inject()
|
||||
userSettingsService: UserSettingsService;
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
||||
import {BaseService, Constants, isEnterprise, NeedSuiteException, NeedVIPException, SysSettingsService} from "@certd/lib-server";
|
||||
import {InjectEntityModel} from "@midwayjs/typeorm";
|
||||
import {In, Repository} from "typeorm";
|
||||
import {SiteInfoEntity} from "../entity/site-info.js";
|
||||
import {siteTester} from "./site-tester.js";
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { BaseService, Constants, isEnterprise, NeedSuiteException, NeedVIPException, SysSettingsService } from "@certd/lib-server";
|
||||
import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||
import { In, Repository } from "typeorm";
|
||||
import { SiteInfoEntity } from "../entity/site-info.js";
|
||||
import { siteTester } from "./site-tester.js";
|
||||
import dayjs from "dayjs";
|
||||
import {logger, utils} from "@certd/basic";
|
||||
import {PeerCertificate} from "tls";
|
||||
import {NotificationService} from "../../pipeline/service/notification-service.js";
|
||||
import {isComm, isPlus} from "@certd/plus-core";
|
||||
import {UserSuiteService} from "@certd/commercial-core";
|
||||
import {UserSettingsService} from "../../mine/service/user-settings-service.js";
|
||||
import {UserSiteMonitorSetting} from "../../mine/service/models.js";
|
||||
import {SiteIpService} from "./site-ip-service.js";
|
||||
import {SiteIpEntity} from "../entity/site-ip.js";
|
||||
import {Cron} from "../../cron/cron.js";
|
||||
import { logger, utils } from "@certd/basic";
|
||||
import { PeerCertificate } from "tls";
|
||||
import { NotificationService } from "../../pipeline/service/notification-service.js";
|
||||
import { isComm, isPlus } from "@certd/plus-core";
|
||||
import { UserSuiteService } from "@certd/commercial-core";
|
||||
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
|
||||
import { UserSiteMonitorSetting } from "../../mine/service/models.js";
|
||||
import { SiteIpService } from "./site-ip-service.js";
|
||||
import { SiteIpEntity } from "../entity/site-ip.js";
|
||||
import { Cron } from "../../cron/cron.js";
|
||||
import { dnsContainer } from "./dns-custom.js";
|
||||
import { merge } from "lodash-es";
|
||||
import { JobHistoryService } from "./job-history-service.js";
|
||||
@@ -23,7 +23,7 @@ import { UserService } from "../../sys/authority/service/user-service.js";
|
||||
import { ProjectService } from "../../sys/enterprise/service/project-service.js";
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, {allowDowngrade: true})
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
@InjectEntityModel(SiteInfoEntity)
|
||||
repository: Repository<SiteInfoEntity>;
|
||||
@@ -87,18 +87,18 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
}
|
||||
|
||||
await this.checkMonitorLimit(data.userId);
|
||||
|
||||
|
||||
data.disabled = false;
|
||||
|
||||
const found = await this.repository.findOne({
|
||||
where: {
|
||||
domain: data.domain,
|
||||
userId: data.userId,
|
||||
httpsPort: data.httpsPort || 443
|
||||
}
|
||||
httpsPort: data.httpsPort || 443,
|
||||
},
|
||||
});
|
||||
if (found) {
|
||||
return {id: found.id};
|
||||
return { id: found.id };
|
||||
}
|
||||
|
||||
return await super.add(data);
|
||||
@@ -113,11 +113,11 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
}
|
||||
|
||||
async getUserMonitorCount(userId: number) {
|
||||
if (userId==null) {
|
||||
if (userId == null) {
|
||||
throw new Error("userId is required");
|
||||
}
|
||||
return await this.repository.count({
|
||||
where: {userId}
|
||||
where: { userId },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,18 +132,18 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
throw new Error("站点域名不能为空");
|
||||
}
|
||||
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId,site.projectId, UserSiteMonitorSetting);
|
||||
const dnsServer = setting.dnsServer
|
||||
let customDns = null
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, site.projectId, UserSiteMonitorSetting);
|
||||
const dnsServer = setting.dnsServer;
|
||||
let customDns = null;
|
||||
if (dnsServer && dnsServer.length > 0) {
|
||||
customDns = dnsContainer.getDns(dnsServer) as any
|
||||
customDns = dnsContainer.getDns(dnsServer) as any;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.update({
|
||||
id: site.id,
|
||||
checkStatus: "checking",
|
||||
lastCheckTime: dayjs().valueOf()
|
||||
lastCheckTime: dayjs().valueOf(),
|
||||
});
|
||||
const res = await siteTester.test({
|
||||
host: site.domain,
|
||||
@@ -177,56 +177,56 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
certExpiresTime: dayjs(expires).valueOf(),
|
||||
lastCheckTime: dayjs().valueOf(),
|
||||
error: null,
|
||||
checkStatus: "ok"
|
||||
checkStatus: "ok",
|
||||
};
|
||||
logger.info(`测试站点成功:id=${updateData.id},site=${site.name},certEffectiveTime=${updateData.certEffectiveTime},expiresTime=${updateData.certExpiresTime}`)
|
||||
logger.info(`测试站点成功:id=${updateData.id},site=${site.name},certEffectiveTime=${updateData.certEffectiveTime},expiresTime=${updateData.certExpiresTime}`);
|
||||
if (site.ipCheck) {
|
||||
delete updateData.checkStatus
|
||||
delete updateData.checkStatus;
|
||||
}
|
||||
await this.update(updateData);
|
||||
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId,site.projectId, UserSiteMonitorSetting)
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, site.projectId, UserSiteMonitorSetting);
|
||||
|
||||
merge(site,updateData)
|
||||
merge(site, updateData);
|
||||
//检查ip
|
||||
await this.checkAllIp(site,retryTimes,setting);
|
||||
await this.checkAllIp(site, retryTimes, setting);
|
||||
|
||||
if (!notify) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.sendExpiresNotify(site.id,setting);
|
||||
await this.sendExpiresNotify(site.id, setting);
|
||||
} catch (e) {
|
||||
logger.error("send notify error", e);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("check site error", e);
|
||||
let message = e.message
|
||||
if (!message){
|
||||
message = e.code
|
||||
let message = e.message;
|
||||
if (!message) {
|
||||
message = e.code;
|
||||
}
|
||||
if (e.errors &&e.errors.length > 0){
|
||||
message += "\n"+e.errors.map((item:any)=>item.message).join("\n")
|
||||
if (e.errors && e.errors.length > 0) {
|
||||
message += "\n" + e.errors.map((item: any) => item.message).join("\n");
|
||||
}
|
||||
await this.update({
|
||||
id: site.id,
|
||||
checkStatus: "error",
|
||||
lastCheckTime: dayjs().valueOf(),
|
||||
error: message
|
||||
error: message,
|
||||
});
|
||||
if (!notify) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.sendCheckErrorNotify(site.id,false,setting);
|
||||
await this.sendCheckErrorNotify(site.id, false, setting);
|
||||
} catch (e) {
|
||||
logger.error("send notify error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async checkAllIp(site: SiteInfoEntity,retryTimes = null,setting: UserSiteMonitorSetting) {
|
||||
async checkAllIp(site: SiteInfoEntity, retryTimes = null, setting: UserSiteMonitorSetting) {
|
||||
if (!site.ipCheck) {
|
||||
return;
|
||||
}
|
||||
@@ -248,11 +248,11 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
errorMessage += `${item.ipAddress}:${item.error}; \n`;
|
||||
} else if (item.certExpiresTime !== certExpiresTime && !site.ipIgnoreCoherence) {
|
||||
errorMessage += `${item.ipAddress}:与主站证书过期时间不一致(主站:${dayjs(certExpiresTime).format("YYYY-MM-DD")},IP:${dayjs(item.certExpiresTime).format("YYYY-MM-DD")}); \n`;
|
||||
} else if (isExpired){
|
||||
} else if (isExpired) {
|
||||
errorMessage += `${item.ipAddress}:证书已过期(过期时间:${dayjs(item.certExpiresTime).format("YYYY-MM-DD")}); \n`;
|
||||
}else if (isWillExpired){
|
||||
} else if (isWillExpired) {
|
||||
errorMessage += `${item.ipAddress}:证书将过期(过期时间:${dayjs(item.certExpiresTime).format("YYYY-MM-DD")}); \n`;
|
||||
}else {
|
||||
} else {
|
||||
errorCount--;
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
id: site.id,
|
||||
checkStatus: "ok",
|
||||
error: "",
|
||||
ipErrorCount: 0
|
||||
ipErrorCount: 0,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -270,20 +270,19 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
id: site.id,
|
||||
checkStatus: "error",
|
||||
error: errorMessage,
|
||||
ipErrorCount: errorCount
|
||||
ipErrorCount: errorCount,
|
||||
});
|
||||
try {
|
||||
await this.sendCheckErrorNotify(site.id, true,setting);
|
||||
await this.sendCheckErrorNotify(site.id, true, setting);
|
||||
} catch (e) {
|
||||
logger.error("send notify error", e);
|
||||
}
|
||||
};
|
||||
if (site.ipSyncAuto === false) {
|
||||
await this.siteIpService.checkAll(site, retryTimes,onFinished);
|
||||
}else{
|
||||
await this.siteIpService.syncAndCheck(site, retryTimes,onFinished);
|
||||
await this.siteIpService.checkAll(site, retryTimes, onFinished);
|
||||
} else {
|
||||
await this.siteIpService.syncAndCheck(site, retryTimes, onFinished);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,13 +297,13 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
throw new Error("站点不存在");
|
||||
}
|
||||
|
||||
this.doCheck(site, notify, retryTimes).catch((err) => {
|
||||
this.doCheck(site, notify, retryTimes).catch(err => {
|
||||
logger.error("check site error", err);
|
||||
});
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
async sendCheckErrorNotify(siteId: number, fromIpCheck = false,setting: UserSiteMonitorSetting) {
|
||||
async sendCheckErrorNotify(siteId: number, fromIpCheck = false, setting: UserSiteMonitorSetting) {
|
||||
const site = await this.info(siteId);
|
||||
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
|
||||
// 发邮件
|
||||
@@ -319,13 +318,13 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
content: `站点名称: ${site.name} \n站点域名: ${site.domain} \n错误信息:${site.error}`,
|
||||
errorMessage: site.error,
|
||||
notificationType: "siteCheckError",
|
||||
}
|
||||
},
|
||||
},
|
||||
site.userId
|
||||
);
|
||||
}
|
||||
|
||||
async sendExpiresNotify(siteId: number,setting: UserSiteMonitorSetting) {
|
||||
async sendExpiresNotify(siteId: number, setting: UserSiteMonitorSetting) {
|
||||
const tipDays = setting?.certValidDays || 10;
|
||||
const site = await this.info(siteId);
|
||||
const expires = site.certExpiresTime;
|
||||
@@ -345,7 +344,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
url,
|
||||
errorMessage: "站点证书即将过期",
|
||||
notificationType: "siteCertExpireRemind",
|
||||
}
|
||||
},
|
||||
},
|
||||
site.userId
|
||||
);
|
||||
@@ -362,7 +361,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
url,
|
||||
errorMessage: "站点证书已过期",
|
||||
notificationType: "siteCertExpireRemind",
|
||||
}
|
||||
},
|
||||
},
|
||||
site.userId
|
||||
);
|
||||
@@ -370,45 +369,44 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
}
|
||||
|
||||
async checkList(sites: SiteInfoEntity[]) {
|
||||
const cache = {}
|
||||
const getFromCache = async (userId: number,projectId?: number) =>{
|
||||
const key = `${userId}_${projectId??""}`
|
||||
const cache = {};
|
||||
const getFromCache = async (userId: number, projectId?: number) => {
|
||||
const key = `${userId}_${projectId ?? ""}`;
|
||||
if (cache[key]) {
|
||||
return cache[key];
|
||||
}
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId,projectId, UserSiteMonitorSetting)
|
||||
cache[key] = setting
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, projectId, UserSiteMonitorSetting);
|
||||
cache[key] = setting;
|
||||
return setting;
|
||||
}
|
||||
};
|
||||
for (const site of sites) {
|
||||
const setting = await getFromCache(site.userId,site.projectId)
|
||||
let retryTimes = setting?.retryTimes
|
||||
this.doCheck(site,true,retryTimes).catch(e => {
|
||||
const setting = await getFromCache(site.userId, site.projectId);
|
||||
const retryTimes = setting?.retryTimes;
|
||||
this.doCheck(site, true, retryTimes).catch(e => {
|
||||
logger.error(`检查站点证书失败,${site.domain}`, e.message);
|
||||
});
|
||||
await utils.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
async getSetting(userId: number,projectId?: number) {
|
||||
return await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId,projectId, UserSiteMonitorSetting);
|
||||
async getSetting(userId: number, projectId?: number) {
|
||||
return await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, projectId, UserSiteMonitorSetting);
|
||||
}
|
||||
|
||||
async saveSetting(userId: number,projectId: number, bean: UserSiteMonitorSetting) {
|
||||
await this.userSettingsService.saveSetting(userId,projectId, bean);
|
||||
if(bean.cron){
|
||||
async saveSetting(userId: number, projectId: number, bean: UserSiteMonitorSetting) {
|
||||
await this.userSettingsService.saveSetting(userId, projectId, bean);
|
||||
if (bean.cron) {
|
||||
//注册job
|
||||
await this.registerSiteMonitorJob(userId,projectId);
|
||||
}else{
|
||||
this.clearSiteMonitorJob(userId,projectId);
|
||||
await this.registerSiteMonitorJob(userId, projectId);
|
||||
} else {
|
||||
this.clearSiteMonitorJob(userId, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
async ipCheckChange(req: { id: any; ipCheck: any }) {
|
||||
|
||||
await this.update({
|
||||
id: req.id,
|
||||
ipCheck: req.ipCheck
|
||||
ipCheck: req.ipCheck,
|
||||
});
|
||||
if (req.ipCheck) {
|
||||
const site = await this.info(req.id);
|
||||
@@ -419,7 +417,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
async disabledChange(req: { disabled: any; id: any }) {
|
||||
await this.update({
|
||||
id: req.id,
|
||||
disabled: req.disabled
|
||||
disabled: req.disabled,
|
||||
});
|
||||
if (!req.disabled) {
|
||||
const site = await this.info(req.id);
|
||||
@@ -427,7 +425,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
}
|
||||
}
|
||||
|
||||
async doImport(req: { text: string; userId: number,groupId?:number,projectId?:number }) {
|
||||
async doImport(req: { text: string; userId: number; groupId?: number; projectId?: number }) {
|
||||
if (!req.text) {
|
||||
throw new Error("text is required");
|
||||
}
|
||||
@@ -459,7 +457,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
if (arr.length > 2) {
|
||||
name = arr[2] || domain;
|
||||
}
|
||||
let remark:string = "";
|
||||
let remark = "";
|
||||
if (arr.length > 3) {
|
||||
remark = arr[3] || "";
|
||||
}
|
||||
@@ -471,7 +469,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
userId: req.userId,
|
||||
remark,
|
||||
groupId: req.groupId,
|
||||
projectId: req.projectId
|
||||
projectId: req.projectId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -485,77 +483,77 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
await batchAdd(list);
|
||||
}
|
||||
|
||||
clearSiteMonitorJob(userId: number,projectId?: number) {
|
||||
this.cron.remove(`siteMonitor_${userId}_${projectId||""}`);
|
||||
clearSiteMonitorJob(userId: number, projectId?: number) {
|
||||
this.cron.remove(`siteMonitor_${userId}_${projectId || ""}`);
|
||||
}
|
||||
|
||||
async registerSiteMonitorJob(userId?: number,projectId?: number) {
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId,projectId, UserSiteMonitorSetting);
|
||||
async registerSiteMonitorJob(userId?: number, projectId?: number) {
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, projectId, UserSiteMonitorSetting);
|
||||
if (!setting.cron) {
|
||||
return;
|
||||
}
|
||||
//注册个人的 或项目的
|
||||
this.cron.register({
|
||||
name: `siteMonitor_${userId}_${projectId||""}`,
|
||||
name: `siteMonitor_${userId}_${projectId || ""}`,
|
||||
cron: setting.cron,
|
||||
job: () => this.triggerJobOnce(userId,projectId),
|
||||
job: () => this.triggerJobOnce(userId, projectId),
|
||||
});
|
||||
}
|
||||
|
||||
async triggerCommonJob(){
|
||||
async triggerCommonJob() {
|
||||
//遍历用户
|
||||
const userIds = await this.userService.getAllUserIds()
|
||||
const userIds = await this.userService.getAllUserIds();
|
||||
for (const userId of userIds) {
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId,null,UserSiteMonitorSetting)
|
||||
if(setting && setting.cron){
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, null, UserSiteMonitorSetting);
|
||||
if (setting && setting.cron) {
|
||||
//该用户有自定义检查时间,跳过公共job
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
await this.triggerJobOnce(userId)
|
||||
await this.triggerJobOnce(userId);
|
||||
}
|
||||
|
||||
//遍历项目
|
||||
const projectIds = await this.projectService.getAllProjectIds()
|
||||
const projectIds = await this.projectService.getAllProjectIds();
|
||||
for (const projectId of projectIds) {
|
||||
const userId = Constants.enterpriseUserId
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId,projectId,UserSiteMonitorSetting)
|
||||
if(setting && setting.cron){
|
||||
const userId = Constants.enterpriseUserId;
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(userId, projectId, UserSiteMonitorSetting);
|
||||
if (setting && setting.cron) {
|
||||
//该项目有自定义检查时间,跳过公共job
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
await this.triggerJobOnce(userId,projectId)
|
||||
await this.triggerJobOnce(userId, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
async triggerJobOnce(userId?:number,projectId?:number) {
|
||||
if(userId==null){
|
||||
async triggerJobOnce(userId?: number, projectId?: number) {
|
||||
if (userId == null) {
|
||||
throw new Error("userId is required");
|
||||
}
|
||||
const query:any = { disabled: false };
|
||||
const query: any = { disabled: false };
|
||||
query.userId = userId;
|
||||
if(projectId){
|
||||
if (projectId) {
|
||||
query.projectId = projectId;
|
||||
}
|
||||
const siteCount = await this.repository.count({
|
||||
where: query,
|
||||
});
|
||||
if (siteCount === 0) {
|
||||
logger.info(`用户/项目[${userId}_${projectId||""}]没有站点证书需要检查`)
|
||||
logger.info(`用户/项目[${userId}_${projectId || ""}]没有站点证书需要检查`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`站点证书检查开始执行[${userId}_${projectId||""}]`);
|
||||
|
||||
let jobEntity :Partial<JobHistoryEntity> = null;
|
||||
|
||||
jobEntity = {
|
||||
logger.info(`站点证书检查开始执行[${userId}_${projectId || ""}]`);
|
||||
|
||||
let jobEntity: Partial<JobHistoryEntity> = null;
|
||||
|
||||
jobEntity = {
|
||||
userId,
|
||||
projectId,
|
||||
type:"siteCertMonitor",
|
||||
title: '站点证书检查',
|
||||
result:"start",
|
||||
startAt:new Date().getTime(),
|
||||
}
|
||||
type: "siteCertMonitor",
|
||||
title: "站点证书检查",
|
||||
result: "start",
|
||||
startAt: new Date().getTime(),
|
||||
};
|
||||
await this.jobHistoryService.add(jobEntity);
|
||||
let offset = 0;
|
||||
const limit = 50;
|
||||
@@ -575,17 +573,17 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
await this.checkList(records);
|
||||
}
|
||||
|
||||
logger.info(`站点证书检查完成[${userId}_${projectId||""}]`);
|
||||
logger.info(`站点证书检查完成[${userId}_${projectId || ""}]`);
|
||||
await this.jobHistoryService.update({
|
||||
id: jobEntity.id,
|
||||
result: "done",
|
||||
content:`共检查${count}个站点`,
|
||||
endAt:new Date().getTime(),
|
||||
updateTime:new Date(),
|
||||
content: `共检查${count}个站点`,
|
||||
endAt: new Date().getTime(),
|
||||
updateTime: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
async batchDelete(ids: number[], userId: number,projectId?:number): Promise<void> {
|
||||
async batchDelete(ids: number[], userId: number, projectId?: number): Promise<void> {
|
||||
await this.repository.delete({
|
||||
id: In(ids),
|
||||
userId,
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
||||
import {BaseService, SysSettingsService} from "@certd/lib-server";
|
||||
import {InjectEntityModel} from "@midwayjs/typeorm";
|
||||
import {Repository} from "typeorm";
|
||||
import {SiteInfoEntity} from "../entity/site-info.js";
|
||||
import {NotificationService} from "../../pipeline/service/notification-service.js";
|
||||
import {UserSuiteService} from "@certd/commercial-core";
|
||||
import {UserSettingsService} from "../../mine/service/user-settings-service.js";
|
||||
import {SiteIpEntity} from "../entity/site-ip.js";
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { BaseService, SysSettingsService } from "@certd/lib-server";
|
||||
import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { SiteInfoEntity } from "../entity/site-info.js";
|
||||
import { NotificationService } from "../../pipeline/service/notification-service.js";
|
||||
import { UserSuiteService } from "@certd/commercial-core";
|
||||
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
|
||||
import { SiteIpEntity } from "../entity/site-ip.js";
|
||||
import dnsSdk from "dns";
|
||||
import {logger} from "@certd/basic";
|
||||
import { logger } from "@certd/basic";
|
||||
import dayjs from "dayjs";
|
||||
import {siteTester} from "./site-tester.js";
|
||||
import {PeerCertificate} from "tls";
|
||||
import { siteTester } from "./site-tester.js";
|
||||
import { PeerCertificate } from "tls";
|
||||
import { UserSiteMonitorSetting } from "../../mine/service/models.js";
|
||||
import { dnsContainer } from "./dns-custom.js";
|
||||
|
||||
const dns = dnsSdk.promises;
|
||||
const dns = dnsSdk.promises;
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
@@ -47,12 +47,11 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
throw new Error("userId is required");
|
||||
}
|
||||
data.disabled = false;
|
||||
const res= await super.add(data);
|
||||
await this.updateIpCount(data.siteId)
|
||||
return res
|
||||
const res = await super.add(data);
|
||||
await this.updateIpCount(data.siteId);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
async update(data: any) {
|
||||
if (!data.id) {
|
||||
throw new Error("id is required");
|
||||
@@ -61,52 +60,52 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
await super.update(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async sync(entity: SiteInfoEntity,check:boolean = true) {
|
||||
|
||||
async sync(entity: SiteInfoEntity, check = true) {
|
||||
const domain = entity.domain;
|
||||
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(entity.userId,entity.projectId, UserSiteMonitorSetting);
|
||||
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(entity.userId, entity.projectId, UserSiteMonitorSetting);
|
||||
|
||||
const dnsServer = setting.dnsServer
|
||||
let resolver = dns
|
||||
const dnsServer = setting.dnsServer;
|
||||
let resolver = dns;
|
||||
if (dnsServer && dnsServer.length > 0) {
|
||||
resolver = dnsContainer.getDns(dnsServer) as any
|
||||
resolver = dnsContainer.getDns(dnsServer) as any;
|
||||
}
|
||||
|
||||
//从域名解析中获取所有ip
|
||||
const ips = await this.getAllIpsFromDomain(domain,resolver,entity.ipSyncMode);
|
||||
if (ips.length === 0 ) {
|
||||
logger.warn(`没有发现${domain}的IP`)
|
||||
return
|
||||
const ips = await this.getAllIpsFromDomain(domain, resolver, entity.ipSyncMode);
|
||||
if (ips.length === 0) {
|
||||
logger.warn(`没有发现${domain}的IP`);
|
||||
return;
|
||||
}
|
||||
|
||||
const oldIps = await this.repository.find({
|
||||
where:{
|
||||
siteId: entity.id,
|
||||
from:"sync"
|
||||
}
|
||||
})
|
||||
where: {
|
||||
siteId: entity.id,
|
||||
from: "sync",
|
||||
},
|
||||
});
|
||||
|
||||
let hasChanged = true
|
||||
if (oldIps.length === ips.length ){
|
||||
let hasChanged = true;
|
||||
if (oldIps.length === ips.length) {
|
||||
//检查是否有变化
|
||||
const oldIpList = oldIps.map(ip=>ip.ipAddress).sort().join(",")
|
||||
const newIpList = ips.sort().join(",")
|
||||
if(oldIpList === newIpList){
|
||||
//无变化
|
||||
hasChanged = false
|
||||
}
|
||||
const oldIpList = oldIps
|
||||
.map(ip => ip.ipAddress)
|
||||
.sort()
|
||||
.join(",");
|
||||
const newIpList = ips.sort().join(",");
|
||||
if (oldIpList === newIpList) {
|
||||
//无变化
|
||||
hasChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasChanged){
|
||||
logger.info(`发现${domain}的IP变化,需要更新,旧IP:${oldIps.map(ip=>ip.ipAddress).join(",")},新IP:${ips.join(",")}`)
|
||||
if (hasChanged) {
|
||||
logger.info(`发现${domain}的IP变化,需要更新,旧IP:${oldIps.map(ip => ip.ipAddress).join(",")},新IP:${ips.join(",")}`);
|
||||
//有变化需要更新
|
||||
//删除所有的ip
|
||||
await this.repository.delete({
|
||||
siteId: entity.id,
|
||||
from: "sync"
|
||||
from: "sync",
|
||||
});
|
||||
|
||||
//添加新的ip
|
||||
@@ -116,36 +115,36 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
userId: entity.userId,
|
||||
siteId: entity.id,
|
||||
from: "sync",
|
||||
disabled:false,
|
||||
disabled: false,
|
||||
});
|
||||
await this.updateIpCount(entity.id)
|
||||
await this.updateIpCount(entity.id);
|
||||
}
|
||||
}
|
||||
if (check){
|
||||
if (check) {
|
||||
await this.checkAll(entity);
|
||||
}
|
||||
}
|
||||
|
||||
async check(ipId: number, domain: string, port: number,retryTimes = null ,tipDays = 10) {
|
||||
if(!ipId){
|
||||
return
|
||||
async check(ipId: number, domain: string, port: number, retryTimes = null, tipDays = 10) {
|
||||
if (!ipId) {
|
||||
return;
|
||||
}
|
||||
const entity = await this.info(ipId);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
logger.info(`开始测试站点ip: id=${entity.id},ip=${entity.ipAddress}`)
|
||||
logger.info(`开始测试站点ip: id=${entity.id},ip=${entity.ipAddress}`);
|
||||
try {
|
||||
await this.update({
|
||||
id: entity.id,
|
||||
checkStatus: "checking",
|
||||
lastCheckTime: dayjs().valueOf()
|
||||
lastCheckTime: dayjs().valueOf(),
|
||||
});
|
||||
const res = await siteTester.test({
|
||||
host: domain,
|
||||
port: port,
|
||||
retryTimes : retryTimes??3,
|
||||
ipAddress: entity.ipAddress
|
||||
retryTimes: retryTimes ?? 3,
|
||||
ipAddress: entity.ipAddress,
|
||||
});
|
||||
|
||||
const certi: PeerCertificate = res.certificate;
|
||||
@@ -170,39 +169,38 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
certExpiresTime: dayjs(expires).valueOf(),
|
||||
lastCheckTime: dayjs().valueOf(),
|
||||
error: null,
|
||||
checkStatus: "ok"
|
||||
checkStatus: "ok",
|
||||
};
|
||||
|
||||
await this.update(updateData);
|
||||
logger.info(`测试站点ip成功: id=${updateData.id},ip=${entity.ipAddress},expiresTime=${updateData.certExpiresTime}`)
|
||||
logger.info(`测试站点ip成功: id=${updateData.id},ip=${entity.ipAddress},expiresTime=${updateData.certExpiresTime}`);
|
||||
|
||||
return {
|
||||
...updateData,
|
||||
ipAddress: entity.ipAddress,
|
||||
}
|
||||
|
||||
};
|
||||
} catch (e) {
|
||||
logger.error("check site ip error", e);
|
||||
await this.update({
|
||||
id: entity.id,
|
||||
checkStatus: "error",
|
||||
lastCheckTime: dayjs().valueOf(),
|
||||
error: e.message
|
||||
error: e.message,
|
||||
});
|
||||
return {
|
||||
id: entity.id,
|
||||
ipAddress: entity.ipAddress,
|
||||
error: e.message
|
||||
}
|
||||
error: e.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async checkAll(siteInfo: SiteInfoEntity,retryTimes = null,onFinish?: (e: any) => void) {
|
||||
async checkAll(siteInfo: SiteInfoEntity, retryTimes = null, onFinish?: (e: any) => void) {
|
||||
const siteId = siteInfo.id;
|
||||
const ips = await this.repository.find({
|
||||
where: {
|
||||
siteId: siteId
|
||||
}
|
||||
siteId: siteId,
|
||||
},
|
||||
});
|
||||
const domain = siteInfo.domain;
|
||||
const port = siteInfo.httpsPort;
|
||||
@@ -210,44 +208,44 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
for (const item of ips) {
|
||||
const func = async () => {
|
||||
try {
|
||||
return await this.check(item.id, domain, port,retryTimes);
|
||||
return await this.check(item.id, domain, port, retryTimes);
|
||||
} catch (e) {
|
||||
logger.error("check site item error", e);
|
||||
return {
|
||||
...item,
|
||||
error:e.message
|
||||
}
|
||||
error: e.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
promiseList.push(func());
|
||||
}
|
||||
Promise.all(promiseList).then((res)=>{
|
||||
const finished = res.filter(item=>{
|
||||
return item!=null
|
||||
})
|
||||
Promise.all(promiseList).then(res => {
|
||||
const finished = res.filter(item => {
|
||||
return item != null;
|
||||
});
|
||||
if (onFinish) {
|
||||
onFinish && onFinish(finished)
|
||||
onFinish && onFinish(finished);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async getAllIpsFromDomain(domain: string,resolver:any = dns,ipSyncMode:string = "all"):Promise<string[]> {
|
||||
const getFromV4 = async ():Promise<string[]> => {
|
||||
try{
|
||||
async getAllIpsFromDomain(domain: string, resolver: any = dns, ipSyncMode = "all"): Promise<string[]> {
|
||||
const getFromV4 = async (): Promise<string[]> => {
|
||||
try {
|
||||
return await resolver.resolve4(domain);
|
||||
}catch (err) {
|
||||
logger.warn(`[${domain}] resolve4 error`, err)
|
||||
return []
|
||||
} catch (err) {
|
||||
logger.warn(`[${domain}] resolve4 error`, err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
const getFromV6 = async ():Promise<string[]> => {
|
||||
try{
|
||||
};
|
||||
const getFromV6 = async (): Promise<string[]> => {
|
||||
try {
|
||||
return await resolver.resolve6(domain);
|
||||
}catch (err) {
|
||||
logger.warn(`[${domain}] resolve6 error`, err)
|
||||
return []
|
||||
} catch (err) {
|
||||
logger.warn(`[${domain}] resolve6 error`, err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (ipSyncMode === "ipv4") {
|
||||
return await getFromV4();
|
||||
@@ -261,24 +259,25 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async updateIpCount(siteId:number){
|
||||
async updateIpCount(siteId: number) {
|
||||
const count = await this.repository.count({
|
||||
where:{
|
||||
siteId
|
||||
}
|
||||
})
|
||||
await this.siteInfoRepository.update({
|
||||
//where
|
||||
id:siteId,
|
||||
},
|
||||
where: {
|
||||
siteId,
|
||||
},
|
||||
});
|
||||
await this.siteInfoRepository.update(
|
||||
{
|
||||
//where
|
||||
id: siteId,
|
||||
},
|
||||
{
|
||||
//update
|
||||
ipCount:count
|
||||
})
|
||||
ipCount: count,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async doImport(req: { text: string; userId:number, siteId:number,projectId?:number }) {
|
||||
async doImport(req: { text: string; userId: number; siteId: number; projectId?: number }) {
|
||||
if (!req.text) {
|
||||
throw new Error("text is required");
|
||||
}
|
||||
@@ -289,9 +288,9 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
const siteEntity = await this.siteInfoRepository.findOne({
|
||||
where: {
|
||||
id: req.siteId,
|
||||
userId:req.userId,
|
||||
projectId:req.projectId
|
||||
}
|
||||
userId: req.userId,
|
||||
projectId: req.projectId,
|
||||
},
|
||||
});
|
||||
if (!siteEntity) {
|
||||
throw new Error(`站点${req.siteId}不存在`);
|
||||
@@ -307,11 +306,11 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
continue;
|
||||
}
|
||||
list.push({
|
||||
ipAddress:item,
|
||||
ipAddress: item,
|
||||
userId: userId,
|
||||
siteId: req.siteId,
|
||||
from: "import",
|
||||
disabled:false,
|
||||
disabled: false,
|
||||
projectId: req.projectId,
|
||||
});
|
||||
}
|
||||
@@ -324,8 +323,8 @@ export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||
await batchAdd(list);
|
||||
}
|
||||
|
||||
async syncAndCheck(siteEntity:SiteInfoEntity,retryTimes = null,onFinish?: (e: any) => void){
|
||||
await this.sync(siteEntity,false);
|
||||
await this.checkAll(siteEntity,retryTimes,onFinish);
|
||||
async syncAndCheck(siteEntity: SiteInfoEntity, retryTimes = null, onFinish?: (e: any) => void) {
|
||||
await this.sync(siteEntity, false);
|
||||
await this.checkAll(siteEntity, retryTimes, onFinish);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { logger, safePromise, utils } from "@certd/basic";
|
||||
import { merge } from "lodash-es";
|
||||
import https from "https";
|
||||
import { PeerCertificate } from "tls";
|
||||
import {DnsCustom} from "./dns-custom.js";
|
||||
import { DnsCustom } from "./dns-custom.js";
|
||||
|
||||
export type SiteTestReq = {
|
||||
host: string; // 只用域名部分
|
||||
@@ -20,8 +20,8 @@ export type SiteTestRes = {
|
||||
|
||||
export class SiteTester {
|
||||
async test(req: SiteTestReq): Promise<SiteTestRes> {
|
||||
const req_ = {...req}
|
||||
delete req_.customDns
|
||||
const req_ = { ...req };
|
||||
delete req_.customDns;
|
||||
logger.info("测试站点:", JSON.stringify(req_));
|
||||
const maxRetryTimes = req.retryTimes == null ? 3 : req.retryTimes;
|
||||
let tryCount = 0;
|
||||
@@ -49,40 +49,40 @@ export class SiteTester {
|
||||
{
|
||||
port: 443,
|
||||
method: "GET",
|
||||
rejectUnauthorized: false
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
req
|
||||
);
|
||||
|
||||
let customLookup = null
|
||||
let customLookup = null;
|
||||
if (req.ipAddress) {
|
||||
//使用固定的ip
|
||||
const ipAddress = req.ipAddress;
|
||||
options.headers = {
|
||||
host: options.host,
|
||||
//sni
|
||||
servername: options.host
|
||||
servername: options.host,
|
||||
};
|
||||
options.host = ipAddress;
|
||||
}else if (req.customDns ) {
|
||||
} else if (req.customDns) {
|
||||
// 非ip address 请求时
|
||||
const customDns = req.customDns
|
||||
customLookup = async (hostname:string, options:any, callback)=> {
|
||||
const customDns = req.customDns;
|
||||
customLookup = async (hostname: string, options: any, callback) => {
|
||||
console.log(hostname, options);
|
||||
|
||||
// { family: undefined, hints: 0, all: true }
|
||||
const res = await customDns.lookup(hostname, options)
|
||||
console.log("custom lookup res:",res)
|
||||
const res = await customDns.lookup(hostname, options);
|
||||
console.log("custom lookup res:", res);
|
||||
if (!res || res.length === 0) {
|
||||
callback(new Error("没有解析到IP"));
|
||||
}
|
||||
callback(null, res);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const agentOptions:any = { keepAlive: false };
|
||||
const agentOptions: any = { keepAlive: false };
|
||||
if (customLookup) {
|
||||
agentOptions.lookup = customLookup
|
||||
agentOptions.lookup = customLookup;
|
||||
}
|
||||
options.agent = new https.Agent(agentOptions);
|
||||
|
||||
@@ -95,19 +95,19 @@ export class SiteTester {
|
||||
});
|
||||
|
||||
// ✅ 关键:在 'socket' 事件中获取证书(握手完成后立即执行)
|
||||
req.on('socket', (socket:any) => {
|
||||
socket.on('secureConnect', () => {
|
||||
req.on("socket", (socket: any) => {
|
||||
socket.on("secureConnect", () => {
|
||||
// TLS握手完成,证书已经可用
|
||||
const certificate = socket.getPeerCertificate();
|
||||
if (certificate.subject) {
|
||||
logger.info('证书获取成功', certificate.subject);
|
||||
logger.info("证书获取成功", certificate.subject);
|
||||
resolve({
|
||||
certificate
|
||||
certificate,
|
||||
});
|
||||
}else{
|
||||
} else {
|
||||
logger.warn("证书信息为空");
|
||||
resolve({
|
||||
certificate: null
|
||||
certificate: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user