chore: page turn

This commit is contained in:
xiaojunnuo
2026-01-24 23:36:44 +08:00
parent 9f21b1a097
commit f7b13c69e9
4 changed files with 62 additions and 23 deletions
+8 -1
View File
@@ -35,7 +35,14 @@ export class Pager {
} }
} }
export async function doPageTurn<T>(req: { pager: Pager; getPage: (pager: Pager) => Promise<PageRes<T>>; itemHandle?: (item: T) => Promise<void>; batchHandle?: (pageRes: PageRes<T>) => Promise<void> }) { export type PageTurnReq<T = any> = {
pager: Pager;
getPage: (pager: Pager) => Promise<PageRes<T>>;
itemHandle?: (item: T) => Promise<void>;
batchHandle?: (pageRes: PageRes<T>) => Promise<void>;
};
export async function doPageTurn<T>(req: PageTurnReq<T>) {
let count = 0; let count = 0;
const { pager, getPage, itemHandle, batchHandle } = req; const { pager, getPage, itemHandle, batchHandle } = req;
while (true) { while (true) {
@@ -59,19 +59,24 @@ export class BackTaskExecutor {
delete task.run; delete task.run;
} }
} }
} }
export class BackTask { export class BackTask {
key: string; key: string;
title: string; title: string;
total: number = 0; total: number = 0;
current: number = 0; current: number = 0;
skip: number = 0;
startTime: number; startTime: number;
endTime: number; endTime: number;
status: string = "pending"; status: string = "pending";
error?: string; errors?: string[] = [];
timeoutId?: NodeJS.Timeout; timeoutId?: NodeJS.Timeout;
run: (task: BackTask) => Promise<void>; run: (task: BackTask) => Promise<void>;
constructor(opts:{ constructor(opts:{
@@ -101,6 +106,31 @@ export class BackTask {
incrementCurrent() { incrementCurrent() {
this.current++ this.current++
} }
addError(error: string) {
logger.error(error)
this.errors.push(error)
}
getSuccessCount() {
return this.current - this.errors.length
}
getErrorCount() {
return this.errors.length
}
getProgress() {
return (this.current / this.total * 1.0).toFixed(2)
}
getSkipCount() {
return this.skip
}
incrementSkip() {
this.skip++
}
} }
export const taskExecutor = new BackTaskExecutor(); export const taskExecutor = new BackTaskExecutor();
@@ -13,7 +13,7 @@ import { SubDomainsGetter } from '../../pipeline/service/getter/sub-domain-gette
import { TaskServiceBuilder } from '../../pipeline/service/getter/task-service-getter.js'; import { TaskServiceBuilder } from '../../pipeline/service/getter/task-service-getter.js';
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js"; import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
import { DomainEntity } from '../entity/domain.js'; import { DomainEntity } from '../entity/domain.js';
import { BackTask, taskExecutor } from './task-executor.js'; import { BackTask, taskExecutor } from '../../basic/service/task-executor.js';
export interface SyncFromProviderReq { export interface SyncFromProviderReq {
userId: number; userId: number;
@@ -208,9 +208,10 @@ export class DomainService extends BaseService<DomainEntity> {
async doSyncFromProvider(req: SyncFromProviderReq) { async doSyncFromProvider(req: SyncFromProviderReq) {
const key = `user_${req.userId || 0}`
taskExecutor.start('syncFromProviderTask', new BackTask({ taskExecutor.start('syncFromProviderTask', new BackTask({
key: `user_${req.userId}`, key,
title: `同步用户${req.userId}从域名提供商导入域名`, title: `从域名提供商导入域名(${key})`,
run: async (task: BackTask) => { run: async (task: BackTask) => {
await this._syncFromProvider(req, task) await this._syncFromProvider(req, task)
}, },
@@ -254,6 +255,7 @@ export class DomainService extends BaseService<DomainEntity> {
// } // }
if (old) { if (old) {
//如果old存在,直接跳过 //如果old存在,直接跳过
task.incrementSkip()
return return
} }
const updateObj: any = { const updateObj: any = {
@@ -279,22 +281,19 @@ export class DomainService extends BaseService<DomainEntity> {
} }
} }
const batchHandle = async (pageRes: PageRes<any>) => { const batchHandle = async (pageRes: PageRes<any>) => {
task.setTotal(pageRes.total || 0) task.setTotal(pageRes.total || 0)
} }
const start = async () => { await doPageTurn({ pager, getPage, itemHandle, batchHandle })
await doPageTurn({ pager, getPage, itemHandle, batchHandle }) const key = `user_${userId || 0}`
logger.info(`同步用户(${req.userId ?? '全部'})从域名提供商${dnsProviderType}导入域名完成`) logger.info(`从域名提供商${dnsProviderType}导入域名完成(${key}),共导入${task.current}个域名,跳过${task.getSkipCount()}个域名`)
}
start()
} }
async doSyncDomainsExpirationDate(req: { userId?: number }) { async doSyncDomainsExpirationDate(req: { userId?: number }) {
const userId = req.userId const userId = req.userId
const key = `user_${userId || 0}`
taskExecutor.start('syncDomainsExpirationDateTask', new BackTask({ taskExecutor.start('syncDomainsExpirationDateTask', new BackTask({
key: `user_${userId}`, key,
title: `同步用户(${userId ?? '全部'})注册域名过期时间`, title: `同步注册域名过期时间(${key}))`,
run: async (task: BackTask) => { run: async (task: BackTask) => {
await this._syncDomainsExpirationDate({ userId, task }) await this._syncDomainsExpirationDate({ userId, task })
} }
@@ -326,13 +325,14 @@ export class DomainService extends BaseService<DomainEntity> {
const parsed = parseDomainByPsl(domain) const parsed = parseDomainByPsl(domain)
const mainDomain = parsed.domain || '' const mainDomain = parsed.domain || ''
if (mainDomain !== domain) { if (mainDomain !== domain) {
logger.warn(`${domain}为子域名,跳过同步`) req.task.addError(`${domain}为子域名,跳过同步`)
return return
} }
const suffix = parsed.tld || '' const suffix = parsed.tld || ''
const rdapUrl = rdapMap[suffix] const rdapUrl = rdapMap[suffix]
if (!rdapUrl) { if (!rdapUrl) {
throw new Error(`未找到${suffix}的rdap地址`) req.task.addError(`${domain}未找到${suffix}的rdap地址`)
return
} }
// https://rdap.nic.work/domain/handsfree.work // https://rdap.nic.work/domain/handsfree.work
const rdap = await http.request({ const rdap = await http.request({
@@ -354,7 +354,7 @@ export class DomainService extends BaseService<DomainEntity> {
const query: any = { const query: any = {
challengeType: "dns", challengeType: "dns",
} }
if (req.userId!=null) { if (req.userId != null) {
query.userId = req.userId query.userId = req.userId
} }
const getDomainPage = async (pager: Pager) => { const getDomainPage = async (pager: Pager) => {
@@ -384,10 +384,10 @@ export class DomainService extends BaseService<DomainEntity> {
} }
const { expirationDate, registrationDate } = res const { expirationDate, registrationDate } = res
if (!expirationDate) { if (!expirationDate) {
logger.error(`获取域名${item.domain}过期时间失败`) req.task.addError(`${item.domain}获取域名${item.domain}过期时间失败`)
return return
} }
logger.info(`更新域名${item.domain}过期时间:${dayjs(expirationDate).format('YYYY-MM-DD')}`) logger.info(`${item.domain}】更新域名过期时间:${dayjs(expirationDate).format('YYYY-MM-DD')}`)
const updateObj: any = { const updateObj: any = {
id: item.id, id: item.id,
expirationDate: expirationDate, expirationDate: expirationDate,
@@ -396,13 +396,15 @@ export class DomainService extends BaseService<DomainEntity> {
//更新 //更新
await super.update(updateObj) await super.update(updateObj)
} catch (error) { } catch (error) {
logger.error(`更新域名${item.domain}过期时间失败:${error}`) const errorMsg = `${item.domain}${error.message ?? error}`
req.task.addError(errorMsg)
} finally { } finally {
await utils.sleep(1000) await utils.sleep(1000)
} }
} }
await doPageTurn({ pager, getPage: getDomainPage, itemHandle: itemHandle }) await doPageTurn({ pager, getPage: getDomainPage, itemHandle: itemHandle })
logger.info(`同步用户(${req.userId ?? '全部'})注册域名过期时间完成`) const key = `user_${req.userId || 'all'}`
logger.info(`同步用户(${key})注册域名过期时间完成(${req.task.getSuccessCount()}个成功,${req.task.errors.length}个失败)` )
} }
} }
@@ -22,7 +22,7 @@ import punycode from "punycode.js";
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js"; import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js"; import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js";
import { TaskServiceBuilder } from "../../pipeline/service/getter/task-service-getter.js"; import { TaskServiceBuilder } from "../../pipeline/service/getter/task-service-getter.js";
import { BackTask, taskExecutor } from "../../cert/service/task-executor.js"; import { BackTask, taskExecutor } from "../../basic/service/task-executor.js";
type CnameCheckCacheValue = { type CnameCheckCacheValue = {
validating: boolean; validating: boolean;