Files
O96u 6fc054328d feat: activity logs, gallery storage filter, and upload rate limits
Add per-user activity logs with storage backend tracking, gallery
storage filter, and IP/token upload rate limiting. Fix storage delete
modal flash, pin local backend in filter order, and link settings
version to GitHub releases.
2026-08-26 17:09:38 +08:00

68 lines
1.5 KiB
TypeScript

import { createHash } from 'node:crypto'
import { createError, getHeader, type H3Event } from 'h3'
import { clientIp } from './logger'
const UPLOAD_IP_MAX = 60
const UPLOAD_TOKEN_MAX = 120
const UPLOAD_WINDOW_MS = 15 * 60 * 1000
const stores = new Map<string, Map<string, { count: number, resetAt: number }>>()
function getStore(name: string): Map<string, { count: number, resetAt: number }> {
let store = stores.get(name)
if (!store) {
store = new Map()
stores.set(name, store)
}
return store
}
function assertRateLimit(
storeName: string,
key: string,
max: number,
windowMs: number,
message: string
): void {
const store = getStore(storeName)
const now = Date.now()
const entry = store.get(key)
if (!entry || now > entry.resetAt) {
store.set(key, { count: 1, resetAt: now + windowMs })
return
}
entry.count += 1
if (entry.count > max) {
throw createError({
statusCode: 429,
statusMessage: message
})
}
}
export function checkUploadRateLimit(event: H3Event, apiToken?: string): void {
const ip = clientIp(event)
assertRateLimit(
'upload-ip',
ip,
UPLOAD_IP_MAX,
UPLOAD_WINDOW_MS,
'上传过于频繁,请稍后再试'
)
const headerToken = getHeader(event, 'auth-token')?.trim() ?? ''
const token = apiToken?.trim() || headerToken
if (!token) return
const hash = createHash('sha256').update(token).digest('hex').slice(0, 32)
assertRateLimit(
'upload-token',
hash,
UPLOAD_TOKEN_MAX,
UPLOAD_WINDOW_MS,
'上传过于频繁,请稍后再试'
)
}