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)
return
}
c.File(filePath)
helper.SuccessWithData(c, filePath)
}
// @Tags Backup Account

View File

@ -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

View File

@ -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 获取文件夹大小

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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)

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": {
"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"
},

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": {
"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"
},

View File

@ -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:

View File

@ -6,7 +6,7 @@ export const searchContainer = (params: Container.ContainerSearch) => {
return http.post<ResPage<Container.ContainerInfo>>(`/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<string>(`/containers/search/log`, params);
@ -38,10 +38,10 @@ export const imagePush = (params: Container.ImagePush) => {
return http.post<string>(`/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<ResPage<Container.ComposeInfo>>(`/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

View File

@ -31,7 +31,7 @@ export const updateStatus = (params: Cronjob.UpdateStatus) => {
};
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) => {

View File

@ -79,6 +79,10 @@ export const DownloadFile = (params: File.FileDownload) => {
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) => {
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);
};
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[] }) => {
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 { Backup } from '@/api/interface/backup';
import { MsgSuccess } from '@/utils/message';
import { DownloadByPath } from '@/api/modules/files';
const selects = ref<any>([]);
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) => {

View File

@ -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 () => {