mirror of
https://github.com/rustfs/console.git
synced 2026-10-30 19:33:40 +08:00
feat: improve object version management (#149)
This commit is contained in:
@@ -2,13 +2,15 @@
|
||||
|
||||
import * as React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { RiFileCopyLine, RiEyeLine, RiDownloadCloud2Line, RiDeleteBin5Line } from "@remixicon/react"
|
||||
import { RiFileCopyLine, RiEyeLine, RiDownloadCloud2Line, RiDeleteBin5Line, RiLoopLeftLine } from "@remixicon/react"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||
import { DataTable } from "@/components/data-table/data-table"
|
||||
import { useDataTable } from "@/hooks/use-data-table"
|
||||
import { useObject } from "@/hooks/use-object"
|
||||
import { usePermissions } from "@/hooks/use-permissions"
|
||||
import { useDialog } from "@/lib/feedback/dialog"
|
||||
import { useMessage } from "@/lib/feedback/message"
|
||||
import { copyToClipboard } from "@/lib/clipboard"
|
||||
import { exportFile } from "@/lib/export-file"
|
||||
@@ -24,6 +26,7 @@ interface VersionRow {
|
||||
LastModified?: Date
|
||||
Size?: number
|
||||
Key?: string
|
||||
IsLatest?: boolean
|
||||
}
|
||||
|
||||
interface ObjectVersionsProps {
|
||||
@@ -45,7 +48,8 @@ export function ObjectVersions({
|
||||
}: ObjectVersionsProps) {
|
||||
const { t } = useTranslation()
|
||||
const message = useMessage()
|
||||
const { listObjectVersions, deleteObject } = useObject(bucketName)
|
||||
const dialog = useDialog()
|
||||
const { listObjectVersions, deleteObject, restoreObjectVersion } = useObject(bucketName)
|
||||
const { canCapability } = usePermissions()
|
||||
const client = useS3()
|
||||
|
||||
@@ -131,10 +135,46 @@ export function ObjectVersions({
|
||||
[deleteObject, objectKey, message, t, fetchVersions, onRefreshParent],
|
||||
)
|
||||
|
||||
const restoreVersion = React.useCallback(
|
||||
(row: VersionRow) => {
|
||||
const versionId = row.VersionId
|
||||
if (!versionId || row.IsLatest) return
|
||||
|
||||
dialog.warning({
|
||||
title: t("Restore Version"),
|
||||
content: t("This will copy the selected historical version and make it the current object version."),
|
||||
positiveText: t("Restore"),
|
||||
negativeText: t("Cancel"),
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await restoreObjectVersion(objectKey, versionId)
|
||||
message.success(t("Restore Success"))
|
||||
await fetchVersions()
|
||||
onRefreshParent()
|
||||
} catch (err) {
|
||||
message.error((err as Error)?.message ?? t("Restore Failed"))
|
||||
return false
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
[dialog, fetchVersions, message, objectKey, onRefreshParent, restoreObjectVersion, t],
|
||||
)
|
||||
|
||||
const versionStats = React.useMemo(
|
||||
() => ({
|
||||
count: versions.length,
|
||||
totalSize: versions.reduce((total, row) => total + (typeof row.Size === "number" ? row.Size : 0), 0),
|
||||
}),
|
||||
[versions],
|
||||
)
|
||||
|
||||
const columns: ColumnDef<VersionRow>[] = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "versionId",
|
||||
accessorKey: "VersionId",
|
||||
enableSorting: false,
|
||||
header: () => t("VersionId"),
|
||||
cell: ({ row }) => {
|
||||
const versionId = row.original.VersionId ?? ""
|
||||
@@ -153,22 +193,33 @@ export function ObjectVersions({
|
||||
>
|
||||
<RiFileCopyLine className="size-3" aria-hidden />
|
||||
</Button>
|
||||
{row.original.IsLatest ? (
|
||||
<Badge variant="secondary" className="shrink-0">
|
||||
{t("Current")}
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
meta: { minWidth: 300 },
|
||||
},
|
||||
{
|
||||
id: "lastModified",
|
||||
accessorFn: (row) => (row.LastModified ? new Date(row.LastModified).getTime() : 0),
|
||||
header: () => t("LastModified"),
|
||||
cell: ({ row }) => (row.original.LastModified ? formatDateTime(row.original.LastModified) : ""),
|
||||
meta: { width: 200 },
|
||||
},
|
||||
{
|
||||
id: "size",
|
||||
accessorFn: (row) => row.Size ?? 0,
|
||||
header: () => t("Size"),
|
||||
cell: ({ row }) => (typeof row.original.Size === "number" ? formatBytes(row.original.Size) : ""),
|
||||
meta: { width: 140 },
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
enableSorting: false,
|
||||
header: () => t("Action"),
|
||||
cell: ({ row }) => {
|
||||
const objectContext = {
|
||||
@@ -177,13 +228,19 @@ export function ObjectVersions({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{canCapability("objects.version.view", objectContext) ? (
|
||||
<Button variant="outline" size="sm" onClick={() => onPreview(row.original.VersionId ?? "")}>
|
||||
<RiEyeLine className="size-4" aria-hidden />
|
||||
{t("Preview")}
|
||||
</Button>
|
||||
) : null}
|
||||
{!row.original.IsLatest && canCapability("objects.version.restore", objectContext) ? (
|
||||
<Button variant="outline" size="sm" onClick={() => restoreVersion(row.original)}>
|
||||
<RiLoopLeftLine className="size-4" aria-hidden />
|
||||
{t("Restore")}
|
||||
</Button>
|
||||
) : null}
|
||||
{canCapability("objects.download", objectContext) ? (
|
||||
<Button variant="outline" size="sm" onClick={() => downloadVersion(row.original)}>
|
||||
<RiDownloadCloud2Line className="size-4" aria-hidden />
|
||||
@@ -204,9 +261,10 @@ export function ObjectVersions({
|
||||
</div>
|
||||
)
|
||||
},
|
||||
meta: { minWidth: 360 },
|
||||
},
|
||||
],
|
||||
[t, onPreview, copyVersionId, downloadVersion, deleteVersion, canCapability, bucketName, objectKey],
|
||||
[t, onPreview, copyVersionId, restoreVersion, downloadVersion, deleteVersion, canCapability, bucketName, objectKey],
|
||||
)
|
||||
|
||||
const { table } = useDataTable<VersionRow>({
|
||||
@@ -216,10 +274,19 @@ export function ObjectVersions({
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onOpenChange={(open) => !open && onClose()} disablePointerDismissal>
|
||||
<DialogContent className="max-h-[80vh] overflow-y-auto overflow-x-hidden sm:max-w-4xl">
|
||||
<DialogContent className="max-h-[80vh] w-[calc(100vw-2rem)] overflow-y-auto overflow-x-hidden sm:max-w-5xl 2xl:w-[80vw] 2xl:max-w-[80vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("Object Versions")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-wrap items-center gap-x-6 gap-y-2 text-sm text-muted-foreground">
|
||||
<span>
|
||||
{t("Versions")}: <span className="font-medium text-foreground">{versionStats.count}</span>
|
||||
</span>
|
||||
<span>
|
||||
{t("Total")} {t("Size")}:{" "}
|
||||
<span className="font-medium text-foreground">{formatBytes(versionStats.totalSize)}</span>
|
||||
</span>
|
||||
</div>
|
||||
<DataTable table={table} isLoading={loading} emptyTitle={t("No Versions")} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -108,6 +108,21 @@ export function useObject(bucket: string) {
|
||||
[client, bucket, deleteObject],
|
||||
)
|
||||
|
||||
const restoreObjectVersion = useCallback(
|
||||
async (key: string, versionId: string) => {
|
||||
return client.send(
|
||||
new CopyObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
CopySource: encodeObjectCopySource(bucket, key, versionId),
|
||||
MetadataDirective: "COPY",
|
||||
TaggingDirective: "COPY",
|
||||
}),
|
||||
)
|
||||
},
|
||||
[client, bucket],
|
||||
)
|
||||
|
||||
const listObject = useCallback(
|
||||
async (
|
||||
bucketName: string,
|
||||
@@ -299,6 +314,7 @@ export function useObject(bucket: string) {
|
||||
putObject,
|
||||
deleteObject,
|
||||
renameObject,
|
||||
restoreObjectVersion,
|
||||
getSignedUrl: getSignedUrlFn,
|
||||
listObject,
|
||||
mapAllFiles,
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "الموقع الحالي",
|
||||
"Current User Policy": "سياسة المستخدم الحالية",
|
||||
"Current Version": "الإصدار الحالي",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "حالة الخلفية الحالية للحاويات والمستخدمين والمجموعات والسياسات وقواعد انتهاء دورة الحياة المنسوخة.",
|
||||
"Current site + remote peers": "الموقع الحالي + النظراء البعيدون",
|
||||
"Current user policy": "سياسة المستخدم الحالية",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "إعادة تشغيل KMS",
|
||||
"Restart Required": "إعادة التشغيل مطلوبة",
|
||||
"Restore Key": "استعادة المفتاح",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "استئناف",
|
||||
"Resync cancel request sent successfully": "تم إرسال طلب إلغاء إعادة المزامنة بنجاح",
|
||||
"Resync request started successfully": "تم بدء طلب إعادة المزامنة بنجاح",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "لا يمكن التراجع عن هذا الإجراء.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "يقوم هذا الإجراء بإخراج التجمع المحدد من الخدمة، ويجب استخدامه فقط بعد التأكد من اكتمال إعادة التوازن.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "يُستخدم هذا المفتاح كمفتاح SSE الافتراضي للمنصة لكل من SSE-KMS وSSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "يُستخدم هذا المفتاح كافتراضي للمنصة لـ SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "ستنتهي صلاحية هذا الرابط عند انتهاء جلستك أو في الوقت المحدد.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "يحذف المفتاح نهائيًا فورًا ولا يمكن التراجع.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Aktueller Standort",
|
||||
"Current User Policy": "Aktuelle Benutzerrichtlinie",
|
||||
"Current Version": "Aktuelle Version",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Aktueller Backend-Status für replizierte Buckets, Benutzer, Gruppen, Richtlinien und Ablaufregeln des Lebenszyklus.",
|
||||
"Current site + remote peers": "Aktuelle Site + entfernte Peers",
|
||||
"Current user policy": "Aktuelle Benutzerrichtlinie",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "KMS neu starten",
|
||||
"Restart Required": "Neustart erforderlich",
|
||||
"Restore Key": "Schlüssel wiederherstellen",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Fortsetzen",
|
||||
"Resync cancel request sent successfully": "Anfrage zum Abbrechen der Neusynchronisierung erfolgreich gesendet",
|
||||
"Resync request started successfully": "Neusynchronisierungsanfrage erfolgreich gestartet",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Diese Aktion legt den ausgewählten Pool still und sollte erst verwendet werden, nachdem der Neuausgleich abgeschlossen ist.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Dieser Schlüssel wird als plattformweiter Standard-SSE-Schlüssel für SSE-KMS und SSE-S3 verwendet.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Dieser Schlüssel ist die Plattform-Standardvorgabe für SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Dieser Link läuft ab, wenn Ihre Sitzung endet oder zum angegebenen Zeitpunkt.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Der Schlüssel wird sofort dauerhaft gelöscht und kann nicht rückgängig gemacht werden.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Current Site",
|
||||
"Current User Policy": "Current User Policy",
|
||||
"Current Version": "Current Version",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.",
|
||||
"Current site + remote peers": "Current site + remote peers",
|
||||
"Current user policy": "Current user policy",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Restart KMS",
|
||||
"Restart Required": "Restart Required",
|
||||
"Restore Key": "Restore Key",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Resume",
|
||||
"Resync cancel request sent successfully": "Resync cancel request sent successfully",
|
||||
"Resync request started successfully": "Resync request started successfully",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "This action cannot be undone.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "This action retires the selected pool and should be used only after verifying rebalance has completed.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "This key is used as the platform default SSE key for SSE-KMS and SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "This key is used as the platform default for SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "This link will expire when your session ends or at the specified time.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "This permanently deletes the key immediately and cannot be undone.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Sitio Actual",
|
||||
"Current User Policy": "Política de Usuario Actual",
|
||||
"Current Version": "Versión Actual",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Estado actual del backend para buckets, usuarios, grupos, políticas y reglas de expiración del ciclo de vida replicados.",
|
||||
"Current site + remote peers": "Sitio actual + pares remotos",
|
||||
"Current user policy": "Política de usuario actual",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Reiniciar KMS",
|
||||
"Restart Required": "Reinicio requerido",
|
||||
"Restore Key": "Restaurar clave",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Reanudar",
|
||||
"Resync cancel request sent successfully": "La solicitud para cancelar la resincronización se envió correctamente",
|
||||
"Resync request started successfully": "La solicitud de resincronización se inició correctamente",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Esta acción no se puede deshacer.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Esta acción retira el pool seleccionado y solo debe usarse tras verificar que el reequilibrio ha finalizado.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Esta clave se usa como la clave SSE predeterminada de la plataforma para SSE-KMS y SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Esta clave se usa como predeterminada de la plataforma para SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Este enlace caducará cuando finalice su sesión o en la hora especificada.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Esto elimina la clave de forma permanente e inmediata y no se puede deshacer.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Site actuel",
|
||||
"Current User Policy": "Politique utilisateur actuelle",
|
||||
"Current Version": "Version actuelle",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "État actuel du backend pour les buckets, utilisateurs, groupes, politiques et règles d’expiration du cycle de vie répliqués.",
|
||||
"Current site + remote peers": "Site actuel + pairs distants",
|
||||
"Current user policy": "Politique utilisateur actuelle",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Redémarrer KMS",
|
||||
"Restart Required": "Redémarrage requis",
|
||||
"Restore Key": "Restaurer la clé",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Reprendre",
|
||||
"Resync cancel request sent successfully": "La demande d'annulation de la resynchronisation a été envoyée avec succès",
|
||||
"Resync request started successfully": "La demande de resynchronisation a été lancée avec succès",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Cette action ne peut pas être annulée.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Cette action retire le pool sélectionné et ne doit être utilisée qu’après avoir vérifié que le rééquilibrage est terminé.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Cette clé est utilisée comme clé SSE par défaut de la plateforme pour SSE-KMS et SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Cette clé est utilisée par défaut sur la plateforme pour SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Ce lien expirera lorsque votre session prendra fin ou à l'heure indiquée.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Supprime définitivement la clé immédiatement, sans retour en arrière.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Situs Saat Ini",
|
||||
"Current User Policy": "Kebijakan Pengguna Saat Ini",
|
||||
"Current Version": "Versi Saat Ini",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Status backend saat ini untuk bucket, pengguna, grup, kebijakan, dan aturan kedaluwarsa siklus hidup yang direplikasi.",
|
||||
"Current site + remote peers": "Situs saat ini + peer jarak jauh",
|
||||
"Current user policy": "Kebijakan pengguna saat ini",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Mulai ulang KMS",
|
||||
"Restart Required": "Perlu Restart",
|
||||
"Restore Key": "Pulihkan kunci",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Lanjutkan",
|
||||
"Resync cancel request sent successfully": "Permintaan pembatalan sinkronisasi ulang berhasil dikirim",
|
||||
"Resync request started successfully": "Permintaan sinkronisasi ulang berhasil dimulai",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Tindakan ini tidak dapat dibatalkan.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Tindakan ini memensiunkan pool yang dipilih dan hanya boleh digunakan setelah penyeimbangan ulang selesai.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Kunci ini digunakan sebagai kunci SSE default platform untuk SSE-KMS dan SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Kunci ini digunakan sebagai default platform untuk SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Tautan ini akan kedaluwarsa saat sesi Anda berakhir atau pada waktu yang ditentukan.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Menghapus kunci secara permanen segera dan tidak dapat dibatalkan.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Sito corrente",
|
||||
"Current User Policy": "Politica utente corrente",
|
||||
"Current Version": "Versione corrente",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Stato attuale del backend per bucket, utenti, gruppi, policy e regole di scadenza del ciclo di vita replicati.",
|
||||
"Current site + remote peers": "Sito corrente + peer remoti",
|
||||
"Current user policy": "Politica utente corrente",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Riavvia KMS",
|
||||
"Restart Required": "Riavvio richiesto",
|
||||
"Restore Key": "Ripristina chiave",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Riprendi",
|
||||
"Resync cancel request sent successfully": "Richiesta di annullamento della risincronizzazione inviata con successo",
|
||||
"Resync request started successfully": "Richiesta di risincronizzazione avviata con successo",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Questa azione non può essere annullata.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Questa azione ritira il pool selezionato e deve essere usata solo dopo aver verificato il completamento del ribilanciamento.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Questa chiave viene usata come chiave SSE predefinita della piattaforma per SSE-KMS e SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Questa chiave è il predefinito della piattaforma per SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Questo link scadrà quando la sessione terminerà o all'ora specificata.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Elimina definitivamente la chiave immediatamente e non può essere annullato.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "現在のサイト",
|
||||
"Current User Policy": "現在のユーザーポリシー",
|
||||
"Current Version": "現在のバージョン",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "複製対象のバケット、ユーザー、グループ、ポリシー、ライフサイクル期限切れルールの現在のバックエンド状態。",
|
||||
"Current site + remote peers": "現在のサイト + リモートピア",
|
||||
"Current user policy": "現在のユーザーポリシー",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "KMS を再起動",
|
||||
"Restart Required": "再起動が必要",
|
||||
"Restore Key": "キーを復元",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "再開",
|
||||
"Resync cancel request sent successfully": "再同期のキャンセルリクエストを正常に送信しました",
|
||||
"Resync request started successfully": "再同期リクエストを正常に開始しました",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "この操作は元に戻せません。",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "この操作は選択したプールを廃止します。リバランス完了を確認した後でのみ実行してください。",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "このキーは、SSE-KMS および SSE-S3 用のプラットフォーム既定の SSE キーとして使用されます。",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "このキーはプラットフォームの SSE-KMS デフォルトとして使用されます。",
|
||||
"This link will expire when your session ends or at the specified time.": "このリンクはセッション終了時、または指定した時刻に期限切れになります。",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "キーをすぐに完全削除します。元に戻せません。",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "현재 사이트",
|
||||
"Current User Policy": "현재 사용자 정책",
|
||||
"Current Version": "현재 버전",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "복제된 버킷, 사용자, 그룹, 정책 및 수명 주기 만료 규칙의 현재 백엔드 상태입니다.",
|
||||
"Current site + remote peers": "현재 사이트 + 원격 피어",
|
||||
"Current user policy": "현재 사용자 정책",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "KMS 재시작",
|
||||
"Restart Required": "재시작 필요",
|
||||
"Restore Key": "키 복원",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "재개",
|
||||
"Resync cancel request sent successfully": "재동기화 취소 요청을 성공적으로 전송했습니다",
|
||||
"Resync request started successfully": "재동기화 요청을 성공적으로 시작했습니다",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "이 작업은 실행 취소할 수 없습니다.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "이 작업은 선택한 풀을 폐기합니다. 리밸런스가 완료된 것을 확인한 후에만 사용하세요.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "이 키는 SSE-KMS 및 SSE-S3에 대한 플랫폼 기본 SSE 키로 사용됩니다.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "이 키는 플랫폼의 SSE-KMS 기본값으로 사용됩니다.",
|
||||
"This link will expire when your session ends or at the specified time.": "이 링크는 세션이 종료되거나 지정한 시각에 만료됩니다.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "키를 즉시 영구 삭제하며 되돌릴 수 없습니다.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Site Atual",
|
||||
"Current User Policy": "Política de Usuário Atual",
|
||||
"Current Version": "Versão Atual",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Status atual do backend para buckets, usuários, grupos, políticas e regras de expiração do ciclo de vida replicados.",
|
||||
"Current site + remote peers": "Site atual + pares remotos",
|
||||
"Current user policy": "Política de usuário atual",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Reiniciar KMS",
|
||||
"Restart Required": "Reinicialização necessária",
|
||||
"Restore Key": "Restaurar chave",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Retomar",
|
||||
"Resync cancel request sent successfully": "Solicitação de cancelamento da resincronização enviada com sucesso",
|
||||
"Resync request started successfully": "Solicitação de resincronização iniciada com sucesso",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Esta ação não pode ser desfeita.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Esta ação aposenta o pool selecionado e só deve ser usada após verificar que o rebalanceamento foi concluído.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Esta chave é usada como a chave SSE padrão da plataforma para SSE-KMS e SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Esta chave é usada como padrão da plataforma para SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Este link expirará quando sua sessão terminar ou no horário especificado.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Exclui a chave permanentemente de imediato e não pode ser desfeito.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Текущий сайт",
|
||||
"Current User Policy": "Текущая политика пользователя",
|
||||
"Current Version": "Текущая версия",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Текущий статус backend для реплицированных бакетов, пользователей, групп, политик и правил истечения жизненного цикла.",
|
||||
"Current site + remote peers": "Текущий сайт + удаленные пиры",
|
||||
"Current user policy": "Текущая политика пользователя",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Перезапустить KMS",
|
||||
"Restart Required": "Требуется перезапуск",
|
||||
"Restore Key": "Восстановить ключ",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Возобновить",
|
||||
"Resync cancel request sent successfully": "Запрос на отмену повторной синхронизации успешно отправлен",
|
||||
"Resync request started successfully": "Запрос повторной синхронизации успешно запущен",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Это действие нельзя отменить.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Это действие выводит выбранный пул из эксплуатации и должно использоваться только после завершения перебалансировки.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Этот ключ используется как ключ SSE по умолчанию платформы для SSE-KMS и SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Этот ключ используется по умолчанию платформы для SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Эта ссылка истечет после завершения вашей сессии или в указанное время.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Немедленно и безвозвратно удаляет ключ.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Mevcut Site",
|
||||
"Current User Policy": "Mevcut Kullanıcı Politikası",
|
||||
"Current Version": "Mevcut Sürüm",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Çoğaltılan bucket'lar, kullanıcılar, gruplar, politikalar ve yaşam döngüsü sona erme kuralları için geçerli backend durumu.",
|
||||
"Current site + remote peers": "Geçerli site + uzak eşler",
|
||||
"Current user policy": "Mevcut kullanıcı politikası",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "KMS’yi yeniden başlat",
|
||||
"Restart Required": "Yeniden Başlatma Gerekli",
|
||||
"Restore Key": "Anahtarı geri yükle",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Devam Et",
|
||||
"Resync cancel request sent successfully": "Yeniden senkronizasyon iptal isteği başarıyla gönderildi",
|
||||
"Resync request started successfully": "Yeniden senkronizasyon isteği başarıyla başlatıldı",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Bu işlem geri alınamaz.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Bu işlem seçilen pool’u devreden çıkarır ve yalnızca yeniden dengeleme tamamlandıktan sonra kullanılmalıdır.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Bu anahtar, SSE-KMS ve SSE-S3 için platformun varsayılan SSE anahtarı olarak kullanılır.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Bu anahtar platformun SSE-KMS varsayılanıdır.",
|
||||
"This link will expire when your session ends or at the specified time.": "Bu bağlantı oturumunuz sona erdiğinde veya belirtilen saatte süresi dolacaktır.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Anahtarı hemen kalıcı olarak siler ve geri alınamaz.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "Cụm máy chủ hiện tại",
|
||||
"Current User Policy": "Chính sách người dùng hiện tại",
|
||||
"Current Version": "Phiên bản hiện tại",
|
||||
"Current": "Current",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "Trạng thái backend hiện tại của các bucket, người dùng, nhóm, chính sách và quy tắc hết hạn vòng đời được sao chép.",
|
||||
"Current site + remote peers": "Site hiện tại + các peer từ xa",
|
||||
"Current user policy": "Chính sách người dùng hiện tại",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "Khởi động lại KMS",
|
||||
"Restart Required": "Cần khởi động lại",
|
||||
"Restore Key": "Khôi phục khóa",
|
||||
"Restore": "Restore",
|
||||
"Restore Failed": "Restore Failed",
|
||||
"Restore Success": "Restore Success",
|
||||
"Restore Version": "Restore Version",
|
||||
"Resume": "Tiếp tục",
|
||||
"Resync cancel request sent successfully": "Đã gửi yêu cầu hủy đồng bộ lại thành công",
|
||||
"Resync request started successfully": "Đã bắt đầu yêu cầu đồng bộ lại thành công",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "Hành động này không thể hoàn tác.",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "Hành động này ngừng sử dụng pool đã chọn và chỉ nên dùng sau khi xác nhận cân bằng lại đã hoàn tất.",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "Khóa này được dùng làm khóa SSE mặc định của nền tảng cho SSE-KMS và SSE-S3.",
|
||||
"This will copy the selected historical version and make it the current object version.": "This will copy the selected historical version and make it the current object version.",
|
||||
"This key is used as the platform default for SSE-KMS.": "Khóa này là mặc định nền tảng cho SSE-KMS.",
|
||||
"This link will expire when your session ends or at the specified time.": "Liên kết này sẽ hết hạn khi phiên làm việc của bạn kết thúc hoặc vào thời gian đã định.",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "Xóa khóa vĩnh viễn ngay lập tức và không thể hoàn tác.",
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
"Current Site": "当前站点",
|
||||
"Current User Policy": "当前用户策略",
|
||||
"Current Version": "当前版本",
|
||||
"Current": "当前",
|
||||
"Current backend status for replicated buckets, users, groups, policies, and lifecycle expiry rules.": "复制存储桶、用户、用户组、策略和生命周期过期规则的当前后端状态。",
|
||||
"Current site + remote peers": "当前站点 + 远程对等站点",
|
||||
"Current user policy": "当前用户策略",
|
||||
@@ -925,6 +926,10 @@
|
||||
"Restart KMS": "重启 KMS",
|
||||
"Restart Required": "需要重启",
|
||||
"Restore Key": "恢复密钥",
|
||||
"Restore": "恢复",
|
||||
"Restore Failed": "恢复失败",
|
||||
"Restore Success": "恢复成功",
|
||||
"Restore Version": "恢复版本",
|
||||
"Resume": "恢复",
|
||||
"Resync cancel request sent successfully": "取消重同步请求已成功发送",
|
||||
"Resync request started successfully": "重同步请求已成功启动",
|
||||
@@ -1133,6 +1138,7 @@
|
||||
"This action cannot be undone.": "此操作无法撤销。",
|
||||
"This action retires the selected pool and should be used only after verifying rebalance has completed.": "此操作会退役所选存储池,仅应在确认再平衡已完成后执行。",
|
||||
"This key is used as the platform default SSE key for SSE-KMS and SSE-S3.": "此密钥将作为平台默认 SSE 密钥,用于 SSE-KMS 和 SSE-S3。",
|
||||
"This will copy the selected historical version and make it the current object version.": "这会复制选中的历史版本,并将其设为当前对象版本。",
|
||||
"This key is used as the platform default for SSE-KMS.": "该密钥将作为平台默认的 SSE-KMS 密钥。",
|
||||
"This link will expire when your session ends or at the specified time.": "此链接将会在您的登录会话结束或达到指定时间后失效。",
|
||||
"This permanently deletes the key immediately and cannot be undone.": "这会立即永久删除该密钥,且无法撤销。",
|
||||
|
||||
@@ -35,7 +35,8 @@ export function validateObjectRename(sourceKey: string, newName: string): Object
|
||||
return null
|
||||
}
|
||||
|
||||
export function encodeObjectCopySource(bucket: string, key: string): string {
|
||||
export function encodeObjectCopySource(bucket: string, key: string, versionId?: string): string {
|
||||
const encodedKey = key.split("/").map(encodeURIComponent).join("/")
|
||||
return `/${encodeURIComponent(bucket)}/${encodedKey}`
|
||||
const source = `/${encodeURIComponent(bucket)}/${encodedKey}`
|
||||
return versionId ? `${source}?versionId=${encodeURIComponent(versionId)}` : source
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export type ConsoleCapability =
|
||||
| "objects.legalHold.edit"
|
||||
| "objects.retention.edit"
|
||||
| "objects.version.view"
|
||||
| "objects.version.restore"
|
||||
| "objects.share"
|
||||
| "accessKeys.create"
|
||||
| "accessKeys.edit"
|
||||
@@ -88,6 +89,7 @@ const CAPABILITY_REQUIREMENTS: Record<ConsoleCapability, CapabilityRequirement[]
|
||||
"objects.legalHold.edit": [{ actions: ["s3:PutObjectLegalHold"], resource: "object" }],
|
||||
"objects.retention.edit": [{ actions: ["s3:PutObjectRetention"], resource: "object" }],
|
||||
"objects.version.view": [{ actions: ["s3:GetObject"], resource: "object" }],
|
||||
"objects.version.restore": [{ actions: ["s3:GetObject", "s3:PutObject"], resource: "object" }],
|
||||
"objects.share": [{ actions: ["s3:GetObject"], resource: "object" }],
|
||||
"accessKeys.create": [{ actions: ["admin:CreateServiceAccount"], resource: "none" }],
|
||||
"accessKeys.edit": [{ actions: ["admin:UpdateServiceAccount"], resource: "none" }],
|
||||
|
||||
@@ -31,3 +31,12 @@ test("encodeObjectCopySource URL-encodes bucket and key path segments", async ()
|
||||
|
||||
assert.equal(encodeObjectCopySource("my.bucket", "a b/c+中文.txt"), "/my.bucket/a%20b/c%2B%E4%B8%AD%E6%96%87.txt")
|
||||
})
|
||||
|
||||
test("encodeObjectCopySource appends an encoded source version id", async () => {
|
||||
const { encodeObjectCopySource } = await loadObjectRename()
|
||||
|
||||
assert.equal(
|
||||
encodeObjectCopySource("my.bucket", "a b/c+中文.txt", "2026/07/04 version+1"),
|
||||
"/my.bucket/a%20b/c%2B%E4%B8%AD%E6%96%87.txt?versionId=2026%2F07%2F04%20version%2B1",
|
||||
)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import test from "node:test"
|
||||
import assert from "node:assert/strict"
|
||||
import fs from "node:fs"
|
||||
|
||||
test("object versions restore copies a source version back to the current object key", () => {
|
||||
const hookSource = fs.readFileSync("hooks/use-object.ts", "utf8")
|
||||
const componentSource = fs.readFileSync("components/object/versions.tsx", "utf8")
|
||||
const permissionSource = fs.readFileSync("lib/permission-capabilities.ts", "utf8")
|
||||
|
||||
assert.match(hookSource, /const restoreObjectVersion = useCallback/)
|
||||
assert.match(hookSource, /CopySource: encodeObjectCopySource\(bucket, key, versionId\)/)
|
||||
assert.match(hookSource, /MetadataDirective: "COPY"/)
|
||||
assert.match(hookSource, /TaggingDirective: "COPY"/)
|
||||
assert.match(componentSource, /restoreObjectVersion\(objectKey, versionId\)/)
|
||||
assert.match(componentSource, /canCapability\("objects\.version\.restore", objectContext\)/)
|
||||
assert.match(componentSource, /!row\.original\.IsLatest/)
|
||||
assert.match(permissionSource, /"objects\.version\.restore": \[\{ actions: \["s3:GetObject", "s3:PutObject"\]/)
|
||||
})
|
||||
|
||||
test("object versions dialog is wide, sortable by date and size, and shows summary statistics", () => {
|
||||
const source = fs.readFileSync("components/object/versions.tsx", "utf8")
|
||||
|
||||
assert.match(source, /2xl:w-\[80vw\]/)
|
||||
assert.match(source, /count: versions\.length/)
|
||||
assert.match(source, /totalSize: versions\.reduce/)
|
||||
assert.match(source, /accessorFn: \(row\) => \(row\.LastModified \? new Date\(row\.LastModified\)\.getTime\(\) : 0\)/)
|
||||
assert.match(source, /accessorFn: \(row\) => row\.Size \?\? 0/)
|
||||
})
|
||||
Reference in New Issue
Block a user