Files
wei-shaw--claude-relay-service/scripts/reset-request-detail-retention-hours.js
T
2026-07-13 12:48:39 +08:00

128 lines
3.8 KiB
JavaScript

#!/usr/bin/env node
let dotenvLoaded = false
try {
require('dotenv').config()
dotenvLoaded = true
} catch (_error) {
dotenvLoaded = false
}
const Redis = require('ioredis')
const CONFIG_KEY = 'claude_relay_config'
const REQUEST_DETAIL_KEY_PATTERN = 'request_detail:*'
const DEFAULT_RETENTION_HOURS = 6
const MAX_RETENTION_HOURS = 720
const SCAN_COUNT = 200
const args = process.argv.slice(2)
const params = {}
args.forEach((arg) => {
const [key, value] = arg.split('=')
params[key.replace(/^--/, '')] = value ?? true
})
const isDryRun = params['dry-run'] === true
const requestedHours = Number.parseInt(params.hours, 10)
const targetHours = Number.isFinite(requestedHours) ? requestedHours : DEFAULT_RETENTION_HOURS
if (!Number.isInteger(targetHours) || targetHours < 1 || targetHours > MAX_RETENTION_HOURS) {
console.error(
`requestDetailRetentionHours must be an integer between 1 and ${MAX_RETENTION_HOURS}`
)
process.exit(1)
}
async function scanRequestDetailKeys(client) {
let cursor = '0'
const keys = []
do {
const [nextCursor, batch] = await client.scan(
cursor,
'MATCH',
REQUEST_DETAIL_KEY_PATTERN,
'COUNT',
SCAN_COUNT
)
cursor = nextCursor
if (Array.isArray(batch) && batch.length > 0) {
keys.push(...batch)
}
} while (cursor !== '0')
return keys
}
async function resetRequestDetailRetentionHours() {
let client = null
try {
console.log('🔄 Resetting request detail retention configuration...')
console.log(`🕒 Target request detail retention: ${targetHours} hour(s)`)
if (isDryRun) {
console.log('📝 DRY RUN mode enabled; no data will be modified')
}
if (dotenvLoaded) {
console.log('📄 Loaded .env configuration')
}
client = new Redis({
host: process.env.REDIS_HOST || '127.0.0.1',
port: Number.parseInt(process.env.REDIS_PORT, 10) || 6379,
password: process.env.REDIS_PASSWORD || undefined,
db: Number.parseInt(process.env.REDIS_DB, 10) || 0,
tls: process.env.REDIS_ENABLE_TLS === 'true' || process.env.REDIS_TLS === 'true' ? {} : false,
lazyConnect: true
})
await client.connect()
const rawConfig = await client.get(CONFIG_KEY)
const currentConfig = rawConfig ? JSON.parse(rawConfig) : {}
const requestDetailKeys = await scanRequestDetailKeys(client)
console.log(`📦 Found ${requestDetailKeys.length} request detail Redis key(s)`)
console.log(
`⚙️ Current config: requestDetailRetentionDays=${currentConfig.requestDetailRetentionDays ?? 'unset'}, requestDetailRetentionHours=${currentConfig.requestDetailRetentionHours ?? 'unset'}`
)
const nextConfig = {
...currentConfig,
requestDetailRetentionHours: targetHours,
updatedAt: new Date().toISOString(),
updatedBy: 'request-detail-retention-hours-reset-script'
}
delete nextConfig.requestDetailRetentionDays
if (!isDryRun) {
if (requestDetailKeys.length > 0) {
for (let index = 0; index < requestDetailKeys.length; index += SCAN_COUNT) {
const batch = requestDetailKeys.slice(index, index + SCAN_COUNT)
// Use UNLINK to avoid blocking Redis with a large DEL.
await client.unlink(...batch)
}
}
await client.set(CONFIG_KEY, JSON.stringify(nextConfig))
}
console.log(
`${isDryRun ? '📝 Would delete' : '🧹 Deleted'} ${requestDetailKeys.length} request detail key(s)`
)
console.log(
`${isDryRun ? '📝 Would write' : '✅ Wrote'} requestDetailRetentionHours=${targetHours} and removed requestDetailRetentionDays`
)
} catch (error) {
console.error('❌ Failed to reset request detail retention configuration:', error)
process.exitCode = 1
} finally {
if (client) {
await client.quit()
}
}
}
resetRequestDetailRetentionHours()