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.
74 lines
2.5 KiB
TypeScript
74 lines
2.5 KiB
TypeScript
import type { ImageListResponse } from '~/types/image'
|
|
import { requireApiOrAdminAuth, getImageUserFilter } from '../../utils/access'
|
|
import { getCurrentUser } from '../../utils/auth'
|
|
import {
|
|
DEFAULT_LIST_LIMIT,
|
|
MAX_LIST_LIMIT
|
|
} from '../../utils/constants'
|
|
import { createApiError } from '../../utils/api-error'
|
|
import { listUserIdUsernameMap } from '../../utils/db'
|
|
import { mapStoredImageToItem } from '../../utils/image-response'
|
|
import { listStorageBackendNameMap } from '../../utils/storage-backends'
|
|
import { isValidFolderName } from '../../utils/image-key'
|
|
import { readBackendIdQuery } from '../../utils/image-query'
|
|
import { searchImages } from '../../utils/storage'
|
|
|
|
function readFolderQuery(query: Record<string, unknown>): string | undefined {
|
|
const raw = typeof query.folder === 'string' ? query.folder.trim() : ''
|
|
if (!raw || raw === 'all') return undefined
|
|
return isValidFolderName(raw) ? raw : undefined
|
|
}
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
await requireApiOrAdminAuth(event)
|
|
|
|
const query = getQuery(event)
|
|
const q = typeof query.q === 'string' ? query.q.trim() : ''
|
|
|
|
if (!q) {
|
|
createApiError(event, 'INVALID_REQUEST', '请提供搜索关键词 q', 400)
|
|
}
|
|
|
|
if (q.length > 200) {
|
|
createApiError(event, 'INVALID_REQUEST', '搜索关键词过长', 400)
|
|
}
|
|
|
|
const limitRaw = Number(query.limit ?? DEFAULT_LIST_LIMIT)
|
|
const limit = Number.isFinite(limitRaw)
|
|
? Math.min(Math.max(1, Math.floor(limitRaw)), MAX_LIST_LIMIT)
|
|
: DEFAULT_LIST_LIMIT
|
|
|
|
const pageRaw = Number(query.page ?? 1)
|
|
const page = Number.isFinite(pageRaw) && pageRaw > 0
|
|
? Math.floor(pageRaw)
|
|
: 1
|
|
|
|
const folder = readFolderQuery(query)
|
|
const backendId = readBackendIdQuery(query)
|
|
if (backendId === null) {
|
|
createApiError(event, 'INVALID_REQUEST', '无效的存储后端', 400)
|
|
}
|
|
const userFilter = await getImageUserFilter(event)
|
|
|
|
const result = await searchImages({ query: q, limit, page, folder, userFilter, backendId })
|
|
|
|
const user = await getCurrentUser(event)
|
|
const ownerMap = user?.role === 'admin' ? listUserIdUsernameMap() : undefined
|
|
const backendMap = listStorageBackendNameMap()
|
|
const items = result.items.map(stored =>
|
|
mapStoredImageToItem(event, stored, { ownerMap, backendMap })
|
|
)
|
|
|
|
items.sort((a, b) => b.uploadedAt.localeCompare(a.uploadedAt))
|
|
|
|
const response: ImageListResponse = {
|
|
items,
|
|
total: result.total,
|
|
page: result.page,
|
|
pageSize: result.pageSize,
|
|
totalPages: result.totalPages
|
|
}
|
|
|
|
return response
|
|
})
|