diff --git a/agent/app/api/v2/file.go b/agent/app/api/v2/file.go index 726165275..aa734e250 100644 --- a/agent/app/api/v2/file.go +++ b/agent/app/api/v2/file.go @@ -12,7 +12,9 @@ import ( "path/filepath" "strconv" "strings" + "sync" "syscall" + "time" "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" "github.com/1Panel-dev/1Panel/agent/app/dto" @@ -28,6 +30,11 @@ import ( qrcode "github.com/skip2/go-qrcode" ) +var cancelledChunkUploads = struct { + sync.RWMutex + ids map[string]struct{} +}{ids: make(map[string]struct{})} + // @Tags File // @Summary List files // @Accept json @@ -873,7 +880,24 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) { } } filename := c.PostForm("filename") - fileDir := filepath.Join(tmpDir, filename) + if filename == "" || filepath.Base(filename) != filename || strings.ContainsAny(filename, `/\\`) { + helper.BadRequest(c, errors.New("invalid filename")) + return + } + uploadID := strings.TrimSpace(c.PostForm("uploadID")) + cancellable := uploadID != "" + if cancellable && (filepath.Base(uploadID) != uploadID || strings.ContainsAny(uploadID, `/\\`)) { + helper.BadRequest(c, errors.New("invalid upload ID")) + return + } + if !cancellable { + uploadID = filename + } + fileDir := filepath.Join(tmpDir, uploadID) + if cancellable && chunkUploadCancelled(uploadID) { + helper.BadRequest(c, errors.New("upload cancelled")) + return + } if chunkIndex == 0 { if fileOp.Stat(fileDir) { _ = fileOp.DeleteDir(fileDir) @@ -884,7 +908,7 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) { defer func() { if err != nil { - _ = os.Remove(fileDir) + _ = os.RemoveAll(fileDir) } }() var ( @@ -904,6 +928,11 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) { helper.InternalServer(c, buserr.WithMap("ErrFileUpload", map[string]interface{}{"name": filename, "detail": err.Error()}, err)) return } + if cancellable && chunkUploadCancelled(uploadID) { + err = errors.New("upload cancelled") + helper.BadRequest(c, err) + return + } chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex)) err = os.WriteFile(chunkPath, chunkData, constant.DirPerm) @@ -922,12 +951,50 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) { helper.InternalServer(c, buserr.WithMap("ErrFileUpload", map[string]interface{}{"name": filename, "detail": err.Error()}, err)) return } + if cancellable { + cancelledChunkUploads.Lock() + delete(cancelledChunkUploads.ids, uploadID) + cancelledChunkUploads.Unlock() + } helper.SuccessWithData(c, true) } else { return } } +// StopChunkUpload removes temporary chunks left by a cancelled upload. +func (b *BaseApi) StopChunkUpload(c *gin.Context) { + var req request.FileProcessReq + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + uploadID := strings.TrimSpace(req.Key) + if uploadID == "" || filepath.Base(uploadID) != uploadID || strings.ContainsAny(uploadID, `/\\`) { + helper.BadRequest(c, errors.New("invalid upload ID")) + return + } + cancelledChunkUploads.Lock() + cancelledChunkUploads.ids[uploadID] = struct{}{} + cancelledChunkUploads.Unlock() + time.AfterFunc(10*time.Minute, func() { + cancelledChunkUploads.Lock() + delete(cancelledChunkUploads.ids, uploadID) + cancelledChunkUploads.Unlock() + }) + if err := os.RemoveAll(filepath.Join(global.Dir.TmpDir, "upload", uploadID)); err != nil { + helper.InternalServer(c, err) + return + } + helper.Success(c) +} + +func chunkUploadCancelled(uploadID string) bool { + cancelledChunkUploads.RLock() + _, ok := cancelledChunkUploads.ids[uploadID] + cancelledChunkUploads.RUnlock() + return ok +} + var wsUpgrade = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true diff --git a/agent/router/ro_file.go b/agent/router/ro_file.go index 6c5eb52af..c8658127e 100644 --- a/agent/router/ro_file.go +++ b/agent/router/ro_file.go @@ -39,6 +39,7 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) { fileRouter.POST("/batch/check", baseApi.BatchCheckFiles) fileRouter.POST("/upload", baseApi.UploadFiles) fileRouter.POST("/chunkupload", baseApi.UploadChunkFiles) + fileRouter.POST("/chunkupload/stop", baseApi.StopChunkUpload) fileRouter.POST("/rename", baseApi.ChangeFileName) fileRouter.POST("/wget", baseApi.WgetFile) fileRouter.POST("/wget/stop", baseApi.StopWget) diff --git a/frontend/src/api/modules/files.ts b/frontend/src/api/modules/files.ts index f42b79deb..5c9ec2d6b 100644 --- a/frontend/src/api/modules/files.ts +++ b/frontend/src/api/modules/files.ts @@ -125,6 +125,10 @@ export const chunkUploadFileData = (params: FormData, config: AxiosRequestConfig return http.upload('files/chunkupload', params, config); }; +export const stopChunkUpload = (key: string) => { + return http.post('files/chunkupload/stop', { key }); +}; + export const renameRile = (params: File.FileRename) => { return http.post('files/rename', params); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index f4e5c05a7..9d6c04bf8 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -5661,15 +5661,12 @@ const message = { dependencies: 'Dependencies', dependencyPurpose: 'Purpose', dependencyPurposeMap: { - kvm: 'Provides hardware virtualization support for accelerated VM execution.', libvirt: 'Provides VM management services for lifecycle control and resource scheduling.', storage: 'Provides the default storage directory or pool for VM disk files.', }, dependencyNotReady: 'VM dependency checks failed. Fix the dependencies before using virtual machines.', helperUnavailable: 'VM capability initialization failed. Check core logs or upgrade again before using VMs.', - helperNotStart: - 'The VM runtime environment is not ready. Fix the host virtualization service before using VMs.', kvmUnavailable: 'No available KVM hardware virtualization capability was detected.', kvmCheckCpu: 'Confirm that the host CPU supports Intel VT-x or AMD-V virtualization.', kvmCheckBios: 'Enable virtualization options in BIOS/UEFI.', @@ -5679,7 +5676,6 @@ const message = { cpu: 'CPU', memory: 'Memory', disk: 'Disk', - totalVM: 'Total VMs', abnormal: 'Abnormal', resourceOverview: 'Resource Overview', cpuAllocated: 'CPU Allocated', @@ -5687,14 +5683,10 @@ const message = { storageUsed: 'Storage Used', riskReminder: 'Risk Reminders', noRisk: 'No risks', - quickEntry: 'Quick Entry', runtimeHealth: 'Runtime Health', - runtimeReady: 'VM runtime is ready', runtimeAbnormal: 'VM runtime is abnormal', - environmentCheck: 'Environment Check', noDependency: 'No dependency check data', storageOverview: 'Storage Overview', - recentVM: 'Recent VMs', totalAllocated: 'Calculated by total VM configuration', runningAllocated: 'Running allocation: {0}', storageAvailable: 'Available: {0}', @@ -5713,22 +5705,15 @@ const message = { storageLimitHelper: 'Current free space in the target storage pool: {0}', storageLimitExceeded: 'Disk capacity exceeds the limit. Current maximum available: {0}.', iso: 'ISO', - createSource: 'Create Source', createFromTemplate: 'Create from template', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: 'Delete template {0}. This operation cannot be rolled back. Continue?', diskSize: 'Disk Size', diskPath: 'Disk Path', - diskPathHelper: - 'Optional. If empty, the system creates .qcow2 in the selected storage pool directory. Custom values must be absolute paths.', - imagePath: 'Image Path', - imagePathHelper: 'Optional. If set, use an existing qcow2 image absolute path.', isoPath: 'ISO Path', storagePool: 'Storage Pool', network: 'Network', @@ -5745,8 +5730,10 @@ const message = { dhcpStart: 'DHCP Start', dhcpEnd: 'DHCP End', bootOrder: 'Boot Order', - bootOrderHelper: - 'HD boots from disk first and then tries the ISO if the disk is not bootable. CD-ROM boots from ISO first.', + bootDiskFirst: 'Disk first (HD → CD-ROM)', + bootIsoFirst: 'ISO first (CD-ROM → HD)', + bootOrderConfirm: + 'Change the boot order to {0}? The VM will try currently mounted, bootable devices in this order the next time it starts.', startAfterCreate: 'Start after creation', autoStart: 'Autostart', resourceAutoStart: 'Autostart', @@ -5754,16 +5741,6 @@ const message = { available: 'Available', vmCount: 'Linked VMs', usedIPs: 'Used IPs', - syncFromServer: 'Sync from Server', - notFound: 'Not found', - resourceStatus: { - running: 'Running', - inactive: 'Inactive', - building: 'Building', - degraded: 'Degraded', - inaccessible: 'Inaccessible', - unknown: 'Unknown', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5790,10 +5767,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: 'Usage', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + 'The current snapshot cannot be deleted. Snapshots can be recovered only when the virtual machine is shut off.', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: 'Network Interface', @@ -5814,11 +5792,7 @@ const message = { downloadFromServer: 'Download from Server', isoTargetDir: 'ISO Target Path', isoTargetDirHelper: 'ISO files uploaded locally or downloaded from the server are saved to this directory.', - isoTargetDirOptional: 'No ISO target path is set. The original ISO source path will be used.', - isoTargetDirAvailable: 'The ISO target path is available.', - isoTargetDirUnavailable: 'The ISO target path must be inside an available VM storage pool.', - isoTargetDirCheckRequired: 'Check the ISO target path before saving.', - checkPath: 'Check', + addingIsoRecord: 'Adding ISO record', isoSize: 'ISO Size', deleteIsoConfirm: 'This delete operation cannot be rolled back. Continue?', deleteSourceFile: 'Delete source file', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index d378ac660..419613ebb 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -5707,7 +5707,6 @@ const message = { dependencies: 'Dependencias', dependencyPurpose: 'Propósito', dependencyPurposeMap: { - kvm: 'Proporciona virtualización por hardware para acelerar la ejecución de las VM.', libvirt: 'Proporciona servicios de gestión de VM para el ciclo de vida y la planificación de recursos.', storage: 'Proporciona el directorio o pool de almacenamiento predeterminado para los archivos de disco de la VM.', @@ -5716,8 +5715,6 @@ const message = { 'Las comprobaciones de dependencias de la VM han fallado. Corrija las dependencias antes de usar máquinas virtuales.', helperUnavailable: 'La inicialización de la capacidad de VM falló. Revise los registros de Core o actualice de nuevo antes de usar máquinas virtuales.', - helperNotStart: - 'El servicio de VM no se está ejecutando. Inicie el servicio antes de usar máquinas virtuales.', cpu: 'CPU', memory: 'Memoria', disk: 'Disco', @@ -5728,22 +5725,15 @@ const message = { storageLimitHelper: 'Espacio libre actual en el pool de almacenamiento de destino: {0}', storageLimitExceeded: 'La capacidad del disco supera el límite. Máximo disponible actual: {0}.', iso: 'ISO', - createSource: 'Origen de creación', createFromTemplate: 'Crear desde plantilla', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: 'Eliminar plantilla {0}. Esta operación no se puede revertir. ¿Continuar?', diskSize: 'Tamaño del disco', diskPath: 'Ruta del disco', - diskPathHelper: - 'Opcional. Si está vacío, el sistema crea .qcow2 en el directorio del pool de almacenamiento seleccionado. Los valores personalizados deben ser rutas absolutas.', - imagePath: 'Ruta de la imagen', - imagePathHelper: 'Opcional. Si se define, use la ruta absoluta de una imagen qcow2 existente.', isoPath: 'Ruta ISO', storagePool: 'Pool de almacenamiento', network: 'Red', @@ -5760,8 +5750,10 @@ const message = { dhcpStart: 'Inicio DHCP', dhcpEnd: 'Fin DHCP', bootOrder: 'Orden de arranque', - bootOrderHelper: - 'HD arranca primero desde el disco y prueba el ISO si el disco no es arrancable. CD-ROM arranca primero desde el ISO.', + bootDiskFirst: 'Disco primero (HD → CD-ROM)', + bootIsoFirst: 'ISO primero (CD-ROM → HD)', + bootOrderConfirm: + '¿Cambiar el orden de arranque a {0}? La próxima vez que se inicie, la VM probará en este orden los dispositivos arrancables que estén montados.', startAfterCreate: 'Iniciar después de crear', autoStart: 'Inicio automático', resourceAutoStart: 'Inicio automático', @@ -5769,16 +5761,6 @@ const message = { available: 'Disponible', vmCount: 'VM vinculadas', usedIPs: 'IP usadas', - syncFromServer: 'Sincronizar desde servidor', - notFound: 'No encontrado', - resourceStatus: { - running: 'En ejecución', - inactive: 'Inactivo', - building: 'Creando', - degraded: 'Degradado', - inaccessible: 'Inaccesible', - unknown: 'Desconocido', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5807,10 +5789,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: 'Uso', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + 'La instantánea en uso no se puede eliminar. Las instantáneas solo se pueden restaurar cuando la máquina virtual está apagada.', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: 'Interfaz de red', @@ -5832,12 +5815,7 @@ const message = { isoTargetDir: 'Ruta de destino ISO', isoTargetDirHelper: 'Los ISO cargados localmente o descargados desde el servidor se guardarán en este directorio.', - isoTargetDirOptional: 'No se definió una ruta de destino ISO. Se usará la ruta original del archivo ISO.', - isoTargetDirAvailable: 'La ruta de destino ISO está disponible.', - isoTargetDirUnavailable: - 'La ruta de destino ISO debe estar dentro de un pool de almacenamiento de VM disponible.', - isoTargetDirCheckRequired: 'Compruebe la ruta de destino ISO antes de guardar.', - checkPath: 'Comprobar', + addingIsoRecord: 'Agregando registro ISO', isoSize: 'Tamaño ISO', deleteIsoConfirm: 'Esta operación de eliminación no se puede revertir. ¿Continuar?', deleteSourceFile: 'Eliminar archivo de origen', diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts index 4b2f88624..b3de4ee55 100644 --- a/frontend/src/lang/modules/fa.ts +++ b/frontend/src/lang/modules/fa.ts @@ -5625,7 +5625,6 @@ const message = { dependencies: 'وابستگی‌ها', dependencyPurpose: 'کاربرد', dependencyPurposeMap: { - kvm: 'قابلیت مجازی‌سازی سخت‌افزاری را فراهم می‌کند و پایه اجرای سریع ماشین مجازی است.', libvirt: 'سرویس مدیریت ماشین مجازی را برای چرخه حیات و زمان‌بندی منابع فراهم می‌کند.', storage: 'دایرکتوری یا استخر ذخیره‌سازی پیش‌فرض را برای فایل‌های دیسک ماشین مجازی فراهم می‌کند.', }, @@ -5633,7 +5632,6 @@ const message = { 'بررسی وابستگی‌های ماشین مجازی موفق نبود. قبل از استفاده از ماشین مجازی، وابستگی‌ها را برطرف کنید.', helperUnavailable: 'راه‌اندازی قابلیت ماشین مجازی ناموفق بود. لاگ Core را بررسی کنید یا دوباره ارتقاء دهید.', - helperNotStart: 'محیط اجرای ماشین مجازی آماده نیست. ابتدا سرویس مجازی‌سازی میزبان را برطرف کنید.', cpu: 'CPU', memory: 'حافظه', disk: 'دیسک', @@ -5644,22 +5642,15 @@ const message = { storageLimitHelper: 'فضای آزاد فعلی در استخر ذخیره‌سازی مقصد: {0}', storageLimitExceeded: 'ظرفیت دیسک از حد مجاز بیشتر است. حداکثر فعلی قابل استفاده: {0}.', iso: 'ISO', - createSource: 'منبع ایجاد', createFromTemplate: 'ایجاد از قالب', - blankDisk: 'دیسک خالی', template: 'قالب', templateName: 'نام قالب', convertToTemplate: 'تبدیل به قالب', sourceVM: 'ماشین مجازی منبع', templateSize: 'اندازه قالب', - deleteTemplate: 'حذف قالب', deleteTemplateConfirm: 'قالب {0} حذف شود؟ این عملیات قابل بازگشت نیست. ادامه می‌دهید؟', diskSize: 'ظرفیت دیسک', diskPath: 'مسیر دیسک', - diskPathHelper: - 'اختیاری است. اگر خالی باشد، سیستم فایل نام-ماشین-مجازی.qcow2 را در مسیر استخر ذخیره‌سازی انتخاب‌شده ایجاد می‌کند. در صورت وارد کردن، باید مسیر مطلق باشد.', - imagePath: 'مسیر ایمیج', - imagePathHelper: 'اختیاری است. در صورت وارد کردن، باید مسیر مطلق یک ایمیج qcow2 موجود باشد.', isoPath: 'مسیر ISO', storagePool: 'استخر ذخیره‌سازی', network: 'شبکه', @@ -5676,8 +5667,10 @@ const message = { dhcpStart: 'شروع DHCP', dhcpEnd: 'پایان DHCP', bootOrder: 'ترتیب بوت', - bootOrderHelper: - 'HD یعنی ابتدا از دیسک بوت شود و اگر دیسک قابل بوت نبود ISO امتحان شود. CD-ROM یعنی ابتدا از ISO بوت شود.', + bootDiskFirst: 'ابتدا دیسک (HD → CD-ROM)', + bootIsoFirst: 'ابتدا ISO (CD-ROM → HD)', + bootOrderConfirm: + 'ترتیب بوت به {0} تغییر کند؟ ماشین مجازی در راه‌اندازی بعدی، دستگاه‌های متصل و قابل بوت را به این ترتیب امتحان می‌کند.', startAfterCreate: 'شروع پس از ایجاد', autoStart: 'شروع خودکار', resourceAutoStart: 'شروع خودکار', @@ -5685,16 +5678,6 @@ const message = { available: 'در دسترس', vmCount: 'ماشین‌های مجازی مرتبط', usedIPs: 'IPهای استفاده‌شده', - syncFromServer: 'همگام‌سازی از سرور', - notFound: 'وجود ندارد', - resourceStatus: { - running: 'در حال اجرا', - inactive: 'شروع نشده', - building: 'در حال ساخت', - degraded: 'تنزل‌یافته', - inaccessible: 'غیرقابل دسترس', - unknown: 'نامشخص', - }, monitor: 'مانیتورینگ', monitorNotReady: 'ماشین مجازی در حال اجرا نیست و داده‌های مانیتورینگ در دسترس نیست.', monitorLoadFailed: 'دریافت داده‌های مانیتورینگ ماشین مجازی ناموفق بود.', @@ -5722,10 +5705,11 @@ const message = { snapshot: 'اسنپ‌شات', createSnapshot: 'ایجاد اسنپ‌شات', recoverSnapshot: 'بازیابی اسنپ‌شات', - snapshotName: 'نام اسنپ‌شات', - snapshotSize: 'اندازه اسنپ‌شات', + snapshotSize: 'فضای مصرف‌شده', currentSnapshot: 'فعلی', snapshotEmpty: 'اسنپ‌شاتی وجود ندارد', + snapshotOperationHelper: + 'اسنپ‌شات فعلی قابل حذف نیست؛ بازیابی اسنپ‌شات فقط زمانی مجاز است که ماشین مجازی خاموش باشد.', snapshotRecoverConfirm: 'ماشین مجازی [{1}] به اسنپ‌شات [{0}] بازیابی شود؟ پس از بازیابی، دیسک ماشین مجازی به وضعیت آن اسنپ‌شات برمی‌گردد.', interface: 'رابط شبکه', @@ -5746,11 +5730,7 @@ const message = { downloadFromServer: 'دانلود از سرور', isoTargetDir: 'مسیر مقصد ISO', isoTargetDirHelper: 'ISO های آپلودشده محلی یا دانلودشده از سرور در این دایرکتوری ذخیره می‌شوند.', - isoTargetDirOptional: 'مسیر مقصد ISO تنظیم نشده است. مسیر فایل منبع ISO اصلی استفاده خواهد شد.', - isoTargetDirAvailable: 'مسیر مقصد ISO قابل استفاده است.', - isoTargetDirUnavailable: 'مسیر مقصد ISO باید داخل یک استخر ذخیره‌سازی ماشین مجازی قابل استفاده باشد.', - isoTargetDirCheckRequired: 'قبل از ذخیره، مسیر مقصد ISO را بررسی کنید.', - checkPath: 'بررسی', + addingIsoRecord: 'در حال افزودن رکورد ISO', isoSize: 'اندازه ISO', deleteIsoConfirm: 'این عملیات حذف قابل بازگشت نیست. ادامه می‌دهید؟', deleteSourceFile: 'حذف فایل منبع', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 295c22b5f..27e30cdc7 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -5673,15 +5673,12 @@ const message = { dependencies: '依存関係', dependencyPurpose: '用途', dependencyPurposeMap: { - kvm: 'ハードウェア仮想化機能を提供し、仮想マシンを高速に実行する基盤です。', libvirt: '仮想マシン管理サービスを提供し、ライフサイクルとリソーススケジューリングを担当します。', storage: '仮想マシンのディスクファイルを保存するデフォルトの保存先またはストレージプールを提供します。', }, dependencyNotReady: '仮想マシンの依存関係チェックに失敗しました。依存関係を処理してから使用してください。', helperUnavailable: '仮想マシン機能の初期化に失敗しました。Core ログを確認するか、再度アップグレードしてから使用してください。', - helperNotStart: - '仮想マシンの実行環境が準備できていません。ホストの仮想化サービスを処理してから使用してください。', cpu: 'CPU', memory: 'メモリ', disk: 'ディスク', @@ -5692,22 +5689,15 @@ const message = { storageLimitHelper: '対象ストレージプールの現在の空き容量:{0}', storageLimitExceeded: 'ディスク容量が上限を超えています。現在利用可能な最大値:{0}。', iso: 'ISO', - createSource: '作成元', createFromTemplate: 'テンプレートから作成', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: 'テンプレート {0} を削除します。この操作は元に戻せません。続行しますか?', diskSize: 'ディスク容量', diskPath: 'ディスクパス', - diskPathHelper: - '空欄にできます。システムは選択したストレージプールディレクトリに 仮想マシン名.qcow2 を自動作成します。入力する場合は絶対パスである必要があります。', - imagePath: 'イメージパス', - imagePathHelper: '空欄にできます。入力する場合は既存の qcow2 イメージの絶対パスを指定してください。', isoPath: 'ISO パス', storagePool: 'ストレージプール', network: 'ネットワーク', @@ -5724,8 +5714,10 @@ const message = { dhcpStart: 'DHCP 開始アドレス', dhcpEnd: 'DHCP 終了アドレス', bootOrder: '起動順序', - bootOrderHelper: - 'HD はディスクから優先起動し、起動できない場合に ISO を試します。CD-ROM は ISO から優先起動します。', + bootDiskFirst: 'ディスク優先(HD → CD-ROM)', + bootIsoFirst: 'ISO 優先(CD-ROM → HD)', + bootOrderConfirm: + '起動順序を {0} に変更しますか?次回の起動時、仮想マシンは現在接続されている起動可能なデバイスをこの順序で試します。', startAfterCreate: '作成後に起動', autoStart: '自動起動', resourceAutoStart: '自動起動', @@ -5733,16 +5725,6 @@ const message = { available: '利用可能', vmCount: '関連 VM', usedIPs: '使用済み IP', - syncFromServer: 'サーバーから同期', - notFound: '存在しません', - resourceStatus: { - running: '実行中', - inactive: '未起動', - building: '構築中', - degraded: '低下', - inaccessible: 'アクセス不可', - unknown: '不明', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5771,10 +5753,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: '使用量', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + '現在使用中のスナップショットは削除できません。スナップショットは仮想マシンがシャットオフ状態の場合にのみ復元できます。', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: 'ネットワークインターフェース', @@ -5796,11 +5779,7 @@ const message = { isoTargetDir: 'ISO ターゲットパス', isoTargetDirHelper: 'ローカルアップロードまたはサーバーからダウンロードした ISO はこのディレクトリに保存されます。', - isoTargetDirOptional: 'ISO ターゲットパスが設定されていないため、元の ISO ソースファイルパスを使用します。', - isoTargetDirAvailable: 'ISO ターゲットパスは利用可能です。', - isoTargetDirUnavailable: 'ISO ターゲットパスは利用可能な仮想マシンストレージプール内である必要があります。', - isoTargetDirCheckRequired: '保存前に ISO ターゲットパスを確認してください。', - checkPath: '確認', + addingIsoRecord: 'ISO レコードを追加しています', isoSize: 'ISO サイズ', deleteIsoConfirm: 'この削除操作は元に戻せません。続行しますか?', deleteSourceFile: '元ファイルを削除', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 9222f1b4e..ce71e57e2 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -5559,14 +5559,12 @@ const message = { dependencies: '종속성', dependencyPurpose: '용도', dependencyPurposeMap: { - kvm: '하드웨어 가상화 기능을 제공하며 가상 머신 가속 실행의 기반입니다.', libvirt: '가상 머신 관리 서비스를 제공하며 수명 주기와 리소스 스케줄링을 담당합니다.', storage: '가상 머신 디스크 파일을 저장하기 위한 기본 저장소 디렉터리 또는 스토리지 풀을 제공합니다.', }, dependencyNotReady: '가상 머신 종속성 검사를 통과하지 못했습니다. 종속성을 처리한 후 사용하세요.', helperUnavailable: '가상 머신 기능 초기화에 실패했습니다. Core 로그를 확인하거나 다시 업그레이드한 후 사용하세요.', - helperNotStart: '가상 머신 실행 환경이 준비되지 않았습니다. 호스트 가상화 서비스를 처리한 후 사용하세요.', cpu: 'CPU', memory: '메모리', disk: '디스크', @@ -5577,22 +5575,15 @@ const message = { storageLimitHelper: '대상 스토리지 풀의 현재 사용 가능 공간: {0}', storageLimitExceeded: '디스크 용량이 제한을 초과했습니다. 현재 최대 사용 가능: {0}.', iso: 'ISO', - createSource: '생성 소스', createFromTemplate: '템플릿에서 생성', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: '템플릿 {0}을(를) 삭제합니다. 이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?', diskSize: '디스크 용량', diskPath: '디스크 경로', - diskPathHelper: - '비워둘 수 있습니다. 시스템은 선택한 스토리지 풀 디렉터리에 가상머신이름.qcow2를 자동 생성합니다. 입력하는 경우 절대 경로여야 합니다.', - imagePath: '이미지 경로', - imagePathHelper: '비워둘 수 있습니다. 입력하는 경우 기존 qcow2 이미지의 절대 경로여야 합니다.', isoPath: 'ISO 경로', storagePool: '스토리지 풀', network: '네트워크', @@ -5609,8 +5600,10 @@ const message = { dhcpStart: 'DHCP 시작 주소', dhcpEnd: 'DHCP 종료 주소', bootOrder: '부팅 순서', - bootOrderHelper: - 'HD는 디스크에서 먼저 부팅하고 디스크가 부팅 불가능하면 ISO를 시도합니다. CD-ROM은 ISO에서 먼저 부팅합니다.', + bootDiskFirst: '디스크 우선 (HD → CD-ROM)', + bootIsoFirst: 'ISO 우선 (CD-ROM → HD)', + bootOrderConfirm: + '부팅 순서를 {0}(으)로 변경하시겠습니까? 다음 시작 시 가상 머신은 현재 연결된 부팅 가능한 장치를 이 순서대로 시도합니다.', startAfterCreate: '생성 후 시작', autoStart: '자동 시작', resourceAutoStart: '자동 시작', @@ -5618,16 +5611,6 @@ const message = { available: '사용 가능', vmCount: '연결된 VM', usedIPs: '사용된 IP', - syncFromServer: '서버에서 동기화', - notFound: '존재하지 않음', - resourceStatus: { - running: '실행 중', - inactive: '시작되지 않음', - building: '빌드 중', - degraded: '저하됨', - inaccessible: '접근 불가', - unknown: '알 수 없음', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5654,10 +5637,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: '사용량', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + '현재 사용 중인 스냅샷은 삭제할 수 없습니다. 가상 머신이 종료된 상태에서만 스냅샷을 복원할 수 있습니다.', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: '네트워크 인터페이스', @@ -5678,11 +5662,7 @@ const message = { downloadFromServer: '서버에서 다운로드', isoTargetDir: 'ISO 대상 경로', isoTargetDirHelper: '로컬 업로드 또는 서버 다운로드 ISO가 이 디렉터리에 저장됩니다.', - isoTargetDirOptional: 'ISO 대상 경로가 입력되지 않아 원래 ISO 소스 파일 경로를 사용합니다.', - isoTargetDirAvailable: 'ISO 대상 경로를 사용할 수 있습니다.', - isoTargetDirUnavailable: 'ISO 대상 경로는 사용 가능한 VM 스토리지 풀 내에 있어야 합니다.', - isoTargetDirCheckRequired: '저장하기 전에 ISO 대상 경로를 확인하세요.', - checkPath: '확인', + addingIsoRecord: 'ISO 레코드를 추가하는 중', isoSize: 'ISO 크기', deleteIsoConfirm: '이 삭제 작업은 되돌릴 수 없습니다. 계속하시겠습니까?', deleteSourceFile: '원본 파일 삭제', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 1ccdd1cfd..4ce70cba8 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -5750,7 +5750,6 @@ const message = { dependencies: 'Kebergantungan', dependencyPurpose: 'Tujuan', dependencyPurposeMap: { - kvm: 'Menyediakan sokongan virtualisasi perkakasan untuk mempercepat pelaksanaan VM.', libvirt: 'Menyediakan perkhidmatan pengurusan VM untuk kitar hayat dan penjadualan sumber.', storage: 'Menyediakan direktori storan lalai atau kolam storan untuk fail cakera VM.', }, @@ -5758,7 +5757,6 @@ const message = { 'Semakan kebergantungan VM gagal. Selesaikan kebergantungan sebelum menggunakan mesin maya.', helperUnavailable: 'Permulaan keupayaan VM gagal. Semak log Core atau naik taraf semula sebelum menggunakan mesin maya.', - helperNotStart: 'Perkhidmatan VM tidak berjalan. Mulakan perkhidmatan sebelum menggunakan mesin maya.', cpu: 'CPU', memory: 'Memori', disk: 'Cakera', @@ -5769,22 +5767,15 @@ const message = { storageLimitHelper: 'Ruang kosong semasa dalam kolam storan sasaran: {0}', storageLimitExceeded: 'Kapasiti cakera melebihi had. Maksimum tersedia semasa: {0}.', iso: 'ISO', - createSource: 'Sumber Cipta', createFromTemplate: 'Cipta daripada templat', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: 'Padam templat {0}. Operasi ini tidak boleh dibuat asal. Teruskan?', diskSize: 'Saiz Cakera', diskPath: 'Laluan Cakera', - diskPathHelper: - 'Pilihan. Jika kosong, sistem akan mencipta .qcow2 dalam direktori kolam storan yang dipilih. Nilai tersuai mestilah laluan mutlak.', - imagePath: 'Laluan Imej', - imagePathHelper: 'Pilihan. Jika ditetapkan, gunakan laluan mutlak imej qcow2 sedia ada.', isoPath: 'Laluan ISO', storagePool: 'Kolam Storan', network: 'Rangkaian', @@ -5801,8 +5792,10 @@ const message = { dhcpStart: 'Mula DHCP', dhcpEnd: 'Tamat DHCP', bootOrder: 'Susunan But', - bootOrderHelper: - 'HD but daripada cakera dahulu dan kemudian mencuba ISO jika cakera tidak boleh dibut. CD-ROM but daripada ISO dahulu.', + bootDiskFirst: 'Cakera dahulu (HD → CD-ROM)', + bootIsoFirst: 'ISO dahulu (CD-ROM → HD)', + bootOrderConfirm: + 'Tukar susunan but kepada {0}? Pada permulaan seterusnya, VM akan mencuba peranti boleh but yang sedang dipasang mengikut susunan ini.', startAfterCreate: 'Mula selepas dicipta', autoStart: 'Mula automatik', resourceAutoStart: 'Mula automatik', @@ -5810,16 +5803,6 @@ const message = { available: 'Tersedia', vmCount: 'VM Terpaut', usedIPs: 'IP Digunakan', - syncFromServer: 'Segerak dari Pelayan', - notFound: 'Tidak ditemui', - resourceStatus: { - running: 'Berjalan', - inactive: 'Tidak aktif', - building: 'Membina', - degraded: 'Menurun', - inaccessible: 'Tidak boleh diakses', - unknown: 'Tidak diketahui', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5846,10 +5829,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: 'Penggunaan', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + 'Syot kilat yang sedang digunakan tidak boleh dipadam. Syot kilat hanya boleh dipulihkan apabila mesin maya dimatikan.', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: 'Antara Muka Rangkaian', @@ -5871,11 +5855,7 @@ const message = { isoTargetDir: 'Laluan Sasaran ISO', isoTargetDirHelper: 'ISO yang dimuat naik secara tempatan atau dimuat turun daripada pelayan akan disimpan ke direktori ini.', - isoTargetDirOptional: 'Tiada laluan sasaran ISO ditetapkan. Laluan fail sumber ISO asal akan digunakan.', - isoTargetDirAvailable: 'Laluan sasaran ISO tersedia.', - isoTargetDirUnavailable: 'Laluan sasaran ISO mesti berada dalam storan pool VM yang tersedia.', - isoTargetDirCheckRequired: 'Semak laluan sasaran ISO sebelum menyimpan.', - checkPath: 'Semak', + addingIsoRecord: 'Menambah rekod ISO', isoSize: 'Saiz ISO', deleteIsoConfirm: 'Operasi pemadaman ini tidak boleh dibuat asal. Teruskan?', deleteSourceFile: 'Padam fail sumber', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index a3571d8ec..b8cdcb515 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -5899,7 +5899,6 @@ const message = { dependencies: 'Dependências', dependencyPurpose: 'Finalidade', dependencyPurposeMap: { - kvm: 'Fornece suporte de virtualização por hardware para execução acelerada de VMs.', libvirt: 'Fornece serviços de gerenciamento de VMs para controle de ciclo de vida e agendamento de recursos.', storage: @@ -5909,7 +5908,6 @@ const message = { 'As verificações de dependência da VM falharam. Corrija as dependências antes de usar máquinas virtuais.', helperUnavailable: 'A inicialização da capacidade de VM falhou. Verifique os logs do Core ou atualize novamente antes de usar VMs.', - helperNotStart: 'O serviço de VM não está em execução. Inicie o serviço antes de usar máquinas virtuais.', cpu: 'CPU', memory: 'Memória', disk: 'Disco', @@ -5920,22 +5918,15 @@ const message = { storageLimitHelper: 'Espaço livre atual no pool de armazenamento de destino: {0}', storageLimitExceeded: 'A capacidade do disco excede o limite. Máximo disponível no momento: {0}.', iso: 'ISO', - createSource: 'Origem da criação', createFromTemplate: 'Criar a partir de modelo', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: 'Excluir modelo {0}. Esta operação não pode ser desfeita. Continuar?', diskSize: 'Tamanho do Disco', diskPath: 'Caminho do Disco', - diskPathHelper: - 'Opcional. Se vazio, o sistema cria .qcow2 no diretório do pool de armazenamento selecionado. Valores personalizados devem ser caminhos absolutos.', - imagePath: 'Caminho da Imagem', - imagePathHelper: 'Opcional. Se definido, use o caminho absoluto de uma imagem qcow2 existente.', isoPath: 'Caminho do ISO', storagePool: 'Pool de Armazenamento', network: 'Rede', @@ -5952,8 +5943,10 @@ const message = { dhcpStart: 'Início DHCP', dhcpEnd: 'Fim DHCP', bootOrder: 'Ordem de Boot', - bootOrderHelper: - 'HD inicializa pelo disco primeiro e tenta o ISO se o disco não for inicializável. CD-ROM inicializa pelo ISO primeiro.', + bootDiskFirst: 'Disco primeiro (HD → CD-ROM)', + bootIsoFirst: 'ISO primeiro (CD-ROM → HD)', + bootOrderConfirm: + 'Alterar a ordem de boot para {0}? Na próxima inicialização, a VM tentará nesta ordem os dispositivos inicializáveis que estiverem montados.', startAfterCreate: 'Iniciar após criação', autoStart: 'Inicialização automática', resourceAutoStart: 'Inicialização automática', @@ -5961,16 +5954,6 @@ const message = { available: 'Disponível', vmCount: 'VMs Vinculadas', usedIPs: 'IPs Usados', - syncFromServer: 'Sincronizar do Servidor', - notFound: 'Não encontrado', - resourceStatus: { - running: 'Em execução', - inactive: 'Inativo', - building: 'Construindo', - degraded: 'Degradado', - inaccessible: 'Inacessível', - unknown: 'Desconhecido', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5997,10 +5980,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: 'Uso', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + 'O snapshot em uso não pode ser excluído. Os snapshots só podem ser restaurados quando a máquina virtual estiver desligada.', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: 'Interface de Rede', @@ -6021,13 +6005,7 @@ const message = { downloadFromServer: 'Baixar do servidor', isoTargetDir: 'Caminho de destino ISO', isoTargetDirHelper: 'ISOs enviados localmente ou baixados do servidor serão salvos neste diretório.', - isoTargetDirOptional: - 'Nenhum caminho de destino ISO foi definido. O caminho original do arquivo ISO será usado.', - isoTargetDirAvailable: 'O caminho de destino ISO está disponível.', - isoTargetDirUnavailable: - 'O caminho de destino ISO deve estar em um pool de armazenamento de VM disponível.', - isoTargetDirCheckRequired: 'Verifique o caminho de destino ISO antes de salvar.', - checkPath: 'Verificar', + addingIsoRecord: 'Adicionando registro ISO', isoSize: 'Tamanho do ISO', deleteIsoConfirm: 'Esta operação de exclusão não pode ser desfeita. Continuar?', deleteSourceFile: 'Excluir arquivo de origem', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 4726267f1..0c4f97d25 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -5749,7 +5749,6 @@ const message = { dependencies: 'Зависимости', dependencyPurpose: 'Назначение', dependencyPurposeMap: { - kvm: 'Предоставляет аппаратную виртуализацию для ускоренного запуска виртуальных машин.', libvirt: 'Предоставляет службу управления ВМ, отвечает за жизненный цикл и распределение ресурсов.', storage: 'Предоставляет каталог хранения по умолчанию или пул хранения для файлов дисков ВМ.', }, @@ -5757,8 +5756,6 @@ const message = { 'Проверка зависимостей ВМ не пройдена. Исправьте зависимости перед использованием виртуальных машин.', helperUnavailable: 'Не удалось инициализировать возможности ВМ. Проверьте журналы Core или повторите обновление перед использованием виртуальных машин.', - helperNotStart: - 'Служба виртуальных машин не запущена. Запустите службу перед использованием виртуальных машин.', cpu: 'CPU', memory: 'Память', disk: 'Диск', @@ -5769,23 +5766,15 @@ const message = { storageLimitHelper: 'Текущее свободное место в целевом пуле хранения: {0}', storageLimitExceeded: 'Ёмкость диска превышает лимит. Текущий максимум: {0}.', iso: 'ISO', - createSource: 'Источник создания', createFromTemplate: 'Создать из шаблона', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: 'Удалить шаблон {0}. Эту операцию нельзя отменить. Продолжить?', diskSize: 'Размер диска', diskPath: 'Путь к диску', - diskPathHelper: - 'Можно оставить пустым. Система автоматически создаст ИмяВМ.qcow2 в каталоге выбранного пула хранения. Если указано, путь должен быть абсолютным.', - imagePath: 'Путь к образу', - imagePathHelper: - 'Можно оставить пустым. Если указано, используйте абсолютный путь к существующему образу qcow2.', isoPath: 'Путь к ISO', storagePool: 'Пул хранения', network: 'Сеть', @@ -5802,8 +5791,10 @@ const message = { dhcpStart: 'Начальный адрес DHCP', dhcpEnd: 'Конечный адрес DHCP', bootOrder: 'Порядок загрузки', - bootOrderHelper: - 'HD сначала загружает с диска, а затем пробует ISO, если диск не загрузочный. CD-ROM сначала загружает с ISO.', + bootDiskFirst: 'Сначала диск (HD → CD-ROM)', + bootIsoFirst: 'Сначала ISO (CD-ROM → HD)', + bootOrderConfirm: + 'Изменить порядок загрузки на {0}? При следующем запуске ВМ проверит в этом порядке подключённые загрузочные устройства.', startAfterCreate: 'Запустить после создания', autoStart: 'Автозапуск', resourceAutoStart: 'Автозапуск', @@ -5811,16 +5802,6 @@ const message = { available: 'Доступно', vmCount: 'Связанные ВМ', usedIPs: 'Использованные IP', - syncFromServer: 'Синхронизировать с сервера', - notFound: 'Не существует', - resourceStatus: { - running: 'Работает', - inactive: 'Не запущено', - building: 'Создаётся', - degraded: 'Деградировано', - inaccessible: 'Недоступно', - unknown: 'Неизвестно', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5848,10 +5829,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: 'Использовано', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + 'Используемый снимок нельзя удалить. Восстановление снимка доступно только при выключенной виртуальной машине.', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: 'Сетевой интерфейс', @@ -5872,11 +5854,7 @@ const message = { downloadFromServer: 'Скачать с сервера', isoTargetDir: 'Целевой путь ISO', isoTargetDirHelper: 'ISO, загруженные локально или скачанные с сервера, будут сохранены в этот каталог.', - isoTargetDirOptional: 'Целевой путь ISO не указан. Будет использован исходный путь файла ISO.', - isoTargetDirAvailable: 'Целевой путь ISO доступен.', - isoTargetDirUnavailable: 'Целевой путь ISO должен находиться в доступном пуле хранения ВМ.', - isoTargetDirCheckRequired: 'Проверьте целевой путь ISO перед сохранением.', - checkPath: 'Проверить', + addingIsoRecord: 'Добавление записи ISO', isoSize: 'Размер ISO', deleteIsoConfirm: 'Эту операцию удаления нельзя отменить. Продолжить?', deleteSourceFile: 'Удалить исходный файл', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 5356084d7..d4abb9cfb 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -5738,7 +5738,6 @@ const message = { dependencies: 'Bağımlılıklar', dependencyPurpose: 'Amaç', dependencyPurposeMap: { - kvm: 'Hızlandırılmış VM çalıştırma için donanım sanallaştırma desteği sağlar.', libvirt: 'Yaşam döngüsü denetimi ve kaynak zamanlaması için VM yönetim hizmetleri sağlar.', storage: 'VM disk dosyaları için varsayılan depolama dizinini veya depolama havuzunu sağlar.', }, @@ -5746,7 +5745,6 @@ const message = { 'VM bağımlılık denetimleri başarısız oldu. Sanal makineleri kullanmadan önce bağımlılıkları düzeltin.', helperUnavailable: 'VM yeteneği başlatılamadı. Sanal makineleri kullanmadan önce Core günlüklerini kontrol edin veya yeniden yükseltin.', - helperNotStart: 'VM hizmeti çalışmıyor. Sanal makineleri kullanmadan önce hizmeti başlatın.', cpu: 'CPU', memory: 'Bellek', disk: 'Disk', @@ -5757,22 +5755,15 @@ const message = { storageLimitHelper: 'Hedef depolama havuzundaki mevcut boş alan: {0}', storageLimitExceeded: 'Disk kapasitesi sınırı aşıyor. Şu anki maksimum kullanılabilir: {0}.', iso: 'ISO', - createSource: 'Oluşturma Kaynağı', createFromTemplate: 'Şablondan oluştur', - blankDisk: 'Blank Disk', template: 'Template', templateName: 'Template Name', convertToTemplate: 'Convert to Template', sourceVM: 'Source VM', templateSize: 'Template Size', - deleteTemplate: 'Delete Template', deleteTemplateConfirm: '{0} şablonu silinsin. Bu işlem geri alınamaz. Devam edilsin mi?', diskSize: 'Disk Boyutu', diskPath: 'Disk Yolu', - diskPathHelper: - 'İsteğe bağlıdır. Boş bırakılırsa sistem seçilen depolama havuzu dizininde .qcow2 oluşturur. Özel değerler mutlak yol olmalıdır.', - imagePath: 'İmaj Yolu', - imagePathHelper: 'İsteğe bağlıdır. Ayarlanırsa mevcut bir qcow2 imajının mutlak yolunu kullanın.', isoPath: 'ISO Yolu', storagePool: 'Depolama Havuzu', network: 'Ağ', @@ -5789,8 +5780,10 @@ const message = { dhcpStart: 'DHCP Başlangıcı', dhcpEnd: 'DHCP Bitişi', bootOrder: 'Önyükleme Sırası', - bootOrderHelper: - 'HD önce diskten önyükler, disk önyüklenebilir değilse ISOyu dener. CD-ROM önce ISOdan önyükler.', + bootDiskFirst: 'Önce disk (HD → CD-ROM)', + bootIsoFirst: 'Önce ISO (CD-ROM → HD)', + bootOrderConfirm: + 'Önyükleme sırası {0} olarak değiştirilsin mi? Sanal makine bir sonraki başlangıçta bağlı ve önyüklenebilir aygıtları bu sırayla dener.', startAfterCreate: 'Oluşturduktan sonra başlat', autoStart: 'Otomatik başlat', resourceAutoStart: 'Otomatik başlat', @@ -5798,16 +5791,6 @@ const message = { available: 'Kullanılabilir', vmCount: 'Bağlı VMler', usedIPs: 'Kullanılan IPler', - syncFromServer: 'Sunucudan senkronize et', - notFound: 'Bulunamadı', - resourceStatus: { - running: 'Çalışıyor', - inactive: 'Etkin değil', - building: 'Oluşturuluyor', - degraded: 'Bozulmuş', - inaccessible: 'Erişilemez', - unknown: 'Bilinmiyor', - }, monitor: 'Monitor', monitorNotReady: 'The virtual machine is not running. Monitoring data is unavailable.', monitorLoadFailed: 'Failed to get VM monitoring data.', @@ -5835,10 +5818,11 @@ const message = { snapshot: 'Snapshot', createSnapshot: 'Create Snapshot', recoverSnapshot: 'Recover Snapshot', - snapshotName: 'Snapshot Name', - snapshotSize: 'Snapshot Size', + snapshotSize: 'Kullanım', currentSnapshot: 'Current', snapshotEmpty: 'No snapshots', + snapshotOperationHelper: + 'Kullanımdaki anlık görüntü silinemez. Anlık görüntüler yalnızca sanal makine kapalıyken geri yüklenebilir.', snapshotRecoverConfirm: 'Recover virtual machine [{1}] to snapshot [{0}]? The VM disk will be restored to the snapshot state.', interface: 'Ağ Arayüzü', @@ -5859,11 +5843,7 @@ const message = { downloadFromServer: 'Sunucudan İndir', isoTargetDir: 'ISO Hedef Yolu', isoTargetDirHelper: 'Yerel olarak yüklenen veya sunucudan indirilen ISO dosyaları bu dizine kaydedilir.', - isoTargetDirOptional: 'ISO hedef yolu ayarlanmadı. Orijinal ISO kaynak dosya yolu kullanılacak.', - isoTargetDirAvailable: 'ISO hedef yolu kullanılabilir.', - isoTargetDirUnavailable: 'ISO hedef yolu kullanılabilir bir VM depolama havuzunun içinde olmalıdır.', - isoTargetDirCheckRequired: 'Kaydetmeden önce ISO hedef yolunu kontrol edin.', - checkPath: 'Kontrol Et', + addingIsoRecord: 'ISO kaydı ekleniyor', isoSize: 'ISO Boyutu', deleteIsoConfirm: 'Bu silme işlemi geri alınamaz. Devam edilsin mi?', deleteSourceFile: 'Kaynak dosyayı sil', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index d2f11d767..3d9dc0de1 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -5269,13 +5269,11 @@ const message = { dependencies: '依賴', dependencyPurpose: '作用', dependencyPurposeMap: { - kvm: '提供硬體虛擬化能力,是虛擬機加速執行的基礎。', libvirt: '提供虛擬機管理服務,負責虛擬機生命週期和資源調度。', storage: '提供預設儲存目錄或儲存池,用於保存虛擬機磁碟檔案。', }, dependencyNotReady: '虛擬機依賴檢測未通過,請處理依賴後再使用虛擬機功能。', helperUnavailable: '虛擬機能力初始化失敗,請檢查 Core 日誌或重新升級後再使用虛擬機功能。', - helperNotStart: '虛擬機執行環境未就緒,請處理主機虛擬化服務後再使用虛擬機功能。', cpu: 'CPU', memory: '記憶體', disk: '磁碟', @@ -5286,21 +5284,15 @@ const message = { storageLimitHelper: '目標儲存池目前可用空間:{0}', storageLimitExceeded: '磁碟容量超出限制,目前最大可用:{0}', iso: '鏡像', - createSource: '建立來源', createFromTemplate: '從範本啟動', - blankDisk: '空白磁碟', template: '範本', templateName: '範本名稱', convertToTemplate: '轉為範本', sourceVM: '來源虛擬機', templateSize: '範本大小', - deleteTemplate: '刪除範本', deleteTemplateConfirm: '刪除範本 {0},該操作不可回滾,是否繼續?', diskSize: '磁碟容量', diskPath: '磁碟路徑', - diskPathHelper: '可留空,系統將在所選儲存池目錄下自動建立 虛擬機名稱.qcow2;如填寫,必須是絕對路徑。', - imagePath: '映像路徑', - imagePathHelper: '可留空;如填寫,需為已存在的 qcow2 映像絕對路徑。', isoPath: '鏡像路徑', storagePool: '儲存池', network: '網路', @@ -5316,7 +5308,9 @@ const message = { dhcpStart: 'DHCP 起始位址', dhcpEnd: 'DHCP 結束位址', bootOrder: '啟動項', - bootOrderHelper: 'HD 表示優先從磁碟啟動,磁碟不可啟動時再嘗試鏡像;CD-ROM 表示優先從鏡像啟動。', + bootDiskFirst: '磁碟優先(HD → CD-ROM)', + bootIsoFirst: '鏡像優先(CD-ROM → HD)', + bootOrderConfirm: '確定將啟動順序修改為 {0} 嗎?虛擬機下次啟動時,將按此順序嘗試目前已掛載且可啟動的裝置。', startAfterCreate: '建立後啟動', autoStart: '開機自啟', resourceAutoStart: '開機自啟', @@ -5324,16 +5318,6 @@ const message = { available: '可用', vmCount: '關聯虛擬機', usedIPs: '已用 IP', - syncFromServer: '從伺服器同步', - notFound: '不存在', - resourceStatus: { - running: '執行中', - inactive: '未啟動', - building: '建置中', - degraded: '降級', - inaccessible: '無法存取', - unknown: '未知', - }, monitor: '監控', monitorNotReady: '虛擬機未執行,無法取得監控資料。', monitorLoadFailed: '取得虛擬機監控資料失敗。', @@ -5360,10 +5344,10 @@ const message = { snapshot: '快照', createSnapshot: '建立快照', recoverSnapshot: '恢復快照', - snapshotName: '快照名稱', - snapshotSize: '快照大小', + snapshotSize: '佔用', currentSnapshot: '目前', snapshotEmpty: '暫無快照', + snapshotOperationHelper: '目前使用中的快照不允許刪除;僅當虛擬機處於關機狀態時才允許恢復快照。', snapshotRecoverConfirm: '確認將虛擬機 [{1}] 恢復到快照 [{0}]?恢復後虛擬機磁碟會回到該快照狀態。', interface: '網路介面', device: '裝置', @@ -5382,11 +5366,7 @@ const message = { downloadFromServer: '從伺服器下載', isoTargetDir: '鏡像目標路徑', isoTargetDirHelper: '本機上傳或從伺服器下載的鏡像會儲存到該目錄。', - isoTargetDirOptional: '未填寫鏡像目標路徑,將直接使用鏡像來源檔案路徑。', - isoTargetDirAvailable: '鏡像目標路徑可用。', - isoTargetDirUnavailable: '鏡像目標路徑必須位於可用的虛擬機儲存池內。', - isoTargetDirCheckRequired: '請先檢查鏡像目標路徑,檢查通過後再儲存。', - checkPath: '檢查', + addingIsoRecord: '正在新增 ISO 記錄', isoSize: '鏡像大小', deleteIsoConfirm: '刪除操作不可回滾,是否繼續?', deleteSourceFile: '刪除源檔案', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 5550c989f..e9ee479e5 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -5262,13 +5262,11 @@ const message = { dependencies: '依赖', dependencyPurpose: '作用', dependencyPurposeMap: { - kvm: '提供硬件虚拟化能力,是虚拟机加速运行的基础。', libvirt: '提供虚拟机管理服务,负责虚拟机生命周期和资源调度。', storage: '提供默认存储目录或存储池,用于保存虚拟机磁盘文件。', }, dependencyNotReady: '虚拟机依赖检测未通过,请处理后再使用虚拟机功能。', helperUnavailable: '虚拟机能力初始化失败,请检查 Core 日志或重新升级后再使用虚拟机功能。', - helperNotStart: '虚拟机运行环境未就绪,请处理宿主机虚拟化服务后再使用虚拟机功能。', kvmUnavailable: '未检测到可用的 KVM 硬件虚拟化能力。', kvmCheckCpu: '确认宿主机 CPU 支持 Intel VT-x 或 AMD-V 虚拟化能力。', kvmCheckBios: '进入 BIOS/UEFI 开启虚拟化相关选项。', @@ -5277,7 +5275,6 @@ const message = { cpu: 'CPU', memory: '内存', disk: '磁盘', - totalVM: '虚拟机总数', abnormal: '异常', resourceOverview: '资源概况', cpuAllocated: 'CPU 分配', @@ -5285,14 +5282,10 @@ const message = { storageUsed: '存储使用', riskReminder: '风险提醒', noRisk: '暂无风险', - quickEntry: '快捷入口', runtimeHealth: '运行环境', - runtimeReady: '虚拟机运行环境正常', runtimeAbnormal: '虚拟机运行环境异常', - environmentCheck: '环境检测', noDependency: '暂无依赖检测数据', storageOverview: '存储池概况', - recentVM: '最近虚拟机', totalAllocated: '按虚拟机配置总量统计', runningAllocated: '运行中占用:{0}', storageAvailable: '剩余可用:{0}', @@ -5311,21 +5304,15 @@ const message = { storageLimitHelper: '目标存储池当前可用空间:{0}', storageLimitExceeded: '磁盘容量超出限制,当前最大可用:{0}', iso: '镜像', - createSource: '创建来源', createFromTemplate: '从模板启动', - blankDisk: '空白磁盘', template: '模板', templateName: '模板名称', convertToTemplate: '转为模板', sourceVM: '来源虚拟机', templateSize: '模板大小', - deleteTemplate: '删除模板', deleteTemplateConfirm: '删除模板 {0},该操作不可回滚,是否继续?', diskSize: '磁盘容量', diskPath: '磁盘路径', - diskPathHelper: '可留空,系统将在所选存储池目录下自动创建 虚拟机名称.qcow2;如填写,必须是绝对路径。', - imagePath: '镜像路径', - imagePathHelper: '可留空;如填写,需为已存在的 qcow2 镜像绝对路径。', isoPath: '镜像源文件', storagePool: '存储池', network: '网络', @@ -5341,7 +5328,9 @@ const message = { dhcpStart: 'DHCP 起始地址', dhcpEnd: 'DHCP 结束地址', bootOrder: '启动项', - bootOrderHelper: 'HD 表示优先从磁盘启动,磁盘不可启动时再尝试镜像;CD-ROM 表示优先从镜像启动。', + bootDiskFirst: '磁盘优先(HD → CD-ROM)', + bootIsoFirst: '镜像优先(CD-ROM → HD)', + bootOrderConfirm: '确定将启动顺序修改为 {0} 吗?虚拟机下次启动时,将按此顺序尝试当前已挂载且可启动的设备。', startAfterCreate: '创建后启动', autoStart: '开机自启', resourceAutoStart: '开机自启', @@ -5349,16 +5338,6 @@ const message = { available: '可用', vmCount: '关联虚拟机', usedIPs: '已用 IP', - syncFromServer: '从服务器同步', - notFound: '不存在', - resourceStatus: { - running: '运行中', - inactive: '未启动', - building: '构建中', - degraded: '降级', - inaccessible: '不可访问', - unknown: '未知', - }, monitor: '监控', monitorNotReady: '虚拟机未运行,无法获取监控数据。', monitorLoadFailed: '获取虚拟机监控数据失败。', @@ -5385,10 +5364,10 @@ const message = { snapshot: '快照', createSnapshot: '创建快照', recoverSnapshot: '恢复快照', - snapshotName: '快照名称', - snapshotSize: '快照大小', + snapshotSize: '占用', currentSnapshot: '当前', snapshotEmpty: '暂无快照', + snapshotOperationHelper: '当前正在使用的快照不允许删除;仅当虚拟机处于已关机状态时才允许恢复快照。', snapshotRecoverConfirm: '确认将虚拟机 [{1}] 恢复到快照 [{0}]?恢复后虚拟机磁盘会回到该快照状态。', interface: '网络接口', device: '设备', @@ -5407,11 +5386,7 @@ const message = { downloadFromServer: '从服务器下载', isoTargetDir: '镜像目标路径', isoTargetDirHelper: '本地上传或从服务器下载的镜像会保存到该目录。', - isoTargetDirOptional: '未填写镜像目标路径,将直接使用镜像源文件路径。', - isoTargetDirAvailable: '镜像目标路径可用。', - isoTargetDirUnavailable: '镜像目标路径必须位于可用的虚拟机存储池内。', - isoTargetDirCheckRequired: '请先检查镜像目标路径,检查通过后再保存。', - checkPath: '检查', + addingIsoRecord: '正在添加 ISO 记录', isoSize: '镜像大小', deleteIsoConfirm: '删除操作不可回滚,是否继续?', deleteSourceFile: '删除源文件', diff --git a/frontend/src/routers/index.ts b/frontend/src/routers/index.ts index 4a80a5712..5a71e2ee2 100644 --- a/frontend/src/routers/index.ts +++ b/frontend/src/routers/index.ts @@ -26,7 +26,7 @@ const clearLoginStatus = () => { clearLicenseStatus(); }; -router.beforeEach(async (to, from, next) => { +router.beforeEach(async (to, from) => { const { entrance, isEnterprise, isEnterpriseLicenseLoaded, isEnterpriseLicensed, isLogin } = useGlobalStore(); NProgress.start(); axiosCanceler.removeAllPending(); @@ -35,85 +35,75 @@ router.beforeEach(async (to, from, next) => { clearLoginStatus(); } if (!isLogin.value && !noLoginWhiteList.includes(String(to.name))) { - next( - entrance.value - ? { - name: 'entrance', - params: { code: entrance.value }, - } - : { - name: 'login', - }, - ); NProgress.done(); - return; + return entrance.value + ? { + name: 'entrance', + params: { code: entrance.value }, + } + : { + name: 'login', + }; } if (to.name === 'login' && !isLogin.value && entrance.value) { - next({ + NProgress.done(); + return { name: 'entrance', params: { code: entrance.value }, - }); - NProgress.done(); - return; + }; } if (to.name === 'login' && isLogin.value) { - next({ - name: 'home', - }); NProgress.done(); - return; + return { + name: 'home', + }; } if (to.name === 'entrance' && isLogin.value) { if (to.params.code === entrance.value) { - next({ - name: 'home', - }); NProgress.done(); - return; + return { + name: 'home', + }; } - next({ name: '404' }); NProgress.done(); - return; + return { name: '404' }; } if (isLogin.value && isEnterprise.value && !enterpriseLicenseCheckWhiteList.includes(String(to.name))) { if (!isEnterpriseLicenseLoaded.value) { await loadProductProFromDB(); } if (!isEnterpriseLicensed.value) { - next({ name: 'EnterpriseLicenseRequired', query: { code: String(to.params.code || '') } }); NProgress.done(); - return; + return { name: 'EnterpriseLicenseRequired', query: { code: String(to.params.code || '') } }; } } if (to.name === 'EnterpriseLicenseRequired') { if (!isLogin.value) { - next({ + NProgress.done(); + return { name: 'entrance', params: to.params, - }); - NProgress.done(); - return; + }; } if (!isEnterprise.value || isEnterpriseLicensed.value) { - next({ name: 'home' }); NProgress.done(); - return; + return { name: 'home' }; } - return next(); + return true; } if (to.path === '/apps/all' && to.query.install != undefined) { - return next(); + return true; } if (to.name === 'Expired') { - return next(); + return true; } const activeMenuKey = 'cachedRoute' + (to.meta.activeMenu || ''); if (to.query.uncached != undefined) { const query = { ...to.query }; delete query.uncached; localStorage.removeItem(activeMenuKey); - return next({ path: to.path, query }); + return { path: to.path, query }; } const cachedRoute = localStorage.getItem(activeMenuKey); @@ -127,20 +117,18 @@ router.beforeEach(async (to, from, next) => { const cachedRouteInfo = router.resolve(cachedRoute); if (cachedRouteInfo.matched.length > 0 && hasRouteAccess(cachedRouteInfo)) { isRedirecting = true; - next(cachedRoute); NProgress.done(); - return; + return cachedRoute; } localStorage.removeItem(activeMenuKey); } if (!hasRouteAccess(to)) { MsgError(i18n.global.t('commons.res.forbidden')); - next(false); NProgress.done(); - return; + return false; } - return next(); + return true; }); router.afterEach((to) => {