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
@@ -1,18 +1,21 @@
|
||||
import { requireAdminAuth } from '../../utils/access'
|
||||
import { requireUserAuth } from '../../utils/access'
|
||||
import { createApiError } from '../../utils/api-error'
|
||||
import { logException } from '../../utils/logger'
|
||||
import {
|
||||
listActivityLogs,
|
||||
listUserIdUsernameMap,
|
||||
summarizeActivityLogs,
|
||||
type LogAction,
|
||||
type LogSource
|
||||
} from '../../utils/db'
|
||||
import { ensureStorageSchema } from '../../utils/storage-backends'
|
||||
import {
|
||||
DEFAULT_LIST_LIMIT,
|
||||
MAX_LIST_LIMIT
|
||||
} from '../../utils/constants'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await requireAdminAuth(event)
|
||||
const user = await requireUserAuth(event)
|
||||
|
||||
const query = getQuery(event)
|
||||
|
||||
@@ -35,13 +38,34 @@ export default defineEventHandler(async (event) => {
|
||||
? query.folder.trim()
|
||||
: undefined
|
||||
|
||||
const searchRaw = typeof query.q === 'string' ? query.q.trim() : ''
|
||||
const search = searchRaw || undefined
|
||||
|
||||
const pageRaw = Number(query.page ?? 1)
|
||||
const page = Number.isFinite(pageRaw) && pageRaw > 0
|
||||
? Math.floor(pageRaw)
|
||||
: 1
|
||||
|
||||
let userId: number | undefined
|
||||
if (user.role === 'admin') {
|
||||
const userIdRaw = query.userId
|
||||
if (userIdRaw !== undefined && userIdRaw !== '' && userIdRaw !== 'all') {
|
||||
const parsed = Number(userIdRaw)
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
createApiError(event, 'INVALID_REQUEST', '无效的用户筛选', 400)
|
||||
}
|
||||
userId = Math.floor(parsed)
|
||||
}
|
||||
} else {
|
||||
userId = user.id
|
||||
}
|
||||
|
||||
try {
|
||||
const result = listActivityLogs({ limit, page, action, source, folder })
|
||||
ensureStorageSchema()
|
||||
const result = listActivityLogs({ limit, page, action, source, folder, userId, search })
|
||||
const summary = summarizeActivityLogs({ source, folder, userId, search })
|
||||
const userMap = user.role === 'admin' ? listUserIdUsernameMap() : undefined
|
||||
|
||||
return {
|
||||
items: result.items.map(row => ({
|
||||
id: row.id,
|
||||
@@ -51,12 +75,27 @@ export default defineEventHandler(async (event) => {
|
||||
size: row.size,
|
||||
contentType: row.content_type,
|
||||
source: row.source,
|
||||
userId: row.user_id,
|
||||
username: row.username,
|
||||
storage: row.backend_id
|
||||
? {
|
||||
id: row.backend_id,
|
||||
name: row.backend_name ?? row.backend_id,
|
||||
type: (row.backend_type === 'local' ? 'local' : 's3') as 'local' | 's3'
|
||||
}
|
||||
: null,
|
||||
createdAt: row.created_at
|
||||
})),
|
||||
total: result.total,
|
||||
page: result.page,
|
||||
pageSize: result.pageSize,
|
||||
totalPages: result.totalPages
|
||||
totalPages: result.totalPages,
|
||||
summary,
|
||||
users: userMap
|
||||
? Array.from(userMap.entries())
|
||||
.map(([id, username]) => ({ id, username }))
|
||||
.sort((a, b) => a.username.localeCompare(b.username))
|
||||
: undefined
|
||||
}
|
||||
} catch (error) {
|
||||
logException('activity logs list failed', error)
|
||||
|
||||
Reference in new issue
Block a user