Remove downloads management features: delete DownloadsEndpoints and related API logic, remove associated frontend components and routes, and update documentation to reflect the removal of the downloads directory from the application structure.
This commit is contained in:
@@ -1,112 +0,0 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ExternalLink, RotateCw } from 'lucide-react'
|
||||
import { HttpError } from '@/shared/api/client'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { toast } from '@/shared/ui/toast-store'
|
||||
import { importDownload, listDownloads } from './api'
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
const units = ['KB', 'MB', 'GB', 'TB']
|
||||
let value = bytes / 1024
|
||||
let i = 0
|
||||
while (value >= 1024 && i < units.length - 1) {
|
||||
value /= 1024
|
||||
i++
|
||||
}
|
||||
return `${value.toFixed(1)} ${units[i]}`
|
||||
}
|
||||
|
||||
export function DownloadsPanel() {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { data, isLoading, refetch, isFetching } = useQuery({
|
||||
queryKey: ['admin', 'downloads'],
|
||||
queryFn: listDownloads,
|
||||
refetchInterval: 15_000,
|
||||
})
|
||||
|
||||
const onError = (error: unknown) =>
|
||||
toast.error(error instanceof HttpError ? error.detail : t('common.error'))
|
||||
|
||||
const importMutation = useMutation({
|
||||
mutationFn: importDownload,
|
||||
onSuccess: () => {
|
||||
toast.success(t('admin.downloads.imported'))
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin', 'media'] })
|
||||
},
|
||||
onError,
|
||||
})
|
||||
|
||||
const clientUrl =
|
||||
typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.hostname}:8090` : '#'
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<h2 className="crt-glow text-xl font-semibold">{t('admin.downloads.title')}</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button size="sm" variant="outline" asChild>
|
||||
<a href={clientUrl} target="_blank" rel="noreferrer">
|
||||
<ExternalLink className="h-4 w-4" />
|
||||
{t('admin.downloads.openClient')}
|
||||
</a>
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" disabled={isFetching} onClick={() => void refetch()}>
|
||||
<RotateCw className="h-4 w-4" />
|
||||
{t('admin.downloads.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground">{t('admin.downloads.hint')}</p>
|
||||
|
||||
<div className="crt-panel overflow-x-auto rounded-md">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="border-b border-border text-left text-muted-foreground">
|
||||
<tr>
|
||||
<th className="px-4 py-2 font-medium">{t('admin.downloads.path')}</th>
|
||||
<th className="px-4 py-2 font-medium">{t('admin.downloads.size')}</th>
|
||||
<th className="px-4 py-2 font-medium">{t('common.actions')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{isLoading && (
|
||||
<tr>
|
||||
<td className="px-4 py-3 text-muted-foreground" colSpan={3}>
|
||||
{t('common.loading')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{data?.map((file) => (
|
||||
<tr key={file.relativePath} className="border-b border-border last:border-0">
|
||||
<td className="px-4 py-2" title={file.relativePath}>
|
||||
{file.relativePath}
|
||||
</td>
|
||||
<td className="px-4 py-2 text-muted-foreground">{formatBytes(file.sizeBytes)}</td>
|
||||
<td className="px-4 py-2">
|
||||
<Button
|
||||
size="sm"
|
||||
disabled={importMutation.isPending}
|
||||
onClick={() => importMutation.mutate(file.relativePath)}
|
||||
>
|
||||
{t('admin.downloads.import')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{data && data.length === 0 && !isLoading && (
|
||||
<tr>
|
||||
<td className="px-4 py-3 text-muted-foreground" colSpan={3}>
|
||||
{t('admin.downloads.empty')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { apiRequest } from '@/shared/api/client'
|
||||
import type { CreatedIdResponse, DownloadFileDto } from '@/shared/api/types'
|
||||
|
||||
export function listDownloads() {
|
||||
return apiRequest<DownloadFileDto[]>('/admin/downloads')
|
||||
}
|
||||
|
||||
export function importDownload(relativePath: string) {
|
||||
return apiRequest<CreatedIdResponse>('/admin/downloads/import', {
|
||||
method: 'POST',
|
||||
body: { relativePath },
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user