1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-01 11:34:13 +08:00

fix: 解决计划任务备份文件失效仍能下载的问题

This commit is contained in:
ssongliu 2023-03-18 10:03:40 +08:00 committed by f2c-ci-robot[bot]
parent fb286d2def
commit 4a974b7e0a
16 changed files with 647 additions and 169 deletions

View File

@ -175,7 +175,7 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
c.File(filePath) helper.SuccessWithData(c, filePath)
} }
// @Tags Backup Account // @Tags Backup Account

View File

@ -198,7 +198,7 @@ func (b *BaseApi) TargetDownload(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
c.File(filePath) helper.SuccessWithData(c, filePath)
} }
// @Tags Cronjob // @Tags Cronjob

View File

@ -445,6 +445,28 @@ func (b *BaseApi) Download(c *gin.Context) {
c.File(filePath) 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 // @Tags File
// @Summary Load file size // @Summary Load file size
// @Description 获取文件夹大小 // @Description 获取文件夹大小

View File

@ -24,7 +24,7 @@ type ICronjobRepo interface {
Update(id uint, vars map[string]interface{}) error Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error Delete(opts ...DBOption) error
DeleteRecord(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) 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 var record model.JobRecords
record.StartTime = time.Now() record.StartTime = time.Now()
record.CronjobID = cronjobID record.CronjobID = cronjobID
record.FromLocal = fromLocal
record.Status = constant.StatusWaiting record.Status = constant.StatusWaiting
if err := global.DB.Create(&record).Error; err != nil { if err := global.DB.Create(&record).Error; err != nil {
global.LOG.Errorf("create record status failed, err: %v", err) global.LOG.Errorf("create record status failed, err: %v", err)

View File

@ -2,17 +2,14 @@ package service
import ( import (
"bufio" "bufio"
"encoding/json"
"fmt" "fmt"
"os" "os"
"strings"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
@ -92,69 +89,28 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
if cronjob.ID == 0 { if cronjob.ID == 0 {
return "", constant.ErrRecordNotFound return "", constant.ErrRecordNotFound
} }
if backup.Type == "LOCAL" {
global.LOG.Infof("start to download records %s from %s", cronjob.Type, backup.Type) if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
varMap := make(map[string]interface{}) return "", constant.ErrRecordNotFound
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil { }
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 return "", err
} }
varMap["type"] = backup.Type tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, record.File)
if backup.Type != "LOCAL" { isOK, _ := client.Download(record.File, tempPath)
varMap["bucket"] = backup.Bucket if !isOK || err != nil {
switch backup.Type { return "", constant.ErrRecordNotFound
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)
} }
return tempPath, nil
} }
func (u *CronjobService) HandleOnce(id uint) error { func (u *CronjobService) HandleOnce(id uint) error {

View File

@ -22,8 +22,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
message []byte message []byte
err error err error
) )
record := cronjobRepo.StartRecords(cronjob.ID, "") record := cronjobRepo.StartRecords(cronjob.ID, cronjob.KeepLocal, "")
record.FromLocal = cronjob.KeepLocal
go func() { go func() {
switch cronjob.Type { switch cronjob.Type {
case "shell": 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" { if backup.Type == "LOCAL" {
u.HandleRmExpired(backup.Type, backupDir, cronjob, nil) u.HandleRmExpired(backup.Type, backupDir, cronjob, nil)
return fullPath, nil return fullPath, nil

View File

@ -32,6 +32,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
fileRouter.POST("/wget", baseApi.WgetFile) fileRouter.POST("/wget", baseApi.WgetFile)
fileRouter.POST("/move", baseApi.MoveFile) fileRouter.POST("/move", baseApi.MoveFile)
fileRouter.POST("/download", baseApi.Download) fileRouter.POST("/download", baseApi.Download)
fileRouter.POST("/download/bypath", baseApi.DownloadFile)
fileRouter.POST("/size", baseApi.Size) fileRouter.POST("/size", baseApi.Size)
fileRouter.GET("/ws", baseApi.Ws) fileRouter.GET("/ws", baseApi.Ws)
fileRouter.GET("/keys", baseApi.Keys) fileRouter.GET("/keys", baseApi.Keys)

View File

@ -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": { "/apps/detail/:appId/:version": {
"get": { "get": {
"security": [ "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": { "/apps/installed/port/change": {
"post": { "post": {
"security": [ "security": [
@ -683,6 +744,20 @@ var doc = `{
} }
} }
}, },
"/auth/demo": {
"get": {
"description": "判断是否为demo环境",
"tags": [
"Auth"
],
"summary": "Check System isDemo",
"responses": {
"200": {
"description": ""
}
}
}
},
"/auth/init": { "/auth/init": {
"post": { "post": {
"description": "初始化用户", "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": { "/files/compress": {
"post": { "post": {
"security": [ "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": { "/files/loadfile": {
"post": { "post": {
"security": [ "security": [
@ -8623,8 +8777,7 @@ var doc = `{
"dto.ComposeCreate": { "dto.ComposeCreate": {
"type": "object", "type": "object",
"required": [ "required": [
"from", "from"
"name"
], ],
"properties": { "properties": {
"file": { "file": {
@ -8860,9 +9013,7 @@ var doc = `{
], ],
"properties": { "properties": {
"day": { "day": {
"type": "integer", "type": "integer"
"maximum": 31,
"minimum": 1
}, },
"dbName": { "dbName": {
"type": "string" "type": "string"
@ -8871,17 +9022,13 @@ var doc = `{
"type": "string" "type": "string"
}, },
"hour": { "hour": {
"type": "integer", "type": "integer"
"maximum": 23,
"minimum": 0
}, },
"keepLocal": { "keepLocal": {
"type": "boolean" "type": "boolean"
}, },
"minute": { "minute": {
"type": "integer", "type": "integer"
"maximum": 59,
"minimum": 0
}, },
"name": { "name": {
"type": "string" "type": "string"
@ -8942,9 +9089,7 @@ var doc = `{
], ],
"properties": { "properties": {
"day": { "day": {
"type": "integer", "type": "integer"
"maximum": 31,
"minimum": 1
}, },
"dbName": { "dbName": {
"type": "string" "type": "string"
@ -8953,9 +9098,7 @@ var doc = `{
"type": "string" "type": "string"
}, },
"hour": { "hour": {
"type": "integer", "type": "integer"
"maximum": 23,
"minimum": 0
}, },
"id": { "id": {
"type": "integer" "type": "integer"
@ -8964,9 +9107,7 @@ var doc = `{
"type": "boolean" "type": "boolean"
}, },
"minute": { "minute": {
"type": "integer", "type": "integer"
"maximum": 59,
"minimum": 0
}, },
"name": { "name": {
"type": "string" "type": "string"
@ -9060,15 +9201,9 @@ var doc = `{
}, },
"dto.DaemonJsonUpdateByFile": { "dto.DaemonJsonUpdateByFile": {
"type": "object", "type": "object",
"required": [
"path"
],
"properties": { "properties": {
"file": { "file": {
"type": "string" "type": "string"
},
"path": {
"type": "string"
} }
} }
}, },
@ -9254,6 +9389,12 @@ var doc = `{
"restart", "restart",
"stop" "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": { "request.AppSearch": {
"type": "object", "type": "object",
"required": [ "required": [
@ -11284,6 +11441,17 @@ var doc = `{
} }
} }
}, },
"request.FilePath": {
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"type": "string"
}
}
},
"request.FilePathCheck": { "request.FilePathCheck": {
"type": "object", "type": "object",
"required": [ "required": [
@ -12042,7 +12210,22 @@ var doc = `{
"response.AppParam": { "response.AppParam": {
"type": "object", "type": "object",
"properties": { "properties": {
"label": { "edit": {
"type": "boolean"
},
"key": {
"type": "string"
},
"labelEn": {
"type": "string"
},
"labelZh": {
"type": "string"
},
"rule": {
"type": "string"
},
"type": {
"type": "string" "type": "string"
}, },
"value": {} "value": {}
@ -12185,6 +12368,9 @@ var doc = `{
"appInstallId": { "appInstallId": {
"type": "integer" "type": "integer"
}, },
"appName": {
"type": "string"
},
"createdAt": { "createdAt": {
"type": "string" "type": "string"
}, },

View File

@ -48,6 +48,25 @@
} }
} }
}, },
"/apps/checkupdate": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取应用更新版本",
"tags": [
"App"
],
"summary": "Get app list update",
"responses": {
"200": {
"description": ""
}
}
}
},
"/apps/detail/:appId/:version": { "/apps/detail/:appId/:version": {
"get": { "get": {
"security": [ "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": { "/apps/installed/port/change": {
"post": { "post": {
"security": [ "security": [
@ -669,6 +730,20 @@
} }
} }
}, },
"/auth/demo": {
"get": {
"description": "判断是否为demo环境",
"tags": [
"Auth"
],
"summary": "Check System isDemo",
"responses": {
"200": {
"description": ""
}
}
}
},
"/auth/init": { "/auth/init": {
"post": { "post": {
"description": "初始化用户", "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": { "/files/compress": {
"post": { "post": {
"security": [ "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": { "/files/loadfile": {
"post": { "post": {
"security": [ "security": [
@ -8609,8 +8763,7 @@
"dto.ComposeCreate": { "dto.ComposeCreate": {
"type": "object", "type": "object",
"required": [ "required": [
"from", "from"
"name"
], ],
"properties": { "properties": {
"file": { "file": {
@ -8846,9 +8999,7 @@
], ],
"properties": { "properties": {
"day": { "day": {
"type": "integer", "type": "integer"
"maximum": 31,
"minimum": 1
}, },
"dbName": { "dbName": {
"type": "string" "type": "string"
@ -8857,17 +9008,13 @@
"type": "string" "type": "string"
}, },
"hour": { "hour": {
"type": "integer", "type": "integer"
"maximum": 23,
"minimum": 0
}, },
"keepLocal": { "keepLocal": {
"type": "boolean" "type": "boolean"
}, },
"minute": { "minute": {
"type": "integer", "type": "integer"
"maximum": 59,
"minimum": 0
}, },
"name": { "name": {
"type": "string" "type": "string"
@ -8928,9 +9075,7 @@
], ],
"properties": { "properties": {
"day": { "day": {
"type": "integer", "type": "integer"
"maximum": 31,
"minimum": 1
}, },
"dbName": { "dbName": {
"type": "string" "type": "string"
@ -8939,9 +9084,7 @@
"type": "string" "type": "string"
}, },
"hour": { "hour": {
"type": "integer", "type": "integer"
"maximum": 23,
"minimum": 0
}, },
"id": { "id": {
"type": "integer" "type": "integer"
@ -8950,9 +9093,7 @@
"type": "boolean" "type": "boolean"
}, },
"minute": { "minute": {
"type": "integer", "type": "integer"
"maximum": 59,
"minimum": 0
}, },
"name": { "name": {
"type": "string" "type": "string"
@ -9046,15 +9187,9 @@
}, },
"dto.DaemonJsonUpdateByFile": { "dto.DaemonJsonUpdateByFile": {
"type": "object", "type": "object",
"required": [
"path"
],
"properties": { "properties": {
"file": { "file": {
"type": "string" "type": "string"
},
"path": {
"type": "string"
} }
} }
}, },
@ -9240,6 +9375,12 @@
"restart", "restart",
"stop" "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": { "request.AppSearch": {
"type": "object", "type": "object",
"required": [ "required": [
@ -11270,6 +11427,17 @@
} }
} }
}, },
"request.FilePath": {
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"type": "string"
}
}
},
"request.FilePathCheck": { "request.FilePathCheck": {
"type": "object", "type": "object",
"required": [ "required": [
@ -12028,7 +12196,22 @@
"response.AppParam": { "response.AppParam": {
"type": "object", "type": "object",
"properties": { "properties": {
"label": { "edit": {
"type": "boolean"
},
"key": {
"type": "string"
},
"labelEn": {
"type": "string"
},
"labelZh": {
"type": "string"
},
"rule": {
"type": "string"
},
"type": {
"type": "string" "type": "string"
}, },
"value": {} "value": {}
@ -12171,6 +12354,9 @@
"appInstallId": { "appInstallId": {
"type": "integer" "type": "integer"
}, },
"appName": {
"type": "string"
},
"createdAt": { "createdAt": {
"type": "string" "type": "string"
}, },

View File

@ -152,7 +152,6 @@ definitions:
type: integer type: integer
required: required:
- from - from
- name
type: object type: object
dto.ComposeOperation: dto.ComposeOperation:
properties: properties:
@ -294,22 +293,16 @@ definitions:
dto.CronjobCreate: dto.CronjobCreate:
properties: properties:
day: day:
maximum: 31
minimum: 1
type: integer type: integer
dbName: dbName:
type: string type: string
exclusionRules: exclusionRules:
type: string type: string
hour: hour:
maximum: 23
minimum: 0
type: integer type: integer
keepLocal: keepLocal:
type: boolean type: boolean
minute: minute:
maximum: 59
minimum: 0
type: integer type: integer
name: name:
type: string type: string
@ -352,24 +345,18 @@ definitions:
dto.CronjobUpdate: dto.CronjobUpdate:
properties: properties:
day: day:
maximum: 31
minimum: 1
type: integer type: integer
dbName: dbName:
type: string type: string
exclusionRules: exclusionRules:
type: string type: string
hour: hour:
maximum: 23
minimum: 0
type: integer type: integer
id: id:
type: integer type: integer
keepLocal: keepLocal:
type: boolean type: boolean
minute: minute:
maximum: 59
minimum: 0
type: integer type: integer
name: name:
type: string type: string
@ -439,10 +426,6 @@ definitions:
properties: properties:
file: file:
type: string type: string
path:
type: string
required:
- path
type: object type: object
dto.DashboardBase: dto.DashboardBase:
properties: properties:
@ -564,6 +547,10 @@ definitions:
- restart - restart
- stop - stop
type: string type: string
stopService:
type: boolean
stopSocket:
type: boolean
required: required:
- operation - operation
type: object type: object
@ -1752,6 +1739,17 @@ definitions:
- page - page
- pageSize - pageSize
type: object type: object
request.AppInstalledUpdate:
properties:
installId:
type: integer
params:
additionalProperties: true
type: object
required:
- installId
- params
type: object
request.AppSearch: request.AppSearch:
properties: properties:
name: name:
@ -1914,6 +1912,13 @@ definitions:
showHidden: showHidden:
type: boolean type: boolean
type: object type: object
request.FilePath:
properties:
path:
type: string
required:
- path
type: object
request.FilePathCheck: request.FilePathCheck:
properties: properties:
path: path:
@ -2421,7 +2426,17 @@ definitions:
type: object type: object
response.AppParam: response.AppParam:
properties: properties:
label: edit:
type: boolean
key:
type: string
labelEn:
type: string
labelZh:
type: string
rule:
type: string
type:
type: string type: string
value: {} value: {}
type: object type: object
@ -2515,6 +2530,8 @@ definitions:
type: string type: string
appInstallId: appInstallId:
type: integer type: integer
appName:
type: string
createdAt: createdAt:
type: string type: string
defaultServer: defaultServer:
@ -2628,6 +2645,17 @@ paths:
summary: Search app by key summary: Search app by key
tags: tags:
- App - App
/apps/checkupdate:
get:
description: 获取应用更新版本
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Get app list update
tags:
- App
/apps/detail/:appId/:version: /apps/detail/:appId/:version:
get: get:
consumes: consumes:
@ -2887,6 +2915,33 @@ paths:
summary: Search params by appInstallId summary: Search params by appInstallId
tags: tags:
- App - 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: /apps/installed/port/change:
post: post:
consumes: consumes:
@ -3022,6 +3077,15 @@ paths:
summary: Load captcha summary: Load captcha
tags: tags:
- Auth - Auth
/auth/demo:
get:
description: 判断是否为demo环境
responses:
"200":
description: ""
summary: Check System isDemo
tags:
- Auth
/auth/init: /auth/init:
post: post:
consumes: consumes:
@ -5065,6 +5129,30 @@ paths:
formatEN: Check whether file [path] exists formatEN: Check whether file [path] exists
formatZH: 检测文件 [path] 是否存在 formatZH: 检测文件 [path] 是否存在
paramKeys: [] 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: /files/compress:
post: post:
consumes: consumes:
@ -5202,6 +5290,33 @@ paths:
formatEN: Download file [name] formatEN: Download file [name]
formatZH: 下载文件 [name] formatZH: 下载文件 [name]
paramKeys: [] 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: /files/loadfile:
post: post:
consumes: consumes:

View File

@ -6,7 +6,7 @@ export const searchContainer = (params: Container.ContainerSearch) => {
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params); return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params);
}; };
export const createContainer = (params: Container.ContainerCreate) => { export const createContainer = (params: Container.ContainerCreate) => {
return http.post(`/containers`, params); return http.post(`/containers`, params, 1200000);
}; };
export const logContainer = (params: Container.ContainerLogSearch) => { export const logContainer = (params: Container.ContainerLogSearch) => {
return http.post<string>(`/containers/search/log`, params); return http.post<string>(`/containers/search/log`, params);
@ -38,10 +38,10 @@ export const imagePush = (params: Container.ImagePush) => {
return http.post<string>(`/containers/image/push`, params); return http.post<string>(`/containers/image/push`, params);
}; };
export const imageLoad = (params: Container.ImageLoad) => { 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) => { 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) => { export const imageTag = (params: Container.ImageTag) => {
return http.post(`/containers/image/tag`, params); return http.post(`/containers/image/tag`, params);
@ -117,13 +117,13 @@ export const searchCompose = (params: SearchWithPage) => {
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params); return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
}; };
export const upCompose = (params: Container.ComposeCreate) => { 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) => { export const composeOperator = (params: Container.ComposeOpration) => {
return http.post(`/containers/compose/operate`, params); return http.post(`/containers/compose/operate`, params);
}; };
export const composeUpdate = (params: Container.ComposeUpdate) => { export const composeUpdate = (params: Container.ComposeUpdate) => {
return http.post(`/containers/compose/update`, params); return http.post(`/containers/compose/update`, params, 600000);
}; };
// docker // docker

View File

@ -31,7 +31,7 @@ export const updateStatus = (params: Cronjob.UpdateStatus) => {
}; };
export const download = (params: Cronjob.Download) => { export const download = (params: Cronjob.Download) => {
return http.download<BlobPart>(`cronjobs/download`, params, { responseType: 'blob' }); return http.post<string>(`cronjobs/download`, params);
}; };
export const handleOnce = (id: number) => { export const handleOnce = (id: number) => {

View File

@ -79,6 +79,10 @@ export const DownloadFile = (params: File.FileDownload) => {
return http.download<BlobPart>('files/download', params, { responseType: 'blob', timeout: 20000 }); return http.download<BlobPart>('files/download', params, { responseType: 'blob', timeout: 20000 });
}; };
export const DownloadByPath = (path: string) => {
return http.download<BlobPart>('files/download/bypath', { path: path }, { responseType: 'blob', timeout: 40000 });
};
export const ComputeDirSize = (params: File.DirSizeReq) => { export const ComputeDirSize = (params: File.DirSizeReq) => {
return http.post<File.DirSizeRes>('files/size', params); return http.post<File.DirSizeRes>('files/size', params);
}; };

View File

@ -63,7 +63,7 @@ export const handleRecoverByUpload = (params: Backup.Recover) => {
return http.post(`/settings/backup/recover/byupload`, params, 400000); return http.post(`/settings/backup/recover/byupload`, params, 400000);
}; };
export const downloadBackupRecord = (params: Backup.RecordDownload) => { export const downloadBackupRecord = (params: Backup.RecordDownload) => {
return http.download<BlobPart>(`/settings/backup/record/download`, params, { responseType: 'blob' }); return http.post<string>(`/settings/backup/record/download`, params);
}; };
export const deleteBackupRecord = (params: { ids: number[] }) => { export const deleteBackupRecord = (params: { ids: number[] }) => {
return http.post(`/settings/backup/record/del`, params); return http.post(`/settings/backup/record/del`, params);

View File

@ -58,6 +58,7 @@ import DrawerHeader from '@/components/drawer-header/index.vue';
import { deleteBackupRecord, downloadBackupRecord, searchBackupRecords } from '@/api/modules/setting'; import { deleteBackupRecord, downloadBackupRecord, searchBackupRecords } from '@/api/modules/setting';
import { Backup } from '@/api/interface/backup'; import { Backup } from '@/api/interface/backup';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { DownloadByPath } from '@/api/modules/files';
const selects = ref<any>([]); const selects = ref<any>([]);
const loading = ref(); const loading = ref();
@ -145,14 +146,16 @@ const onDownload = async (row: Backup.RecordInfo) => {
fileDir: row.fileDir, fileDir: row.fileDir,
fileName: row.fileName, fileName: row.fileName,
}; };
const res = await downloadBackupRecord(params); await downloadBackupRecord(params).then(async (res) => {
const downloadUrl = window.URL.createObjectURL(new Blob([res])); const file = await DownloadByPath(res.data);
const a = document.createElement('a'); const downloadUrl = window.URL.createObjectURL(new Blob([file]));
a.style.display = 'none'; const a = document.createElement('a');
a.href = downloadUrl; a.style.display = 'none';
a.download = row.fileName; a.href = downloadUrl;
const event = new MouseEvent('click'); a.download = row.fileName;
a.dispatchEvent(event); const event = new MouseEvent('click');
a.dispatchEvent(event);
});
}; };
const onBatchDelete = async (row: Backup.RecordInfo | null) => { const onBatchDelete = async (row: Backup.RecordInfo | null) => {

View File

@ -283,7 +283,7 @@ import { searchRecords, download, handleOnce, updateStatus } from '@/api/modules
import { dateFormat } from '@/utils/util'; import { dateFormat } from '@/utils/util';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElMessageBox } from 'element-plus'; 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 LayoutContent from '@/layout/layout-content.vue';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
@ -472,17 +472,19 @@ const onDownload = async (record: any, backupID: number) => {
recordID: record.id, recordID: record.id,
backupAccountID: backupID, backupAccountID: backupID,
}; };
const res = await download(params); await download(params).then(async (res) => {
const downloadUrl = window.URL.createObjectURL(new Blob([res])); const file = await DownloadByPath(res.data);
const a = document.createElement('a'); const downloadUrl = window.URL.createObjectURL(new Blob([file]));
a.style.display = 'none'; const a = document.createElement('a');
a.href = downloadUrl; a.style.display = 'none';
if (record.file && record.file.indexOf('/') !== -1) { a.href = downloadUrl;
let pathItem = record.file.split('/'); if (record.file && record.file.indexOf('/') !== -1) {
a.download = pathItem[pathItem.length - 1]; let pathItem = record.file.split('/');
} a.download = pathItem[pathItem.length - 1];
const event = new MouseEvent('click'); }
a.dispatchEvent(event); const event = new MouseEvent('click');
a.dispatchEvent(event);
});
}; };
const nextPage = async () => { const nextPage = async () => {