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.
This commit is contained in:
1 parent
901fefb3f5
commit
6fc054328d
34 files changed
+1357
-72
No files matched your search
@@ -0,0 +1,67 @@
|
||||
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,
|
||||
'上传过于频繁,请稍后再试'
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user