diff --git a/backend/app/api/v1/backup.go b/backend/app/api/v1/backup.go index d1334d7d9..c54b57c2a 100644 --- a/backend/app/api/v1/backup.go +++ b/backend/app/api/v1/backup.go @@ -175,7 +175,7 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } - c.File(filePath) + helper.SuccessWithData(c, filePath) } // @Tags Backup Account diff --git a/backend/app/api/v1/cronjob.go b/backend/app/api/v1/cronjob.go index 16896b1b6..a53bc7f25 100644 --- a/backend/app/api/v1/cronjob.go +++ b/backend/app/api/v1/cronjob.go @@ -198,7 +198,7 @@ func (b *BaseApi) TargetDownload(c *gin.Context) { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } - c.File(filePath) + helper.SuccessWithData(c, filePath) } // @Tags Cronjob diff --git a/backend/app/api/v1/file.go b/backend/app/api/v1/file.go index c0ff30769..dac795cd3 100644 --- a/backend/app/api/v1/file.go +++ b/backend/app/api/v1/file.go @@ -445,6 +445,28 @@ func (b *BaseApi) Download(c *gin.Context) { c.File(filePath) } +// @Tags File +// @Summary Download file with path +// @Description 下载指定文件 +// @Accept json +// @Param request body request.FilePath true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /files/download/bypath [post] +// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [path]","formatEN":"Download file [path]"} +func (b *BaseApi) DownloadFile(c *gin.Context) { + var req dto.FilePath + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + c.File(req.Path) +} + // @Tags File // @Summary Load file size // @Description 获取文件夹大小 diff --git a/backend/app/repo/cronjob.go b/backend/app/repo/cronjob.go index daa53f167..b950b12f4 100644 --- a/backend/app/repo/cronjob.go +++ b/backend/app/repo/cronjob.go @@ -24,7 +24,7 @@ type ICronjobRepo interface { Update(id uint, vars map[string]interface{}) error Delete(opts ...DBOption) error DeleteRecord(opts ...DBOption) error - StartRecords(cronjobID uint, targetPath string) model.JobRecords + StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords EndRecords(record model.JobRecords, status, message, records string) } @@ -112,10 +112,11 @@ func (c *CronjobRepo) WithByJobID(id int) DBOption { } } -func (u *CronjobRepo) StartRecords(cronjobID uint, targetPath string) model.JobRecords { +func (u *CronjobRepo) StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords { var record model.JobRecords record.StartTime = time.Now() record.CronjobID = cronjobID + record.FromLocal = fromLocal record.Status = constant.StatusWaiting if err := global.DB.Create(&record).Error; err != nil { global.LOG.Errorf("create record status failed, err: %v", err) diff --git a/backend/app/service/cornjob.go b/backend/app/service/cornjob.go index 712d07217..e601d809a 100644 --- a/backend/app/service/cornjob.go +++ b/backend/app/service/cornjob.go @@ -2,17 +2,14 @@ package service import ( "bufio" - "encoding/json" "fmt" "os" - "strings" "time" "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" - "github.com/1Panel-dev/1Panel/backend/utils/cloud_storage" "github.com/jinzhu/copier" "github.com/pkg/errors" "github.com/robfig/cron/v3" @@ -92,69 +89,28 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) { if cronjob.ID == 0 { return "", constant.ErrRecordNotFound } - - global.LOG.Infof("start to download records %s from %s", cronjob.Type, backup.Type) - varMap := make(map[string]interface{}) - if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { + if backup.Type == "LOCAL" { + if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) { + return "", constant.ErrRecordNotFound + } + return record.File, nil + } + if record.FromLocal { + local, _ := loadLocalDir() + if _, err := os.Stat(local + "/" + record.File); err == nil { + return local + "/" + record.File, nil + } + } + client, err := NewIBackupService().NewClient(&backup) + if err != nil { return "", err } - varMap["type"] = backup.Type - if backup.Type != "LOCAL" { - varMap["bucket"] = backup.Bucket - switch backup.Type { - case constant.Sftp: - varMap["username"] = backup.AccessKey - varMap["password"] = backup.Credential - case constant.OSS, constant.S3, constant.MinIo: - varMap["accessKey"] = backup.AccessKey - varMap["secretKey"] = backup.Credential - } - backClient, err := cloud_storage.NewCloudStorageClient(varMap) - if err != nil { - return "", fmt.Errorf("new cloud storage client failed, err: %v", err) - } - global.LOG.Info("new backup client successful") - commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name) - name := fmt.Sprintf("%s%s.tar.gz", commonDir, record.StartTime.Format("20060102150405")) - if cronjob.Type == "database" { - name = fmt.Sprintf("%s%s.gz", commonDir, record.StartTime.Format("20060102150405")) - } - tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, commonDir) - if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) { - if err = os.MkdirAll(tempPath, os.ModePerm); err != nil { - global.LOG.Errorf("mkdir %s failed, err: %v", tempPath, err) - } - } - - global.LOG.Infof("download records %s from %s to %s", name, commonDir, tempPath) - targetPath := tempPath + strings.ReplaceAll(name, commonDir, "") - if _, err = os.Stat(targetPath); err != nil && os.IsNotExist(err) { - isOK, err := backClient.Download(name, targetPath) - if !isOK { - return "", fmt.Errorf("cloud storage download failed, err: %v", err) - } - } - return targetPath, nil - } - if _, ok := varMap["dir"]; !ok { - return "", errors.New("load local backup dir failed") - } - global.LOG.Infof("record is save in local dir %s", varMap["dir"]) - - switch cronjob.Type { - case "website": - return fmt.Sprintf("%v/website/%s/website_%s_%s.tar.gz", varMap["dir"], cronjob.Website, cronjob.Website, record.StartTime.Format("20060102150405")), nil - case "database": - mysqlInfo, err := appInstallRepo.LoadBaseInfo("mysql", "") - if err != nil { - return "", fmt.Errorf("load mysqlInfo failed, err: %v", err) - } - return fmt.Sprintf("%v/database/mysql/%s/%s/db_%s_%s.sql.gz", varMap["dir"], mysqlInfo.Name, cronjob.DBName, cronjob.DBName, record.StartTime.Format("20060102150405")), nil - case "directory": - return fmt.Sprintf("%v/%s/%s/directory%s_%s.tar.gz", varMap["dir"], cronjob.Type, cronjob.Name, strings.ReplaceAll(cronjob.SourceDir, "/", "_"), record.StartTime.Format("20060102150405")), nil - default: - return "", fmt.Errorf("not support type %s", cronjob.Type) + tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, record.File) + isOK, _ := client.Download(record.File, tempPath) + if !isOK || err != nil { + return "", constant.ErrRecordNotFound } + return tempPath, nil } func (u *CronjobService) HandleOnce(id uint) error { diff --git a/backend/app/service/cronjob_helper.go b/backend/app/service/cronjob_helper.go index 375fe96a4..6377ffc8a 100644 --- a/backend/app/service/cronjob_helper.go +++ b/backend/app/service/cronjob_helper.go @@ -22,8 +22,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) { message []byte err error ) - record := cronjobRepo.StartRecords(cronjob.ID, "") - record.FromLocal = cronjob.KeepLocal + record := cronjobRepo.StartRecords(cronjob.ID, cronjob.KeepLocal, "") go func() { switch cronjob.Type { case "shell": @@ -135,7 +134,10 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim } } - fullPath := fmt.Sprintf("%s/%s", record.FileDir, fileName) + fullPath := fmt.Sprintf("%s/%s", backupDir, fileName) + if backup.Type != "LOCAL" { + fullPath = fmt.Sprintf("%s/%s", itemFileDir, fileName) + } if backup.Type == "LOCAL" { u.HandleRmExpired(backup.Type, backupDir, cronjob, nil) return fullPath, nil diff --git a/backend/router/ro_file.go b/backend/router/ro_file.go index 22628c4bb..8a552c844 100644 --- a/backend/router/ro_file.go +++ b/backend/router/ro_file.go @@ -32,6 +32,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) { fileRouter.POST("/wget", baseApi.WgetFile) fileRouter.POST("/move", baseApi.MoveFile) fileRouter.POST("/download", baseApi.Download) + fileRouter.POST("/download/bypath", baseApi.DownloadFile) fileRouter.POST("/size", baseApi.Size) fileRouter.GET("/ws", baseApi.Ws) fileRouter.GET("/keys", baseApi.Keys) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 5144d9f4d..6386b5a7a 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -62,6 +62,25 @@ var doc = `{ } } }, + "/apps/checkupdate": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取应用更新版本", + "tags": [ + "App" + ], + "summary": "Get app list update", + "responses": { + "200": { + "description": "" + } + } + } + }, "/apps/detail/:appId/:version": { "get": { "security": [ @@ -470,6 +489,48 @@ var doc = `{ } } }, + "/apps/installed/params/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "修改应用参数", + "consumes": [ + "application/json" + ], + "tags": [ + "App" + ], + "summary": "Change app params", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AppInstalledUpdate" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "installId" + ], + "formatEN": "Application param update [installId]", + "formatZH": "应用参数修改 [installId]", + "paramKeys": [] + } + } + }, "/apps/installed/port/change": { "post": { "security": [ @@ -683,6 +744,20 @@ var doc = `{ } } }, + "/auth/demo": { + "get": { + "description": "判断是否为demo环境", + "tags": [ + "Auth" + ], + "summary": "Check System isDemo", + "responses": { + "200": { + "description": "" + } + } + } + }, "/auth/init": { "post": { "description": "初始化用户", @@ -3899,6 +3974,43 @@ var doc = `{ } } }, + "/files/chunkupload": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "分片上传文件", + "tags": [ + "File" + ], + "summary": "ChunkUpload file", + "parameters": [ + { + "type": "file", + "description": "request", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "path" + ], + "formatEN": "Upload file [path]", + "formatZH": "上传文件 [path]", + "paramKeys": [] + } + } + }, "/files/compress": { "post": { "security": [ @@ -4112,6 +4224,48 @@ var doc = `{ } } }, + "/files/download/bypath": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "下载指定文件", + "consumes": [ + "application/json" + ], + "tags": [ + "File" + ], + "summary": "Download file with path", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.FilePath" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "path" + ], + "formatEN": "Download file [path]", + "formatZH": "下载文件 [path]", + "paramKeys": [] + } + } + }, "/files/loadfile": { "post": { "security": [ @@ -8623,8 +8777,7 @@ var doc = `{ "dto.ComposeCreate": { "type": "object", "required": [ - "from", - "name" + "from" ], "properties": { "file": { @@ -8860,9 +9013,7 @@ var doc = `{ ], "properties": { "day": { - "type": "integer", - "maximum": 31, - "minimum": 1 + "type": "integer" }, "dbName": { "type": "string" @@ -8871,17 +9022,13 @@ var doc = `{ "type": "string" }, "hour": { - "type": "integer", - "maximum": 23, - "minimum": 0 + "type": "integer" }, "keepLocal": { "type": "boolean" }, "minute": { - "type": "integer", - "maximum": 59, - "minimum": 0 + "type": "integer" }, "name": { "type": "string" @@ -8942,9 +9089,7 @@ var doc = `{ ], "properties": { "day": { - "type": "integer", - "maximum": 31, - "minimum": 1 + "type": "integer" }, "dbName": { "type": "string" @@ -8953,9 +9098,7 @@ var doc = `{ "type": "string" }, "hour": { - "type": "integer", - "maximum": 23, - "minimum": 0 + "type": "integer" }, "id": { "type": "integer" @@ -8964,9 +9107,7 @@ var doc = `{ "type": "boolean" }, "minute": { - "type": "integer", - "maximum": 59, - "minimum": 0 + "type": "integer" }, "name": { "type": "string" @@ -9060,15 +9201,9 @@ var doc = `{ }, "dto.DaemonJsonUpdateByFile": { "type": "object", - "required": [ - "path" - ], "properties": { "file": { "type": "string" - }, - "path": { - "type": "string" } } }, @@ -9254,6 +9389,12 @@ var doc = `{ "restart", "stop" ] + }, + "stopService": { + "type": "boolean" + }, + "stopSocket": { + "type": "boolean" } } }, @@ -11042,6 +11183,22 @@ var doc = `{ } } }, + "request.AppInstalledUpdate": { + "type": "object", + "required": [ + "installId", + "params" + ], + "properties": { + "installId": { + "type": "integer" + }, + "params": { + "type": "object", + "additionalProperties": true + } + } + }, "request.AppSearch": { "type": "object", "required": [ @@ -11284,6 +11441,17 @@ var doc = `{ } } }, + "request.FilePath": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + } + } + }, "request.FilePathCheck": { "type": "object", "required": [ @@ -12042,7 +12210,22 @@ var doc = `{ "response.AppParam": { "type": "object", "properties": { - "label": { + "edit": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "labelEn": { + "type": "string" + }, + "labelZh": { + "type": "string" + }, + "rule": { + "type": "string" + }, + "type": { "type": "string" }, "value": {} @@ -12185,6 +12368,9 @@ var doc = `{ "appInstallId": { "type": "integer" }, + "appName": { + "type": "string" + }, "createdAt": { "type": "string" }, diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 62a61e568..1f933d905 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -48,6 +48,25 @@ } } }, + "/apps/checkupdate": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取应用更新版本", + "tags": [ + "App" + ], + "summary": "Get app list update", + "responses": { + "200": { + "description": "" + } + } + } + }, "/apps/detail/:appId/:version": { "get": { "security": [ @@ -456,6 +475,48 @@ } } }, + "/apps/installed/params/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "修改应用参数", + "consumes": [ + "application/json" + ], + "tags": [ + "App" + ], + "summary": "Change app params", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AppInstalledUpdate" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "installId" + ], + "formatEN": "Application param update [installId]", + "formatZH": "应用参数修改 [installId]", + "paramKeys": [] + } + } + }, "/apps/installed/port/change": { "post": { "security": [ @@ -669,6 +730,20 @@ } } }, + "/auth/demo": { + "get": { + "description": "判断是否为demo环境", + "tags": [ + "Auth" + ], + "summary": "Check System isDemo", + "responses": { + "200": { + "description": "" + } + } + } + }, "/auth/init": { "post": { "description": "初始化用户", @@ -3885,6 +3960,43 @@ } } }, + "/files/chunkupload": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "分片上传文件", + "tags": [ + "File" + ], + "summary": "ChunkUpload file", + "parameters": [ + { + "type": "file", + "description": "request", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "path" + ], + "formatEN": "Upload file [path]", + "formatZH": "上传文件 [path]", + "paramKeys": [] + } + } + }, "/files/compress": { "post": { "security": [ @@ -4098,6 +4210,48 @@ } } }, + "/files/download/bypath": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "下载指定文件", + "consumes": [ + "application/json" + ], + "tags": [ + "File" + ], + "summary": "Download file with path", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.FilePath" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "x-panel-log": { + "BeforeFuntions": [], + "bodyKeys": [ + "path" + ], + "formatEN": "Download file [path]", + "formatZH": "下载文件 [path]", + "paramKeys": [] + } + } + }, "/files/loadfile": { "post": { "security": [ @@ -8609,8 +8763,7 @@ "dto.ComposeCreate": { "type": "object", "required": [ - "from", - "name" + "from" ], "properties": { "file": { @@ -8846,9 +8999,7 @@ ], "properties": { "day": { - "type": "integer", - "maximum": 31, - "minimum": 1 + "type": "integer" }, "dbName": { "type": "string" @@ -8857,17 +9008,13 @@ "type": "string" }, "hour": { - "type": "integer", - "maximum": 23, - "minimum": 0 + "type": "integer" }, "keepLocal": { "type": "boolean" }, "minute": { - "type": "integer", - "maximum": 59, - "minimum": 0 + "type": "integer" }, "name": { "type": "string" @@ -8928,9 +9075,7 @@ ], "properties": { "day": { - "type": "integer", - "maximum": 31, - "minimum": 1 + "type": "integer" }, "dbName": { "type": "string" @@ -8939,9 +9084,7 @@ "type": "string" }, "hour": { - "type": "integer", - "maximum": 23, - "minimum": 0 + "type": "integer" }, "id": { "type": "integer" @@ -8950,9 +9093,7 @@ "type": "boolean" }, "minute": { - "type": "integer", - "maximum": 59, - "minimum": 0 + "type": "integer" }, "name": { "type": "string" @@ -9046,15 +9187,9 @@ }, "dto.DaemonJsonUpdateByFile": { "type": "object", - "required": [ - "path" - ], "properties": { "file": { "type": "string" - }, - "path": { - "type": "string" } } }, @@ -9240,6 +9375,12 @@ "restart", "stop" ] + }, + "stopService": { + "type": "boolean" + }, + "stopSocket": { + "type": "boolean" } } }, @@ -11028,6 +11169,22 @@ } } }, + "request.AppInstalledUpdate": { + "type": "object", + "required": [ + "installId", + "params" + ], + "properties": { + "installId": { + "type": "integer" + }, + "params": { + "type": "object", + "additionalProperties": true + } + } + }, "request.AppSearch": { "type": "object", "required": [ @@ -11270,6 +11427,17 @@ } } }, + "request.FilePath": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + } + } + }, "request.FilePathCheck": { "type": "object", "required": [ @@ -12028,7 +12196,22 @@ "response.AppParam": { "type": "object", "properties": { - "label": { + "edit": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "labelEn": { + "type": "string" + }, + "labelZh": { + "type": "string" + }, + "rule": { + "type": "string" + }, + "type": { "type": "string" }, "value": {} @@ -12171,6 +12354,9 @@ "appInstallId": { "type": "integer" }, + "appName": { + "type": "string" + }, "createdAt": { "type": "string" }, diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 15596eb07..c09325529 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -152,7 +152,6 @@ definitions: type: integer required: - from - - name type: object dto.ComposeOperation: properties: @@ -294,22 +293,16 @@ definitions: dto.CronjobCreate: properties: day: - maximum: 31 - minimum: 1 type: integer dbName: type: string exclusionRules: type: string hour: - maximum: 23 - minimum: 0 type: integer keepLocal: type: boolean minute: - maximum: 59 - minimum: 0 type: integer name: type: string @@ -352,24 +345,18 @@ definitions: dto.CronjobUpdate: properties: day: - maximum: 31 - minimum: 1 type: integer dbName: type: string exclusionRules: type: string hour: - maximum: 23 - minimum: 0 type: integer id: type: integer keepLocal: type: boolean minute: - maximum: 59 - minimum: 0 type: integer name: type: string @@ -439,10 +426,6 @@ definitions: properties: file: type: string - path: - type: string - required: - - path type: object dto.DashboardBase: properties: @@ -564,6 +547,10 @@ definitions: - restart - stop type: string + stopService: + type: boolean + stopSocket: + type: boolean required: - operation type: object @@ -1752,6 +1739,17 @@ definitions: - page - pageSize type: object + request.AppInstalledUpdate: + properties: + installId: + type: integer + params: + additionalProperties: true + type: object + required: + - installId + - params + type: object request.AppSearch: properties: name: @@ -1914,6 +1912,13 @@ definitions: showHidden: type: boolean type: object + request.FilePath: + properties: + path: + type: string + required: + - path + type: object request.FilePathCheck: properties: path: @@ -2421,7 +2426,17 @@ definitions: type: object response.AppParam: properties: - label: + edit: + type: boolean + key: + type: string + labelEn: + type: string + labelZh: + type: string + rule: + type: string + type: type: string value: {} type: object @@ -2515,6 +2530,8 @@ definitions: type: string appInstallId: type: integer + appName: + type: string createdAt: type: string defaultServer: @@ -2628,6 +2645,17 @@ paths: summary: Search app by key tags: - App + /apps/checkupdate: + get: + description: 获取应用更新版本 + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: Get app list update + tags: + - App /apps/detail/:appId/:version: get: consumes: @@ -2887,6 +2915,33 @@ paths: summary: Search params by appInstallId tags: - App + /apps/installed/params/update: + post: + consumes: + - application/json + description: 修改应用参数 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/request.AppInstalledUpdate' + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: Change app params + tags: + - App + x-panel-log: + BeforeFuntions: [] + bodyKeys: + - installId + formatEN: Application param update [installId] + formatZH: 应用参数修改 [installId] + paramKeys: [] /apps/installed/port/change: post: consumes: @@ -3022,6 +3077,15 @@ paths: summary: Load captcha tags: - Auth + /auth/demo: + get: + description: 判断是否为demo环境 + responses: + "200": + description: "" + summary: Check System isDemo + tags: + - Auth /auth/init: post: consumes: @@ -5065,6 +5129,30 @@ paths: formatEN: Check whether file [path] exists formatZH: 检测文件 [path] 是否存在 paramKeys: [] + /files/chunkupload: + post: + description: 分片上传文件 + parameters: + - description: request + in: formData + name: file + required: true + type: file + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: ChunkUpload file + tags: + - File + x-panel-log: + BeforeFuntions: [] + bodyKeys: + - path + formatEN: Upload file [path] + formatZH: 上传文件 [path] + paramKeys: [] /files/compress: post: consumes: @@ -5202,6 +5290,33 @@ paths: formatEN: Download file [name] formatZH: 下载文件 [name] paramKeys: [] + /files/download/bypath: + post: + consumes: + - application/json + description: 下载指定文件 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/request.FilePath' + responses: + "200": + description: "" + security: + - ApiKeyAuth: [] + summary: Download file with path + tags: + - File + x-panel-log: + BeforeFuntions: [] + bodyKeys: + - path + formatEN: Download file [path] + formatZH: 下载文件 [path] + paramKeys: [] /files/loadfile: post: consumes: diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index c16decc02..18a7b02fb 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -6,7 +6,7 @@ export const searchContainer = (params: Container.ContainerSearch) => { return http.post>(`/containers/search`, params); }; export const createContainer = (params: Container.ContainerCreate) => { - return http.post(`/containers`, params); + return http.post(`/containers`, params, 1200000); }; export const logContainer = (params: Container.ContainerLogSearch) => { return http.post(`/containers/search/log`, params); @@ -38,10 +38,10 @@ export const imagePush = (params: Container.ImagePush) => { return http.post(`/containers/image/push`, params); }; export const imageLoad = (params: Container.ImageLoad) => { - return http.post(`/containers/image/load`, params); + return http.post(`/containers/image/load`, params, 1200000); }; export const imageSave = (params: Container.ImageSave) => { - return http.post(`/containers/image/save`, params); + return http.post(`/containers/image/save`, params, 1200000); }; export const imageTag = (params: Container.ImageTag) => { return http.post(`/containers/image/tag`, params); @@ -117,13 +117,13 @@ export const searchCompose = (params: SearchWithPage) => { return http.post>(`/containers/compose/search`, params); }; export const upCompose = (params: Container.ComposeCreate) => { - return http.post(`/containers/compose`, params); + return http.post(`/containers/compose`, params, 600000); }; export const composeOperator = (params: Container.ComposeOpration) => { return http.post(`/containers/compose/operate`, params); }; export const composeUpdate = (params: Container.ComposeUpdate) => { - return http.post(`/containers/compose/update`, params); + return http.post(`/containers/compose/update`, params, 600000); }; // docker diff --git a/frontend/src/api/modules/cronjob.ts b/frontend/src/api/modules/cronjob.ts index de64b3560..73d51fe82 100644 --- a/frontend/src/api/modules/cronjob.ts +++ b/frontend/src/api/modules/cronjob.ts @@ -31,7 +31,7 @@ export const updateStatus = (params: Cronjob.UpdateStatus) => { }; export const download = (params: Cronjob.Download) => { - return http.download(`cronjobs/download`, params, { responseType: 'blob' }); + return http.post(`cronjobs/download`, params); }; export const handleOnce = (id: number) => { diff --git a/frontend/src/api/modules/files.ts b/frontend/src/api/modules/files.ts index 94768942e..869590357 100644 --- a/frontend/src/api/modules/files.ts +++ b/frontend/src/api/modules/files.ts @@ -79,6 +79,10 @@ export const DownloadFile = (params: File.FileDownload) => { return http.download('files/download', params, { responseType: 'blob', timeout: 20000 }); }; +export const DownloadByPath = (path: string) => { + return http.download('files/download/bypath', { path: path }, { responseType: 'blob', timeout: 40000 }); +}; + export const ComputeDirSize = (params: File.DirSizeReq) => { return http.post('files/size', params); }; diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index 9b7507299..a2c2b321f 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -63,7 +63,7 @@ export const handleRecoverByUpload = (params: Backup.Recover) => { return http.post(`/settings/backup/recover/byupload`, params, 400000); }; export const downloadBackupRecord = (params: Backup.RecordDownload) => { - return http.download(`/settings/backup/record/download`, params, { responseType: 'blob' }); + return http.post(`/settings/backup/record/download`, params); }; export const deleteBackupRecord = (params: { ids: number[] }) => { return http.post(`/settings/backup/record/del`, params); diff --git a/frontend/src/components/backup/index.vue b/frontend/src/components/backup/index.vue index 53dee6852..0a4098a71 100644 --- a/frontend/src/components/backup/index.vue +++ b/frontend/src/components/backup/index.vue @@ -58,6 +58,7 @@ import DrawerHeader from '@/components/drawer-header/index.vue'; import { deleteBackupRecord, downloadBackupRecord, searchBackupRecords } from '@/api/modules/setting'; import { Backup } from '@/api/interface/backup'; import { MsgSuccess } from '@/utils/message'; +import { DownloadByPath } from '@/api/modules/files'; const selects = ref([]); const loading = ref(); @@ -145,14 +146,16 @@ const onDownload = async (row: Backup.RecordInfo) => { fileDir: row.fileDir, fileName: row.fileName, }; - const res = await downloadBackupRecord(params); - const downloadUrl = window.URL.createObjectURL(new Blob([res])); - const a = document.createElement('a'); - a.style.display = 'none'; - a.href = downloadUrl; - a.download = row.fileName; - const event = new MouseEvent('click'); - a.dispatchEvent(event); + await downloadBackupRecord(params).then(async (res) => { + const file = await DownloadByPath(res.data); + const downloadUrl = window.URL.createObjectURL(new Blob([file])); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = downloadUrl; + a.download = row.fileName; + const event = new MouseEvent('click'); + a.dispatchEvent(event); + }); }; const onBatchDelete = async (row: Backup.RecordInfo | null) => { diff --git a/frontend/src/views/cronjob/record/index.vue b/frontend/src/views/cronjob/record/index.vue index aa3a187b2..f5671147a 100644 --- a/frontend/src/views/cronjob/record/index.vue +++ b/frontend/src/views/cronjob/record/index.vue @@ -283,7 +283,7 @@ import { searchRecords, download, handleOnce, updateStatus } from '@/api/modules import { dateFormat } from '@/utils/util'; import i18n from '@/lang'; import { ElMessageBox } from 'element-plus'; -import { LoadFile } from '@/api/modules/files'; +import { DownloadByPath, LoadFile } from '@/api/modules/files'; import LayoutContent from '@/layout/layout-content.vue'; import { Codemirror } from 'vue-codemirror'; import { javascript } from '@codemirror/lang-javascript'; @@ -472,17 +472,19 @@ const onDownload = async (record: any, backupID: number) => { recordID: record.id, backupAccountID: backupID, }; - const res = await download(params); - const downloadUrl = window.URL.createObjectURL(new Blob([res])); - const a = document.createElement('a'); - a.style.display = 'none'; - a.href = downloadUrl; - if (record.file && record.file.indexOf('/') !== -1) { - let pathItem = record.file.split('/'); - a.download = pathItem[pathItem.length - 1]; - } - const event = new MouseEvent('click'); - a.dispatchEvent(event); + await download(params).then(async (res) => { + const file = await DownloadByPath(res.data); + const downloadUrl = window.URL.createObjectURL(new Blob([file])); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = downloadUrl; + if (record.file && record.file.indexOf('/') !== -1) { + let pathItem = record.file.split('/'); + a.download = pathItem[pathItem.length - 1]; + } + const event = new MouseEvent('click'); + a.dispatchEvent(event); + }); }; const nextPage = async () => {