From dcaaae366bff2b37e2c14915b7f32405729d3fe0 Mon Sep 17 00:00:00 2001 From: Noah Hsu Date: Sat, 8 Oct 2022 22:16:41 +0800 Subject: [PATCH] feat: add support for mega.nz (close 1553) --- cmd/server.go | 4 +- drivers/all.go | 1 + drivers/mega/driver.go | 192 +++++++++++++++++++++++++++++++++++++++++ drivers/mega/meta.go | 26 ++++++ drivers/mega/types.go | 40 +++++++++ drivers/mega/util.go | 3 + go.mod | 15 ++-- go.sum | 19 ++-- pkg/chanio/chanio.go | 62 +++++++++++++ 9 files changed, 341 insertions(+), 21 deletions(-) create mode 100644 drivers/mega/driver.go create mode 100644 drivers/mega/meta.go create mode 100644 drivers/mega/types.go create mode 100644 drivers/mega/util.go create mode 100644 pkg/chanio/chanio.go diff --git a/cmd/server.go b/cmd/server.go index fcbeac3d..cc700518 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -61,7 +61,7 @@ the address is defined in config file`, <-quit utils.Log.Println("Shutdown Server ...") - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { utils.Log.Fatal("Server Shutdown:", err) @@ -69,7 +69,7 @@ the address is defined in config file`, // catching ctx.Done(). timeout of 3 seconds. select { case <-ctx.Done(): - utils.Log.Println("timeout of 3 seconds.") + utils.Log.Println("timeout of 1 seconds.") } utils.Log.Println("Server exiting") }, diff --git a/drivers/all.go b/drivers/all.go index 64c2d86d..1a171306 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -15,6 +15,7 @@ import ( _ "github.com/alist-org/alist/v3/drivers/lanzou" _ "github.com/alist-org/alist/v3/drivers/local" _ "github.com/alist-org/alist/v3/drivers/mediatrack" + _ "github.com/alist-org/alist/v3/drivers/mega" _ "github.com/alist-org/alist/v3/drivers/onedrive" _ "github.com/alist-org/alist/v3/drivers/pikpak" _ "github.com/alist-org/alist/v3/drivers/quark" diff --git a/drivers/mega/driver.go b/drivers/mega/driver.go new file mode 100644 index 00000000..745c39f8 --- /dev/null +++ b/drivers/mega/driver.go @@ -0,0 +1,192 @@ +package mega + +import ( + "context" + "errors" + "fmt" + "io" + + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/errs" + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/pkg/chanio" + "github.com/alist-org/alist/v3/pkg/utils" + log "github.com/sirupsen/logrus" + "github.com/t3rm1n4l/go-mega" +) + +type Mega struct { + model.Storage + Addition + c *mega.Mega +} + +func (d *Mega) Config() driver.Config { + return config +} + +func (d *Mega) GetAddition() driver.Additional { + return d.Addition +} + +func (d *Mega) Init(ctx context.Context, storage model.Storage) error { + d.Storage = storage + err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition) + if err != nil { + return err + } + d.c = mega.New() + return d.c.Login(d.Email, d.Password) +} + +func (d *Mega) Drop(ctx context.Context) error { + return nil +} + +func (d *Mega) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { + if node, ok := dir.(*MegaNode); ok { + nodes, err := d.c.FS.GetChildren(node.Node) + if err != nil { + return nil, err + } + res := make([]model.Obj, 0) + for i := range nodes { + n := nodes[i] + if n.GetType() == mega.FILE || n.GetType() == mega.FOLDER { + res = append(res, &MegaNode{n}) + } + } + return res, nil + } + log.Errorf("can't convert: %+v", dir) + return nil, fmt.Errorf("unable to convert dir to mega node") +} + +func (d *Mega) Get(ctx context.Context, path string) (model.Obj, error) { + if path == "/" { + n := d.c.FS.GetRoot() + log.Debugf("mega root: %+v", *n) + return &MegaNode{n}, nil + } + return nil, errs.NotSupport +} + +func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { + if node, ok := file.(*MegaNode); ok { + //link, err := d.c.Link(node.Node, true) + //if err != nil { + // return nil, err + //} + //return &model.Link{URL: link}, nil + down, err := d.c.NewDownload(node.Node) + if err != nil { + return nil, err + } + //u := down.GetResourceUrl() + //u = strings.Replace(u, "http", "https", 1) + //return &model.Link{URL: u}, nil + c := chanio.New() + go func() { + defer func() { + _ = recover() + }() + log.Debugf("chunk size: %d", down.Chunks()) + for id := 0; id < down.Chunks(); id++ { + chunk, err := down.DownloadChunk(id) + if err != nil { + log.Errorf("mega down: %+v", err) + return + } + log.Debugf("id: %d,len: %d", id, len(chunk)) + //_, _, err = down.ChunkLocation(id) + //if err != nil { + // log.Errorf("mega down: %+v", err) + // return + //} + //_, err = c.Write(chunk) + _, err = c.Write(chunk) + } + err := c.Close() + if err != nil { + log.Errorf("mega down: %+v", err) + } + }() + return &model.Link{Data: c}, nil + } + return nil, fmt.Errorf("unable to convert dir to mega node") +} + +func (d *Mega) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { + if parentNode, ok := parentDir.(*MegaNode); ok { + _, err := d.c.CreateDir(dirName, parentNode.Node) + return err + } + return fmt.Errorf("unable to convert dir to mega node") +} + +func (d *Mega) Move(ctx context.Context, srcObj, dstDir model.Obj) error { + if srcNode, ok := srcObj.(*MegaNode); ok { + if dstNode, ok := dstDir.(*MegaNode); ok { + return d.c.Move(srcNode.Node, dstNode.Node) + } + } + return fmt.Errorf("unable to convert dir to mega node") +} + +func (d *Mega) Rename(ctx context.Context, srcObj model.Obj, newName string) error { + if srcNode, ok := srcObj.(*MegaNode); ok { + return d.c.Rename(srcNode.Node, newName) + } + return fmt.Errorf("unable to convert dir to mega node") +} + +func (d *Mega) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { + return errs.NotImplement +} + +func (d *Mega) Remove(ctx context.Context, obj model.Obj) error { + if node, ok := obj.(*MegaNode); ok { + return d.c.Delete(node.Node, false) + } + return fmt.Errorf("unable to convert dir to mega node") +} + +func (d *Mega) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { + if dstNode, ok := dstDir.(*MegaNode); ok { + u, err := d.c.NewUpload(dstNode.Node, stream.GetName(), stream.GetSize()) + if err != nil { + return err + } + + for id := 0; id < u.Chunks(); id++ { + _, chkSize, err := u.ChunkLocation(id) + if err != nil { + return err + } + chunk := make([]byte, chkSize) + n, err := io.ReadFull(stream, chunk) + if err != nil && err != io.EOF { + return err + } + if n != len(chunk) { + return errors.New("chunk too short") + } + + err = u.UploadChunk(id, chunk) + if err != nil { + return err + } + up(id * 100 / u.Chunks()) + } + + _, err = u.Finish() + return err + } + return fmt.Errorf("unable to convert dir to mega node") +} + +//func (d *Mega) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { +// return nil, errs.NotSupport +//} + +var _ driver.Driver = (*Mega)(nil) diff --git a/drivers/mega/meta.go b/drivers/mega/meta.go new file mode 100644 index 00000000..d66ed198 --- /dev/null +++ b/drivers/mega/meta.go @@ -0,0 +1,26 @@ +package mega + +import ( + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/op" +) + +type Addition struct { + // Usually one of two + //driver.RootPath + //driver.RootID + Email string `json:"email" required:"true"` + Password string `json:"password" required:"true"` +} + +var config = driver.Config{ + Name: "Mega_nz", + LocalSort: true, + OnlyLocal: true, +} + +func init() { + op.RegisterDriver(config, func() driver.Driver { + return &Mega{} + }) +} diff --git a/drivers/mega/types.go b/drivers/mega/types.go new file mode 100644 index 00000000..4e7b3a9a --- /dev/null +++ b/drivers/mega/types.go @@ -0,0 +1,40 @@ +package mega + +import ( + "time" + + "github.com/alist-org/alist/v3/internal/model" + "github.com/t3rm1n4l/go-mega" +) + +type MegaNode struct { + *mega.Node +} + +//func (m *MegaNode) GetSize() int64 { +// //TODO implement me +// panic("implement me") +//} +// +//func (m *MegaNode) GetName() string { +// //TODO implement me +// panic("implement me") +//} + +func (m *MegaNode) ModTime() time.Time { + return m.GetTimeStamp() +} + +func (m *MegaNode) IsDir() bool { + return m.GetType() == mega.FOLDER || m.GetType() == mega.ROOT +} + +func (m *MegaNode) GetID() string { + return m.GetHash() +} + +func (m *MegaNode) GetPath() string { + return "" +} + +var _ model.Obj = (*MegaNode)(nil) diff --git a/drivers/mega/util.go b/drivers/mega/util.go new file mode 100644 index 00000000..d31041ec --- /dev/null +++ b/drivers/mega/util.go @@ -0,0 +1,3 @@ +package mega + +// do others that not defined in Driver interface diff --git a/go.mod b/go.mod index a036a98f..17d1c276 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,28 @@ go 1.18 require ( github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a + github.com/aws/aws-sdk-go v1.44.88 github.com/caarlos0/env/v6 v6.9.3 github.com/disintegration/imaging v1.6.2 github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.8.0 + github.com/go-resty/resty/v2 v2.7.0 github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.5.0 + github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4 github.com/json-iterator/go v1.1.12 github.com/natefinch/lumberjack v2.0.0+incompatible github.com/pkg/errors v0.9.1 + github.com/pkg/sftp v1.13.5 github.com/pquerna/otp v1.3.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.5.0 + github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf + github.com/upyun/go-sdk/v3 v3.0.3 github.com/winfsp/cgofuse v1.5.0 + golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b gorm.io/driver/mysql v1.3.4 gorm.io/driver/postgres v1.3.7 gorm.io/driver/sqlite v1.3.4 @@ -25,13 +33,11 @@ require ( ) require ( - github.com/aws/aws-sdk-go v1.44.88 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.0 // indirect - github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/goccy/go-json v0.9.7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -47,7 +53,6 @@ require ( github.com/jackc/pgx/v4 v4.16.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect @@ -56,13 +61,9 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect - github.com/pkg/sftp v1.13.5 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/ugorji/go/codec v1.2.7 // indirect - github.com/upyun/go-sdk/v3 v3.0.3 // indirect - golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect - golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/protobuf v1.28.0 // indirect diff --git a/go.sum b/go.sum index f0cdaf37..1be3c684 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,7 @@ github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4 h1:8bWaY08VCoFn17gezY github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -211,9 +212,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf h1:Y43S3e9P1NPs/QF4R5/SdlXj2d31540hP4Gk8VKNvDg= +github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf/go.mod h1:c+cGNU1qi9bO7ZF4IRMYk+KaZTNiQ/gQrSbyMmGFq1Q= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= @@ -235,6 +237,7 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -246,10 +249,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0= +golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU= golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= @@ -264,8 +265,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA= -golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -285,14 +284,11 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -334,7 +330,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q= diff --git a/pkg/chanio/chanio.go b/pkg/chanio/chanio.go new file mode 100644 index 00000000..074229fe --- /dev/null +++ b/pkg/chanio/chanio.go @@ -0,0 +1,62 @@ +package chanio + +import ( + "io" + "sync/atomic" +) + +type ChanIO struct { + cl atomic.Bool + c chan []byte + buf []byte +} + +func New() *ChanIO { + return &ChanIO{ + cl: atomic.Bool{}, + c: make(chan []byte), + buf: make([]byte, 0), + } +} + +func (c *ChanIO) Read(p []byte) (int, error) { + if c.cl.Load() { + if len(c.buf) == 0 { + return 0, io.EOF + } + n := copy(p, c.buf) + if len(c.buf) > n { + c.buf = c.buf[n:] + } else { + c.buf = make([]byte, 0) + } + return n, nil + } + for len(c.buf) < len(p) && !c.cl.Load() { + c.buf = append(c.buf, <-c.c...) + } + n := copy(p, c.buf) + if len(c.buf) > n { + c.buf = c.buf[n:] + } else { + c.buf = make([]byte, 0) + } + return n, nil +} + +func (c *ChanIO) Write(p []byte) (int, error) { + if c.cl.Load() { + return 0, io.ErrClosedPipe + } + c.c <- p + return len(p), nil +} + +func (c *ChanIO) Close() error { + if c.cl.Load() { + return io.ErrClosedPipe + } + c.cl.Store(true) + close(c.c) + return nil +}