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:
O96u committed 2026-08-26 17:09:38 +08:00
1 parent 901fefb3f5
commit 6fc054328d
34 files changed
+1357 -72

No files matched your search

+39 -3
View File
@@ -41,7 +41,11 @@ const {
setActiveFolder,
submitSearch,
goToPage,
removeItems
removeItems,
activeStorageBackend,
storageBackendOptions,
setActiveStorageBackend,
loadStorageBackendOptions
} = useImageList()
const { formatFileSize } = useFileSize()
@@ -60,6 +64,7 @@ const deleteTargetKeys = ref<string[]>([])
const batchDeleting = ref(false)
const showScrollTop = ref(false)
const currentFolder = ref('all')
const currentStorage = ref('all')
const folderOptions = ref<string[]>(['images'])
const folderSelectItems = computed(() => [
@@ -67,6 +72,14 @@ const folderSelectItems = computed(() => [
...folderOptions.value.map(folder => ({ label: folder, value: folder }))
])
const storageSelectItems = computed(() => [
{ label: t('stats.filterStorageAll'), value: 'all' },
...storageBackendOptions.value.map(backend => ({
label: backend.name,
value: backend.id
}))
])
const selectedCount = computed(() => selectedKeys.value.size)
const FOLDER_CHART_COLORS = [
@@ -165,7 +178,8 @@ async function refreshGallery() {
if (!isAuthenticated.value) return
selectedKeys.value = new Set()
try {
await Promise.all([refreshList(), loadFolders()])
await Promise.all([refreshList(), loadFolders(), loadStorageBackendOptions()])
currentStorage.value = activeStorageBackend.value
} catch (error: unknown) {
handleAuthError(error)
}
@@ -188,8 +202,10 @@ async function refreshAll() {
await Promise.all([
fetchStats(),
reloadGallery(),
loadFolders()
loadFolders(),
loadStorageBackendOptions()
])
currentStorage.value = activeStorageBackend.value
} catch (error: unknown) {
handleAuthError(error)
if (isAuthenticated.value) {
@@ -211,6 +227,17 @@ async function handleFolderChange(folder: string) {
}
}
async function handleStorageChange(backendId: string) {
if (!isAuthenticated.value) return
currentStorage.value = backendId
selectedKeys.value = new Set()
try {
await setActiveStorageBackend(backendId)
} catch (error: unknown) {
handleAuthError(error)
}
}
async function handleSearch() {
if (!isAuthenticated.value) return
selectedKeys.value = new Set()
@@ -481,6 +508,14 @@ watch(isAuthenticated, async (authed, prev) => {
:aria-label="t('stats.folder')"
@update:model-value="handleFolderChange"
/>
<USelect
v-model="currentStorage"
:items="storageSelectItems"
class="w-[6.5rem] shrink-0 sm:w-36"
size="sm"
:aria-label="t('stats.storage')"
@update:model-value="handleStorageChange"
/>
<UInput
v-model="searchQuery"
icon="i-lucide-search"
@@ -542,6 +577,7 @@ watch(isAuthenticated, async (authed, prev) => {
<ImageGrid
v-else
:show-key="false"
show-storage
:items="items"
:selected-keys="selectedKeys"
:deleting-keys="deletingKeys"