Skip to content

Commit e57dc12

Browse files
authored
feat: add stop functionality for decompress tasks and improve UI for task management (#12582)
1 parent 4c396d6 commit e57dc12

18 files changed

Lines changed: 466 additions & 85 deletions

File tree

agent/app/api/v2/file.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,26 @@ func (b *BaseApi) DeCompressFile(c *gin.Context) {
292292
helper.Success(c)
293293
}
294294

295+
// @Tags File
296+
// @Summary Stop decompress task
297+
// @Accept json
298+
// @Param request body request.FileDeCompressStopReq true "request"
299+
// @Success 200
300+
// @Security ApiKeyAuth
301+
// @Security Timestamp
302+
// @Router /files/decompress/stop [post]
303+
func (b *BaseApi) StopDeCompressFile(c *gin.Context) {
304+
var req request.FileDeCompressStopReq
305+
if err := helper.CheckBindAndValidate(&req, c); err != nil {
306+
return
307+
}
308+
if err := fileService.StopDeCompress(req.TaskID); err != nil {
309+
helper.InternalServer(c, err)
310+
return
311+
}
312+
helper.Success(c)
313+
}
314+
295315
// @Tags File
296316
// @Summary Load file content
297317
// @Accept json

agent/app/dto/request/file.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ type FileDeCompress struct {
9090
Type string `json:"type" validate:"required"`
9191
Path string `json:"path" validate:"required"`
9292
Secret string `json:"secret"`
93+
TaskID string `json:"taskID"`
94+
}
95+
96+
type FileDeCompressStopReq struct {
97+
TaskID string `json:"taskID" validate:"required"`
9398
}
9499

95100
type FileEdit struct {

agent/app/service/app.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ func getAppFromRepo(downloadPath string) error {
896896
return err
897897
}
898898

899-
if err := fileOp.Decompress(packagePath, global.Dir.ResourceDir, files.SdkZip, ""); err != nil {
899+
if err := fileOp.Decompress(context.Background(), packagePath, global.Dir.ResourceDir, files.SdkZip, ""); err != nil {
900900
return err
901901
}
902902
defer func() {

agent/app/service/app_utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
10341034
}
10351035
return
10361036
}
1037-
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
1037+
if err = fileOp.Decompress(context.Background(), filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
10381038
if logger == nil {
10391039
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
10401040
} else {

agent/app/service/backup_mysql.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package service
22

33
import (
4+
"context"
45
"fmt"
56
"os"
67
"path"
@@ -263,7 +264,7 @@ func loadSqlFile(file string) (string, error) {
263264
_ = os.RemoveAll(dstDir)
264265
return "", err
265266
}
266-
if err := archiver.Extract(file, dstDir, ""); err != nil {
267+
if err := archiver.Extract(context.Background(), file, dstDir, ""); err != nil {
267268
_ = os.RemoveAll(dstDir)
268269
return "", err
269270
}

agent/app/service/file.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"golang.org/x/text/transform"
4141

4242
"github.com/1Panel-dev/1Panel/agent/global"
43+
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
4344
"github.com/1Panel-dev/1Panel/agent/utils/common"
4445
"github.com/1Panel-dev/1Panel/agent/utils/files"
4546
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
@@ -59,6 +60,7 @@ type IFileService interface {
5960
Compress(c request.FileCompress) error
6061
StopCompress(taskID string) error
6162
DeCompress(c request.FileDeCompress) error
63+
StopDeCompress(taskID string) error
6264
GetContent(op request.FileContentReq) (response.FileInfo, error)
6365
GetPreviewContent(op request.FileContentReq) (response.FileInfo, error)
6466
SaveContent(edit request.FileEdit) error
@@ -89,7 +91,7 @@ const (
8991
fileRemarkEncodedMaxLen = 256
9092
)
9193

92-
func NewIFileService() IFileService {
94+
func NewIFileService() FileService {
9395
return &FileService{}
9496
}
9597

@@ -472,6 +474,16 @@ func preflightCompressTool(compressType files.CompressType) error {
472474
}
473475
}
474476

477+
func preflightDecompressTool(decompressType files.CompressType) error {
478+
switch decompressType {
479+
case files.Tar, files.Zip, files.TarGz, files.Rar, files.X7z:
480+
_, err := files.NewExtractShellArchiver(decompressType)
481+
return err
482+
default:
483+
return nil
484+
}
485+
}
486+
475487
func (f *FileService) StopCompress(taskID string) error {
476488
if cancel, ok := global.TaskCtxMap[taskID]; ok {
477489
cancel()
@@ -480,12 +492,62 @@ func (f *FileService) StopCompress(taskID string) error {
480492
return buserr.New("TaskNotFound")
481493
}
482494

495+
func (f *FileService) StopDeCompress(taskID string) error {
496+
if cancel, ok := global.TaskCtxMap[taskID]; ok {
497+
cancel()
498+
return nil
499+
}
500+
return buserr.New("TaskNotFound")
501+
}
502+
483503
func (f *FileService) DeCompress(c request.FileDeCompress) error {
484504
fo := files.NewFileOp()
485505
if c.Type == "tar" && len(c.Secret) != 0 {
486506
c.Type = "tar.gz"
487507
}
488-
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type), c.Secret)
508+
if err := preflightDecompressTool(files.CompressType(c.Type)); err != nil {
509+
return err
510+
}
511+
taskItem, err := task.NewTask(c.Path, task.TaskExec, task.TaskScopeTask, c.TaskID, 1)
512+
if err != nil {
513+
return err
514+
}
515+
go func() {
516+
taskItem.AddSubTask(c.Path, func(t *task.Task) error {
517+
t.LogStart(c.Path)
518+
dstExisted := fo.Stat(c.Dst)
519+
parentDir := filepath.Dir(c.Dst)
520+
if !fo.Stat(parentDir) {
521+
if err := fo.CreateDir(parentDir, constant.DirPerm); err != nil {
522+
return err
523+
}
524+
}
525+
tempDst, err := os.MkdirTemp(parentDir, ".decompress-*")
526+
if err != nil {
527+
return err
528+
}
529+
success := false
530+
defer func() {
531+
_ = os.RemoveAll(tempDst)
532+
if !success && !dstExisted {
533+
_ = os.RemoveAll(c.Dst)
534+
}
535+
}()
536+
if err := fo.Decompress(t.TaskCtx, c.Path, tempDst, files.CompressType(c.Type), c.Secret); err != nil {
537+
return err
538+
}
539+
if err := fo.CreateDir(c.Dst, constant.DirPerm); err != nil {
540+
return err
541+
}
542+
if err := cmd.NewCommandMgr(cmd.WithContext(t.TaskCtx)).RunBashCf("cp -rfp '%s'/. '%s'", tempDst, c.Dst); err != nil {
543+
return err
544+
}
545+
success = true
546+
return nil
547+
}, nil)
548+
_ = taskItem.Execute()
549+
}()
550+
return nil
489551
}
490552

491553
func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo, error) {

agent/router/ro_file.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) {
2525
fileRouter.POST("/compress", baseApi.CompressFile)
2626
fileRouter.POST("/compress/stop", baseApi.StopCompressFile)
2727
fileRouter.POST("/decompress", baseApi.DeCompressFile)
28+
fileRouter.POST("/decompress/stop", baseApi.StopDeCompressFile)
2829
fileRouter.POST("/content", baseApi.GetContent)
2930
fileRouter.POST("/preview", baseApi.PreviewContent)
3031
fileRouter.POST("/save", baseApi.SaveContent)

agent/utils/files/archiver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type ShellArchiver interface {
11-
Extract(filePath, dstDir string, secret string) error
11+
Extract(ctx context.Context, filePath, dstDir string, secret string) error
1212
Compress(ctx context.Context, sourcePaths []string, dstFile string, secret string) error
1313
}
1414

agent/utils/files/file_op.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -910,13 +910,13 @@ func decodeGBK(input string) (string, error) {
910910
return decoded, nil
911911
}
912912

913-
func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType) error {
913+
func (f FileOp) decompressWithSDK(ctx context.Context, srcFile string, dst string, cType CompressType) error {
914914
format := getFormat(cType)
915915
if cType == Gz {
916-
if err := f.tryDecompressTarGz(srcFile, dst, format); err == nil {
916+
if err := f.tryDecompressTarGz(ctx, srcFile, dst, format); err == nil {
917917
return nil
918918
}
919-
return f.DecompressGzFile(srcFile, dst)
919+
return f.DecompressGzFile(ctx, srcFile, dst)
920920
}
921921

922922
type dirEntry struct {
@@ -976,7 +976,7 @@ func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType
976976
return err
977977
}
978978
defer input.Close()
979-
if err := format.Extract(context.Background(), input, nil, handler); err != nil {
979+
if err := format.Extract(ctx, input, nil, handler); err != nil {
980980
return err
981981
}
982982
for i := len(dirs) - 1; i >= 0; i-- {
@@ -985,21 +985,21 @@ func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType
985985
return nil
986986
}
987987

988-
func (f FileOp) Decompress(srcFile string, dst string, cType CompressType, secret string) error {
988+
func (f FileOp) Decompress(ctx context.Context, srcFile string, dst string, cType CompressType, secret string) error {
989989
if cType == Tar || cType == Zip || cType == TarGz || cType == Rar || cType == X7z {
990990
shellArchiver, err := NewExtractShellArchiver(cType)
991991
if !f.Stat(dst) {
992992
_ = f.CreateDir(dst, 0755)
993993
}
994994
if err == nil {
995-
if err = shellArchiver.Extract(srcFile, dst, secret); err == nil {
995+
if err = shellArchiver.Extract(ctx, srcFile, dst, secret); err == nil {
996996
return nil
997997
}
998998
if cType == TarGz {
999999
if strings.Contains(err.Error(), "bad decrypt") {
10001000
return buserr.New("ErrBadDecrypt")
10011001
}
1002-
if err := shellArchiver.Extract(srcFile, dst, "-"); strings.Contains(err.Error(), "bad decrypt") {
1002+
if err := shellArchiver.Extract(ctx, srcFile, dst, "-"); strings.Contains(err.Error(), "bad decrypt") {
10031003
return buserr.New("ErrBadDecrypt")
10041004
}
10051005
}
@@ -1009,7 +1009,7 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType, secre
10091009
}
10101010
}
10111011
}
1012-
return f.decompressWithSDK(srcFile, dst, cType)
1012+
return f.decompressWithSDK(ctx, srcFile, dst, cType)
10131013
}
10141014

10151015
func ZipFile(ctx context.Context, files []archiver.File, dst afero.File, progress func(current, total int, message string)) error {
@@ -1088,7 +1088,7 @@ func (r *contextReader) Read(p []byte) (int, error) {
10881088
}
10891089
}
10901090

1091-
func (f FileOp) tryDecompressTarGz(srcFile string, dst string, format archiver.CompressedArchive) error {
1091+
func (f FileOp) tryDecompressTarGz(ctx context.Context, srcFile string, dst string, format archiver.CompressedArchive) error {
10921092
input, err := f.Fs.Open(srcFile)
10931093
if err != nil {
10941094
return err
@@ -1139,7 +1139,7 @@ func (f FileOp) tryDecompressTarGz(srcFile string, dst string, format archiver.C
11391139
return nil
11401140
}
11411141

1142-
if err := format.Extract(context.Background(), input, nil, handler); err != nil {
1142+
if err := format.Extract(ctx, input, nil, handler); err != nil {
11431143
return err
11441144
}
11451145
if !extracted {
@@ -1151,7 +1151,7 @@ func (f FileOp) tryDecompressTarGz(srcFile string, dst string, format archiver.C
11511151
return nil
11521152
}
11531153

1154-
func (f FileOp) DecompressGzFile(srcFile, dst string) error {
1154+
func (f FileOp) DecompressGzFile(ctx context.Context, srcFile, dst string) error {
11551155
var archiveModTime time.Time
11561156
if st, err := f.Fs.Stat(srcFile); err == nil {
11571157
archiveModTime = st.ModTime()
@@ -1163,7 +1163,7 @@ func (f FileOp) DecompressGzFile(srcFile, dst string) error {
11631163
}
11641164
defer in.Close()
11651165

1166-
gr, err := gzip.NewReader(in)
1166+
gr, err := gzip.NewReader(&contextReader{ctx: ctx, r: in})
11671167
if err != nil {
11681168
return fmt.Errorf("gzip reader creation failed: %w", err)
11691169
}

agent/utils/files/rar.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ func NewRarArchiver() ShellArchiver {
1919
return &RarArchiver{}
2020
}
2121

22-
func (z RarArchiver) Extract(filePath, dstDir string, _ string) error {
22+
func (z RarArchiver) Extract(ctx context.Context, filePath, dstDir string, _ string) error {
2323
if err := checkCmdAvailability("unrar"); err != nil {
2424
return err
2525
}
26-
return cmd.RunDefaultBashCf("unrar x -y -o+ %q %q", filePath, dstDir)
26+
return cmd.NewCommandMgr(cmd.WithContext(ctx)).RunBashCf("unrar x -y -o+ %q %q", filePath, dstDir)
2727
}
2828

2929
func (z RarArchiver) Compress(ctx context.Context, sourcePaths []string, dstFile string, _ string) (err error) {

0 commit comments

Comments
 (0)