Skip to main content
Glama

MCPDemo - Visual SQL Chat Platform

by Ayi456
QuotaScheduler.ts6.98 kB
import { UserManager } from './UserManager.js'; import { getSmsService } from './SmsService.js'; export class QuotaScheduler { private userManager: UserManager; private dailyResetInterval: NodeJS.Timeout | null = null; private monthlyResetInterval: NodeJS.Timeout | null = null; private cleanupInterval: NodeJS.Timeout | null = null; private isRunning: boolean = false; private lastDailyReset: Date | null = null; private lastMonthlyReset: Date | null = null; private lastCleanup: Date | null = null; constructor(userManager: UserManager) { this.userManager = userManager; } public start(): void { if (this.isRunning) { console.log('配额重置调度器已经在运行中'); return; } this.isRunning = true; console.log('启动配额重置调度器...'); // 启动日配额重置任务(每天0点执行) this.scheduleDailyReset(); // 启动月配额重置任务(每月1号0点执行) this.scheduleMonthlyReset(); // 启动定时清理任务(每小时执行一次) this.scheduleCleanup(); console.log('配额重置调度器启动成功'); } /** * 停止配额重置调度器 */ public stop(): void { if (this.dailyResetInterval) { clearTimeout(this.dailyResetInterval); this.dailyResetInterval = null; } if (this.monthlyResetInterval) { clearTimeout(this.monthlyResetInterval); this.monthlyResetInterval = null; } if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } this.isRunning = false; console.log('配额重置调度器已停止'); } /** * 调度日配额重置任务 */ private scheduleDailyReset(): void { const now = new Date(); const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(0, 0, 0, 0); // 明天0点(本地时区) let timeUntilReset = tomorrow.getTime() - now.getTime(); // 防止因时区/系统时间导致的负值或0,避免紧急循环 if (!Number.isFinite(timeUntilReset) || timeUntilReset <= 0) { timeUntilReset = 60 * 1000; // 至少等待1分钟 } console.log(`下次日配额重置时间: ${tomorrow.toLocaleString()}`); this.dailyResetInterval = setTimeout(async () => { await this.executeDailyReset(); this.scheduleDailyReset(); }, timeUntilReset); } /** * 调度月配额重置任务 */ private scheduleMonthlyReset(): void { // 采用“每天0点检查是否为月初”的方式,避免 setTimeout 超过 2^31-1 ms 被立即触发 const now = new Date(); const nextMidnight = new Date(now); nextMidnight.setDate(nextMidnight.getDate() + 1); nextMidnight.setHours(0, 0, 0, 0); let timeUntilCheck = nextMidnight.getTime() - now.getTime(); if (!Number.isFinite(timeUntilCheck) || timeUntilCheck <= 0) { timeUntilCheck = 60 * 1000; // 至少等待1分钟 } console.log(`下次月配额重置检查时间: ${nextMidnight.toLocaleString()}`); this.monthlyResetInterval = setTimeout(async () => { const today = new Date(); if (today.getDate() === 1) { await this.executeMonthlyReset(); } this.scheduleMonthlyReset(); }, timeUntilCheck); } /** * 执行日配额重置 */ private async executeDailyReset(): Promise<void> { try { console.log('开始执行日配额重置...'); const result = await this.userManager.resetDailyQuota(); this.lastDailyReset = new Date(); console.log(`日配额重置完成,影响用户数: ${result.affectedUsers}`); } catch (error) { console.error('日配额重置失败:', error); } } /** * 执行月配额重置 */ private async executeMonthlyReset(): Promise<void> { try { console.log('开始执行月配额重置...'); const result = await this.userManager.resetMonthlyQuota(); this.lastMonthlyReset = new Date(); console.log(`月配额重置完成,影响用户数: ${result.affectedUsers}`); } catch (error) { console.error('月配额重置失败:', error); } } /** * 手动触发日配额重置 */ public async manualDailyReset(): Promise<{ affectedUsers: number }> { console.log('手动触发日配额重置'); const result = await this.userManager.resetDailyQuota(); this.lastDailyReset = new Date(); return result; } /** * 手动触发月配额重置 */ public async manualMonthlyReset(): Promise<{ affectedUsers: number }> { console.log('手动触发月配额重置'); const result = await this.userManager.resetMonthlyQuota(); this.lastMonthlyReset = new Date(); return result; } /** * 调度清理任务(每小时执行一次) */ private scheduleCleanup(): void { // 立即执行一次 this.executeCleanup(); // 每小时执行一次 this.cleanupInterval = setInterval(async () => { await this.executeCleanup(); }, 60 * 60 * 1000); // 1小时 console.log('定时清理任务已启动(每小时执行一次)'); } /** * 执行清理任务 */ private async executeCleanup(): Promise<void> { try { // 清理过期的短信验证码 try { const smsService = getSmsService(); const smsCount = await smsService.cleanupExpiredCodes(); if (smsCount > 0) { console.log(`已清理 ${smsCount} 条过期的短信验证码`); } } catch (error) { console.error('清理短信验证码时出错:', error); } // 清理过期的密码重置令牌 try { const tokenCount = await this.userManager.cleanupExpiredResetTokens(); if (tokenCount > 0) { console.log(`已清理 ${tokenCount} 条过期的密码重置令牌`); } } catch (error) { console.error('清理密码重置令牌时出错:', error); } this.lastCleanup = new Date(); console.log('定时清理任务执行完成'); } catch (error) { console.error('定时清理任务执行失败:', error); } } /** * 获取调度器状态 */ public getStatus(): { running: boolean; lastDailyReset: Date | null; lastMonthlyReset: Date | null; lastCleanup: Date | null; nextDailyReset: Date; nextMonthlyReset: Date; } { const now = new Date(); // 计算下次日重置时间 const nextDailyReset = new Date(now); nextDailyReset.setDate(nextDailyReset.getDate() + 1); nextDailyReset.setHours(0, 0, 0, 0); // 计算下次月重置时间 const nextMonthlyReset = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 0, 0, 0); return { running: this.isRunning, lastDailyReset: this.lastDailyReset, lastMonthlyReset: this.lastMonthlyReset, lastCleanup: this.lastCleanup, nextDailyReset, nextMonthlyReset }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ayi456/visual-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server