Files
claude-relay-service/src/services/tokenRefreshService.js
千羽 8a74bf5afe refactor: standardize code formatting and linting configuration
- Replace .eslintrc.js with .eslintrc.cjs for better ES module compatibility
- Add .prettierrc configuration for consistent code formatting
- Update package.json with new lint and format scripts
- Add nodemon.json for development hot reloading configuration
- Standardize code formatting across all JavaScript and Vue files
- Update web admin SPA with improved linting rules and formatting
- Add prettier configuration to web admin SPA

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 18:19:31 +09:00

144 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const redis = require('../models/redis')
const logger = require('../utils/logger')
const { v4: uuidv4 } = require('uuid')
/**
* Token 刷新锁服务
* 提供分布式锁机制,避免并发刷新问题
*/
class TokenRefreshService {
constructor() {
this.lockTTL = 60 // 锁的TTL: 60秒token刷新通常在30秒内完成
this.lockValue = new Map() // 存储每个锁的唯一值
}
/**
* 获取分布式锁
* 使用唯一标识符作为值,避免误释放其他进程的锁
*/
async acquireLock(lockKey) {
try {
const client = redis.getClientSafe()
const lockId = uuidv4()
const result = await client.set(lockKey, lockId, 'NX', 'EX', this.lockTTL)
if (result === 'OK') {
this.lockValue.set(lockKey, lockId)
logger.debug(`🔒 Acquired lock ${lockKey} with ID ${lockId}, TTL: ${this.lockTTL}s`)
return true
}
return false
} catch (error) {
logger.error(`Failed to acquire lock ${lockKey}:`, error)
return false
}
}
/**
* 释放分布式锁
* 使用 Lua 脚本确保只释放自己持有的锁
*/
async releaseLock(lockKey) {
try {
const client = redis.getClientSafe()
const lockId = this.lockValue.get(lockKey)
if (!lockId) {
logger.warn(`⚠️ No lock ID found for ${lockKey}, skipping release`)
return
}
// Lua 脚本:只有当值匹配时才删除
const luaScript = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`
const result = await client.eval(luaScript, 1, lockKey, lockId)
if (result === 1) {
this.lockValue.delete(lockKey)
logger.debug(`🔓 Released lock ${lockKey} with ID ${lockId}`)
} else {
logger.warn(`⚠️ Lock ${lockKey} was not released - value mismatch or already expired`)
}
} catch (error) {
logger.error(`Failed to release lock ${lockKey}:`, error)
}
}
/**
* 获取刷新锁
* @param {string} accountId - 账户ID
* @param {string} platform - 平台类型 (claude/gemini)
* @returns {Promise<boolean>} 是否成功获取锁
*/
async acquireRefreshLock(accountId, platform = 'claude') {
const lockKey = `token_refresh_lock:${platform}:${accountId}`
return await this.acquireLock(lockKey)
}
/**
* 释放刷新锁
* @param {string} accountId - 账户ID
* @param {string} platform - 平台类型 (claude/gemini)
*/
async releaseRefreshLock(accountId, platform = 'claude') {
const lockKey = `token_refresh_lock:${platform}:${accountId}`
await this.releaseLock(lockKey)
}
/**
* 检查刷新锁状态
* @param {string} accountId - 账户ID
* @param {string} platform - 平台类型 (claude/gemini)
* @returns {Promise<boolean>} 锁是否存在
*/
async isRefreshLocked(accountId, platform = 'claude') {
const lockKey = `token_refresh_lock:${platform}:${accountId}`
try {
const client = redis.getClientSafe()
const exists = await client.exists(lockKey)
return exists === 1
} catch (error) {
logger.error(`Failed to check lock status ${lockKey}:`, error)
return false
}
}
/**
* 获取锁的剩余TTL
* @param {string} accountId - 账户ID
* @param {string} platform - 平台类型 (claude/gemini)
* @returns {Promise<number>} 剩余秒数,-1表示锁不存在
*/
async getLockTTL(accountId, platform = 'claude') {
const lockKey = `token_refresh_lock:${platform}:${accountId}`
try {
const client = redis.getClientSafe()
const ttl = await client.ttl(lockKey)
return ttl
} catch (error) {
logger.error(`Failed to get lock TTL ${lockKey}:`, error)
return -1
}
}
/**
* 清理本地锁记录
* 在进程退出时调用,避免内存泄漏
*/
cleanup() {
this.lockValue.clear()
logger.info('🧹 Cleaned up local lock records')
}
}
// 创建单例实例
const tokenRefreshService = new TokenRefreshService()
module.exports = tokenRefreshService