From 2d7729dbe98f29088f5f317db2b52cc1ede223a6 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Thu, 5 Jun 2025 23:31:36 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E7=AB=99=E7=82=B9=E7=9B=91=E6=8E=A7?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=B9=E9=87=8F=E5=AF=BC=E5=85=A5=E5=9F=9F?= =?UTF-8?q?=E5=90=8D=E5=92=8Cip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 + ...te-import.csv => site-import-template.csv} | 0 .../src/views/certd/monitor/site/api.ts | 8 + .../src/views/certd/monitor/site/crud.tsx | 17 +- .../src/views/certd/monitor/site/ip/api.ts | 7 + .../src/views/certd/monitor/site/ip/crud.tsx | 21 +- .../src/views/certd/monitor/site/ip/use.tsx | 40 ++- .../src/views/certd/monitor/site/use.tsx | 41 +++ .../user/monitor/site-info-controller.ts | 11 + .../user/monitor/site-ip-controller.ts | 11 + .../monitor/service/site-info-service.ts | 248 +++++++++++------- .../monitor/service/site-ip-service.ts | 48 +++- 12 files changed, 358 insertions(+), 98 deletions(-) rename packages/ui/certd-client/public/{template-import.csv => site-import-template.csv} (100%) create mode 100644 packages/ui/certd-client/src/views/certd/monitor/site/use.tsx diff --git a/README.md b/README.md index 13eee2ba..3bec4e90 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,11 @@ https://afdian.com/a/greper 1. 可以调整开源协议以使其更严格或更宽松。 2. 可以用于商业用途。 +感谢以下贡献者做出的贡献。 + + + ## 九、 开源许可 * 本项目遵循 GNU Affero General Public License(AGPL)开源协议。 diff --git a/packages/ui/certd-client/public/template-import.csv b/packages/ui/certd-client/public/site-import-template.csv similarity index 100% rename from packages/ui/certd-client/public/template-import.csv rename to packages/ui/certd-client/public/site-import-template.csv diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/api.ts b/packages/ui/certd-client/src/views/certd/monitor/site/api.ts index 8121be30..4e85d98f 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/api.ts +++ b/packages/ui/certd-client/src/views/certd/monitor/site/api.ts @@ -56,6 +56,14 @@ export const siteInfoApi = { }); }, + async Import(form: any) { + return await request({ + url: apiPrefix + "/import", + method: "post", + data: form, + }); + }, + async DisabledChange(id: number, disabled: boolean) { return await request({ url: apiPrefix + "/disabledChange", diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx index c3b3e5b1..abba9c88 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx @@ -8,6 +8,7 @@ import { useSettingStore } from "/@/store/settings"; import { mySuiteApi } from "/@/views/certd/suite/mine/api"; import { mitter } from "/@/utils/util.mitt"; import { useSiteIpMonitor } from "./ip/use"; +import { useSiteImport } from "/@/views/certd/monitor/site/use"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const { t } = useI18n(); @@ -44,6 +45,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }); const { openSiteIpMonitorDialog } = useSiteIpMonitor(); + const { openSiteImportDialog } = useSiteImport(); return { crudOptions: { request: { @@ -97,6 +99,19 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat await crudExpose.openAdd({}); }, }, + //导入按钮 + import: { + show: true, + text: "批量导入", + type: "primary", + async click() { + openSiteImportDialog({ + afterSubmit() { + crudExpose.doRefresh(); + }, + }); + }, + }, }, }, rowHandle: { @@ -235,7 +250,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, }; return ( - + {row.certDomains} ); diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/ip/api.ts b/packages/ui/certd-client/src/views/certd/monitor/site/ip/api.ts index 2cc40972..15c83e5a 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/ip/api.ts +++ b/packages/ui/certd-client/src/views/certd/monitor/site/ip/api.ts @@ -68,4 +68,11 @@ export const siteIpApi = { }, }); }, + async Import(form: any) { + return await request({ + url: apiPrefix + "/import", + method: "post", + data: form, + }); + }, }; diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx index 3715863e..1f7efe97 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx @@ -4,13 +4,11 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi import { siteIpApi } from "./api"; import dayjs from "dayjs"; import { Modal, notification } from "ant-design-vue"; -import { useSettingStore } from "/@/store/settings"; +import { useSiteIpMonitor } from "/@/views/certd/monitor/site/ip/use"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const { t } = useI18n(); const api = siteIpApi; - const { crudBinding } = crudExpose; const pageRequest = async (query: UserPageQuery): Promise => { if (!query.query) { query.query = {}; @@ -36,8 +34,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat return res; }; - const settingsStore = useSettingStore(); - const checkStatusDict = dict({ data: [ { label: "成功", value: "ok", color: "green" }, @@ -45,6 +41,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat { label: "异常", value: "error", color: "red" }, ], }); + const { openSiteIpImportDialog } = useSiteIpMonitor(); return { crudOptions: { request: { @@ -75,6 +72,19 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat await crudExpose.openAdd({}); }, }, + import: { + show: true, + text: "批量导入", + type: "primary", + async click() { + openSiteIpImportDialog({ + siteId: context.props.siteId, + afterSubmit() { + crudExpose.doRefresh(); + }, + }); + }, + }, load: { text: "同步IP", type: "primary", @@ -295,6 +305,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat data: [ { label: "同步", value: "sync", color: "green" }, { label: "手动", value: "manual", color: "blue" }, + { label: "导入", value: "import", color: "blue" }, ], }), form: { diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/ip/use.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/ip/use.tsx index 764cdd51..2a00d4d3 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/ip/use.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/ip/use.tsx @@ -1,11 +1,10 @@ import { useFormWrapper } from "@fast-crud/fast-crud"; -import { useRouter } from "vue-router"; import SiteIpCertMonitor from "./index.vue"; +import { siteIpApi } from "/@/views/certd/monitor/site/ip/api"; export function useSiteIpMonitor() { - const { openDialog } = useFormWrapper(); - const router = useRouter(); + const { openDialog, openCrudFormDialog } = useFormWrapper(); async function openSiteIpMonitorDialog(opts: { siteId: number }) { await openDialog({ @@ -34,7 +33,42 @@ export function useSiteIpMonitor() { }); } + async function openSiteIpImportDialog(opts: { afterSubmit: any; siteId: any }) { + const { afterSubmit } = opts; + await openCrudFormDialog({ + crudOptions: { + columns: { + text: { + type: "textarea", + title: "IP列表", + form: { + helper: "IP或者CNAME域名,一行一个", + rules: [{ required: true, message: "请输入要导入的IP或域名" }], + component: { + placeholder: "192.168.1.2\ncname.foo.com", + rows: 8, + }, + col: { + span: 24, + }, + }, + }, + }, + form: { + async doSubmit({ form }) { + return siteIpApi.Import({ + ...form, + siteId: opts.siteId, + }); + }, + afterSubmit, + }, + }, + }); + } + return { openSiteIpMonitorDialog, + openSiteIpImportDialog, }; } diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/use.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/use.tsx new file mode 100644 index 00000000..a4b913b7 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/monitor/site/use.tsx @@ -0,0 +1,41 @@ +import { useFormWrapper } from "@fast-crud/fast-crud"; +import { siteInfoApi } from "./api"; + +export function useSiteImport() { + const { openCrudFormDialog } = useFormWrapper(); + + async function openSiteImportDialog(opts: { afterSubmit: any }) { + const { afterSubmit } = opts; + await openCrudFormDialog({ + crudOptions: { + columns: { + text: { + type: "textarea", + title: "域名列表", + form: { + helper: "格式【域名:端口:名称】,一行一个,其中端口、名称可以省略\n比如:\nwww.baidu.com:443:百度\nwww.taobao.com::淘宝\nwww.google.com", + rules: [{ required: true, message: "请输入要导入的域名" }], + component: { + placeholder: "www.baidu.com:443:百度\nwww.taobao.com::淘宝\nwww.google.com\n", + rows: 8, + }, + col: { + span: 24, + }, + }, + }, + }, + form: { + async doSubmit({ form }) { + return siteInfoApi.Import(form); + }, + afterSubmit, + }, + }, + }); + } + + return { + openSiteImportDialog, + }; +} diff --git a/packages/ui/certd-server/src/controller/user/monitor/site-info-controller.ts b/packages/ui/certd-server/src/controller/user/monitor/site-info-controller.ts index ae56f156..1d686c0c 100644 --- a/packages/ui/certd-server/src/controller/user/monitor/site-info-controller.ts +++ b/packages/ui/certd-server/src/controller/user/monitor/site-info-controller.ts @@ -105,6 +105,17 @@ export class SiteInfoController extends CrudController { await this.service.checkAllByUsers(userId); return this.ok(); } + + @Post('/import', { summary: Constants.per.authOnly }) + async doImport(@Body(ALL) body: any) { + const userId = this.getUserId(); + await this.service.doImport({ + text:body.text, + userId + }) + return this.ok(); + } + @Post('/ipCheckChange', { summary: Constants.per.authOnly }) async ipCheckChange(@Body(ALL) bean: any) { const userId = this.getUserId(); diff --git a/packages/ui/certd-server/src/controller/user/monitor/site-ip-controller.ts b/packages/ui/certd-server/src/controller/user/monitor/site-ip-controller.ts index 0bd175c8..713928cb 100644 --- a/packages/ui/certd-server/src/controller/user/monitor/site-ip-controller.ts +++ b/packages/ui/certd-server/src/controller/user/monitor/site-ip-controller.ts @@ -111,5 +111,16 @@ export class SiteInfoController extends CrudController { return this.ok(); } + @Post('/import', { summary: Constants.per.authOnly }) + async doImport(@Body(ALL) body: any) { + const userId = this.getUserId(); + await this.service.doImport({ + text:body.text, + userId, + siteId:body.siteId + }) + return this.ok(); + } + } diff --git a/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts b/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts index 183dc359..8886481f 100644 --- a/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts +++ b/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts @@ -1,19 +1,19 @@ -import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core'; -import { BaseService, NeedSuiteException, NeedVIPException, SysSettingsService } from '@certd/lib-server'; -import { InjectEntityModel } from '@midwayjs/typeorm'; -import { 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 { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { BaseService, NeedSuiteException, NeedVIPException, SysSettingsService } from "@certd/lib-server"; +import { InjectEntityModel } from "@midwayjs/typeorm"; +import { 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 { UserSiteMonitorSetting } from "../../mine/service/models.js"; +import { SiteIpService } from "./site-ip-service.js"; +import { SiteIpEntity } from "../entity/site-ip.js"; @Provide() @Scope(ScopeEnum.Request, { allowDowngrade: true }) @@ -43,7 +43,7 @@ export class SiteInfoService extends BaseService { async add(data: SiteInfoEntity) { if (!data.userId) { - throw new Error('userId is required'); + throw new Error("userId is required"); } if (isComm()) { @@ -51,25 +51,34 @@ export class SiteInfoService extends BaseService { if (suiteSetting.enabled) { const userSuite = await this.userSuiteService.getMySuiteDetail(data.userId); if (userSuite.monitorCount.max != -1 && userSuite.monitorCount.max <= userSuite.monitorCount.used) { - throw new NeedSuiteException('站点监控数量已达上限,请购买或升级套餐'); + throw new NeedSuiteException("站点监控数量已达上限,请购买或升级套餐"); } } - }else if (!isPlus()) { - const count = await this.getUserMonitorCount(data.userId); - if (count >= 1) { - throw new NeedVIPException('站点监控数量已达上限,请升级专业版'); - } + } else if (!isPlus()) { + const count = await this.getUserMonitorCount(data.userId); + if (count >= 1) { + throw new NeedVIPException("站点监控数量已达上限,请升级专业版"); + } + } + data.disabled = false; + + const found = await this.repository.findOne({ + where: { + domain: data.domain, + userId: data.userId, + httpsPort: data.httpsPort || 443 + } + }); + if (found) { + return { id: found.id }; } - - - data.disabled = false; return await super.add(data); } async update(data: any) { if (!data.id) { - throw new Error('id is required'); + throw new Error("id is required"); } delete data.userId; await super.update(data); @@ -77,10 +86,10 @@ export class SiteInfoService extends BaseService { async getUserMonitorCount(userId: number) { if (!userId) { - throw new Error('userId is required'); + throw new Error("userId is required"); } return await this.repository.count({ - where: { userId }, + where: { userId } }); } @@ -92,26 +101,26 @@ export class SiteInfoService extends BaseService { */ async doCheck(site: SiteInfoEntity, notify = true, retryTimes = 3) { if (!site?.domain) { - throw new Error('站点域名不能为空'); + throw new Error("站点域名不能为空"); } try { await this.update({ id: site.id, - checkStatus: 'checking', - lastCheckTime: dayjs().valueOf(), + checkStatus: "checking", + lastCheckTime: dayjs().valueOf() }); const res = await siteTester.test({ host: site.domain, port: site.httpsPort, - retryTimes, + retryTimes }); const certi: PeerCertificate = res.certificate; if (!certi) { - throw new Error('没有发现证书'); + throw new Error("没有发现证书"); } const expires = certi.valid_to; - const allDomains = certi.subjectaltname?.replaceAll('DNS:', '').split(',') ||[]; + const allDomains = certi.subjectaltname?.replaceAll("DNS:", "").split(",") || []; const mainDomain = certi.subject?.CN; let domains = allDomains; if (!allDomains.includes(mainDomain)) { @@ -119,23 +128,26 @@ export class SiteInfoService extends BaseService { } const issuer = `${certi.issuer.O}<${certi.issuer.CN}>`; const isExpired = dayjs().valueOf() > dayjs(expires).valueOf(); - const status = isExpired ? 'expired' : 'ok'; + const status = isExpired ? "expired" : "ok"; const updateData = { id: site.id, - certDomains: domains.join(','), + certDomains: domains.join(","), certStatus: status, certProvider: issuer, certExpiresTime: dayjs(expires).valueOf(), lastCheckTime: dayjs().valueOf(), error: null, - checkStatus: 'ok', + checkStatus: "ok" }; + if (site.ipCheck) { + delete updateData.checkStatus + } await this.update(updateData); //检查ip - await this.checkAllIp(site) + await this.checkAllIp(site); if (!notify) { return; @@ -143,15 +155,15 @@ export class SiteInfoService extends BaseService { try { await this.sendExpiresNotify(site); } catch (e) { - logger.error('send notify error', e); + logger.error("send notify error", e); } } catch (e) { - logger.error('check site error', e); + logger.error("check site error", e); await this.update({ id: site.id, - checkStatus: 'error', + checkStatus: "error", lastCheckTime: dayjs().valueOf(), - error: e.message, + error: e.message }); if (!notify) { return; @@ -159,49 +171,56 @@ export class SiteInfoService extends BaseService { try { await this.sendCheckErrorNotify(site); } catch (e) { - logger.error('send notify error', e); + logger.error("send notify error", e); } } } - async checkAllIp(site:SiteInfoEntity){ - if( !site.ipCheck){ + async checkAllIp(site: SiteInfoEntity) { + if (!site.ipCheck) { return; } const certExpiresTime = site.certExpiresTime; - const onFinished = async (list:SiteIpEntity[])=>{ - let errorCount = 0 - let errorMessage = "" + const onFinished = async (list: SiteIpEntity[]) => { + let errorCount = 0; + let errorMessage = ""; for (const item of list) { if (!item) { continue; } - errorCount++ - if(item.error){ - errorMessage += `${item.ipAddress}:${item.error}; \n` - }else if(item.certExpiresTime!==certExpiresTime){ - errorMessage += `${item.ipAddress}:与主站证书过期时间不一致; \n` - }else{ - errorCount-- + errorCount++; + if (item.error) { + errorMessage += `${item.ipAddress}:${item.error}; \n`; + } else if (item.certExpiresTime !== certExpiresTime) { + errorMessage += `${item.ipAddress}:与主站证书过期时间不一致; \n`; + } else { + errorCount--; } } - if (errorCount<=0){ - return + if (errorCount <= 0) { + //检查正常 + await this.update({ + id: site.id, + checkStatus: "ok", + error: "", + ipErrorCount: 0 + }); + return; } await this.update({ id: site.id, - checkStatus: 'error', + checkStatus: "error", error: errorMessage, - ipErrorCount: errorCount, - }) + ipErrorCount: errorCount + }); try { - site = await this.info(site.id) - await this.sendCheckErrorNotify(site,true); + site = await this.info(site.id); + await this.sendCheckErrorNotify(site, true); } catch (e) { - logger.error('send notify error', e); + logger.error("send notify error", e); } - } - await this.siteIpService.checkAll(site,onFinished) + }; + await this.siteIpService.checkAll(site, onFinished); } /** @@ -213,13 +232,13 @@ export class SiteInfoService extends BaseService { async check(id: number, notify = false, retryTimes = 3) { const site = await this.info(id); if (!site) { - throw new Error('站点不存在'); + throw new Error("站点不存在"); } return await this.doCheck(site, notify, retryTimes); } - async sendCheckErrorNotify(site: SiteInfoEntity,fromIpCheck=false) { - const url = await this.notificationService.getBindUrl('#/certd/monitor/site'); + async sendCheckErrorNotify(site: SiteInfoEntity, fromIpCheck = false) { + const url = await this.notificationService.getBindUrl("#/certd/monitor/site"); // 发邮件 await this.notificationService.send( { @@ -227,22 +246,23 @@ export class SiteInfoService extends BaseService { logger: logger, body: { url, - title: `站点证书${fromIpCheck?"(IP)":""}检查出错<${site.name}>`, + title: `站点证书${fromIpCheck ? "(IP)" : ""}检查出错<${site.name}>`, content: `站点名称: ${site.name} \n站点域名: ${site.domain} \n错误信息:${site.error}`, - errorMessage: site.error, - }, + errorMessage: site.error + } }, site.userId ); } + async sendExpiresNotify(site: SiteInfoEntity) { - const tipDays = 10 + const tipDays = 10; const expires = site.certExpiresTime; - const validDays = dayjs(expires).diff(dayjs(), 'day'); - const url = await this.notificationService.getBindUrl('#/certd/monitor/site'); - const content = `站点名称: ${site.name} \n站点域名: ${site.domain} \n证书域名: ${site.certDomains} \n颁发机构: ${site.certProvider} \n过期时间: ${dayjs(site.certExpiresTime).format('YYYY-MM-DD')} \n`; + const validDays = dayjs(expires).diff(dayjs(), "day"); + const url = await this.notificationService.getBindUrl("#/certd/monitor/site"); + const content = `站点名称: ${site.name} \n站点域名: ${site.domain} \n证书域名: ${site.certDomains} \n颁发机构: ${site.certProvider} \n过期时间: ${dayjs(site.certExpiresTime).format("YYYY-MM-DD")} \n`; if (validDays >= 0 && validDays < tipDays) { // 发通知 await this.notificationService.send( @@ -252,8 +272,8 @@ export class SiteInfoService extends BaseService { body: { title: `站点证书即将过期,剩余${validDays}天,<${site.name}>`, content, - url, - }, + url + } }, site.userId ); @@ -268,7 +288,7 @@ export class SiteInfoService extends BaseService { content, url, errorMessage: "站点证书已过期" - }, + } }, site.userId ); @@ -277,10 +297,10 @@ export class SiteInfoService extends BaseService { async checkAllByUsers(userId: any) { if (!userId) { - throw new Error('userId is required'); + throw new Error("userId is required"); } const sites = await this.repository.find({ - where: { userId }, + where: { userId } }); this.checkList(sites); } @@ -294,7 +314,7 @@ export class SiteInfoService extends BaseService { } } - async getSetting(userId: number){ + async getSetting(userId: number) { return await this.userSettingsService.getSetting(userId, UserSiteMonitorSetting); } @@ -302,26 +322,78 @@ export class SiteInfoService extends BaseService { await this.userSettingsService.saveSetting(userId, bean); } - async ipCheckChange(req: {id: any; ipCheck: any}) { + async ipCheckChange(req: { id: any; ipCheck: any }) { await this.update({ id: req.id, - ipCheck: req.ipCheck, + ipCheck: req.ipCheck }); - if(req.ipCheck){ + if (req.ipCheck) { const site = await this.info(req.id); - await this.siteIpService.sync(site) + await this.siteIpService.sync(site); } } async disabledChange(req: { disabled: any; id: any }) { await this.update({ id: req.id, - disabled: req.disabled, + disabled: req.disabled }); - if(!req.disabled){ + if (!req.disabled) { const site = await this.info(req.id); - await this.doCheck(site) + await this.doCheck(site); } } + + async doImport(req: { text: string; userId: number }) { + if (!req.text) { + throw new Error("text is required"); + } + if (!req.userId) { + throw new Error("userId is required"); + } + + const rows = req.text.split("\n"); + + const list = []; + for (const item of rows) { + if (!item) { + continue; + } + const arr = item.trim().split(":"); + if (arr.length === 0) { + continue; + } + const domain = arr[0]; + let port = 443; + let name = domain; + if (arr.length > 1) { + try { + port = parseInt(arr[1] || "443"); + } catch (e) { + throw new Error(`${item}格式错误`); + } + + } + if (arr.length > 2) { + name = arr[2] || domain; + } + + list.push({ + domain, + name, + httpsPort: port, + userId: req.userId + }); + } + + const batchAdd = async (list: any[]) => { + for (const item of list) { + await this.add(item); + } + + // await this.checkAllByUsers(req.userId); + }; + await batchAdd(list); + } } diff --git a/packages/ui/certd-server/src/modules/monitor/service/site-ip-service.ts b/packages/ui/certd-server/src/modules/monitor/service/site-ip-service.ts index 6c89975f..bf7ba7e8 100644 --- a/packages/ui/certd-server/src/modules/monitor/service/site-ip-service.ts +++ b/packages/ui/certd-server/src/modules/monitor/service/site-ip-service.ts @@ -182,7 +182,7 @@ export class SiteIpService extends BaseService { const finished = res.filter(item=>{ return item!=null }) - if (finished.length > 0) { + if (onFinish) { onFinish && onFinish(finished) } }) @@ -232,4 +232,50 @@ export class SiteIpService extends BaseService { ipCount:count }) } + + async doImport(req: { text: string; userId:number, siteId:number }) { + if (!req.text) { + throw new Error("text is required"); + } + if (!req.siteId) { + throw new Error("siteId is required"); + } + + const siteEntity = await this.siteInfoRepository.findOne({ + where: { + id: req.siteId, + userId:req.userId + } + }); + if (!siteEntity) { + throw new Error(`站点${req.siteId}不存在`); + } + + const userId = siteEntity.userId; + + const rows = req.text.split("\n"); + + const list = []; + for (const item of rows) { + if (!item) { + continue; + } + list.push({ + ipAddress:item, + userId: userId, + siteId: req.siteId, + from: "import", + disabled:false, + }); + } + + const batchAdd = async (list: any[]) => { + for (const item of list) { + await this.add(item); + } + + // await this.checkAllByUsers(req.userId); + }; + await batchAdd(list); + } }