diff --git a/agent/app/service/file.go b/agent/app/service/file.go index 5f05ef8730dc..f66101691d3c 100644 --- a/agent/app/service/file.go +++ b/agent/app/service/file.go @@ -810,13 +810,12 @@ func (f *FileService) ChangeName(req request.FileRename) error { return buserr.New("ErrInvalidChar") } fo := files.NewFileOp() - info, _ := files.NewFileInfo(files.FileOption{Path: req.OldName, Expand: false}) - content, shouldRecordHistory := readEditableFileHistoryContent(req.OldName, info) + content, mode, shouldRecordHistory := readEditableFileHistoryContent(req.OldName) if err := fo.Rename(req.OldName, req.NewName); err != nil { return err } if shouldRecordHistory { - if histErr := historyService.RecordOperation(fileHistoryOpRename, req.OldName, content, info.FileMode, req.OldName, req.NewName); histErr != nil { + if histErr := historyService.RecordOperation(fileHistoryOpRename, req.OldName, content, mode, req.OldName, req.NewName); histErr != nil { global.LOG.Warnf("record file rename history failed for %s: %v", req.OldName, histErr) } } @@ -848,19 +847,13 @@ func (f *FileService) MvFile(m request.FileMove) error { mode os.FileMode record bool } - snapshots := make([]moveSnapshot, 0, len(m.OldPaths)) - for _, oldPath := range m.OldPaths { - mode := os.FileMode(0640) - record := false - var content []byte - if info, err := files.NewFileInfo(files.FileOption{Path: oldPath, Expand: false}); err == nil { - mode = info.FileMode - content, record = readEditableFileHistoryContent(oldPath, info) - } - snapshots = append(snapshots, moveSnapshot{path: oldPath, content: content, mode: mode, record: record}) - } var errs []error if m.Type == "cut" { + snapshots := make([]moveSnapshot, 0, len(m.OldPaths)) + for _, oldPath := range m.OldPaths { + content, mode, record := readEditableFileHistoryContent(oldPath) + snapshots = append(snapshots, moveSnapshot{path: oldPath, content: content, mode: mode, record: record}) + } if len(m.CoverPaths) > 0 { for _, src := range m.CoverPaths { if err := fo.CopyAndReName(src, m.NewPath, "", true); err != nil { @@ -909,32 +902,37 @@ func (f *FileService) MvFile(m request.FileMove) error { return nil } -func readEditableFileHistoryContent(filePath string, info *files.FileInfo) ([]byte, bool) { - if info == nil || info.IsDir || files.IsBlockDevice(info.FileMode) || info.Size > fileHistorySnapshotMaxSize { - return nil, false +func readEditableFileHistoryContent(filePath string) ([]byte, os.FileMode, bool) { + info, err := os.Lstat(filePath) + if err != nil { + return nil, 0640, false + } + mode := info.Mode() + if mode.IsDir() || mode&os.ModeSymlink != 0 || !mode.IsRegular() || files.IsBlockDevice(mode) || info.Size() > fileHistorySnapshotMaxSize { + return nil, mode, false } file, err := os.Open(filePath) if err != nil { - return nil, false + return nil, mode, false } defer file.Close() headBuf := make([]byte, 1024) n, err := file.Read(headBuf) if err != nil && err != io.EOF { - return nil, false + return nil, mode, false } if n > 0 && files.DetectBinary(headBuf[:n]) { - return nil, false + return nil, mode, false } if _, err := file.Seek(0, 0); err != nil { - return nil, false + return nil, mode, false } content, err := io.ReadAll(io.LimitReader(file, fileHistorySnapshotMaxSize+1)) if err != nil || int64(len(content)) > fileHistorySnapshotMaxSize { - return nil, false + return nil, mode, false } - return content, true + return content, mode, true } func buildHistoryMoveTargetPath(dst, name, sourcePath string, sourceCount int) string { diff --git a/agent/utils/files/file_op.go b/agent/utils/files/file_op.go index 43181ce2eca1..cf53cad75e5c 100644 --- a/agent/utils/files/file_op.go +++ b/agent/utils/files/file_op.go @@ -32,6 +32,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/mholt/archiver/v4" "github.com/spf13/afero" + "golang.org/x/sync/singleflight" ) const ( @@ -54,6 +55,11 @@ var protectedPaths = []string{ "/root", } +var ( + dirSizeGroup singleflight.Group + dirSizeLimiter = make(chan struct{}, 2) +) + func IsProtected(path string) bool { real, err := filepath.EvalSymlinks(path) if err == nil { @@ -682,7 +688,24 @@ func (f FileOp) CopyFile(src, dst string) error { } func (f FileOp) GetDirSize(path string) (int64, error) { - duCmd := exec.Command("du", "-s", path) + cleanPath := filepath.Clean(path) + result, err, _ := dirSizeGroup.Do("single:"+cleanPath, func() (interface{}, error) { + dirSizeLimiter <- struct{}{} + defer func() { + <-dirSizeLimiter + }() + return f.getDirSize(cleanPath) + }) + if err != nil { + return 0, err + } + return result.(int64), nil +} + +func (f FileOp) getDirSize(path string) (int64, error) { + ctx, cancel := context.WithTimeout(context.Background(), cmdRecursiveTimeout) + defer cancel() + duCmd := exec.CommandContext(ctx, "du", "-s", path) output, err := duCmd.Output() if err == nil { fields := strings.Fields(string(output)) @@ -694,6 +717,9 @@ func (f FileOp) GetDirSize(path string) (int64, error) { } } } + if ctx.Err() != nil { + return 0, ctx.Err() + } var size int64 err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error { @@ -717,12 +743,31 @@ type DirSize struct { } func (f FileOp) GetDepthDirSize(path string) ([]DirSize, error) { + cleanPath := filepath.Clean(path) + result, err, _ := dirSizeGroup.Do("depth:"+cleanPath, func() (interface{}, error) { + dirSizeLimiter <- struct{}{} + defer func() { + <-dirSizeLimiter + }() + return f.getDepthDirSize(cleanPath) + }) + if err != nil { + return nil, err + } + return result.([]DirSize), nil +} + +func (f FileOp) getDepthDirSize(path string) ([]DirSize, error) { var result []DirSize sizeMap := make(map[string]int64) - duCmd := exec.Command("du", "-k", "--max-depth=1", "--exclude=proc", path) + ctx, cancel := context.WithTimeout(context.Background(), cmdRecursiveTimeout) + defer cancel() + duCmd := exec.CommandContext(ctx, "du", "-k", "--max-depth=1", "--exclude=proc", path) output, err := duCmd.Output() if err == nil { parseDUOutput(output, sizeMap) + } else if ctx.Err() != nil { + return nil, ctx.Err() } else { calculateDirSizeFallback(path, sizeMap) } @@ -743,12 +788,17 @@ func parseDUOutput(output []byte, sizeMap map[string]int64) { if strings.TrimSpace(line) == "" { continue } - fields := strings.Fields(line) - if len(fields) == 2 { - if sizeKB, err := strconv.ParseInt(fields[0], 10, 64); err == nil { - dir := fields[1] - sizeMap[dir] = sizeKB * 1024 + sizeText, dir, ok := strings.Cut(strings.TrimSpace(line), "\t") + if !ok { + fields := strings.Fields(line) + if len(fields) < 2 { + continue } + sizeText = fields[0] + dir = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), sizeText)) + } + if sizeKB, err := strconv.ParseInt(strings.TrimSpace(sizeText), 10, 64); err == nil { + sizeMap[strings.TrimSpace(dir)] = sizeKB * 1024 } } } diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go index 36e9f0a484bb..8e136518505f 100644 --- a/core/app/service/upgrade.go +++ b/core/app/service/upgrade.go @@ -10,6 +10,7 @@ import ( "sort" "strconv" "strings" + "syscall" "github.com/1Panel-dev/1Panel/core/app/dto" "github.com/1Panel-dev/1Panel/core/app/model" @@ -33,6 +34,8 @@ type serviceInfo struct { selAgentName string } +const minUpgradeFreeSpace = 500 << 20 // 500MB + func loadServiceInfo() (serviceInfo, error) { basePath, err := controller.GetServicePath("") if err != nil { @@ -139,6 +142,18 @@ func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) { func (u *UpgradeService) Upgrade(req dto.Upgrade) error { global.LOG.Info("start to upgrade now...") + itemArch, err := loadArch() + if err != nil { + return err + } + svcInfo, err := loadServiceInfo() + if err != nil { + return err + } + if err := checkUpgradeSpace(); err != nil { + return err + } + baseDir := path.Join(global.CONF.Base.InstallDir, fmt.Sprintf("1panel/tmp/upgrade/%s", req.Version)) downloadDir := path.Join(baseDir, "downloads") _ = os.RemoveAll(baseDir) @@ -149,14 +164,6 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { if err := os.MkdirAll(originalDir, os.ModePerm); err != nil { return err } - itemArch, err := loadArch() - if err != nil { - return err - } - svcInfo, err := loadServiceInfo() - if err != nil { - return err - } mode := global.CONF.Base.Mode if strings.Contains(req.Version, "beta") { @@ -206,7 +213,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { return } - if err := files.CopyItem(false, true, path.Join(tmpDir, "1pctl"), "/usr/local/bin"); err != nil { + if err := files.CopyFileWithRename(path.Join(tmpDir, "1pctl"), "/usr/local/bin/1pctl"); err != nil { global.LOG.Errorf("upgrade 1pctl failed, err: %v", err) _ = settingRepo.Update("SystemStatus", "Free") u.handleRollback(originalDir, 2, svcInfo) @@ -241,11 +248,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { global.LOG.Errorf("Update language files failed: %v", err) _ = settingRepo.Update("SystemStatus", "Free") u.handleRollback(originalDir, 4, svcInfo) + return } - if err := files.CopyItem(false, true, path.Join(tmpDir, "GeoIP.mmdb"), path.Join(global.CONF.Base.InstallDir, "1panel/geo")); err != nil { + if err := files.CopyFileWithRename(path.Join(tmpDir, "GeoIP.mmdb"), path.Join(global.CONF.Base.InstallDir, "1panel/geo/GeoIP.mmdb")); err != nil { global.LOG.Warnf("Update GeoIP database failed: %v", err) _ = settingRepo.Update("SystemStatus", "Free") u.handleRollback(originalDir, 4, svcInfo) + return } global.LOG.Info("upgrade successful!") @@ -349,6 +358,19 @@ func analyzeDoc(version, content string) dto.ReleasesNotes { return item } +func checkUpgradeSpace() error { + dir := global.CONF.Base.InstallDir + var stat syscall.Statfs_t + if err := syscall.Statfs(dir, &stat); err != nil { + return err + } + avail := stat.Bavail * uint64(stat.Bsize) + if avail < minUpgradeFreeSpace { + return fmt.Errorf("available space of %s is %d MB, less than required 500MB", dir, avail>>20) + } + return nil +} + func (u *UpgradeService) handleBackup(originalDir string, svcInfo serviceInfo) error { if err := files.CopyItem(false, true, "/usr/local/bin/1panel-core", originalDir); err != nil { return err @@ -385,25 +407,25 @@ func (u *UpgradeService) handleRollback(originalDir string, errStep int, svcInfo global.LOG.Errorf("rollback 1panel db failed, err: %v", err) } } - if err := files.CopyItem(false, true, path.Join(originalDir, "1panel-core"), "/usr/local/bin"); err != nil { + if err := files.CopyFileWithRename(path.Join(originalDir, "1panel-core"), "/usr/local/bin/1panel-core"); err != nil { global.LOG.Errorf("rollback 1panel-core failed, err: %v", err) } - if err := files.CopyItem(false, true, path.Join(originalDir, "1panel-agent"), "/usr/local/bin"); err != nil { + if err := files.CopyFileWithRename(path.Join(originalDir, "1panel-agent"), "/usr/local/bin/1panel-agent"); err != nil { global.LOG.Errorf("rollback 1panel-agent failed, err: %v", err) } if errStep == 1 { return } - if err := files.CopyItem(false, true, path.Join(originalDir, "1pctl"), "/usr/local/bin"); err != nil { + if err := files.CopyFileWithRename(path.Join(originalDir, "1pctl"), "/usr/local/bin/1pctl"); err != nil { global.LOG.Errorf("rollback 1pctl failed, err: %v", err) } if errStep == 2 { return } - if err := files.CopyItem(false, true, path.Join(originalDir, svcInfo.coreName), svcInfo.basePath); err != nil { + if err := files.CopyFileWithRename(path.Join(originalDir, svcInfo.coreName), path.Join(svcInfo.basePath, svcInfo.coreName)); err != nil { global.LOG.Errorf("rollback %s failed, err: %v", svcInfo.coreName, err) } - if err := files.CopyItem(false, true, path.Join(originalDir, svcInfo.agentName), svcInfo.basePath); err != nil { + if err := files.CopyFileWithRename(path.Join(originalDir, svcInfo.agentName), path.Join(svcInfo.basePath, svcInfo.agentName)); err != nil { global.LOG.Errorf("rollback %s failed, err: %v", svcInfo.agentName, err) } if errStep == 3 { @@ -412,7 +434,7 @@ func (u *UpgradeService) handleRollback(originalDir string, errStep int, svcInfo if err := files.CopyItem(true, true, path.Join(originalDir, "lang"), "/usr/local/bin"); err != nil { global.LOG.Errorf("rollback language files failed, err: %v", err) } - if err := files.CopyItem(false, true, path.Join(originalDir, "GeoIP.mmdb"), path.Join(global.CONF.Base.InstallDir, "1panel/geo")); err != nil { + if err := files.CopyFileWithRename(path.Join(originalDir, "GeoIP.mmdb"), path.Join(global.CONF.Base.InstallDir, "1panel/geo/GeoIP.mmdb")); err != nil { global.LOG.Errorf("rollback GeoIP database failed, err: %v", err) } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index afb38b65554c..10ef4ef84c49 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,10 +21,10 @@ "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", "anser": "^2.3.2", - "axios": "^1.7.2", + "axios": "^1.16.0", "codemirror": "^6.0.2", "crypto-js": "^4.2.0", - "dompurify": "^3.3.1", + "dompurify": "^3.4.2", "echarts": "^5.5.0", "element-plus": "2.11.9", "js-base64": "^3.7.7", @@ -35,7 +35,7 @@ "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^1.6.1", "punycode": "^2.3.1", - "qs": "^6.12.1", + "qs": "^6.15.1", "screenfull": "^6.0.2", "sortablejs": "^1.15.7", "uuid": "^10.0.0", @@ -55,27 +55,27 @@ "@vitejs/plugin-vue-jsx": "^5.1.1", "@vue/compiler-sfc": "^3.5.32", "autoprefixer": "^10.4.7", - "commitizen": "^4.2.4", + "commitizen": "^4.3.1", "esbuild": "^0.27.3", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.2", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-vue": "^9.33.0", "lint-staged": "^16.4.0", - "postcss": "^8.4.31", - "postcss-html": "^1.4.1", + "postcss": "^8.5.14", + "postcss-html": "^1.8.1", "prettier": "^3.8.2", "rollup-plugin-visualizer": "^5.5.4", - "sass": "^1.83.0", + "sass": "^1.99.0", "standard-version": "^9.5.0", "tailwindcss": "^3.4.1", "typescript": "^5.9.2", - "unplugin-auto-import": "^0.16.4", - "unplugin-vue-components": "^0.25.0", - "vite": "^7.1.5", + "unplugin-auto-import": "^0.16.7", + "unplugin-vue-components": "^0.25.2", + "vite": "^7.3.2", "vite-plugin-compression": "^0.5.1", "vite-plugin-eslint2": "^5.0.3", - "vite-svg-loader": "^5.1.0", + "vite-svg-loader": "^5.1.1", "vue-tsc": "^2.2.12" } }, @@ -854,448 +854,6 @@ "vue": "^3.2.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -2369,16 +1927,6 @@ "win32" ] }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@types/conventional-commits-parser": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz", @@ -3372,14 +2920,14 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" } }, "node_modules/balanced-match": { @@ -4285,6 +3833,23 @@ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", "license": "MIT" }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -4644,9 +4209,9 @@ } }, "node_modules/dompurify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", - "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.2.tgz", + "integrity": "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -6068,9 +5633,9 @@ "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -6773,9 +6338,9 @@ } }, "node_modules/immutable": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", - "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", "dev": true, "license": "MIT" }, @@ -8932,9 +8497,9 @@ } }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "funding": [ { "type": "opencollective", @@ -8961,9 +8526,9 @@ } }, "node_modules/postcss-html": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.0.tgz", - "integrity": "sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.1.tgz", + "integrity": "sha512-OLF6P7qctfAWayOhLpcVnTGqVeJzu2W3WpIYelfz2+JV5oGxfkcEvweN9U4XpeqE0P98dcD9ssusGwlF0TK0uQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9139,10 +8704,13 @@ "license": "MIT" }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/punycode": { "version": "2.3.1", @@ -9166,9 +8734,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -9712,15 +9280,15 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.94.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", - "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", + "version": "1.99.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", + "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "chokidar": "^4.0.0", - "immutable": "^5.0.2", + "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -9733,6 +9301,16 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/screenfull": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz", @@ -10258,19 +9836,19 @@ } }, "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", + "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", "dev": true, "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", - "picocolors": "^1.0.0" + "picocolors": "^1.0.0", + "sax": "^1.5.0" }, "bin": { "svgo": "bin/svgo" @@ -10293,23 +9871,6 @@ "node": ">= 10" } }, - "node_modules/svgo/node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/synckit": { "version": "0.11.12", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", @@ -11141,14 +10702,14 @@ } }, "node_modules/vite": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", - "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "esbuild": "^0.25.0", + "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -11358,60 +10919,19 @@ } }, "node_modules/vite-svg-loader": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.0.tgz", - "integrity": "sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.1.tgz", + "integrity": "sha512-RPzcXA/EpKJA0585x58DBgs7my2VfeJ+j2j1EoHY4Zh82Y7hV4cR1fElgy2aZi85+QSrcLLoTStQ5uZjD68u+Q==", "dev": true, "license": "MIT", "dependencies": { - "svgo": "^3.0.2" + "debug": "^4.3.4", + "svgo": "^3.3.3" }, "peerDependencies": { "vue": ">=3.2.13" } }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, "node_modules/vite/node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 52237c15da5f..8f9e3864ce15 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,10 +33,10 @@ "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", "anser": "^2.3.2", - "axios": "^1.7.2", + "axios": "^1.16.0", "codemirror": "^6.0.2", "crypto-js": "^4.2.0", - "dompurify": "^3.3.1", + "dompurify": "^3.4.2", "echarts": "^5.5.0", "element-plus": "2.11.9", "js-base64": "^3.7.7", @@ -47,7 +47,7 @@ "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^1.6.1", "punycode": "^2.3.1", - "qs": "^6.12.1", + "qs": "^6.15.1", "screenfull": "^6.0.2", "sortablejs": "^1.15.7", "uuid": "^10.0.0", @@ -67,27 +67,27 @@ "@vitejs/plugin-vue-jsx": "^5.1.1", "@vue/compiler-sfc": "^3.5.32", "autoprefixer": "^10.4.7", - "commitizen": "^4.2.4", + "commitizen": "^4.3.1", "esbuild": "^0.27.3", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.2", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-vue": "^9.33.0", "lint-staged": "^16.4.0", - "postcss": "^8.4.31", - "postcss-html": "^1.4.1", + "postcss": "^8.5.14", + "postcss-html": "^1.8.1", "prettier": "^3.8.2", "rollup-plugin-visualizer": "^5.5.4", - "sass": "^1.83.0", + "sass": "^1.99.0", "standard-version": "^9.5.0", "tailwindcss": "^3.4.1", "typescript": "^5.9.2", - "unplugin-auto-import": "^0.16.4", - "unplugin-vue-components": "^0.25.0", - "vite": "^7.1.5", + "unplugin-auto-import": "^0.16.7", + "unplugin-vue-components": "^0.25.2", + "vite": "^7.3.2", "vite-plugin-compression": "^0.5.1", "vite-plugin-eslint2": "^5.0.3", - "vite-svg-loader": "^5.1.0", + "vite-svg-loader": "^5.1.1", "vue-tsc": "^2.2.12" }, "trustedDependencies": [ diff --git a/frontend/src/components/complex-table/index.vue b/frontend/src/components/complex-table/index.vue index 3579e28544ba..0256da295ed9 100644 --- a/frontend/src/components/complex-table/index.vue +++ b/frontend/src/components/complex-table/index.vue @@ -123,9 +123,12 @@ const rightClick = ref({ top: 0, currentRow: null, }); +const selectedRows = ref([]); const handleRightClick = (row, column, event) => { - clearSelects(); - tableRef.value.refElTable.toggleRowSelection(row); + if (!selectedRows.value.includes(row)) { + clearSelects(); + tableRef.value.refElTable.toggleRowSelection(row); + } if (!props.rightButtons) { return; } @@ -140,7 +143,6 @@ const handleRightClick = (row, column, event) => { }; const closeRightClick = () => { rightClick.value.visible = false; - clearSelects(); document.removeEventListener('click', closeRightClick); }; const disabled = computed(() => { @@ -178,6 +180,7 @@ function sizeChange() { } function handleSelectionChange(row: any) { + selectedRows.value = row; emit('update:selects', row); if (row.length > 0) { leftSelect.value = true; @@ -304,7 +307,7 @@ onMounted(() => { }); window.addEventListener('resize', calcHeight); watch( - () => props.height, + () => [props.height, props.heightDiff], () => { calcHeight(); }, diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 70b01787ae87..11a228955c3c 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -859,6 +859,36 @@ const message = { from_remote: 'This model was not downloaded via 1Panel, no related pull logs.', no_logs: 'The pull logs for this model have been deleted and cannot be viewed.', vllmVersionHelper: 'For FusionXpark GB 10 servers, please select the -cu130 version.', + downloader: 'Downloader', + modelDir: 'Model directory', + manualDownload: 'Manual Download', + modelSource: 'Model source', + repoID: 'Repository ID', + modelScopeEndpoint: 'ModelScope endpoint', + modelScopeToken: 'ModelScope Token (optional)', + hfToken: 'HF Token (optional)', + hfEndpoint: 'HuggingFace acceleration endpoint', + hfEndpointHelper: + 'Configure an acceleration endpoint when HuggingFace access is unstable. Search, details, and downloads will use it.', + searchModelScope: 'Search ModelScope', + searchHuggingFace: 'Search HuggingFace', + sortTrending: 'Trending', + sortLikes: 'Most liked', + sortDownloads: 'Most downloaded', + sortUpdated: 'Recently updated', + sortCreated: 'Newest', + downloadsCount: 'Downloads', + likes: 'Likes', + taskQueue: 'Download Queue', + progress: 'Progress', + localDownloaded: 'Downloaded', + modelInfo: 'Model Details', + modelCard: 'Model Card', + downloading: 'Downloading', + canceled: 'Canceled', + downloadQueued: 'Added to the download queue', + cancelTaskConfirm: 'Cancel this download task?', + deleteLocalModelConfirm: 'Delete local model {0}?', }, proxy: { proxy: 'AI Proxy Enhancement', @@ -2585,6 +2615,8 @@ const message = { app: 'Upgrade to Pro to view service details, anomaly monitoring, and more in the mobile app.', cluster: 'Upgrade to Pro Edition to manage MySQL/Postgres/Redis primary-replica clusters.', vllm: 'Upgrade to Pro Edition to manage vLLM services in 1Panel with centralized deployment, configuration, lifecycle operations, and task tracking.', + 'model-downloader': + 'Upgrade to Pro Edition to search, download, and manage local model files from HuggingFace, ModelScope, or compatible mirrors with centralized model directories and download tasks.', }, clean: { scan: 'Start scanning', @@ -4163,6 +4195,18 @@ const message = { vllmTitle4: 'Controlled Configuration', vllmContent4: 'Standardize ports, containers, startup commands, and Compose settings through advanced options for easier delivery and later adjustments.', + modelDownloaderTitle1: 'Model Discovery', + modelDownloaderContent1: + 'Search HuggingFace model repositories directly and view downloads, likes, model details, and file lists.', + modelDownloaderTitle2: 'Accelerated Downloads', + modelDownloaderContent2: + 'Configure a HuggingFace acceleration endpoint and HF Token for the official site, mirrors, and private models.', + modelDownloaderTitle3: 'Task Tracking', + modelDownloaderContent3: + 'Review download queues, task status, progress, and errors, with support for canceling, retrying, and clearing records.', + modelDownloaderTitle4: 'Local Management', + modelDownloaderContent4: + 'Manage local model directories in one place, including paths, sizes, timestamps, and deleting unused model files.', }, node: { master: 'Main Node', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 0bf9b2a76228..d21b78728b65 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -872,6 +872,36 @@ const message = { from_remote: 'Este modelo no fue descargado vía 1Panel, no hay registros de descarga relacionados.', no_logs: 'Los registros de descarga de este modelo han sido eliminados y no se pueden consultar.', vllmVersionHelper: 'Para servidores FusionXpark GB 10, seleccione la versión -cu130.', + downloader: 'Descargador', + modelDir: 'Directorio del modelo', + manualDownload: 'Descarga manual', + modelSource: 'Fuente del modelo', + repoID: 'ID del repositorio', + modelScopeEndpoint: 'Endpoint de ModelScope', + modelScopeToken: 'Token de ModelScope (opcional)', + hfToken: 'HF Token (opcional)', + hfEndpoint: 'Endpoint de aceleración de HuggingFace', + hfEndpointHelper: + 'Configure un endpoint de aceleración cuando el acceso a HuggingFace sea inestable. La búsqueda, los detalles y las descargas lo utilizarán.', + searchModelScope: 'Buscar en ModelScope', + searchHuggingFace: 'Buscar en HuggingFace', + sortTrending: 'Tendencias', + sortLikes: 'Más valorados', + sortDownloads: 'Más descargados', + sortUpdated: 'Actualizados recientemente', + sortCreated: 'Más recientes', + downloadsCount: 'Descargas', + likes: 'Me gusta', + taskQueue: 'Cola de descargas', + progress: 'Progreso', + localDownloaded: 'Descargados', + modelInfo: 'Detalles del modelo', + modelCard: 'Tarjeta del modelo', + downloading: 'Descargando', + canceled: 'Cancelado', + downloadQueued: 'Agregado a la cola de descargas', + cancelTaskConfirm: '¿Cancelar esta tarea de descarga?', + deleteLocalModelConfirm: '¿Eliminar el modelo local {0}?', }, proxy: { proxy: 'Mejoras de proxy de IA', @@ -2645,6 +2675,8 @@ const message = { fileExchange: 'Pro permite transferir archivos entre varios servidores.', app: 'Pro permite ver información de servicio, monitoreo, etc. vía app móvil.', cluster: 'Actualizar a la edición Pro permite gestionar clústeres primario-réplica de MySQL/Postgres/Redis.', + 'model-downloader': + 'Actualiza a la edición Pro para buscar, descargar y gestionar archivos de modelos locales desde HuggingFace, ModelScope o espejos compatibles, con directorios de modelos y tareas de descarga centralizados.', offLine: 'Sin conexión', }, clean: { @@ -4184,6 +4216,18 @@ const message = { clusterTitle3: 'Estado de Replicación', clusterContent3: 'Muestra estado de replicación maestro-esclavo y retrasos, ayudando a diagnosticar problemas de sincronización', + modelDownloaderTitle1: 'Descubrimiento de modelos', + modelDownloaderContent1: + 'Busca repositorios de modelos de HuggingFace directamente y consulta descargas, me gusta, detalles y archivos.', + modelDownloaderTitle2: 'Descargas aceleradas', + modelDownloaderContent2: + 'Configura un endpoint de aceleración de HuggingFace y HF Token para el sitio oficial, espejos y modelos privados.', + modelDownloaderTitle3: 'Seguimiento de tareas', + modelDownloaderContent3: + 'Consulta la cola, el estado, el progreso y los errores de descarga, con soporte para cancelar, reintentar y limpiar registros.', + modelDownloaderTitle4: 'Gestión local', + modelDownloaderContent4: + 'Gestiona directorios de modelos locales en un solo lugar, incluyendo rutas, tamaños, fechas y eliminación de modelos no usados.', }, node: { master: 'Nodo Principal', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 17e116b475e0..15f8df065467 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -863,6 +863,36 @@ const message = { from_remote: 'このモデルは1Panelを介してダウンロードされておらず、関連するプルログはありません。', no_logs: 'このモデルのプルログは削除されており、関連するログを表示できません。', vllmVersionHelper: 'FusionXpark GB 10 サーバーでは -cu130 バージョンを選択してください。', + downloader: 'ダウンローダー', + modelDir: 'モデルディレクトリ', + manualDownload: '手動ダウンロード', + modelSource: 'モデルソース', + repoID: 'リポジトリ ID', + modelScopeEndpoint: 'ModelScope エンドポイント', + modelScopeToken: 'ModelScope Token(任意)', + hfToken: 'HF Token(任意)', + hfEndpoint: 'HuggingFace アクセラレーションエンドポイント', + hfEndpointHelper: + 'HuggingFace へのアクセスが不安定な場合はアクセラレーションエンドポイントを設定してください。検索、詳細、ダウンロードで使用されます。', + searchModelScope: 'ModelScope を検索', + searchHuggingFace: 'HuggingFace を検索', + sortTrending: 'トレンド', + sortLikes: 'いいね順', + sortDownloads: 'ダウンロード数順', + sortUpdated: '最近更新', + sortCreated: '新規作成', + downloadsCount: 'ダウンロード数', + likes: 'いいね', + taskQueue: 'ダウンロードキュー', + progress: '進捗', + localDownloaded: 'ダウンロード済み', + modelInfo: 'モデル詳細', + modelCard: 'モデルカード', + downloading: 'ダウンロード中', + canceled: 'キャンセル済み', + downloadQueued: 'ダウンロードキューに追加しました', + cancelTaskConfirm: 'このダウンロードタスクをキャンセルしますか?', + deleteLocalModelConfirm: 'ローカルモデル {0} を削除しますか?', }, proxy: { proxy: 'AI プロキシ強化', @@ -2601,6 +2631,8 @@ const message = { 'Pro Edition にアップグレードすると、マルチノードのアプリ、Web サイト、データベース、スケジュールタスクを一元管理できます。', fileExchange: 'Pro Edition にアップグレードすると、複数サーバー間でファイルをすばやく転送できます。', cluster: 'Pro Edition にアップグレードすると、MySQL/Postgres/Redis のプライマリ/レプリカ構成を管理できます。', + 'model-downloader': + 'Pro にアップグレードすると、HuggingFace、ModelScope、または互換ミラーからローカルモデルファイルを検索、ダウンロード、管理できます。モデルディレクトリとダウンロードタスクを一元管理できます。', exceptionalHelper: 'ライセンス同期検証が異常です。手動で同期して機能を確認してください。詳細: ', tamperHelper: '操作失敗。ファイル/フォルダの改ざん保護を確認してください。', }, @@ -4171,6 +4203,18 @@ const message = { clusterTitle3: 'レプリケーション状態', clusterContent3: 'マスタースレーブレプリケーション状態と遅延情報を表示し、同期の問題を解決するのに役立ちます', + modelDownloaderTitle1: 'モデル検索', + modelDownloaderContent1: + 'HuggingFace のモデルリポジトリを直接検索し、ダウンロード数、いいね、モデル詳細、ファイル一覧を確認できます。', + modelDownloaderTitle2: '高速ダウンロード', + modelDownloaderContent2: + 'HuggingFace アクセラレーションエンドポイントと HF Token を設定し、公式サイト、ミラー、プライベートモデルに対応します。', + modelDownloaderTitle3: 'タスク追跡', + modelDownloaderContent3: + 'ダウンロードキュー、タスク状態、進捗、エラーを一元確認し、キャンセル、再試行、記録の削除を行えます。', + modelDownloaderTitle4: 'ローカル管理', + modelDownloaderContent4: + 'ローカルモデルディレクトリを一元管理し、パス、サイズ、日時の確認と不要なモデルファイルの削除を行えます。', }, node: { master: '主ノード', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index b9374ba32cad..6658799c8e86 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -847,6 +847,36 @@ const message = { from_remote: '이 모델은 1Panel을 통해 다운로드되지 않았으며 관련 풀 로그가 없습니다.', no_logs: '이 모델의 풀 로그가 삭제되어 관련 로그를 볼 수 없습니다.', vllmVersionHelper: 'FusionXpark GB 10 서버는 -cu130 버전을 선택하세요.', + downloader: '다운로더', + modelDir: '모델 디렉토리', + manualDownload: '수동 다운로드', + modelSource: '모델 소스', + repoID: '저장소 ID', + modelScopeEndpoint: 'ModelScope 엔드포인트', + modelScopeToken: 'ModelScope Token (선택)', + hfToken: 'HF Token (선택 사항)', + hfEndpoint: 'HuggingFace 가속 엔드포인트', + hfEndpointHelper: + 'HuggingFace 접속이 불안정할 때 가속 엔드포인트를 설정하세요. 검색, 상세 정보, 다운로드에 사용됩니다.', + searchModelScope: 'ModelScope 검색', + searchHuggingFace: 'HuggingFace 검색', + sortTrending: '인기 트렌드', + sortLikes: '좋아요 많은 순', + sortDownloads: '다운로드 많은 순', + sortUpdated: '최근 업데이트', + sortCreated: '최신 생성', + downloadsCount: '다운로드 수', + likes: '좋아요', + taskQueue: '다운로드 대기열', + progress: '진행률', + localDownloaded: '다운로드됨', + modelInfo: '모델 상세 정보', + modelCard: '모델 카드', + downloading: '다운로드 중', + canceled: '취소됨', + downloadQueued: '다운로드 대기열에 추가되었습니다', + cancelTaskConfirm: '이 다운로드 작업을 취소하시겠습니까?', + deleteLocalModelConfirm: '로컬 모델 {0}을(를) 삭제하시겠습니까?', }, proxy: { proxy: 'AI 프록시 강화', @@ -2539,6 +2569,8 @@ const message = { fileExchange: 'Pro Edition으로 업그레이드하면 여러 서버 간에 파일을 빠르게 전송할 수 있습니다.', app: 'Pro로 업그레이드하면 모바일 앱에서 서비스 정보와 이상 모니터링을 확인할 수 있습니다.', cluster: 'Pro Edition으로 업그레이드하면 MySQL/Postgres/Redis 기본-복제 클러스터를 관리할 수 있습니다.', + 'model-downloader': + 'Pro로 업그레이드하면 HuggingFace, ModelScope 또는 호환 미러에서 로컬 모델 파일을 검색, 다운로드 및 관리하고 모델 디렉터리와 다운로드 작업을 통합 관리할 수 있습니다.', exceptionalHelper: '라이선스 동기화 검증이 비정상입니다. 수동으로 동기화해 Pro 기능이 정상적으로 동작하는지 확인하세요. 상세: ', tamperHelper: '작업 실패, 파일/폴더에 변조 방지가 활성화되어 있습니다. 확인 후 다시 시도하세요!', @@ -4074,6 +4106,18 @@ const message = { clusterTitle3: '복제 상태', clusterContent3: '마스터-슬레이브 복제 상태 및 지연 정보를 표시하여 동기화 문제를 해결하는 데 도움을 줍니다', + modelDownloaderTitle1: '모델 탐색', + modelDownloaderContent1: + 'HuggingFace 모델 저장소를 직접 검색하고 다운로드 수, 좋아요, 모델 상세 정보, 파일 목록을 확인합니다.', + modelDownloaderTitle2: '가속 다운로드', + modelDownloaderContent2: + 'HuggingFace 가속 엔드포인트와 HF Token을 설정하여 공식 사이트, 미러, 비공개 모델에 대응합니다.', + modelDownloaderTitle3: '작업 추적', + modelDownloaderContent3: + '다운로드 대기열, 작업 상태, 진행률, 오류를 한곳에서 확인하고 취소, 재시도, 기록 정리를 지원합니다.', + modelDownloaderTitle4: '로컬 관리', + modelDownloaderContent4: + '로컬 모델 디렉토리를 중앙에서 관리하고 경로, 크기, 시간을 확인하며 불필요한 모델 파일을 삭제합니다.', }, node: { master: '주 노드', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 5dd2c3b38bdd..c8fd924dcf77 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -872,6 +872,36 @@ const message = { from_remote: 'Este modelo não foi baixado via 1Panel, sem logs de pull relacionados.', no_logs: 'Os logs de pull deste modelo foram excluídos e não podem ser visualizados.', vllmVersionHelper: 'Untuk pelayan FusionXpark GB 10, sila pilih versi -cu130.', + downloader: 'Pemuat Turun', + modelDir: 'Direktori Model', + manualDownload: 'Muat Turun Manual', + modelSource: 'Sumber model', + repoID: 'ID Repositori', + modelScopeEndpoint: 'Endpoint ModelScope', + modelScopeToken: 'Token ModelScope (pilihan)', + hfToken: 'HF Token (pilihan)', + hfEndpoint: 'Titik akhir pecutan HuggingFace', + hfEndpointHelper: + 'Konfigurasikan titik akhir pecutan apabila akses HuggingFace tidak stabil. Carian, butiran dan muat turun akan menggunakannya.', + searchModelScope: 'Cari ModelScope', + searchHuggingFace: 'Cari HuggingFace', + sortTrending: 'Trend popular', + sortLikes: 'Paling disukai', + sortDownloads: 'Paling banyak dimuat turun', + sortUpdated: 'Dikemas kini baru-baru ini', + sortCreated: 'Terbaru dibuat', + downloadsCount: 'Muat turun', + likes: 'Suka', + taskQueue: 'Baris Gilir Muat Turun', + progress: 'Kemajuan', + localDownloaded: 'Dimuat turun', + modelInfo: 'Butiran Model', + modelCard: 'Kad Model', + downloading: 'Sedang memuat turun', + canceled: 'Dibatalkan', + downloadQueued: 'Ditambah ke baris gilir muat turun', + cancelTaskConfirm: 'Batalkan tugas muat turun ini?', + deleteLocalModelConfirm: 'Padam model tempatan {0}?', }, proxy: { proxy: 'Peningkatan Proksi AI', @@ -2648,6 +2678,8 @@ const message = { fileExchange: 'Naik taraf ke Edisi Pro untuk memindahkan fail dengan pantas antara pelayan.', app: 'Naik taraf ke Edisi Pro untuk melihat maklumat perkhidmatan dan pemantauan anomali dalam aplikasi mudah alih.', cluster: 'Naik taraf ke Edisi Pro untuk mengurus kelompok utama-replika MySQL/Postgres/Redis.', + 'model-downloader': + 'Naik taraf kepada edisi Pro untuk mencari, memuat turun dan mengurus fail model tempatan daripada HuggingFace, ModelScope atau cermin serasi, dengan direktori model dan tugasan muat turun berpusat.', exceptionalHelper: 'Pengesahan penyegerakan lesen tidak normal. Klik butang sync secara manual untuk pastikan fungsi Pro berjalan baik. butiran: ', tamperHelper: 'Operasi gagal, fail atau folder mempunyai perlindungan gangguan. Sila semak dan cuba lagi!', @@ -4233,6 +4265,18 @@ const message = { clusterTitle3: 'Status Replikasi', clusterContent3: 'Memaparkan status replikasi utama-hamba dan maklumat kelewatan, membantu menyelesaikan masalah sinkronisasi', + modelDownloaderTitle1: 'Penemuan Model', + modelDownloaderContent1: + 'Cari repositori model HuggingFace secara langsung dan lihat muat turun, suka, butiran model dan senarai fail.', + modelDownloaderTitle2: 'Muat Turun Dipercepat', + modelDownloaderContent2: + 'Konfigurasikan titik akhir pecutan HuggingFace dan HF Token untuk laman rasmi, cermin dan model persendirian.', + modelDownloaderTitle3: 'Penjejakan Tugas', + modelDownloaderContent3: + 'Semak baris gilir muat turun, status tugas, kemajuan dan ralat, dengan sokongan batal, cuba semula dan bersihkan rekod.', + modelDownloaderTitle4: 'Pengurusan Tempatan', + modelDownloaderContent4: + 'Urus direktori model tempatan di satu tempat, termasuk laluan, saiz, masa dan pemadaman fail model yang tidak digunakan.', }, node: { master: 'Nod Utama', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index bf3b14a38cb9..d261f0e719a0 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -868,6 +868,36 @@ const message = { from_remote: 'Model ini tidak dimuat turun melalui 1Panel, tiada log pengambilan berkaitan.', no_logs: 'Log pengambilan untuk model ini telah dipadam dan tidak dapat dilihat.', vllmVersionHelper: 'Para servidores FusionXpark GB 10, selecione a versão -cu130.', + downloader: 'Baixador', + modelDir: 'Diretório do modelo', + manualDownload: 'Download manual', + modelSource: 'Fonte do modelo', + repoID: 'ID do repositório', + modelScopeEndpoint: 'Endpoint do ModelScope', + modelScopeToken: 'Token do ModelScope (opcional)', + hfToken: 'HF Token (opcional)', + hfEndpoint: 'Endpoint de aceleração do HuggingFace', + hfEndpointHelper: + 'Configure um endpoint de aceleração quando o acesso ao HuggingFace estiver instável. A pesquisa, os detalhes e os downloads usarão esse endereço.', + searchModelScope: 'Pesquisar no ModelScope', + searchHuggingFace: 'Pesquisar no HuggingFace', + sortTrending: 'Tendências', + sortLikes: 'Mais curtidos', + sortDownloads: 'Mais baixados', + sortUpdated: 'Atualizados recentemente', + sortCreated: 'Criados recentemente', + downloadsCount: 'Downloads', + likes: 'Curtidas', + taskQueue: 'Fila de downloads', + progress: 'Progresso', + localDownloaded: 'Baixados', + modelInfo: 'Detalhes do modelo', + modelCard: 'Cartão do modelo', + downloading: 'Baixando', + canceled: 'Cancelado', + downloadQueued: 'Adicionado à fila de downloads', + cancelTaskConfirm: 'Cancelar esta tarefa de download?', + deleteLocalModelConfirm: 'Excluir o modelo local {0}?', }, proxy: { proxy: 'Melhoria de Proxy AI', @@ -2770,6 +2800,8 @@ const message = { fileExchange: 'Atualize para a edição Pro para transferir arquivos rapidamente entre vários servidores.', app: 'Faça upgrade para a edição Pro para visualizar informações do serviço e monitoramento de anomalias no aplicativo móvel.', cluster: 'Atualize para a edição Pro para gerenciar clusters primário-réplica de MySQL/Postgres/Redis.', + 'model-downloader': + 'Atualize para a edição Pro para pesquisar, baixar e gerenciar arquivos de modelos locais do HuggingFace, ModelScope ou mirrors compatíveis, com diretórios de modelos e tarefas de download centralizados.', exceptionalHelper: 'A verificação da sincronização da licença está anormal. Clique em sincronizar manualmente para garantir o funcionamento da edição Pro. detalhe: ', tamperHelper: @@ -4378,6 +4410,18 @@ const message = { clusterTitle3: 'Estado de Replicação', clusterContent3: 'Exibe o estado de replicação mestre-escravo e informações de atraso, auxiliando na resolução de problemas de sincronização', + modelDownloaderTitle1: 'Descoberta de modelos', + modelDownloaderContent1: + 'Pesquise repositórios de modelos do HuggingFace diretamente e veja downloads, curtidas, detalhes do modelo e lista de arquivos.', + modelDownloaderTitle2: 'Downloads acelerados', + modelDownloaderContent2: + 'Configure um endpoint de aceleração do HuggingFace e HF Token para o site oficial, espelhos e modelos privados.', + modelDownloaderTitle3: 'Acompanhamento de tarefas', + modelDownloaderContent3: + 'Veja filas de download, status, progresso e erros, com suporte para cancelar, tentar novamente e limpar registros.', + modelDownloaderTitle4: 'Gerenciamento local', + modelDownloaderContent4: + 'Gerencie diretórios de modelos locais em um só lugar, incluindo caminhos, tamanhos, horários e exclusão de modelos não usados.', }, node: { master: 'Nó Principal', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index d91362fbc053..f8232eede5aa 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -862,6 +862,36 @@ const message = { from_remote: 'Эта модель не была загружена через 1Panel, нет связанных журналов извлечения.', no_logs: 'Журналы извлечения для этой модели были удалены и не могут быть просмотрены.', vllmVersionHelper: 'Для серверов FusionXpark GB 10 выберите версию -cu130.', + downloader: 'Загрузчик', + modelDir: 'Каталог модели', + manualDownload: 'Ручная загрузка', + modelSource: 'Источник модели', + repoID: 'ID репозитория', + modelScopeEndpoint: 'Endpoint ModelScope', + modelScopeToken: 'ModelScope Token (необязательно)', + hfToken: 'HF Token (необязательно)', + hfEndpoint: 'Endpoint ускорения HuggingFace', + hfEndpointHelper: + 'Настройте endpoint ускорения, если доступ к HuggingFace нестабилен. Он будет использоваться для поиска, деталей и загрузок.', + searchModelScope: 'Поиск в ModelScope', + searchHuggingFace: 'Поиск в HuggingFace', + sortTrending: 'Популярное', + sortLikes: 'Больше всего лайков', + sortDownloads: 'Больше всего загрузок', + sortUpdated: 'Недавно обновлено', + sortCreated: 'Недавно создано', + downloadsCount: 'Загрузки', + likes: 'Лайки', + taskQueue: 'Очередь загрузок', + progress: 'Прогресс', + localDownloaded: 'Загружено', + modelInfo: 'Сведения о модели', + modelCard: 'Карточка модели', + downloading: 'Загрузка', + canceled: 'Отменено', + downloadQueued: 'Добавлено в очередь загрузок', + cancelTaskConfirm: 'Отменить эту задачу загрузки?', + deleteLocalModelConfirm: 'Удалить локальную модель {0}?', }, proxy: { proxy: 'Усиление AI-прокси', @@ -2632,6 +2662,8 @@ const message = { app: 'Обновите до профессиональной версии, чтобы просматривать информацию о сервисах, мониторинг аномалий и т.д. через мобильное приложение.', cluster: 'Обновление до профессиональной версии позволяет управлять кластерами мастер-слейв MySQL/Postgres/Redis.', + 'model-downloader': + 'Обновите до Pro, чтобы искать, загружать и управлять локальными файлами моделей из HuggingFace, ModelScope или совместимых зеркал, централизованно управляя каталогами моделей и задачами загрузки.', exceptionalHelper: 'Проверка синхронизации лицензии выполнена с ошибкой. Нажмите синхронизацию вручную для корректной работы Pro. Детали: ', tamperHelper: 'Операция не выполнена: для файла или папки включена защита от изменений. Проверьте и повторите!', @@ -4227,6 +4259,18 @@ const message = { clusterTitle3: 'Состояние репликации', clusterContent3: 'Отображает состояние репликации мастер-слейв и информацию о задержке, помогая в устранении проблем синхронизации', + modelDownloaderTitle1: 'Поиск моделей', + modelDownloaderContent1: + 'Ищите репозитории моделей HuggingFace напрямую и просматривайте загрузки, лайки, сведения о модели и список файлов.', + modelDownloaderTitle2: 'Ускоренные загрузки', + modelDownloaderContent2: + 'Настройте endpoint ускорения HuggingFace и HF Token для официального сайта, зеркал и приватных моделей.', + modelDownloaderTitle3: 'Отслеживание задач', + modelDownloaderContent3: + 'Просматривайте очередь загрузок, статус, прогресс и ошибки, отменяйте, повторяйте и очищайте записи.', + modelDownloaderTitle4: 'Локальное управление', + modelDownloaderContent4: + 'Управляйте локальными каталогами моделей в одном месте: пути, размеры, время и удаление неиспользуемых файлов моделей.', }, node: { master: 'Главный узел', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 6635009635f1..1e924cd7a604 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -870,6 +870,36 @@ const message = { from_remote: 'Bu model 1Panel aracılığıyla indirilmedi, ilgili çekme logları yok.', no_logs: 'Bu modelin çekme logları silindi ve görüntülenemiyor.', vllmVersionHelper: 'FusionXpark GB 10 sunucuları için lütfen -cu130 sürümünü seçin.', + downloader: 'İndirici', + modelDir: 'Model dizini', + manualDownload: 'Manuel indirme', + modelSource: 'Model kaynağı', + repoID: 'Depo ID', + modelScopeEndpoint: 'ModelScope endpointi', + modelScopeToken: 'ModelScope Token (isteğe bağlı)', + hfToken: 'HF Token (isteğe bağlı)', + hfEndpoint: 'HuggingFace hızlandırma endpointi', + hfEndpointHelper: + 'HuggingFace erişimi kararsız olduğunda bir hızlandırma endpointi yapılandırın. Arama, ayrıntılar ve indirmeler bunu kullanır.', + searchModelScope: 'ModelScope ara', + searchHuggingFace: 'HuggingFace ara', + sortTrending: 'Popüler trendler', + sortLikes: 'En çok beğenilen', + sortDownloads: 'En çok indirilenler', + sortUpdated: 'Son güncellenenler', + sortCreated: 'Yeni oluşturulanlar', + downloadsCount: 'İndirmeler', + likes: 'Beğeniler', + taskQueue: 'İndirme Kuyruğu', + progress: 'İlerleme', + localDownloaded: 'İndirilenler', + modelInfo: 'Model ayrıntıları', + modelCard: 'Model kartı', + downloading: 'İndiriliyor', + canceled: 'İptal edildi', + downloadQueued: 'İndirme kuyruğuna eklendi', + cancelTaskConfirm: 'Bu indirme görevi iptal edilsin mi?', + deleteLocalModelConfirm: 'Yerel model {0} silinsin mi?', }, proxy: { proxy: 'AI Proxy Geliştirmesi', @@ -2638,6 +2668,8 @@ const message = { fileExchange: 'Pro Sürüme yükselterek birden fazla sunucu arasında dosyaları hızlıca aktarabilirsiniz.', app: 'Pro sürüme yükselterek mobil uygulamada hizmet bilgilerini ve anormallik izlemeyi görüntüleyebilirsiniz.', cluster: 'Pro Sürüme yükselterek MySQL/Postgres/Redis birincil-kopya kümelerini yönetebilirsiniz.', + 'model-downloader': + 'Pro sürümüne yükselterek HuggingFace, ModelScope veya uyumlu aynalardan yerel model dosyalarını arayabilir, indirebilir ve yönetebilir; model dizinlerini ve indirme görevlerini merkezi olarak takip edebilirsiniz.', }, clean: { scan: 'Taramayı başlat', @@ -4226,6 +4258,18 @@ const message = { clusterTitle3: 'Çoğaltma Durumu', clusterContent3: 'Ana-çalışan çoğaltma durumunu ve gecikme bilgilerini görüntüleyerek senkronizasyon sorunlarını gidermeye yardımcı olur', + modelDownloaderTitle1: 'Model Keşfi', + modelDownloaderContent1: + 'HuggingFace model depolarını doğrudan arayın; indirme sayısı, beğeni, model ayrıntıları ve dosya listesini görüntüleyin.', + modelDownloaderTitle2: 'Hızlandırılmış İndirme', + modelDownloaderContent2: + 'Resmi site, aynalar ve özel modeller için HuggingFace hızlandırma endpointi ve HF Token yapılandırın.', + modelDownloaderTitle3: 'Görev Takibi', + modelDownloaderContent3: + 'İndirme kuyruğunu, görev durumunu, ilerlemeyi ve hataları görüntüleyin; iptal, yeniden deneme ve kayıt temizlemeyi destekler.', + modelDownloaderTitle4: 'Yerel Yönetim', + modelDownloaderContent4: + 'Yerel model dizinlerini tek yerden yönetin; yolları, boyutları, zamanları görüntüleyin ve kullanılmayan model dosyalarını silin.', }, node: { master: 'Ana Düğüm', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 6243dccfeb28..690982cbb7f1 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -811,6 +811,35 @@ const message = { from_remote: '該模型並非透過 1Panel 下載,無相關拉取日誌。', no_logs: '該模型的拉取日誌已被刪除,無法檢視相關日誌。', vllmVersionHelper: 'FusionXpark GB 10 伺服器請選擇 -cu130 版本', + downloader: '下載器', + modelDir: '模型目錄', + manualDownload: '手動下載', + modelSource: '模型來源', + repoID: '倉庫 ID', + modelScopeEndpoint: 'ModelScope 地址', + modelScopeToken: 'ModelScope Token(可選)', + hfToken: 'HF Token(可選)', + hfEndpoint: 'HuggingFace 加速地址', + hfEndpointHelper: '國內存取 HuggingFace 不穩定時可設定加速地址,搜尋、詳情和下載都會使用該地址。', + searchModelScope: '搜尋 ModelScope', + searchHuggingFace: '搜尋 HuggingFace', + sortTrending: '熱門趨勢', + sortLikes: '按讚最多', + sortDownloads: '下載最多', + sortUpdated: '最近更新', + sortCreated: '最新建立', + downloadsCount: '下載量', + likes: '點讚', + taskQueue: '下載佇列', + progress: '進度', + localDownloaded: '已下載', + modelInfo: '模型詳情', + modelCard: '模型卡片', + downloading: '下載中', + canceled: '已取消', + downloadQueued: '已加入下載佇列', + cancelTaskConfirm: '確認取消該下載任務?', + deleteLocalModelConfirm: '確認刪除本地模型 {0}?', }, proxy: { proxy: 'AI 代理增強', @@ -2394,6 +2423,8 @@ const message = { app: '升級專業版可透過手機APP,檢視服務資訊、異常監控等。', cluster: '升級專業版可以管理 MySQL/Postgres/Redis 主從叢集。', vllm: '升級專業版可將 vLLM 服務統一納入 1Panel 管理,集中完成部署、設定調整、狀態維運與任務追蹤,降低本地模型服務維護成本。', + 'model-downloader': + '升級專業版可從 HuggingFace、ModelScope 或相容鏡像站搜尋、下載並管理本機模型檔案,統一維護本機模型目錄與下載任務。', }, clean: { scan: '開始掃描', @@ -3835,6 +3866,14 @@ const message = { vllmContent3: '在同一頁面完成建立、編輯、啟停、重啟、刪除與任務追蹤,提升本地模型服務維護效率。', vllmTitle4: '設定可控', vllmContent4: '結合進階設定統一管理連接埠、容器、啟動命令與 Compose 設定,便於標準化交付與後續調整。', + modelDownloaderTitle1: '模型發現', + modelDownloaderContent1: '直接搜尋 HuggingFace 模型倉庫,檢視下載量、點讚數、模型詳情與檔案列表。', + modelDownloaderTitle2: '加速下載', + modelDownloaderContent2: '支援設定 HuggingFace 加速地址與 HF Token,適配官方站、鏡像站和私有模型存取。', + modelDownloaderTitle3: '任務追蹤', + modelDownloaderContent3: '統一檢視下載佇列、任務狀態、進度、錯誤資訊,並支援取消、重試和記錄清理。', + modelDownloaderTitle4: '本地管理', + modelDownloaderContent4: '集中維護本地模型目錄,檢視模型路徑、大小和時間,支援刪除不再需要的模型檔案。', }, node: { master: '主節點', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 267b6389b197..6c5cd6ddb433 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -805,6 +805,35 @@ const message = { from_remote: '该模型并非通过 1Panel 下载,无相关拉取日志。', no_logs: '该模型的拉取日志已被删除,无法查看相关日志。', vllmVersionHelper: 'FusionXpark GB 10 服务器请选择 -cu130 版本', + downloader: '下载器', + modelDir: '模型目录', + manualDownload: '手动下载', + modelSource: '模型来源', + repoID: '仓库 ID', + modelScopeEndpoint: 'ModelScope 地址', + modelScopeToken: 'ModelScope Token(可选)', + hfToken: 'HF Token(可选)', + hfEndpoint: 'HuggingFace 加速地址', + hfEndpointHelper: '国内访问 HuggingFace 不稳定时可配置加速地址,搜索、详情和下载都会使用该地址。', + searchModelScope: '搜索 ModelScope', + searchHuggingFace: '搜索 HuggingFace', + sortTrending: '热门趋势', + sortLikes: '点赞最多', + sortDownloads: '下载最多', + sortUpdated: '最近更新', + sortCreated: '最新创建', + downloadsCount: '下载量', + likes: '点赞', + taskQueue: '下载队列', + progress: '进度', + localDownloaded: '已下载', + modelInfo: '模型详情', + modelCard: '模型卡片', + downloading: '下载中', + canceled: '已取消', + downloadQueued: '已加入下载队列', + cancelTaskConfirm: '确认取消该下载任务?', + deleteLocalModelConfirm: '确认删除本地模型 {0}?', }, proxy: { proxy: 'AI 代理增强', @@ -2387,6 +2416,8 @@ const message = { app: '升级专业版可通过手机 APP,查看服务信息、异常监控等。', cluster: '升级专业版可以管理 MySQL/Postgres/Redis 主从集群。', vllm: '升级专业版可将 vLLM 服务统一纳入 1Panel 管理,集中完成部署、配置调整、状态运维与任务追踪,降低本地模型服务维护成本。', + 'model-downloader': + '升级专业版可从 HuggingFace、ModelScope 或兼容镜像站搜索、下载并管理本地模型文件,统一维护本地模型目录与下载任务。', }, clean: { scan: '开始扫描', @@ -3830,6 +3861,14 @@ const message = { vllmContent3: '在同一页面完成创建、编辑、启停、重启、删除与任务追踪,提升本地模型服务维护效率。', vllmTitle4: '配置可控', vllmContent4: '结合高级设置统一管理端口、容器、启动命令与 Compose 配置,便于标准化交付与后续调整。', + modelDownloaderTitle1: '模型发现', + modelDownloaderContent1: '直接搜索 HuggingFace 模型仓库,查看下载量、点赞数、模型详情与文件列表。', + modelDownloaderTitle2: '加速下载', + modelDownloaderContent2: '支持配置 HuggingFace 加速地址与 HF Token,适配官方站、镜像站和私有模型访问。', + modelDownloaderTitle3: '任务追踪', + modelDownloaderContent3: '统一查看下载队列、任务状态、进度、错误信息,并支持取消、重试和记录清理。', + modelDownloaderTitle4: '本地管理', + modelDownloaderContent4: '集中维护本地模型目录,查看模型路径、大小和时间,支持删除不再需要的模型文件。', }, node: { master: '主节点', diff --git a/frontend/src/views/ai/model/local/index.vue b/frontend/src/views/ai/model/local/index.vue index de1c22a2f9f8..147b89943688 100644 --- a/frontend/src/views/ai/model/local/index.vue +++ b/frontend/src/views/ai/model/local/index.vue @@ -28,10 +28,14 @@ import { useRoute, useRouter } from 'vue-router'; import OllamaView from '@/views/ai/model/ollama/index.vue'; import TensorRTView from '@/views/ai/model/tensorrt/index.vue'; import { loadOptionalComponent } from '@/extensions/optional'; +import i18n from '@/lang'; const VllmView = defineAsyncComponent(() => loadOptionalComponent('/src/xpack/views/vllm/index.vue')); +const ModelDownloaderView = defineAsyncComponent(() => + loadOptionalComponent('/src/xpack/views/model-downloader/index.vue'), +); -type LocalTab = 'ollama' | 'vllm' | 'tensorrt'; +type LocalTab = 'ollama' | 'vllm' | 'tensorrt' | 'downloader'; const route = useRoute(); const router = useRouter(); @@ -40,17 +44,19 @@ const tabLabels: Record = { ollama: 'Ollama', vllm: 'vLLM', tensorrt: 'TensorRT LLM', + downloader: i18n.global.t('aiTools.model.downloader'), }; -const buttons = [ +const buttons: Array<{ label: string; value: LocalTab }> = [ { label: tabLabels.ollama, value: 'ollama' }, { label: tabLabels.vllm, value: 'vllm' }, { label: tabLabels.tensorrt, value: 'tensorrt' }, + { label: tabLabels.downloader, value: 'downloader' }, ]; const currentTab = computed(() => { const tab = route.query.tab; - if (tab === 'vllm' || tab === 'tensorrt') { + if (tab === 'vllm' || tab === 'tensorrt' || tab === 'downloader') { return tab; } return 'ollama'; @@ -62,6 +68,8 @@ const currentComponent = computed(() => { return VllmView; case 'tensorrt': return TensorRTView; + case 'downloader': + return ModelDownloaderView; default: return OllamaView; } diff --git a/frontend/src/views/host/file-management/code-editor/history/index.vue b/frontend/src/views/host/file-management/code-editor/history/index.vue index 883ebd246e9f..cb59665cef86 100644 --- a/frontend/src/views/host/file-management/code-editor/history/index.vue +++ b/frontend/src/views/host/file-management/code-editor/history/index.vue @@ -62,7 +62,11 @@