| @@ -0,0 +1,40 @@ | |||
| module gitlink.org.cn/cloudream/storage-common | |||
| require ( | |||
| github.com/baohan10/reedsolomon v0.0.0-20230406042632-43574cac9fa7 | |||
| github.com/go-sql-driver/mysql v1.7.1 | |||
| github.com/jmoiron/sqlx v1.3.5 | |||
| github.com/samber/lo v1.36.0 | |||
| github.com/smartystreets/goconvey v1.8.0 | |||
| gitlink.org.cn/cloudream/common v0.0.0 | |||
| google.golang.org/grpc v1.54.0 | |||
| google.golang.org/protobuf v1.30.0 | |||
| ) | |||
| require ( | |||
| github.com/antonfisher/nested-logrus-formatter v1.3.1 // indirect | |||
| github.com/golang/protobuf v1.5.3 // indirect | |||
| github.com/google/uuid v1.3.0 // indirect | |||
| github.com/gopherjs/gopherjs v1.17.2 // indirect | |||
| github.com/hashicorp/errwrap v1.1.0 // indirect | |||
| github.com/hashicorp/go-multierror v1.1.1 // indirect | |||
| github.com/json-iterator/go v1.1.12 // indirect | |||
| github.com/jtolds/gls v4.20.0+incompatible // indirect | |||
| github.com/klauspost/cpuid/v2 v2.2.3 // indirect | |||
| github.com/mitchellh/mapstructure v1.5.0 // indirect | |||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect | |||
| github.com/modern-go/reflect2 v1.0.2 // indirect | |||
| github.com/sirupsen/logrus v1.9.2 // indirect | |||
| github.com/smartystreets/assertions v1.13.1 // indirect | |||
| github.com/streadway/amqp v1.1.0 // indirect | |||
| github.com/zyedidia/generic v1.2.1 // indirect | |||
| golang.org/x/exp v0.0.0-20230519143937-03e91628a987 // indirect | |||
| golang.org/x/net v0.8.0 // indirect | |||
| golang.org/x/sys v0.6.0 // indirect | |||
| golang.org/x/text v0.8.0 // indirect | |||
| google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect | |||
| ) | |||
| go 1.20 | |||
| replace gitlink.org.cn/cloudream/common v0.0.0 => ../../common | |||
| @@ -0,0 +1,84 @@ | |||
| github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= | |||
| github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= | |||
| github.com/baohan10/reedsolomon v0.0.0-20230406042632-43574cac9fa7 h1:wcvD6enR///dFvb9cRodx5SGbPH4G4jPjw+aVIWkAKE= | |||
| github.com/baohan10/reedsolomon v0.0.0-20230406042632-43574cac9fa7/go.mod h1:rAxMF6pVaFK/s6T4gGczvloccNbtwzuYaP2Y7W6flE8= | |||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | |||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
| github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | |||
| github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= | |||
| github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= | |||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | |||
| github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | |||
| github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | |||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | |||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | |||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | |||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | |||
| github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= | |||
| github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= | |||
| github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | |||
| github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= | |||
| github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | |||
| github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= | |||
| github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= | |||
| github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= | |||
| github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | |||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | |||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | |||
| github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= | |||
| github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | |||
| github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= | |||
| github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= | |||
| github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= | |||
| github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | |||
| github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= | |||
| github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= | |||
| github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | |||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | |||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= | |||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | |||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | |||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | |||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |||
| github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= | |||
| github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= | |||
| github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= | |||
| github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | |||
| github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU= | |||
| github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY= | |||
| github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w= | |||
| github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg= | |||
| github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM= | |||
| github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg= | |||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |||
| github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= | |||
| github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= | |||
| github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XDc= | |||
| github.com/zyedidia/generic v1.2.1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= | |||
| golang.org/x/exp v0.0.0-20230519143937-03e91628a987 h1:3xJIFvzUFbu4ls0BTBYcgbCGhA63eAOEMxIHugyXJqA= | |||
| golang.org/x/exp v0.0.0-20230519143937-03e91628a987/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= | |||
| golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= | |||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | |||
| golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
| golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= | |||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
| golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= | |||
| golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | |||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |||
| google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU= | |||
| google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= | |||
| google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= | |||
| google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= | |||
| google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | |||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | |||
| google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= | |||
| google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | |||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | |||
| @@ -0,0 +1,73 @@ | |||
| package models | |||
| /// TODO 将分散在各处的公共结构体定义集中到这里来 | |||
| const ( | |||
| RedundancyRep = "rep" | |||
| RedundancyEC = "ec" | |||
| ) | |||
| type RedundancyConfigTypes interface{} | |||
| type RedundancyConfigTypesConst interface { | |||
| RepRedundancyConfig | ECRedundancyConfig | |||
| } | |||
| type RepRedundancyConfig struct { | |||
| RepCount int `json:"repCount"` | |||
| } | |||
| type ECRedundancyConfig struct { | |||
| } | |||
| type RedundancyDataTypes interface{} | |||
| type RedundancyDataTypesConst interface { | |||
| RepRedundancyData | ECRedundancyData | |||
| } | |||
| type RepRedundancyData struct { | |||
| FileHash string `json:"fileHash"` | |||
| } | |||
| func NewRedundancyRepData(fileHash string) RepRedundancyData { | |||
| return RepRedundancyData{ | |||
| FileHash: fileHash, | |||
| } | |||
| } | |||
| type ECRedundancyData struct { | |||
| Ec EC `json:"ec"` | |||
| Blocks []ObjectBlock `json:"blocks"` | |||
| } | |||
| func NewRedundancyEcData(ec EC, blocks []ObjectBlock) ECRedundancyData { | |||
| return ECRedundancyData{ | |||
| Ec: ec, | |||
| Blocks: blocks, | |||
| } | |||
| } | |||
| type EC struct { | |||
| ID int `json:"id"` | |||
| Name string `json:"name"` | |||
| EcK int `json:"ecK"` | |||
| EcN int `json:"ecN"` | |||
| } | |||
| type ObjectBlock struct { | |||
| Index int `json:"index"` | |||
| FileHash string `json:"fileHash"` | |||
| } | |||
| func NewObjectBlock(index int, fileHash string) ObjectBlock { | |||
| return ObjectBlock{ | |||
| Index: index, | |||
| FileHash: fileHash, | |||
| } | |||
| } | |||
| func NewEc(id int, name string, ecK int, ecN int) EC{ | |||
| return EC{ | |||
| ID: id, | |||
| Name: name, | |||
| EcK: ecK, | |||
| EcN: ecN, | |||
| } | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| package db | |||
| import ( | |||
| "database/sql" | |||
| "errors" | |||
| "fmt" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type BucketDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) Bucket() *BucketDB { | |||
| return &BucketDB{DB: db} | |||
| } | |||
| // GetIDByName 根据BucketName查询BucketID | |||
| func (db *BucketDB) GetIDByName(bucketName string) (int64, error) { | |||
| //桶结构体 | |||
| var result struct { | |||
| BucketID int64 `db:"BucketID"` | |||
| BucketName string `db:"BucketName"` | |||
| } | |||
| sql := "select BucketID, BucketName from Bucket where BucketName=? " | |||
| if err := db.d.Get(&result, sql, bucketName); err != nil { | |||
| return 0, err | |||
| } | |||
| return result.BucketID, nil | |||
| } | |||
| // IsAvailable 判断用户是否有指定Bucekt的权限 | |||
| func (db *BucketDB) IsAvailable(ctx SQLContext, bucketID int64, userID int64) (bool, error) { | |||
| _, err := db.GetUserBucket(ctx, userID, bucketID) | |||
| if errors.Is(err, sql.ErrNoRows) { | |||
| return false, nil | |||
| } | |||
| if err != nil { | |||
| return false, fmt.Errorf("find bucket failed, err: %w", err) | |||
| } | |||
| return true, nil | |||
| } | |||
| func (*BucketDB) GetUserBucket(ctx SQLContext, userID int64, bucketID int64) (model.Bucket, error) { | |||
| var ret model.Bucket | |||
| err := sqlx.Get(ctx, &ret, | |||
| "select Bucket.* from UserBucket, Bucket where UserID = ? and "+ | |||
| "UserBucket.BucketID = Bucket.BucketID and "+ | |||
| "Bucket.BucketID = ?", userID, bucketID) | |||
| return ret, err | |||
| } | |||
| func (*BucketDB) GetUserBuckets(ctx SQLContext, userID int64) ([]model.Bucket, error) { | |||
| var ret []model.Bucket | |||
| err := sqlx.Select(ctx, &ret, "select Bucket.* from UserBucket, Bucket where UserID = ? and UserBucket.BucketID = Bucket.BucketID", userID) | |||
| return ret, err | |||
| } | |||
| func (db *BucketDB) Create(ctx SQLContext, userID int64, bucketName string) (int64, error) { | |||
| var bucketID int64 | |||
| err := sqlx.Get(ctx, &bucketID, "select Bucket.BucketID from UserBucket, Bucket where UserBucket.UserID = ? and UserBucket.BucketID = Bucket.BucketID and Bucket.Name = ?", userID, bucketName) | |||
| if err == nil { | |||
| return 0, fmt.Errorf("bucket name exsits") | |||
| } | |||
| if err != sql.ErrNoRows { | |||
| return 0, err | |||
| } | |||
| ret, err := ctx.Exec("insert into Bucket(Name,CreatorID) values(?,?)", bucketName, userID) | |||
| if err != nil { | |||
| return 0, fmt.Errorf("insert bucket failed, err: %w", err) | |||
| } | |||
| bucketID, err = ret.LastInsertId() | |||
| if err != nil { | |||
| return 0, fmt.Errorf("get inserted bucket id failed, err: %w", err) | |||
| } | |||
| _, err = ctx.Exec("insert into UserBucket(UserID,BucketID) values(?,?)", userID, bucketID) | |||
| if err != nil { | |||
| return 0, fmt.Errorf("insert into user bucket failed, err: %w", err) | |||
| } | |||
| return bucketID, err | |||
| } | |||
| func (db *BucketDB) Delete(ctx SQLContext, bucketID int64) error { | |||
| _, err := ctx.Exec("delete from UserBucket where BucketID = ?", bucketID) | |||
| if err != nil { | |||
| return fmt.Errorf("delete user bucket failed, err: %w", err) | |||
| } | |||
| _, err = ctx.Exec("delete from Bucket where BucketID = ?", bucketID) | |||
| if err != nil { | |||
| return fmt.Errorf("delete bucket failed, err: %w", err) | |||
| } | |||
| // 删除Bucket内的Object | |||
| var objIDs []int64 | |||
| err = sqlx.Select(ctx, &objIDs, "select ObjectID from Object where BucketID = ?", bucketID) | |||
| if err != nil { | |||
| return fmt.Errorf("query object failed, err: %w", err) | |||
| } | |||
| for _, objID := range objIDs { | |||
| // TODO 不一定所有的错误都要中断后续过程 | |||
| err = db.Object().SoftDelete(ctx, objID) | |||
| if err != nil { | |||
| return fmt.Errorf("set object seleted failed, err: %w", err) | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| @@ -0,0 +1,97 @@ | |||
| package db | |||
| import ( | |||
| "time" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/common/consts" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type CacheDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) Cache() *CacheDB { | |||
| return &CacheDB{DB: db} | |||
| } | |||
| func (*CacheDB) Get(ctx SQLContext, fileHash string, nodeID int64) (model.Cache, error) { | |||
| var ret model.Cache | |||
| err := sqlx.Get(ctx, &ret, "select * from Cache where FileHash = ? and NodeID = ?", fileHash, nodeID) | |||
| return ret, err | |||
| } | |||
| func (*CacheDB) BatchGetAllFileHashes(ctx SQLContext, start int, count int) ([]string, error) { | |||
| var ret []string | |||
| err := sqlx.Select(ctx, &ret, "select distinct FileHash from Cache limit ?, ?", start, count) | |||
| return ret, err | |||
| } | |||
| func (*CacheDB) GetNodeCaches(ctx SQLContext, nodeID int64) ([]model.Cache, error) { | |||
| var ret []model.Cache | |||
| err := sqlx.Select(ctx, &ret, "select * from Cache where NodeID = ?", nodeID) | |||
| return ret, err | |||
| } | |||
| // CreateNew 创建一条新的缓存记录 | |||
| func (*CacheDB) CreateNew(ctx SQLContext, fileHash string, nodeID int64) error { | |||
| _, err := ctx.Exec("insert into Cache values(?,?,?,?)", fileHash, nodeID, consts.CacheStatePinned, time.Now()) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| // CreatePinned 创建一条缓存记录,如果已存在,但不是pinned状态,则将其设置为pin状态 | |||
| func (*CacheDB) CreatePinned(ctx SQLContext, fileHash string, nodeID int64, priority int) error { | |||
| _, err := ctx.Exec("replace into Cache values(?,?,?,?,?)", fileHash, nodeID, consts.CacheStatePinned, time.Now(), priority) | |||
| return err | |||
| } | |||
| // Create 创建一条Temp状态的缓存记录,如果已存在则不产生效果 | |||
| func (*CacheDB) CreateTemp(ctx SQLContext, fileHash string, nodeID int64) error { | |||
| _, err := ctx.Exec("insert ignore into Cache values(?,?,?,?)", fileHash, nodeID, consts.CacheStateTemp, time.Now()) | |||
| return err | |||
| } | |||
| // GetCachingFileNodes 查找缓存了指定文件的节点 | |||
| func (*CacheDB) GetCachingFileNodes(ctx SQLContext, fileHash string) ([]model.Node, error) { | |||
| var x []model.Node | |||
| err := sqlx.Select(ctx, &x, | |||
| "select Node.* from Cache, Node where Cache.FileHash=? and Cache.NodeID = Node.NodeID", fileHash) | |||
| return x, err | |||
| } | |||
| // DeleteTemp 删除一条Temp状态的记录 | |||
| func (*CacheDB) DeleteTemp(ctx SQLContext, fileHash string, nodeID int64) error { | |||
| _, err := ctx.Exec("delete from Cache where FileHash = ? and NodeID = ? and State = ?", fileHash, nodeID, consts.CacheStateTemp) | |||
| return err | |||
| } | |||
| // DeleteNodeAll 删除一个节点所有的记录 | |||
| func (*CacheDB) DeleteNodeAll(ctx SQLContext, nodeID int64) error { | |||
| _, err := ctx.Exec("delete from Cache where NodeID = ?", nodeID) | |||
| return err | |||
| } | |||
| // FindCachingFileUserNodes 在缓存表中查询指定数据所在的节点 | |||
| func (*CacheDB) FindCachingFileUserNodes(ctx SQLContext, userID int64, fileHash string) ([]model.Node, error) { | |||
| var x []model.Node | |||
| err := sqlx.Select(ctx, &x, | |||
| "select Node.* from Cache, UserNode, Node where "+ | |||
| "Cache.FileHash=? and Cache.NodeID = UserNode.NodeID and "+ | |||
| "UserNode.UserID = ? and UserNode.NodeID = Node.NodeID", fileHash, userID) | |||
| return x, err | |||
| } | |||
| func (*CacheDB) SetTemp(ctx SQLContext, fileHash string, nodeID int64) error { | |||
| _, err := ctx.Exec("update Cache set State = ?, CacheTime = ? where FileHash = ? and NodeID = ?", | |||
| consts.CacheStateTemp, | |||
| time.Now(), | |||
| fileHash, | |||
| nodeID, | |||
| ) | |||
| return err | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| package config | |||
| import "fmt" | |||
| type Config struct { | |||
| Address string `json:"address"` | |||
| Account string `json:"account"` | |||
| Password string `json:"password"` | |||
| DatabaseName string `json:"databaseName"` | |||
| } | |||
| func (cfg *Config) MakeSourceString() string { | |||
| return fmt.Sprintf( | |||
| "%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=%s", | |||
| cfg.Account, | |||
| cfg.Password, | |||
| cfg.Address, | |||
| cfg.DatabaseName, | |||
| "Asia%2FShanghai", | |||
| ) | |||
| } | |||
| @@ -0,0 +1,238 @@ | |||
| package db | |||
| import ( | |||
| "context" | |||
| "database/sql" | |||
| "errors" | |||
| "fmt" | |||
| "time" | |||
| _ "github.com/go-sql-driver/mysql" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/common/consts" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/config" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type DB struct { | |||
| d *sqlx.DB | |||
| } | |||
| type SQLContext interface { | |||
| sqlx.Queryer | |||
| sqlx.Execer | |||
| } | |||
| func NewDB(cfg *config.Config) (*DB, error) { | |||
| db, err := sqlx.Open("mysql", cfg.MakeSourceString()) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("open database connection failed, err: %w", err) | |||
| } | |||
| // 尝试连接一下数据库,如果数据库配置有错误在这里就能报出来 | |||
| err = db.Ping() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return &DB{ | |||
| d: db, | |||
| }, nil | |||
| } | |||
| func (db *DB) DoTx(isolation sql.IsolationLevel, fn func(tx *sqlx.Tx) error) error { | |||
| tx, err := db.d.BeginTxx(context.Background(), &sql.TxOptions{Isolation: isolation}) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if err := fn(tx); err != nil { | |||
| tx.Rollback() | |||
| return err | |||
| } | |||
| if err := tx.Commit(); err != nil { | |||
| tx.Rollback() | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func (db *DB) SQLCtx() SQLContext { | |||
| return db.d | |||
| } | |||
| // 纠删码对象表插入 | |||
| // TODO 需要使用事务保证查询之后插入的正确性 | |||
| func (db *DB) InsertECObject(objectName string, bucketID int, fileSize int64, ecName string) (int64, error) { | |||
| // TODO 参考CreateRepObject重写 | |||
| // 根据objectname和bucketid查询,若不存在则插入,若存在则不操作 | |||
| //查询 | |||
| /*type Object struct { | |||
| ObjectID int64 `db:"ObjectID"` | |||
| Name string `db:"Name"` | |||
| BucketID int `db:"BucketID"` | |||
| } | |||
| var x Object | |||
| err := db.d.Get(&x, "select ObjectID, Name, BucketID from Object where Name=? AND BucketID=?", objectName, bucketID) | |||
| //不存在才插入 | |||
| if errors.Is(err, sql.ErrNoRows) { | |||
| sql := "insert into Object(Name, BucketID, FileSize, Redundancy, NumRep, EcName) values(?,?,?,?,?,?)" | |||
| r, err := db.d.Exec(sql, objectName, bucketID, fileSize, false, "-1", ecName) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| id, err := r.LastInsertId() | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| // TODO 需要考虑失败后的处理 | |||
| return id, nil | |||
| } else if err == nil { | |||
| return x.ObjectID, nil | |||
| } | |||
| return 0, err*/ | |||
| panic("not implement yet") | |||
| } | |||
| // QueryObjectBlock 查询对象编码块表 | |||
| func (db *DB) QueryObjectBlock(objectID int64) ([]model.ObjectBlock, error) { | |||
| var x []model.ObjectBlock | |||
| sql := "select * from ObjectBlock where ObjectID=?" | |||
| err := db.d.Select(&x, sql, objectID) | |||
| return x, err | |||
| } | |||
| // 对象编码块表Echash插入 | |||
| func (db *DB) InsertECHash(objectID int64, hashes []string) { | |||
| for i := 0; i < len(hashes); i++ { | |||
| sql := "update ObjectBlock set BlockHash =? where ObjectID = ? AND InnerID = ?" | |||
| // TODO 需要处理错误 | |||
| db.d.Exec(sql, hashes[i], objectID, i) | |||
| } | |||
| } | |||
| // 对象编码块表插入 | |||
| func (db *DB) InsertEcObjectBlock(objectID int64, innerID int) error { | |||
| // 根据objectID查询,若不存在则插入,若存在则不操作 | |||
| _, err := db.d.Exec( | |||
| "insert into ObjectBlock(ObjectID, InnerID) select ?, ? where not exists (select ObjectID from ObjectBlock where ObjectID=? AND InnerID=?)", | |||
| objectID, | |||
| innerID, | |||
| objectID, | |||
| innerID, | |||
| ) | |||
| return err | |||
| } | |||
| // BatchInsertOrUpdateCache 批量更新缓存表 | |||
| func (db *DB) BatchInsertOrUpdateCache(blockHashes []string, nodeID int64) error { | |||
| //jh:将hashs中的hash,IP插入缓存表中,TempOrPin字段为true,Time为插入时的时间戳 | |||
| //-如果要插入的hash、IP在表中已存在且所对应的TempOrPin字段为false,则不做任何操作 | |||
| //-如果要插入的hash、IP在表中已存在且所对应的TempOrPin字段为true,则更新Time | |||
| tx, err := db.d.BeginTxx(context.Background(), &sql.TxOptions{ | |||
| Isolation: sql.LevelSerializable, | |||
| }) | |||
| if err != nil { | |||
| return fmt.Errorf("start transaction failed, err: %w", err) | |||
| } | |||
| for _, blockHash := range blockHashes { | |||
| //根据hash和nodeip查询缓存表里是否存在此条记录 | |||
| var cache model.Cache | |||
| err := tx.Get( | |||
| &cache, | |||
| "select NodeID, TempOrPin, Cachetime from Cache where FileHash=? AND NodeID=?", | |||
| blockHash, | |||
| nodeID, | |||
| ) | |||
| // 不存在记录则创建新记录 | |||
| if errors.Is(err, sql.ErrNoRows) { | |||
| _, err := tx.Exec("insert into Cache values(?,?,?,?)", blockHash, nodeID, true, time.Now()) | |||
| if err != nil { | |||
| tx.Rollback() | |||
| return fmt.Errorf("insert cache failed, err: %w", err) | |||
| } | |||
| } else if err == nil && cache.State == consts.CacheStateTemp { | |||
| //若在表中已存在且所对应的TempOrPin字段为true,则更新Time | |||
| _, err := tx.Exec( | |||
| "update Cache set Cachetime=? where FileHash=? AND NodeID=?", | |||
| time.Now(), | |||
| blockHash, | |||
| nodeID, | |||
| ) | |||
| if err != nil { | |||
| tx.Rollback() | |||
| return fmt.Errorf("update cache failed, err: %w", err) | |||
| } | |||
| } | |||
| } | |||
| err = tx.Commit() | |||
| if err != nil { | |||
| tx.Rollback() | |||
| return fmt.Errorf("commit transaction failed, err: %w", err) | |||
| } | |||
| return nil | |||
| } | |||
| // 查询节点延迟表 | |||
| func (db *DB) QueryNodeDelay(inNodeIP string, outNodeIP string) (int, error) { | |||
| //节点延迟结构体 | |||
| var x struct { | |||
| DelayInMs int `db:"DelayInMs"` | |||
| } | |||
| sql := "select DelayInMs from NodeDelay where InNodeIP=? AND OutNodeIP=?" | |||
| err := db.d.Get(&x, sql, inNodeIP, outNodeIP) | |||
| return x.DelayInMs, err | |||
| } | |||
| // 节点延迟表插入 | |||
| // TODO 需要使用事务确保插入的记录完整 | |||
| func (db *DB) InsertNodeDelay(srcNodeID int64, dstNodeIDs []int64, delay []int) { | |||
| insSql := "insert into NodeDelay values(?,?,?)" | |||
| updateSql := "UPDATE NodeDelay SET DelayInMs=? WHERE SourceNodeID=? AND DestinationNodeID=?" | |||
| for i := 0; i < len(dstNodeIDs); i++ { | |||
| _, err := db.d.Exec(insSql, srcNodeID, dstNodeIDs[i], delay[i]) | |||
| if err != nil { | |||
| // TODO 处理错误 | |||
| db.d.Exec(updateSql, delay[i], srcNodeID, dstNodeIDs[i]) | |||
| } | |||
| } | |||
| } | |||
| // 节点表插入 | |||
| // TODO 需要使用事务保证查询之后插入的正确性 | |||
| func (db *DB) InsertNode(nodeip string, nodelocation string, ipfsstatus string, localdirstatus string) error { | |||
| // 根据NodeIP查询,若不存在则插入,若存在则更新 | |||
| //查询 | |||
| type Node struct { | |||
| NodeIP string `db:"NodeIP"` | |||
| } | |||
| var x Node | |||
| err := db.d.Get(&x, "select NodeIP from Node where NodeIP=?", nodeip) | |||
| //local和ipfs同时可达才可达 | |||
| // TODO 将status字段改成字符串(枚举值) | |||
| NodeStatus := ipfsstatus == consts.IPFSStateOK && localdirstatus == consts.StorageDirectoryStateOK | |||
| //不存在才插入 | |||
| if errors.Is(err, sql.ErrNoRows) { | |||
| sql := "insert into Node values(?,?,?)" | |||
| _, err := db.d.Exec(sql, nodeip, nodelocation, NodeStatus) | |||
| return err | |||
| } | |||
| //存在则更新 | |||
| sql := "update Node set NodeStatus=? where NodeIP=?" | |||
| _, err = db.d.Exec(sql, NodeStatus, nodeip) | |||
| return err | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package db | |||
| import ( | |||
| //"database/sql" | |||
| "github.com/jmoiron/sqlx" | |||
| //"gitlink.org.cn/cloudream/common/consts" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type EcDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) Ec() *EcDB { | |||
| return &EcDB{DB: db} | |||
| } | |||
| // GetEc 查询纠删码参数 | |||
| func (db *EcDB) GetEc(ctx SQLContext, ecName string) (model.Ec, error) { | |||
| var ret model.Ec | |||
| err := sqlx.Get(ctx, &ret, "select * from Ec where Name = ?", ecName) | |||
| return ret, err | |||
| } | |||
| func (db *EcDB) GetEcName(ctx SQLContext, objectID int) (string, error) { | |||
| var ret string | |||
| err := sqlx.Get(ctx, &ret, "select Redundancy from Object where ObjectID = ?") | |||
| return ret, err | |||
| } | |||
| @@ -0,0 +1,103 @@ | |||
| package model | |||
| import "time" | |||
| type Node struct { | |||
| NodeID int64 `db:"NodeID" json:"nodeID"` | |||
| Name string `db:"Name" json:"name"` | |||
| LocalIP string `db:"LocalIP" json:"localIP"` | |||
| ExternalIP string `db:"ExternalIP" json:"externalIP"` | |||
| LocationID int64 `db:"LocationID" json:"locationID"` | |||
| State string `db:"State" json:"state"` | |||
| LastReportTime *time.Time `db:"LastReportTime" json:"lastReportTime"` | |||
| } | |||
| type Storage struct { | |||
| StorageID int64 `db:"StorageID" json:"storageID"` | |||
| Name string `db:"Name" json:"name"` | |||
| NodeID int64 `db:"NodeID" json:"nodeID"` | |||
| Directory string `db:"Directory" json:"directory"` | |||
| State string `db:"State" json:"state"` | |||
| } | |||
| type NodeDelay struct { | |||
| SourceNodeID int64 `db:"SourceNodeID"` | |||
| DestinationNodeID int64 `db:"DestinationNodeID"` | |||
| DelayInMs int `db:"DelayInMs"` | |||
| } | |||
| type User struct { | |||
| UserID int64 `db:"UserID" json:"userID"` | |||
| Password string `db:"PassWord" json:"password"` | |||
| } | |||
| type UserBucket struct { | |||
| UserID int64 `db:"UserID" json:"userID"` | |||
| BucketID int64 `db:"BucketID" json:"bucketID"` | |||
| } | |||
| type UserNode struct { | |||
| UserID int64 `db:"UserID" json:"userID"` | |||
| NodeID int64 `db:"NodeID" json:"nodeID"` | |||
| } | |||
| type UserStorage struct { | |||
| UserID int64 `db:"UserID" json:"userID"` | |||
| StorageID int64 `db:"StorageID" json:"storageID"` | |||
| } | |||
| type Bucket struct { | |||
| BucketID int64 `db:"BucketID" json:"bucketID"` | |||
| Name string `db:"Name" json:"name"` | |||
| CreatorID int64 `db:"CreatorID" json:"creatorID"` | |||
| } | |||
| type Object struct { | |||
| ObjectID int64 `db:"ObjectID" json:"objectID"` | |||
| Name string `db:"Name" json:"name"` | |||
| BucketID int64 `db:"BucketID" json:"bucketID"` | |||
| State string `db:"State" json:"state"` | |||
| FileSize int64 `db:"FileSize" json:"fileSize,string"` | |||
| Redundancy string `db:"Redundancy" json:"redundancy"` | |||
| DirName string `db:"DirName" json:"dirName"` | |||
| } | |||
| type ObjectRep struct { | |||
| ObjectID int64 `db:"ObjectID" json:"objectID"` | |||
| RepCount int `db:"RepCount" json:"repCount"` | |||
| FileHash string `db:"FileHash" json:"fileHash"` | |||
| } | |||
| type ObjectBlock struct { | |||
| BlockID int64 `db:"BlockID" json:"blockID"` | |||
| ObjectID int64 `db:"ObjectID" json:"objectID"` | |||
| InnerID int `db:"InnerID" json:"innerID"` | |||
| BlockHash string `db:"BlockHash" json:"blockHash"` | |||
| } | |||
| type Cache struct { | |||
| FileHash string `db:"FileHash" json:"fileHash"` | |||
| NodeID int64 `db:"NodeID" json:"nodeID"` | |||
| State string `db:"State" json:"state"` | |||
| CacheTime time.Time `db:"CacheTime" json:"cacheTime"` | |||
| Priority int `db:"Priority" json:"priority"` | |||
| } | |||
| type StorageObject struct { | |||
| ObjectID int64 `db:"ObjectID" json:"objectID"` | |||
| StorageID int64 `db:"StorageID" json:"storageID"` | |||
| UserID int64 `db:"UserID" json:"userID"` | |||
| State string `db:"State" json:"state"` | |||
| } | |||
| type Location struct { | |||
| LocationID int64 `db:"LocationID" json:"locationID"` | |||
| Name string `db:"Name" json:"name"` | |||
| } | |||
| type Ec struct { | |||
| EcID int `db:"EcID" json:"ecID"` | |||
| Name string `db:"Name" json:"name"` | |||
| EcK int `db:"EcK" json:"ecK"` | |||
| EcN int `db:"EcN" json:"ecN"` | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package db | |||
| import ( | |||
| "time" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type NodeDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) Node() *NodeDB { | |||
| return &NodeDB{DB: db} | |||
| } | |||
| func (db *NodeDB) GetByID(ctx SQLContext, nodeID int64) (model.Node, error) { | |||
| var ret model.Node | |||
| err := sqlx.Get(ctx, &ret, "select * from Node where NodeID = ?", nodeID) | |||
| return ret, err | |||
| } | |||
| func (db *NodeDB) GetAllNodes(ctx SQLContext) ([]model.Node, error) { | |||
| var ret []model.Node | |||
| err := sqlx.Select(ctx, &ret, "select * from Node") | |||
| return ret, err | |||
| } | |||
| // GetByExternalIP 根据外网IP查找节点 | |||
| func (db *NodeDB) GetByExternalIP(ctx SQLContext, exterIP string) (model.Node, error) { | |||
| var ret model.Node | |||
| err := sqlx.Get(ctx, &ret, "select * from Node where ExternalIP = ?", exterIP) | |||
| return ret, err | |||
| } | |||
| // GetUserNodes 根据用户id查询可用node | |||
| func (db *NodeDB) GetUserNodes(ctx SQLContext, userID int64) ([]model.Node, error) { | |||
| var nodes []model.Node | |||
| err := sqlx.Select(ctx, &nodes, "select Node.* from UserNode, Node where UserNode.NodeID = Node.NodeID and UserNode.UserID=?", userID) | |||
| return nodes, err | |||
| } | |||
| // UpdateState 更新状态,并且设置上次上报时间为现在 | |||
| func (db *NodeDB) UpdateState(ctx SQLContext, nodeID int64, state string) error { | |||
| _, err := ctx.Exec("update Node set State = ?, LastReportTime = ? where NodeID = ?", state, time.Now(), nodeID) | |||
| return err | |||
| } | |||
| @@ -0,0 +1,316 @@ | |||
| package db | |||
| import ( | |||
| "database/sql" | |||
| "errors" | |||
| "fmt" | |||
| "github.com/jmoiron/sqlx" | |||
| "github.com/samber/lo" | |||
| "gitlink.org.cn/cloudream/common/consts" | |||
| "gitlink.org.cn/cloudream/common/models" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type ObjectDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) Object() *ObjectDB { | |||
| return &ObjectDB{DB: db} | |||
| } | |||
| func (db *ObjectDB) GetByID(ctx SQLContext, objectID int64) (model.Object, error) { | |||
| var ret model.Object | |||
| err := sqlx.Get(ctx, &ret, "select * from Object where ObjectID = ?", objectID) | |||
| return ret, err | |||
| } | |||
| func (db *ObjectDB) GetByName(ctx SQLContext, bucketID int64, name string) (model.Object, error) { | |||
| var ret model.Object | |||
| err := sqlx.Get(ctx, &ret, "select * from Object where BucketID = ? and Name = ?", bucketID, name) | |||
| return ret, err | |||
| } | |||
| func (db *ObjectDB) GetBucketObjects(ctx SQLContext, userID int64, bucketID int64) ([]model.Object, error) { | |||
| var ret []model.Object | |||
| err := sqlx.Select(ctx, &ret, "select Object.* from UserBucket, Object where UserID = ? and UserBucket.BucketID = ? and UserBucket.BucketID = Object.BucketID", userID, bucketID) | |||
| return ret, err | |||
| } | |||
| func (db *ObjectDB) GetByDirName(ctx SQLContext, dirName string) ([]model.Object, error) { | |||
| var ret []model.Object | |||
| err := sqlx.Select(ctx, &ret, "select * from Object where DirName = ? ", dirName) | |||
| return ret, err | |||
| } | |||
| // IsAvailable 判断一个用户是否拥有指定对象 | |||
| func (db *ObjectDB) IsAvailable(ctx SQLContext, userID int64, objectID int64) (bool, error) { | |||
| var objID int64 | |||
| // 先根据ObjectID找到Object,然后判断此Object所在的Bucket是不是归此用户所有 | |||
| err := sqlx.Get(ctx, &objID, | |||
| "select Object.ObjectID from Object, UserBucket where "+ | |||
| "Object.ObjectID = ? and "+ | |||
| "Object.BucketID = UserBucket.BucketID and "+ | |||
| "UserBucket.UserID = ?", | |||
| objectID, userID) | |||
| if err == sql.ErrNoRows { | |||
| return false, nil | |||
| } | |||
| if err != nil { | |||
| return false, fmt.Errorf("find object failed, err: %w", err) | |||
| } | |||
| return true, nil | |||
| } | |||
| // GetUserObject 获得Object,如果用户没有权限访问,则不会获得结果 | |||
| func (db *ObjectDB) GetUserObject(ctx SQLContext, userID int64, objectID int64) (model.Object, error) { | |||
| var ret model.Object | |||
| err := sqlx.Get(ctx, &ret, | |||
| "select Object.* from Object, UserBucket where "+ | |||
| "Object.ObjectID = ? and "+ | |||
| "Object.BucketID = UserBucket.BucketID and "+ | |||
| "UserBucket.UserID = ?", | |||
| objectID, userID) | |||
| return ret, err | |||
| } | |||
| // CreateRepObject 创建多副本对象相关的记录 | |||
| func (db *ObjectDB) CreateRepObject(ctx SQLContext, bucketID int64, objectName string, fileSize int64, repCount int, nodeIDs []int64, fileHash string, dirName string) (int64, error) { | |||
| // 根据objectname和bucketid查询,若不存在则插入,若存在则返回错误 | |||
| var objectID int64 | |||
| err := sqlx.Get(ctx, &objectID, "select ObjectID from Object where Name = ? AND BucketID = ?", objectName, bucketID) | |||
| // 无错误代表存在记录 | |||
| if err == nil { | |||
| return 0, fmt.Errorf("object with given Name and BucketID already exists") | |||
| } | |||
| // 错误不是记录不存在 | |||
| if err != nil && !errors.Is(err, sql.ErrNoRows) { | |||
| return 0, fmt.Errorf("query Object by ObjectName and BucketID failed, err: %w", err) | |||
| } | |||
| // 创建对象的记录 | |||
| sql := "insert into Object(Name, BucketID, State, FileSize, Redundancy, DirName) values(?,?,?,?,?,?)" | |||
| r, err := ctx.Exec(sql, objectName, bucketID, consts.ObjectStateNormal, fileSize, models.RedundancyRep, dirName) | |||
| if err != nil { | |||
| return 0, fmt.Errorf("insert object failed, err: %w", err) | |||
| } | |||
| objectID, err = r.LastInsertId() | |||
| if err != nil { | |||
| return 0, fmt.Errorf("get id of inserted object failed, err: %w", err) | |||
| } | |||
| // 创建对象副本的记录 | |||
| _, err = ctx.Exec("insert into ObjectRep(ObjectID, RepCount, FileHash) values(?,?,?)", objectID, repCount, fileHash) | |||
| if err != nil { | |||
| return 0, fmt.Errorf("insert object rep failed, err: %w", err) | |||
| } | |||
| // 创建缓存记录 | |||
| priority := 0 //优先级暂时设置为0 | |||
| for _, nodeID := range nodeIDs { | |||
| err = db.Cache().CreatePinned(ctx, fileHash, nodeID, priority) | |||
| if err != nil { | |||
| return 0, fmt.Errorf("create cache failed, err: %w", err) | |||
| } | |||
| } | |||
| return objectID, nil | |||
| } | |||
| func (db *ObjectDB) CreateEcObject(ctx SQLContext, bucketID int64, objectName string, fileSize int64, userID int64, nodeIDs []int64, hashs []string, ecName string, dirName string) (int64, error) { | |||
| // 根据objectname和bucketid查询,若不存在则插入,若存在则返回错误 | |||
| var objectID int64 | |||
| err := sqlx.Get(ctx, &objectID, "select ObjectID from Object where Name = ? AND BucketID = ?", objectName, bucketID) | |||
| // 无错误代表存在记录 | |||
| if err == nil { | |||
| return 0, fmt.Errorf("object with given Name and BucketID already exists") | |||
| } | |||
| // 错误不是记录不存在 | |||
| if err != nil && !errors.Is(err, sql.ErrNoRows) { | |||
| return 0, fmt.Errorf("query Object by ObjectName and BucketID failed, err: %w", err) | |||
| } | |||
| // 创建对象的记录 | |||
| sql := "insert into Object(Name, BucketID, State, FileSize, Redundancy, DirName) values(?,?,?,?,?,?)" | |||
| r, err := ctx.Exec(sql, objectName, bucketID, consts.ObjectStateNormal, fileSize, ecName, dirName) | |||
| if err != nil { | |||
| return 0, fmt.Errorf("insert Ec object failed, err: %w", err) | |||
| } | |||
| objectID, err = r.LastInsertId() | |||
| if err != nil { | |||
| return 0, fmt.Errorf("get id of inserted object failed, err: %w", err) | |||
| } | |||
| // 创建编码块的记录 | |||
| for i := 0; i < len(hashs); i++ { | |||
| _, err = ctx.Exec("insert into ObjectBlock(ObjectID, InnerID, BlockHash) values(?,?,?)", objectID, i, hashs[i]) | |||
| if err != nil { | |||
| return 0, fmt.Errorf("insert object rep failed, err: %w", err) | |||
| } | |||
| } | |||
| // 创建缓存记录 | |||
| priority := 0 //优先级暂时设置为0 | |||
| i := 0 | |||
| for _, nodeID := range nodeIDs { | |||
| err = db.Cache().CreatePinned(ctx, hashs[i], nodeID, priority) | |||
| i += 1 | |||
| if err != nil { | |||
| return 0, fmt.Errorf("create cache failed, err: %w", err) | |||
| } | |||
| } | |||
| return objectID, nil | |||
| } | |||
| func (db *ObjectDB) UpdateRepObject(ctx SQLContext, objectID int64, fileSize int64, nodeIDs []int64, fileHash string) error { | |||
| obj, err := db.GetByID(ctx, objectID) | |||
| if err != nil { | |||
| return fmt.Errorf("get object failed, err: %w", err) | |||
| } | |||
| if obj.Redundancy != models.RedundancyRep { | |||
| return fmt.Errorf("object is not a rep object") | |||
| } | |||
| _, err = db.UpdateFileInfo(ctx, objectID, fileSize) | |||
| if err != nil { | |||
| if err != nil { | |||
| return fmt.Errorf("update rep object failed, err: %w", err) | |||
| } | |||
| } | |||
| objRep, err := db.ObjectRep().GetByID(ctx, objectID) | |||
| if err != nil { | |||
| return fmt.Errorf("get object rep failed, err: %w", err) | |||
| } | |||
| // 如果新文件与旧文件的Hash不同,则需要更新关联的FileHash,设置Storage中的文件已过期,重新插入Cache记录 | |||
| if objRep.FileHash != fileHash { | |||
| _, err := db.ObjectRep().UpdateFileHash(ctx, objectID, fileHash) | |||
| if err != nil { | |||
| return fmt.Errorf("update rep object file hash failed, err: %w", err) | |||
| } | |||
| _, err = db.StorageObject().SetAllObjectOutdated(ctx, objectID) | |||
| if err != nil { | |||
| return fmt.Errorf("set storage object outdated failed, err: %w", err) | |||
| } | |||
| for _, nodeID := range nodeIDs { | |||
| err := db.Cache().CreatePinned(ctx, fileHash, nodeID, 0) //priority = 0 | |||
| if err != nil { | |||
| return fmt.Errorf("create cache failed, err: %w", err) | |||
| } | |||
| } | |||
| } else { | |||
| // 如果相同,则只增加Cache中不存在的记录 | |||
| cachedNodes, err := db.Cache().GetCachingFileNodes(ctx, fileHash) | |||
| if err != nil { | |||
| return fmt.Errorf("find caching file nodes failed, err: %w", err) | |||
| } | |||
| // 筛选出不在cachedNodes中的id | |||
| newNodeIDs := lo.Filter(nodeIDs, func(id int64, index int) bool { | |||
| return lo.NoneBy(cachedNodes, func(node model.Node) bool { | |||
| return node.NodeID == id | |||
| }) | |||
| }) | |||
| for _, nodeID := range newNodeIDs { | |||
| err := db.Cache().CreatePinned(ctx, fileHash, nodeID, 0) //priority | |||
| if err != nil { | |||
| return fmt.Errorf("create cache failed, err: %w", err) | |||
| } | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| // SoftDelete 设置一个对象被删除,并将相关数据删除 | |||
| func (db *ObjectDB) SoftDelete(ctx SQLContext, objectID int64) error { | |||
| obj, err := db.GetByID(ctx, objectID) | |||
| if err != nil { | |||
| return fmt.Errorf("get object failed, err: %w", err) | |||
| } | |||
| // 不是正常状态的Object,则不删除 | |||
| // TODO 未来可能有其他状态 | |||
| if obj.State != consts.ObjectStateNormal { | |||
| return nil | |||
| } | |||
| err = db.ChangeState(ctx, objectID, consts.ObjectStateDeleted) | |||
| if err != nil { | |||
| return fmt.Errorf("change object state failed, err: %w", err) | |||
| } | |||
| if obj.Redundancy == models.RedundancyRep { | |||
| err = db.ObjectRep().Delete(ctx, objectID) | |||
| if err != nil { | |||
| return fmt.Errorf("delete from object rep failed, err: %w", err) | |||
| } | |||
| } else { | |||
| err = db.ObjectBlock().Delete(ctx, objectID) | |||
| if err != nil { | |||
| return fmt.Errorf("delete from object rep failed, err: %w", err) | |||
| } | |||
| } | |||
| _, err = db.StorageObject().SetAllObjectDeleted(ctx, objectID) | |||
| if err != nil { | |||
| return fmt.Errorf("set storage object deleted failed, err: %w", err) | |||
| } | |||
| return nil | |||
| } | |||
| // DeleteUnused 删除一个已经是Deleted状态,且不再被使用的对象。目前可能被使用的地方只有StorageObject | |||
| func (ObjectDB) DeleteUnused(ctx SQLContext, objectID int64) error { | |||
| _, err := ctx.Exec("delete from Object where ObjectID = ? and State = ? and "+ | |||
| "not exists(select StorageID from StorageObject where ObjectID = ?)", | |||
| objectID, | |||
| consts.ObjectStateDeleted, | |||
| objectID, | |||
| ) | |||
| return err | |||
| } | |||
| func (*ObjectDB) BatchGetAllObjectIDs(ctx SQLContext, start int, count int) ([]int64, error) { | |||
| var ret []int64 | |||
| err := sqlx.Select(ctx, &ret, "select ObjectID from Object limit ?, ?", start, count) | |||
| return ret, err | |||
| } | |||
| func (*ObjectDB) BatchGetAllEcObjectIDs(ctx SQLContext, start int, count int) ([]int64, error) { | |||
| var ret []int64 | |||
| rep := "rep" | |||
| err := sqlx.Select(ctx, &ret, "SELECT ObjectID FROM object where Redundancy != ? limit ?, ?", rep, start, count) | |||
| return ret, err | |||
| } | |||
| func (*ObjectDB) ChangeState(ctx SQLContext, objectID int64, state string) error { | |||
| _, err := ctx.Exec("update Object set State = ? where ObjectID = ?", state, objectID) | |||
| return err | |||
| } | |||
| func (*ObjectDB) UpdateFileInfo(ctx SQLContext, objectID int64, fileSize int64) (bool, error) { | |||
| ret, err := ctx.Exec("update Object set FileSize = ? where ObjectID = ?", fileSize, objectID) | |||
| if err != nil { | |||
| return false, err | |||
| } | |||
| cnt, err := ret.RowsAffected() | |||
| if err != nil { | |||
| return false, fmt.Errorf("get affected rows failed, err: %w", err) | |||
| } | |||
| return cnt > 0, nil | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| package db | |||
| import ( | |||
| "database/sql" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/common/consts" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type ObjectBlockDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) ObjectBlock() *ObjectBlockDB { | |||
| return &ObjectBlockDB{DB: db} | |||
| } | |||
| func (db *ObjectBlockDB) Delete(ctx SQLContext, objectID int64) error { | |||
| _, err := ctx.Exec("delete from ObjectBlock where ObjectID = ?", objectID) | |||
| return err | |||
| } | |||
| func (db *ObjectBlockDB) CountBlockWithHash(ctx SQLContext, fileHash string) (int, error) { | |||
| var cnt int | |||
| err := sqlx.Get(ctx, &cnt, | |||
| "select count(BlockHash) from ObjectBlock, Object where BlockHash = ? and "+ | |||
| "ObjectBlock.ObjectID = Object.ObjectID and "+ | |||
| "Object.State = ?", fileHash, consts.ObjectStateNormal) | |||
| if err == sql.ErrNoRows { | |||
| return 0, nil | |||
| } | |||
| return cnt, err | |||
| } | |||
| func (db *ObjectBlockDB) GetBatchObjectBlocks(ctx SQLContext, objectIDs []int64) ([][]string, error) { | |||
| blocks := make([][]string, len(objectIDs)) | |||
| var err error | |||
| for i, objectID := range objectIDs { | |||
| var x []model.ObjectBlock | |||
| sql := "select * from ObjectBlock where ObjectID=?" | |||
| err = db.d.Select(&x, sql, objectID) | |||
| xx := make([]string, len(x)) | |||
| for ii := 0; ii < len(x); ii++ { | |||
| xx[x[ii].InnerID] = x[ii].BlockHash | |||
| } | |||
| blocks[i] = xx | |||
| } | |||
| return blocks, err | |||
| } | |||
| func (db *ObjectBlockDB) GetBatchBlocksNodes(ctx SQLContext, hashs [][]string) ([][][]int64, error) { | |||
| nodes := make([][][]int64, len(hashs)) | |||
| var err error | |||
| for i, hs := range hashs { | |||
| fileNodes := make([][]int64, len(hs)) | |||
| for j, h := range hs { | |||
| var x []model.Node | |||
| err = sqlx.Select(ctx, &x, | |||
| "select Node.* from Cache, Node where "+ | |||
| "Cache.FileHash=? and Cache.NodeID = Node.NodeID and Cache.State=?", h, consts.CacheStatePinned) | |||
| xx := make([]int64, len(x)) | |||
| for ii := 0; ii < len(x); ii++ { | |||
| xx[ii] = x[ii].NodeID | |||
| } | |||
| fileNodes[j] = xx | |||
| } | |||
| nodes[i] = fileNodes | |||
| } | |||
| return nodes, err | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| package db | |||
| import ( | |||
| "database/sql" | |||
| "fmt" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/common/consts" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type ObjectRepDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) ObjectRep() *ObjectRepDB { | |||
| return &ObjectRepDB{DB: db} | |||
| } | |||
| // GetObjectRep 查询对象副本表 | |||
| func (db *ObjectRepDB) GetByID(ctx SQLContext, objectID int64) (model.ObjectRep, error) { | |||
| var ret model.ObjectRep | |||
| err := sqlx.Get(ctx, &ret, "select * from ObjectRep where ObjectID = ?", objectID) | |||
| return ret, err | |||
| } | |||
| func (db *ObjectRepDB) UpdateFileHash(ctx SQLContext, objectID int64, fileHash string) (int64, error) { | |||
| ret, err := ctx.Exec("update ObjectRep set FileHash = ? where ObjectID = ?", fileHash, objectID) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| cnt, err := ret.RowsAffected() | |||
| if err != nil { | |||
| return 0, fmt.Errorf("get affected rows failed, err: %w", err) | |||
| } | |||
| return cnt, nil | |||
| } | |||
| func (db *ObjectRepDB) Delete(ctx SQLContext, objectID int64) error { | |||
| _, err := ctx.Exec("delete from ObjectRep where ObjectID = ?", objectID) | |||
| return err | |||
| } | |||
| func (db *ObjectRepDB) GetFileMaxRepCount(ctx SQLContext, fileHash string) (int, error) { | |||
| var maxRepCnt *int | |||
| err := sqlx.Get(ctx, &maxRepCnt, | |||
| "select max(RepCount) from ObjectRep, Object where FileHash = ? and "+ | |||
| "ObjectRep.ObjectID = Object.ObjectID and "+ | |||
| "Object.State = ?", fileHash, consts.ObjectStateNormal) | |||
| if err == sql.ErrNoRows { | |||
| return 0, nil | |||
| } | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| if maxRepCnt == nil { | |||
| return 0, nil | |||
| } | |||
| return *maxRepCnt, err | |||
| } | |||
| @@ -0,0 +1,64 @@ | |||
| package db | |||
| import ( | |||
| "database/sql" | |||
| "fmt" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type StorageDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) Storage() *StorageDB { | |||
| return &StorageDB{DB: db} | |||
| } | |||
| func (db *StorageDB) GetByID(ctx SQLContext, stgID int64) (model.Storage, error) { | |||
| var stg model.Storage | |||
| err := sqlx.Get(ctx, &stg, "select * from Storage where StorageID = ?", stgID) | |||
| return stg, err | |||
| } | |||
| func (db *StorageDB) BatchGetAllStorageIDs(ctx SQLContext, start int, count int) ([]int64, error) { | |||
| var ret []int64 | |||
| err := sqlx.Select(ctx, &ret, "select StorageID from Storage limit ?, ?", start, count) | |||
| return ret, err | |||
| } | |||
| func (db *StorageDB) IsAvailable(ctx SQLContext, userID int64, storageID int64) (bool, error) { | |||
| var stgID int64 | |||
| err := sqlx.Get(ctx, &stgID, | |||
| "select Storage.StorageID from Storage, UserStorage where "+ | |||
| "Storage.StorageID = ? and "+ | |||
| "Storage.StorageID = UserStorage.StorageID and "+ | |||
| "UserStorage.UserID = ?", | |||
| storageID, userID) | |||
| if err == sql.ErrNoRows { | |||
| return false, nil | |||
| } | |||
| if err != nil { | |||
| return false, fmt.Errorf("find storage failed, err: %w", err) | |||
| } | |||
| return true, nil | |||
| } | |||
| func (db *StorageDB) GetUserStorage(ctx SQLContext, userID int64, storageID int64) (model.Storage, error) { | |||
| var stg model.Storage | |||
| err := sqlx.Get(ctx, &stg, | |||
| "select Storage.* from UserStorage, Storage where UserID = ? and UserStorage.StorageID = ? and UserStorage.StorageID = Storage.StorageID", | |||
| userID, | |||
| storageID) | |||
| return stg, err | |||
| } | |||
| func (db *StorageDB) ChangeState(ctx SQLContext, storageID int64, state string) error { | |||
| _, err := ctx.Exec("update Storage set State = ? where StorageID = ?", state, storageID) | |||
| return err | |||
| } | |||
| @@ -0,0 +1,116 @@ | |||
| package db | |||
| import ( | |||
| "fmt" | |||
| "github.com/jmoiron/sqlx" | |||
| "gitlink.org.cn/cloudream/common/consts" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type StorageObjectDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) StorageObject() *StorageObjectDB { | |||
| return &StorageObjectDB{DB: db} | |||
| } | |||
| func (*StorageObjectDB) Get(ctx SQLContext, storageID int64, objectID int64, userID int64) (model.StorageObject, error) { | |||
| var ret model.StorageObject | |||
| err := sqlx.Get(ctx, &ret, "select * from StorageObject where StorageID = ? and ObjectID = ? and UserID = ?", storageID, objectID, userID) | |||
| return ret, err | |||
| } | |||
| func (*StorageObjectDB) GetAllByStorageAndObjectID(ctx SQLContext, storageID int64, objectID int64) ([]model.StorageObject, error) { | |||
| var ret []model.StorageObject | |||
| err := sqlx.Select(ctx, &ret, "select * from StorageObject where StorageID = ? and ObjectID = ?", storageID, objectID) | |||
| return ret, err | |||
| } | |||
| func (*StorageObjectDB) GetAllByStorageID(ctx SQLContext, storageID int64) ([]model.StorageObject, error) { | |||
| var ret []model.StorageObject | |||
| err := sqlx.Select(ctx, &ret, "select * from StorageObject where StorageID = ?", storageID) | |||
| return ret, err | |||
| } | |||
| func (*StorageObjectDB) MoveObjectTo(ctx SQLContext, objectID int64, storageID int64, userID int64) error { | |||
| _, err := ctx.Exec("insert into StorageObject values(?,?,?,?)", objectID, storageID, userID, consts.StorageObjectStateNormal) | |||
| return err | |||
| } | |||
| func (*StorageObjectDB) ChangeState(ctx SQLContext, storageID int64, objectID int64, userID int64, state string) error { | |||
| _, err := ctx.Exec("update StorageObject set State = ? where StorageID = ? and ObjectID = ? and UserID = ?", state, storageID, objectID, userID) | |||
| return err | |||
| } | |||
| // SetStateNormal 将状态设置为Normal,如果记录状态是Deleted,则不进行操作 | |||
| func (*StorageObjectDB) SetStateNormal(ctx SQLContext, storageID int64, objectID int64, userID int64) error { | |||
| _, err := ctx.Exec("update StorageObject set State = ? where StorageID = ? and ObjectID = ? and UserID = ? and State <> ?", | |||
| consts.StorageObjectStateNormal, | |||
| storageID, | |||
| objectID, | |||
| userID, | |||
| consts.StorageObjectStateDeleted, | |||
| ) | |||
| return err | |||
| } | |||
| func (*StorageObjectDB) SetAllObjectState(ctx SQLContext, objectID int64, state string) (int64, error) { | |||
| ret, err := ctx.Exec( | |||
| "update StorageObject set State = ? where ObjectID = ?", | |||
| state, | |||
| objectID, | |||
| ) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| cnt, err := ret.RowsAffected() | |||
| if err != nil { | |||
| return 0, fmt.Errorf("get affected rows failed, err: %w", err) | |||
| } | |||
| return cnt, nil | |||
| } | |||
| // SetAllObjectOutdated 将Storage中指定对象设置为已过期。 | |||
| // 注:只会设置Normal状态的对象 | |||
| func (*StorageObjectDB) SetAllObjectOutdated(ctx SQLContext, objectID int64) (int64, error) { | |||
| ret, err := ctx.Exec( | |||
| "update StorageObject set State = ? where State = ? and ObjectID = ?", | |||
| consts.StorageObjectStateOutdated, | |||
| consts.StorageObjectStateNormal, | |||
| objectID, | |||
| ) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| cnt, err := ret.RowsAffected() | |||
| if err != nil { | |||
| return 0, fmt.Errorf("get affected rows failed, err: %w", err) | |||
| } | |||
| return cnt, nil | |||
| } | |||
| func (db *StorageObjectDB) SetAllObjectDeleted(ctx SQLContext, objectID int64) (int64, error) { | |||
| return db.SetAllObjectState(ctx, objectID, consts.StorageObjectStateDeleted) | |||
| } | |||
| func (*StorageObjectDB) Delete(ctx SQLContext, storageID int64, objectID int64, userID int64) error { | |||
| _, err := ctx.Exec("delete from StorageObject where StorageID = ? and ObjectID = ? and UserID = ?", storageID, objectID, userID) | |||
| return err | |||
| } | |||
| // FindObjectStorages 查询存储了指定对象的Storage | |||
| func (*StorageObjectDB) FindObjectStorages(ctx SQLContext, objectID int64) ([]model.Storage, error) { | |||
| var ret []model.Storage | |||
| err := sqlx.Select(ctx, &ret, | |||
| "select Storage.* from StorageObject, Storage where ObjectID = ? and "+ | |||
| "StorageObject.StorageID = Storage.StorageID", | |||
| objectID, | |||
| ) | |||
| return ret, err | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package db | |||
| type UserBucketDB struct { | |||
| *DB | |||
| } | |||
| func (db *DB) UserBucket() *UserBucketDB { | |||
| return &UserBucketDB{DB: db} | |||
| } | |||
| func (*UserBucketDB) Create(ctx SQLContext, userID int64, bucketID int64) error { | |||
| _, err := ctx.Exec("insert into UserBucket(UserID,BucketID) values(?,?)", userID, bucketID) | |||
| return err | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| package ec | |||
| import ( | |||
| "fmt" | |||
| "os" | |||
| "github.com/baohan10/reedsolomon" | |||
| ) | |||
| type rs struct { | |||
| r *(reedsolomon.ReedSolomon) | |||
| ecN int | |||
| ecK int | |||
| ecP int | |||
| } | |||
| func NewRsEnc(ecK int, ecN int) *rs { | |||
| enc := rs{ | |||
| ecN: ecN, | |||
| ecK: ecK, | |||
| ecP: ecN - ecK, | |||
| } | |||
| enc.r = reedsolomon.GetReedSolomonIns(ecK, ecN) | |||
| return &enc | |||
| } | |||
| func (r *rs) Encode(all [][]byte) { | |||
| r.r.Encode(all) | |||
| } | |||
| func (r *rs) Repair(all [][]byte) error { | |||
| return r.r.Reconstruct(all) | |||
| } | |||
| func checkErr(err error) { | |||
| if err != nil { | |||
| fmt.Fprintf(os.Stderr, "Error: %s", err.Error()) | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| func (client *Client) GetState(msg agtmsg.GetState, opts ...mq.RequestOption) (*agtmsg.GetStateResp, error) { | |||
| return mq.Request[agtmsg.GetStateResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| mymq "gitlink.org.cn/cloudream/storage-common/pkgs/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/mq/config" | |||
| ) | |||
| type Client struct { | |||
| rabbitCli *mq.RabbitMQClient | |||
| id int64 | |||
| } | |||
| func NewClient(id int64, cfg *config.Config) (*Client, error) { | |||
| rabbitCli, err := mq.NewRabbitMQClient(cfg.MakeConnectingURL(), mymq.MakeAgentQueueName(id), "") | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return &Client{ | |||
| rabbitCli: rabbitCli, | |||
| id: id, | |||
| }, nil | |||
| } | |||
| func (c *Client) Close() { | |||
| c.rabbitCli.Close() | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| func (client *Client) CheckIPFS(msg agtmsg.CheckIPFS, opts ...mq.RequestOption) (*agtmsg.CheckIPFSResp, error) { | |||
| return mq.Request[agtmsg.CheckIPFSResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| func (client *Client) StartPinningObject(msg agtmsg.StartPinningObject, opts ...mq.RequestOption) (*agtmsg.StartPinningObjectResp, error) { | |||
| return mq.Request[agtmsg.StartPinningObjectResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| func (client *Client) WaitPinningObject(msg agtmsg.WaitPinningObject, opts ...mq.RequestOption) (*agtmsg.WaitPinningObjectResp, error) { | |||
| return mq.Request[agtmsg.WaitPinningObjectResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| func (client *Client) StartStorageMoveObject(msg agtmsg.StartStorageMoveObject, opts ...mq.RequestOption) (*agtmsg.StartStorageMoveObjectResp, error) { | |||
| return mq.Request[agtmsg.StartStorageMoveObjectResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| func (client *Client) WaitStorageMoveObject(msg agtmsg.WaitStorageMoveObject, opts ...mq.RequestOption) (*agtmsg.WaitStorageMoveObjectResp, error) { | |||
| return mq.Request[agtmsg.WaitStorageMoveObjectResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| func (client *Client) StorageCheck(msg agtmsg.StorageCheck, opts ...mq.RequestOption) (*agtmsg.StorageCheckResp, error) { | |||
| return mq.Request[agtmsg.StorageCheckResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| func (client *Client) StartStorageUploadRepObject(msg agtmsg.StartStorageUploadRepObject, opts ...mq.RequestOption) (*agtmsg.StartStorageUploadRepObjectResp, error) { | |||
| return mq.Request[agtmsg.StartStorageUploadRepObjectResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| func (client *Client) WaitStorageUploadRepObject(msg agtmsg.WaitStorageUploadRepObject, opts ...mq.RequestOption) (*agtmsg.WaitStorageUploadRepObjectResp, error) { | |||
| return mq.Request[agtmsg.WaitStorageUploadRepObjectResp](client.rabbitCli, msg, opts...) | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| ) | |||
| func (client *Client) TempCacheReport(msg coormsg.TempCacheReport) error { | |||
| return mq.Send(client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) AgentStatusReport(msg coormsg.AgentStatusReport) error { | |||
| return mq.Send(client.rabbitCli, msg) | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| ) | |||
| func (client *Client) GetUserBuckets(msg coormsg.GetUserBuckets) (*coormsg.GetUserBucketsResp, error) { | |||
| return mq.Request[coormsg.GetUserBucketsResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) GetBucketObjects(msg coormsg.GetBucketObjects) (*coormsg.GetBucketObjectsResp, error) { | |||
| return mq.Request[coormsg.GetBucketObjectsResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) CreateBucket(msg coormsg.CreateBucket) (*coormsg.CreateBucketResp, error) { | |||
| return mq.Request[coormsg.CreateBucketResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) DeleteBucket(msg coormsg.DeleteBucket) (*coormsg.DeleteBucketResp, error) { | |||
| return mq.Request[coormsg.DeleteBucketResp](client.rabbitCli, msg) | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| mymq "gitlink.org.cn/cloudream/storage-common/pkgs/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/mq/config" | |||
| ) | |||
| type Client struct { | |||
| rabbitCli *mq.RabbitMQClient | |||
| } | |||
| func NewClient(cfg *config.Config) (*Client, error) { | |||
| rabbitCli, err := mq.NewRabbitMQClient(cfg.MakeConnectingURL(), mymq.COORDINATOR_QUEUE_NAME, "") | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return &Client{ | |||
| rabbitCli: rabbitCli, | |||
| }, nil | |||
| } | |||
| func (c *Client) Close() { | |||
| c.rabbitCli.Close() | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| ) | |||
| func (client *Client) GetObjectsByDirName(msg coormsg.GetObjectsByDirName) (*coormsg.GetObjectsResp, error) { | |||
| return mq.Request[coormsg.GetObjectsResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) PreDownloadObject(msg coormsg.PreDownloadObject) (*coormsg.PreDownloadObjectResp, error) { | |||
| return mq.Request[coormsg.PreDownloadObjectResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) PreUploadRepObject(msg coormsg.PreUploadRepObject) (*coormsg.PreUploadResp, error) { | |||
| return mq.Request[coormsg.PreUploadResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) CreateRepObject(msg coormsg.CreateRepObject) (*coormsg.CreateObjectResp, error) { | |||
| return mq.Request[coormsg.CreateObjectResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) PreUploadEcObject(msg coormsg.PreUploadEcObject) (*coormsg.PreUploadEcResp, error) { | |||
| return mq.Request[coormsg.PreUploadEcResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) CreateEcObject(msg coormsg.CreateEcObject) (*coormsg.CreateObjectResp, error) { | |||
| return mq.Request[coormsg.CreateObjectResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) PreUpdateRepObject(msg coormsg.PreUpdateRepObject) (*coormsg.PreUpdateRepObjectResp, error) { | |||
| return mq.Request[coormsg.PreUpdateRepObjectResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) UpdateRepObject(msg coormsg.UpdateRepObject) (*coormsg.UpdateRepObjectResp, error) { | |||
| return mq.Request[coormsg.UpdateRepObjectResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) DeleteObject(msg coormsg.DeleteObject) (*coormsg.DeleteObjectResp, error) { | |||
| return mq.Request[coormsg.DeleteObjectResp](client.rabbitCli, msg) | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| ) | |||
| func (client *Client) GetStorageInfo(msg coormsg.GetStorageInfo) (*coormsg.GetStorageInfoResp, error) { | |||
| return mq.Request[coormsg.GetStorageInfoResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) PreMoveObjectToStorage(msg coormsg.PreMoveObjectToStorage) (*coormsg.PreMoveObjectToStorageResp, error) { | |||
| return mq.Request[coormsg.PreMoveObjectToStorageResp](client.rabbitCli, msg) | |||
| } | |||
| func (client *Client) MoveObjectToStorage(msg coormsg.MoveObjectToStorage) (*coormsg.MoveObjectToStorageResp, error) { | |||
| return mq.Request[coormsg.MoveObjectToStorageResp](client.rabbitCli, msg) | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package scanner | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| mymq "gitlink.org.cn/cloudream/storage-common/pkgs/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/mq/config" | |||
| ) | |||
| type Client struct { | |||
| rabbitCli *mq.RabbitMQClient | |||
| } | |||
| func NewClient(cfg *config.Config) (*Client, error) { | |||
| rabbitCli, err := mq.NewRabbitMQClient(cfg.MakeConnectingURL(), mymq.SCANNER_QUEUE_NAME, "") | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return &Client{ | |||
| rabbitCli: rabbitCli, | |||
| }, nil | |||
| } | |||
| func (c *Client) Close() { | |||
| c.rabbitCli.Close() | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package scanner | |||
| import ( | |||
| "fmt" | |||
| "time" | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| scmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/scanner" | |||
| ) | |||
| func (cli *Client) PostEvent(event any, isEmergency bool, dontMerge bool, opts ...mq.SendOption) error { | |||
| opt := mq.SendOption{ | |||
| Timeout: time.Second * 30, | |||
| } | |||
| if len(opts) > 0 { | |||
| opt = opts[0] | |||
| } | |||
| body, err := scmsg.NewPostEvent(event, isEmergency, dontMerge) | |||
| if err != nil { | |||
| return fmt.Errorf("new post event body failed, err: %w", err) | |||
| } | |||
| return mq.Send(cli.rabbitCli, body, opt) | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package config | |||
| import "fmt" | |||
| type Config struct { | |||
| Address string `json:"address"` | |||
| Account string `json:"account"` | |||
| Password string `json:"password"` | |||
| VHost string `json:"vhost"` | |||
| } | |||
| func (cfg *Config) MakeConnectingURL() string { | |||
| return fmt.Sprintf("amqp://%s:%s@%s%s", cfg.Account, cfg.Password, cfg.Address, cfg.VHost) | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| package mq | |||
| import "fmt" | |||
| const ( | |||
| COORDINATOR_QUEUE_NAME = "Coordinator" | |||
| SCANNER_QUEUE_NAME = "Scanner" | |||
| ) | |||
| func MakeAgentQueueName(id int64) string { | |||
| return fmt.Sprintf("Agent@%d", id) | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| ) | |||
| type GetState struct { | |||
| } | |||
| func NewGetState() GetState { | |||
| return GetState{} | |||
| } | |||
| type GetStateResp struct { | |||
| IPFSState string `json:"ipfsState"` | |||
| } | |||
| func NewGetStateRespBody(ipfsState string) GetStateResp { | |||
| return GetStateResp{ | |||
| IPFSState: ipfsState, | |||
| } | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[GetState]() | |||
| mq.RegisterMessage[GetStateResp]() | |||
| } | |||
| @@ -0,0 +1,50 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type CheckIPFS struct { | |||
| IsComplete bool `json:"isComplete"` | |||
| Caches []model.Cache `json:"caches"` | |||
| } | |||
| func NewCheckIPFS(isComplete bool, caches []model.Cache) CheckIPFS { | |||
| return CheckIPFS{ | |||
| IsComplete: isComplete, | |||
| Caches: caches, | |||
| } | |||
| } | |||
| type CheckIPFSResp struct { | |||
| Entries []CheckIPFSRespEntry `json:"entries"` | |||
| } | |||
| const ( | |||
| CHECK_IPFS_RESP_OP_DELETE_TEMP = "DeleteTemp" | |||
| CHECK_IPFS_RESP_OP_CREATE_TEMP = "CreateTemp" | |||
| ) | |||
| type CheckIPFSRespEntry struct { | |||
| FileHash string `json:"fileHash"` | |||
| Operation string `json:"operation"` | |||
| } | |||
| func NewCheckIPFSRespEntry(fileHash string, op string) CheckIPFSRespEntry { | |||
| return CheckIPFSRespEntry{ | |||
| FileHash: fileHash, | |||
| Operation: op, | |||
| } | |||
| } | |||
| func NewCheckIPFSResp(entries []CheckIPFSRespEntry) CheckIPFSResp { | |||
| return CheckIPFSResp{ | |||
| Entries: entries, | |||
| } | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[CheckIPFS]() | |||
| mq.RegisterMessage[CheckIPFSResp]() | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| package agent | |||
| import "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| type StartPinningObject struct { | |||
| FileHash string `json:"fileHash"` | |||
| } | |||
| func NewStartPinningObject(fileHash string) StartPinningObject { | |||
| return StartPinningObject{ | |||
| FileHash: fileHash, | |||
| } | |||
| } | |||
| type StartPinningObjectResp struct { | |||
| TaskID string `json:"taskID"` | |||
| } | |||
| func NewStartPinningObjectResp(taskID string) StartPinningObjectResp { | |||
| return StartPinningObjectResp{ | |||
| TaskID: taskID, | |||
| } | |||
| } | |||
| type WaitPinningObject struct { | |||
| TaskID string `json:"taskID"` | |||
| WaitTimeoutMs int64 `json:"waitTimeout"` | |||
| } | |||
| func NewWaitPinningObject(taskID string, waitTimeoutMs int64) WaitPinningObject { | |||
| return WaitPinningObject{ | |||
| TaskID: taskID, | |||
| WaitTimeoutMs: waitTimeoutMs, | |||
| } | |||
| } | |||
| type WaitPinningObjectResp struct { | |||
| IsComplete bool `json:"isComplete"` | |||
| Error string `json:"error"` | |||
| } | |||
| func NewWaitPinningObjectResp(isComplete bool, err string) WaitPinningObjectResp { | |||
| return WaitPinningObjectResp{ | |||
| IsComplete: isComplete, | |||
| Error: err, | |||
| } | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[StartPinningObject]() | |||
| mq.RegisterMessage[StartPinningObjectResp]() | |||
| mq.RegisterMessage[WaitPinningObject]() | |||
| mq.RegisterMessage[WaitPinningObjectResp]() | |||
| } | |||
| @@ -0,0 +1,184 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/models" | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| // 客户端发给代理端,告知要调度多副本冗余的数据,以及要调度数据的详情 | |||
| type StartStorageMoveObject struct { | |||
| UserID int64 `json:"userID"` | |||
| ObjectID int64 `json:"objectID"` | |||
| ObjectName string `json:"objectName"` | |||
| Directory string `json:"directory"` | |||
| FileSize int64 `json:"fileSize,string"` | |||
| Redundancy models.RedundancyDataTypes `json:"redundancy"` | |||
| } | |||
| func NewStartStorageMoveObject[T models.RedundancyDataTypes](dir string, objectID int64, objectName string, userID int64, fileSize int64, redundancy T) StartStorageMoveObject { | |||
| return StartStorageMoveObject{ | |||
| Directory: dir, | |||
| ObjectID: objectID, | |||
| ObjectName: objectName, | |||
| UserID: userID, | |||
| FileSize: fileSize, | |||
| Redundancy: redundancy, | |||
| } | |||
| } | |||
| // 代理端发给客户端,告知调度的结果 | |||
| type StartStorageMoveObjectResp struct { | |||
| TaskID string `json:"taskID"` | |||
| } | |||
| func NewStartStorageMoveObjectResp(taskID string) StartStorageMoveObjectResp { | |||
| return StartStorageMoveObjectResp{ | |||
| TaskID: taskID, | |||
| } | |||
| } | |||
| type WaitStorageMoveObject struct { | |||
| TaskID string `json:"taskID"` | |||
| WaitTimeoutMs int64 `json:"waitTimeout"` | |||
| } | |||
| func NewWaitStorageMoveObject(taskID string, waitTimeoutMs int64) WaitStorageMoveObject { | |||
| return WaitStorageMoveObject{ | |||
| TaskID: taskID, | |||
| WaitTimeoutMs: waitTimeoutMs, | |||
| } | |||
| } | |||
| type WaitStorageMoveObjectResp struct { | |||
| IsComplete bool `json:"isComplete"` | |||
| Error string `json:"error"` | |||
| } | |||
| func NewWaitStorageMoveObjectResp(isComplete bool, err string) WaitStorageMoveObjectResp { | |||
| return WaitStorageMoveObjectResp{ | |||
| IsComplete: isComplete, | |||
| Error: err, | |||
| } | |||
| } | |||
| type StorageCheck struct { | |||
| StorageID int64 `json:"storageID"` | |||
| Directory string `json:"directory"` | |||
| IsComplete bool `json:"isComplete"` | |||
| Objects []model.StorageObject `json:"objects"` | |||
| } | |||
| func NewStorageCheck(storageID int64, directory string, isComplete bool, objects []model.StorageObject) StorageCheck { | |||
| return StorageCheck{ | |||
| StorageID: storageID, | |||
| Directory: directory, | |||
| IsComplete: isComplete, | |||
| Objects: objects, | |||
| } | |||
| } | |||
| type StorageCheckResp struct { | |||
| DirectoryState string `json:"directoryState"` | |||
| Entries []StorageCheckRespEntry `json:"entries"` | |||
| } | |||
| const ( | |||
| CHECK_STORAGE_RESP_OP_DELETE = "Delete" | |||
| CHECK_STORAGE_RESP_OP_SET_NORMAL = "SetNormal" | |||
| ) | |||
| type StorageCheckRespEntry struct { | |||
| ObjectID int64 `json:"objectID"` | |||
| UserID int64 `json:"userID"` | |||
| Operation string `json:"operation"` | |||
| } | |||
| func NewStorageCheckRespEntry(objectID int64, userID int64, op string) StorageCheckRespEntry { | |||
| return StorageCheckRespEntry{ | |||
| ObjectID: objectID, | |||
| UserID: userID, | |||
| Operation: op, | |||
| } | |||
| } | |||
| func NewStorageCheckResp(dirState string, entries []StorageCheckRespEntry) StorageCheckResp { | |||
| return StorageCheckResp{ | |||
| DirectoryState: dirState, | |||
| Entries: entries, | |||
| } | |||
| } | |||
| type StartStorageUploadRepObject struct { | |||
| UserID int64 `json:"userID"` | |||
| FilePath string `json:"filePath"` | |||
| BucketID int64 `json:"bucketID"` | |||
| ObjectName string `json:"objectName"` | |||
| RepCount int `json:"repCount"` | |||
| StorageDirectory string `json:"storageDirectory"` | |||
| } | |||
| func NewStartStorageUploadRepObject(userID int64, filePath string, bucketID int64, objectName string, repCount int, storageDirectory string) StartStorageUploadRepObject { | |||
| return StartStorageUploadRepObject{ | |||
| UserID: userID, | |||
| FilePath: filePath, | |||
| BucketID: bucketID, | |||
| ObjectName: objectName, | |||
| RepCount: repCount, | |||
| StorageDirectory: storageDirectory, | |||
| } | |||
| } | |||
| type StartStorageUploadRepObjectResp struct { | |||
| TaskID string `json:"taskID"` | |||
| } | |||
| func NewStartStorageUploadRepObjectResp(taskID string) StartStorageUploadRepObjectResp { | |||
| return StartStorageUploadRepObjectResp{ | |||
| TaskID: taskID, | |||
| } | |||
| } | |||
| type WaitStorageUploadRepObject struct { | |||
| TaskID string `json:"taskID"` | |||
| WaitTimeoutMs int64 `json:"waitTimeout"` | |||
| } | |||
| func NewWaitStorageUploadRepObject(taskID string, waitTimeoutMs int64) WaitStorageUploadRepObject { | |||
| return WaitStorageUploadRepObject{ | |||
| TaskID: taskID, | |||
| WaitTimeoutMs: waitTimeoutMs, | |||
| } | |||
| } | |||
| type WaitStorageUploadRepObjectResp struct { | |||
| IsComplete bool `json:"isComplete"` | |||
| Error string `json:"error"` | |||
| ObjectID int64 `json:"objectID"` | |||
| FileHash string `json:"fileHash"` | |||
| } | |||
| func NewWaitStorageUploadRepObjectResp(isComplete bool, err string, objectID int64, fileHash string) WaitStorageUploadRepObjectResp { | |||
| return WaitStorageUploadRepObjectResp{ | |||
| IsComplete: isComplete, | |||
| Error: err, | |||
| ObjectID: objectID, | |||
| FileHash: fileHash, | |||
| } | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[StartStorageMoveObject]() | |||
| mq.RegisterMessage[StartStorageMoveObjectResp]() | |||
| mq.RegisterMessage[WaitStorageMoveObject]() | |||
| mq.RegisterMessage[WaitStorageMoveObjectResp]() | |||
| mq.RegisterMessage[StorageCheck]() | |||
| mq.RegisterMessage[StorageCheckResp]() | |||
| mq.RegisterMessage[StartStorageUploadRepObject]() | |||
| mq.RegisterMessage[StartStorageUploadRepObjectResp]() | |||
| mq.RegisterMessage[WaitStorageUploadRepObject]() | |||
| mq.RegisterMessage[WaitStorageUploadRepObjectResp]() | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| package coordinator | |||
| import "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| // 代理端发给协调端,告知临时缓存的数据 | |||
| type TempCacheReport struct { | |||
| NodeID int64 `json:"nodeID"` | |||
| Hashes []string `json:"hashes"` | |||
| } | |||
| func NewTempCacheReportBody(nodeID int64, hashes []string) TempCacheReport { | |||
| return TempCacheReport{ | |||
| NodeID: nodeID, | |||
| Hashes: hashes, | |||
| } | |||
| } | |||
| // 代理端发给协调端,告知延迟、ipfs和资源目录的可达性 | |||
| type AgentStatusReport struct { | |||
| NodeID int64 `json:"nodeID"` | |||
| NodeDelayIDs []int64 `json:"nodeDelayIDs"` | |||
| NodeDelays []int `json:"nodeDelays"` | |||
| IPFSStatus string `json:"ipfsStatus"` | |||
| LocalDirStatus string `json:"localDirStatus"` | |||
| } | |||
| func NewAgentStatusReportBody(nodeID int64, nodeDelayIDs []int64, nodeDelays []int, ipfsStatus string, localDirStatus string) AgentStatusReport { | |||
| return AgentStatusReport{ | |||
| NodeID: nodeID, | |||
| NodeDelayIDs: nodeDelayIDs, | |||
| NodeDelays: nodeDelays, | |||
| IPFSStatus: ipfsStatus, | |||
| LocalDirStatus: localDirStatus, | |||
| } | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[TempCacheReport]() | |||
| mq.RegisterMessage[AgentStatusReport]() | |||
| } | |||
| @@ -0,0 +1,102 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type GetUserBuckets struct { | |||
| UserID int64 `json:"userID"` | |||
| } | |||
| func NewGetUserBuckets(userID int64) GetUserBuckets { | |||
| return GetUserBuckets{ | |||
| UserID: userID, | |||
| } | |||
| } | |||
| type GetUserBucketsResp struct { | |||
| Buckets []model.Bucket `json:"buckets"` | |||
| } | |||
| func NewGetUserBucketsResp(buckets []model.Bucket) GetUserBucketsResp { | |||
| return GetUserBucketsResp{ | |||
| Buckets: buckets, | |||
| } | |||
| } | |||
| type GetBucketObjects struct { | |||
| UserID int64 `json:"userID"` | |||
| BucketID int64 `json:"bucketID"` | |||
| } | |||
| func NewGetBucketObjects(userID int64, bucketID int64) GetBucketObjects { | |||
| return GetBucketObjects{ | |||
| UserID: userID, | |||
| BucketID: bucketID, | |||
| } | |||
| } | |||
| type GetBucketObjectsResp struct { | |||
| Objects []model.Object `json:"objects"` | |||
| } | |||
| func NewGetBucketObjectsResp(objects []model.Object) GetBucketObjectsResp { | |||
| return GetBucketObjectsResp{ | |||
| Objects: objects, | |||
| } | |||
| } | |||
| type CreateBucket struct { | |||
| UserID int64 `json:"userID"` | |||
| BucketName string `json:"bucketName"` | |||
| } | |||
| func NewCreateBucket(userID int64, bucketName string) CreateBucket { | |||
| return CreateBucket{ | |||
| UserID: userID, | |||
| BucketName: bucketName, | |||
| } | |||
| } | |||
| type CreateBucketResp struct { | |||
| BucketID int64 `json:"bucketID"` | |||
| } | |||
| func NewCreateBucketResp(bucketID int64) CreateBucketResp { | |||
| return CreateBucketResp{ | |||
| BucketID: bucketID, | |||
| } | |||
| } | |||
| type DeleteBucket struct { | |||
| UserID int64 `json:"userID"` | |||
| BucketID int64 `json:"bucketID"` | |||
| } | |||
| func NewDeleteBucket(userID int64, bucketID int64) DeleteBucket { | |||
| return DeleteBucket{ | |||
| UserID: userID, | |||
| BucketID: bucketID, | |||
| } | |||
| } | |||
| type DeleteBucketResp struct{} | |||
| func NewDeleteBucketResp() DeleteBucketResp { | |||
| return DeleteBucketResp{} | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[GetUserBuckets]() | |||
| mq.RegisterMessage[GetUserBucketsResp]() | |||
| mq.RegisterMessage[GetBucketObjects]() | |||
| mq.RegisterMessage[GetBucketObjectsResp]() | |||
| mq.RegisterMessage[CreateBucket]() | |||
| mq.RegisterMessage[CreateBucketResp]() | |||
| mq.RegisterMessage[DeleteBucket]() | |||
| mq.RegisterMessage[DeleteBucketResp]() | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| package coordinator | |||
| import ( | |||
| "testing" | |||
| . "github.com/smartystreets/goconvey/convey" | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| ) | |||
| func TestSerder(t *testing.T) { | |||
| Convey("序列化ReadCmd", t, func() { | |||
| msg := mq.MakeMessage(NewPreDownloadObject(1, 123, "")) | |||
| data, err := mq.Serialize(msg) | |||
| So(err, ShouldBeNil) | |||
| deMsg, err := mq.Deserialize(data) | |||
| So(err, ShouldBeNil) | |||
| So(*deMsg, ShouldResemble, msg) | |||
| }) | |||
| } | |||
| @@ -0,0 +1,295 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ramsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message" | |||
| ) | |||
| // 客户端发给协调端,告知要查询的文件夹名称 | |||
| type GetObjectsByDirName struct { | |||
| UserID int64 `json:"userID"` | |||
| DirName string `json:"dirName"` | |||
| } | |||
| func NewGetObjectsByDirName(userID int64, dirName string) GetObjectsByDirName { | |||
| return GetObjectsByDirName{ | |||
| UserID: userID, | |||
| DirName: dirName, | |||
| } | |||
| } | |||
| // 协调端告知客户端,查询到的object信息 | |||
| type GetObjectsResp struct { | |||
| Objects []model.Object `json:"objects"` | |||
| } | |||
| func NewGetObjectsResp(objects []model.Object) GetObjectsResp { | |||
| return GetObjectsResp{ | |||
| Objects: objects, | |||
| } | |||
| } | |||
| // 客户端发给协调端,告知要读取数据 | |||
| type PreDownloadObject struct { | |||
| ObjectID int64 `json:"objectID"` | |||
| UserID int64 `json:"userID"` | |||
| ClientExternalIP string `json:"clientExternalIP"` // 客户端的外网IP | |||
| } | |||
| func NewPreDownloadObject(objectID int64, userID int64, clientExternalIP string) PreDownloadObject { | |||
| return PreDownloadObject{ | |||
| ObjectID: objectID, | |||
| UserID: userID, | |||
| ClientExternalIP: clientExternalIP, | |||
| } | |||
| } | |||
| // 协调端告知客户端,待读取数据的元数据 | |||
| type PreDownloadObjectResp struct { | |||
| FileSize int64 `json:"fileSize,string"` | |||
| Redundancy ramsg.RespRedundancyDataTypes `json:"redundancy"` | |||
| } | |||
| func NewPreDownloadObjectResp[T ramsg.RespRedundancyDataTypesConst](fileSize int64, redundancy T) PreDownloadObjectResp { | |||
| return PreDownloadObjectResp{ | |||
| Redundancy: redundancy, | |||
| FileSize: fileSize, | |||
| } | |||
| } | |||
| // 客户端发给协调端,告知要以多副本方式执行写入操作 | |||
| type PreUploadRepObject struct { | |||
| BucketID int64 `json:"bucketID"` | |||
| ObjectName string `json:"objectName"` | |||
| FileSize int64 `json:"fileSize,string"` | |||
| UserID int64 `json:"userID"` | |||
| ClientExternalIP string `json:"clientExternalIP"` // 客户端的外网IP | |||
| } | |||
| func NewPreUploadRepObjectBody(bucketID int64, objectName string, fileSize int64, userID int64, clientExterIP string) PreUploadRepObject { | |||
| return PreUploadRepObject{ | |||
| BucketID: bucketID, | |||
| ObjectName: objectName, | |||
| FileSize: fileSize, | |||
| UserID: userID, | |||
| ClientExternalIP: clientExterIP, | |||
| } | |||
| } | |||
| // 协调端发给客户端,返回副本的写入目的地节点IP | |||
| type PreUploadResp struct { | |||
| Nodes []ramsg.RespNode `json:"nodes"` | |||
| } | |||
| func NewPreUploadResp(nodes []ramsg.RespNode) PreUploadResp { | |||
| return PreUploadResp{ | |||
| Nodes: nodes, | |||
| } | |||
| } | |||
| // 客户端发给协调端,告知要以纠删码方式执行写入操作 | |||
| type PreUploadEcObject struct { | |||
| BucketID int64 `json:"bucketID"` | |||
| ObjectName string `json:"objectName"` | |||
| FileSize int64 `json:"fileSize,string"` | |||
| EcName string `json:"ecName"` | |||
| UserID int64 `json:"userID"` | |||
| ClientExternalIP string `json:"clientExternalIP"` // 读取方的外网IP | |||
| } | |||
| func NewPreUploadEcObject(bucketID int64, objectName string, fileSize int64, ecName string, userID int64, writerExterIP string) PreUploadEcObject { | |||
| return PreUploadEcObject{ | |||
| BucketID: bucketID, | |||
| ObjectName: objectName, | |||
| FileSize: fileSize, | |||
| EcName: ecName, | |||
| UserID: userID, | |||
| ClientExternalIP: writerExterIP, | |||
| } | |||
| } | |||
| // 协调端发给客户端,返回编码块的写入目的地节点IP | |||
| type PreUploadEcResp struct { | |||
| Nodes []ramsg.RespNode `json:"nodes"` | |||
| Ec ramsg.Ec `json:"ec"` | |||
| } | |||
| func NewPreUploadEcResp(nodes []ramsg.RespNode, ec ramsg.Ec) PreUploadEcResp { | |||
| return PreUploadEcResp{ | |||
| Nodes: nodes, | |||
| Ec: ec, | |||
| } | |||
| } | |||
| type CreateRepObject struct { | |||
| BucketID int64 `json:"bucketID"` | |||
| ObjectName string `json:"objectName"` | |||
| NodeIDs []int64 `json:"nodeIDs"` | |||
| FileHash string `json:"fileHash"` | |||
| FileSize int64 `json:"fileSize,string"` | |||
| RepCount int `json:"repCount"` | |||
| UserID int64 `json:"userID"` | |||
| DirName string `json:"dirName"` | |||
| } | |||
| func NewCreateRepObject(bucketID int64, objectName string, fileSize int64, repCount int, userID int64, nodeIDs []int64, fileHash string, dirName string) CreateRepObject { | |||
| return CreateRepObject{ | |||
| BucketID: bucketID, | |||
| ObjectName: objectName, | |||
| FileSize: fileSize, | |||
| RepCount: repCount, | |||
| UserID: userID, | |||
| NodeIDs: nodeIDs, | |||
| FileHash: fileHash, | |||
| } | |||
| } | |||
| type CreateEcObject struct { | |||
| BucketID int64 `json:"bucketID"` | |||
| ObjectName string `json:"objectName"` | |||
| NodeIDs []int64 `json:"nodeIDs"` | |||
| Hashes []string `json:"hashes"` | |||
| FileSize int64 `json:"fileSize,string"` | |||
| UserID int64 `json:"userID"` | |||
| EcName string `json:"ecName"` | |||
| DirName string `json:"dirName"` | |||
| } | |||
| func NewCreateEcObject(bucketID int64, objectName string, fileSize int64, userID int64, nodeIDs []int64, hashes []string, ecName string, dirName string) CreateEcObject { | |||
| return CreateEcObject{ | |||
| BucketID: bucketID, | |||
| ObjectName: objectName, | |||
| FileSize: fileSize, | |||
| UserID: userID, | |||
| NodeIDs: nodeIDs, | |||
| Hashes: hashes, | |||
| EcName: ecName, | |||
| DirName: dirName, | |||
| } | |||
| } | |||
| // 协调端发给客户端,告知哈希写入结果 | |||
| type CreateObjectResp struct { | |||
| ObjectID int64 `json:"objectID"` | |||
| } | |||
| func NewCreateObjectResp(objectID int64) CreateObjectResp { | |||
| return CreateObjectResp{ | |||
| ObjectID: objectID, | |||
| } | |||
| } | |||
| // PreUpdateRepObject 更新Rep对象 | |||
| type PreUpdateRepObject struct { | |||
| ObjectID int64 `json:"objectID"` | |||
| FileSize int64 `json:"fileSize,string"` | |||
| UserID int64 `json:"userID"` | |||
| ClientExternalIP string `json:"clientExternalIP"` | |||
| } | |||
| func NewPreUpdateRepObject(objectID int64, fileSize int64, userID int64, clientExternalIP string) PreUpdateRepObject { | |||
| return PreUpdateRepObject{ | |||
| ObjectID: objectID, | |||
| FileSize: fileSize, | |||
| UserID: userID, | |||
| ClientExternalIP: clientExternalIP, | |||
| } | |||
| } | |||
| type PreUpdateRepObjectResp struct { | |||
| Nodes []PreUpdateRepObjectRespNode `json:"nodes"` | |||
| } | |||
| type PreUpdateRepObjectRespNode struct { | |||
| ID int64 `json:"id"` | |||
| ExternalIP string `json:"externalIP"` | |||
| LocalIP string `json:"localIP"` | |||
| IsSameLocation bool `json:"isSameLocation"` // 客户端是否与此节点在同一个地域 | |||
| HasOldObject bool `json:"hasOldObject"` // 节点是否存有旧的对象文件 | |||
| } | |||
| func NewPreUpdateRepObjectRespNode(id int64, exterIP string, localIP string, isSameLocation bool, hasOldObject bool) PreUpdateRepObjectRespNode { | |||
| return PreUpdateRepObjectRespNode{ | |||
| ID: id, | |||
| ExternalIP: exterIP, | |||
| LocalIP: localIP, | |||
| IsSameLocation: isSameLocation, | |||
| HasOldObject: hasOldObject, | |||
| } | |||
| } | |||
| func NewPreUpdateRepObjectResp(nodes []PreUpdateRepObjectRespNode) PreUpdateRepObjectResp { | |||
| return PreUpdateRepObjectResp{ | |||
| Nodes: nodes, | |||
| } | |||
| } | |||
| // UpdateRepObject 更新Rep对象 | |||
| type UpdateRepObject struct { | |||
| ObjectID int64 `json:"objectID"` | |||
| FileHash string `json:"fileHash"` | |||
| FileSize int64 `json:"fileSize,string"` | |||
| NodeIDs []int64 `json:"nodeIDs"` | |||
| UserID int64 `json:"userID"` | |||
| } | |||
| func NewUpdateRepObject(objectID int64, fileHash string, fileSize int64, nodeIDs []int64, userID int64) UpdateRepObject { | |||
| return UpdateRepObject{ | |||
| ObjectID: objectID, | |||
| FileHash: fileHash, | |||
| FileSize: fileSize, | |||
| NodeIDs: nodeIDs, | |||
| UserID: userID, | |||
| } | |||
| } | |||
| type UpdateRepObjectResp struct{} | |||
| func NewUpdateRepObjectResp() UpdateRepObjectResp { | |||
| return UpdateRepObjectResp{} | |||
| } | |||
| // DeleteObjectBody 删除对象 | |||
| type DeleteObject struct { | |||
| UserID int64 `db:"userID"` | |||
| ObjectID int64 `db:"objectID"` | |||
| } | |||
| func NewDeleteObject(userID int64, objectID int64) DeleteObject { | |||
| return DeleteObject{ | |||
| UserID: userID, | |||
| ObjectID: objectID, | |||
| } | |||
| } | |||
| type DeleteObjectResp struct{} | |||
| func NewDeleteObjectResp() DeleteObjectResp { | |||
| return DeleteObjectResp{} | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[GetObjectsByDirName]() | |||
| mq.RegisterMessage[GetObjectsResp]() | |||
| mq.RegisterMessage[PreDownloadObject]() | |||
| mq.RegisterMessage[PreDownloadObjectResp]() | |||
| mq.RegisterMessage[PreUploadRepObject]() | |||
| mq.RegisterMessage[PreUploadResp]() | |||
| mq.RegisterMessage[PreUploadEcObject]() | |||
| mq.RegisterMessage[PreUploadEcResp]() | |||
| mq.RegisterMessage[CreateRepObject]() | |||
| mq.RegisterMessage[CreateEcObject]() | |||
| mq.RegisterMessage[CreateObjectResp]() | |||
| mq.RegisterMessage[PreUpdateRepObject]() | |||
| mq.RegisterMessage[PreUpdateRepObjectResp]() | |||
| mq.RegisterMessage[UpdateRepObject]() | |||
| mq.RegisterMessage[UpdateRepObjectResp]() | |||
| mq.RegisterMessage[DeleteObject]() | |||
| mq.RegisterMessage[DeleteObjectResp]() | |||
| } | |||
| @@ -0,0 +1,100 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/models" | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/db/model" | |||
| ) | |||
| type GetStorageInfo struct { | |||
| UserID int64 `json:"userID"` | |||
| StorageID int64 `json:"storageID"` | |||
| } | |||
| func NewGetStorageInfo(userID int64, storageID int64) GetStorageInfo { | |||
| return GetStorageInfo{ | |||
| UserID: userID, | |||
| StorageID: storageID, | |||
| } | |||
| } | |||
| type GetStorageInfoResp struct { | |||
| model.Storage | |||
| } | |||
| func NewGetStorageInfoResp(storageID int64, name string, nodeID int64, dir string, state string) GetStorageInfoResp { | |||
| return GetStorageInfoResp{ | |||
| model.Storage{ | |||
| StorageID: storageID, | |||
| Name: name, | |||
| NodeID: nodeID, | |||
| Directory: dir, | |||
| State: state, | |||
| }, | |||
| } | |||
| } | |||
| // 客户端发给协调端,告知要调度数据 | |||
| type PreMoveObjectToStorage struct { | |||
| ObjectID int64 `json:"objectID"` | |||
| StorageID int64 `json:"storageID"` | |||
| UserID int64 `json:"userID"` | |||
| } | |||
| func NewPreMoveObjectToStorage(objectID int64, stgID int64, userID int64) PreMoveObjectToStorage { | |||
| return PreMoveObjectToStorage{ | |||
| ObjectID: objectID, | |||
| StorageID: stgID, | |||
| UserID: userID, | |||
| } | |||
| } | |||
| // 协调端发给客户端,告知要调度数据的详情 | |||
| type PreMoveObjectToStorageResp struct { | |||
| NodeID int64 `json:"nodeID"` | |||
| Directory string `json:"directory"` | |||
| Object model.Object `json:"object"` | |||
| Redundancy models.RedundancyDataTypes `json:"redundancy"` | |||
| } | |||
| func NewPreMoveObjectToStorageRespBody[T models.RedundancyDataTypes](nodeID int64, dir string, object model.Object, redundancy T) PreMoveObjectToStorageResp { | |||
| return PreMoveObjectToStorageResp{ | |||
| NodeID: nodeID, | |||
| Directory: dir, | |||
| Object: object, | |||
| Redundancy: redundancy, | |||
| } | |||
| } | |||
| // 调度完成 | |||
| type MoveObjectToStorage struct { | |||
| ObjectID int64 `json:"objectID"` | |||
| StorageID int64 `json:"storageID"` | |||
| UserID int64 `json:"userID"` | |||
| } | |||
| func NewMoveObjectToStorage(objectID int64, stgID int64, userID int64) MoveObjectToStorage { | |||
| return MoveObjectToStorage{ | |||
| ObjectID: objectID, | |||
| StorageID: stgID, | |||
| UserID: userID, | |||
| } | |||
| } | |||
| // 协调端发给客户端,告知要调度数据的详情 | |||
| type MoveObjectToStorageResp struct{} | |||
| func NewMoveObjectToStorageResp() MoveObjectToStorageResp { | |||
| return MoveObjectToStorageResp{} | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[GetStorageInfo]() | |||
| mq.RegisterMessage[GetStorageInfoResp]() | |||
| mq.RegisterMessage[PreMoveObjectToStorage]() | |||
| mq.RegisterMessage[PreMoveObjectToStorageResp]() | |||
| mq.RegisterMessage[MoveObjectToStorage]() | |||
| mq.RegisterMessage[MoveObjectToStorageResp]() | |||
| } | |||
| @@ -0,0 +1,112 @@ | |||
| package message | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/models" | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| myreflect "gitlink.org.cn/cloudream/common/utils/reflect" | |||
| ) | |||
| type Node struct { | |||
| ID int64 `json:"id"` | |||
| ExternalIP string `json:"externalIP"` | |||
| LocalIP string `json:"localIP"` | |||
| } | |||
| func NewNode(id int64, externalIP string, localIP string) Node { | |||
| return Node{ | |||
| ID: id, | |||
| ExternalIP: externalIP, | |||
| LocalIP: localIP, | |||
| } | |||
| } | |||
| type RespNode struct { | |||
| Node | |||
| IsSameLocation bool `json:"isSameLocation"` // 客户端是否与此节点在同一个地域 | |||
| } | |||
| func NewRespNode(id int64, externalIP string, localIP string, isSameLocation bool) RespNode { | |||
| return RespNode{ | |||
| Node: Node{ | |||
| ID: id, | |||
| ExternalIP: externalIP, | |||
| LocalIP: localIP, | |||
| }, | |||
| IsSameLocation: isSameLocation, | |||
| } | |||
| } | |||
| // Resp开头的RedundancyData与RedundancyData的区别在于,多了Nodes等字段。需要一个更好的名称。 | |||
| type RespRedundancyDataTypesConst interface { | |||
| RespRepRedundancyData | RespEcRedundancyData | |||
| } | |||
| type RespRedundancyDataTypes interface{} | |||
| type RespRepRedundancyData struct { | |||
| models.RepRedundancyData | |||
| Nodes []RespNode `json:"nodes"` | |||
| } | |||
| func NewRespRepRedundancyData(fileHash string, nodes []RespNode) RespRepRedundancyData { | |||
| return RespRepRedundancyData{ | |||
| RepRedundancyData: models.RepRedundancyData{ | |||
| FileHash: fileHash, | |||
| }, | |||
| Nodes: nodes, | |||
| } | |||
| } | |||
| type RespEcRedundancyData struct { | |||
| Ec Ec `json:"ec"` | |||
| Nodes [][]RespNode `json:"nodes"` | |||
| Blocks []RespObjectBlock `json:"blocks"` | |||
| } | |||
| func NewRespEcRedundancyData(ec Ec, blocks []RespObjectBlock, nodes [][]RespNode) RespEcRedundancyData { | |||
| return RespEcRedundancyData{ | |||
| Ec: ec, | |||
| Nodes: nodes, | |||
| Blocks: blocks, | |||
| } | |||
| } | |||
| type RespObjectBlock struct { | |||
| models.ObjectBlock | |||
| //Node RespNode `json:"node"` | |||
| } | |||
| // func NewRespObjectBlock(index int, fileHash string, node RespNode) RespObjectBlock { | |||
| func NewRespObjectBlock(index int, fileHash string) RespObjectBlock { | |||
| return RespObjectBlock{ | |||
| ObjectBlock: models.ObjectBlock{ | |||
| Index: index, | |||
| FileHash: fileHash, | |||
| }, | |||
| //Node: node, | |||
| } | |||
| } | |||
| type Ec struct { | |||
| ID int `json:"id"` | |||
| Name string `json:"name"` | |||
| EcK int `json:"ecK"` | |||
| EcN int `json:"ecN"` | |||
| } | |||
| func NewEc(id int, name string, k int, n int) Ec { | |||
| return Ec{ | |||
| ID: id, | |||
| Name: name, | |||
| EcK: k, | |||
| EcN: n, | |||
| } | |||
| } | |||
| func init() { | |||
| mq.RegisterTypeSet[models.RedundancyConfigTypes](myreflect.TypeOf[models.RepRedundancyConfig](), myreflect.TypeOf[models.ECRedundancyConfig]()) | |||
| mq.RegisterTypeSet[models.RedundancyDataTypes](myreflect.TypeOf[models.RepRedundancyData](), myreflect.TypeOf[models.ECRedundancyData]()) | |||
| mq.RegisterTypeSet[RespRedundancyDataTypes](myreflect.TypeOf[RespRepRedundancyData](), myreflect.TypeOf[RespEcRedundancyData]()) | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| package scanner | |||
| import ( | |||
| "fmt" | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| scevt "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/scanner/event" | |||
| ) | |||
| type PostEvent struct { | |||
| Event map[string]any `json:"event"` | |||
| IsEmergency bool `json:"isEmergency"` // 重要消息,优先处理 | |||
| DontMerge bool `json:"dontMerge"` // 不可合并此消息 | |||
| } | |||
| func NewPostEvent(event any, isEmergency bool, dontMerge bool) (PostEvent, error) { | |||
| mp, err := scevt.MessageToMap(event) | |||
| if err != nil { | |||
| return PostEvent{}, fmt.Errorf("message to map failed, err: %w", err) | |||
| } | |||
| return PostEvent{ | |||
| Event: mp, | |||
| IsEmergency: isEmergency, | |||
| DontMerge: dontMerge, | |||
| }, nil | |||
| } | |||
| func init() { | |||
| mq.RegisterMessage[PostEvent]() | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| package event | |||
| type AgentCheckCache struct { | |||
| NodeID int64 `json:"nodeID"` | |||
| FileHashes []string `json:"fileHashes"` // 需要检查的FileHash列表,如果为nil(不是为空),则代表进行全量检查 | |||
| } | |||
| func NewAgentCheckCache(nodeID int64, fileHashes []string) AgentCheckCache { | |||
| return AgentCheckCache{ | |||
| NodeID: nodeID, | |||
| FileHashes: fileHashes, | |||
| } | |||
| } | |||
| func init() { | |||
| Register[AgentCheckCache]() | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package event | |||
| type AgentCheckState struct { | |||
| NodeID int64 `json:"nodeID"` | |||
| } | |||
| func NewAgentCheckState(nodeID int64) AgentCheckState { | |||
| return AgentCheckState{ | |||
| NodeID: nodeID, | |||
| } | |||
| } | |||
| func init() { | |||
| Register[AgentCheckState]() | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| package event | |||
| type AgentCheckStorage struct { | |||
| StorageID int64 `json:"storageID"` | |||
| ObjectIDs []int64 `json:"objectIDs"` // 需要检查的Object文件列表,如果为nil(不是为空),则代表进行全量检查 | |||
| } | |||
| func NewAgentCheckStorage(storageID int64, objectIDs []int64) AgentCheckStorage { | |||
| return AgentCheckStorage{ | |||
| StorageID: storageID, | |||
| ObjectIDs: objectIDs, | |||
| } | |||
| } | |||
| func init() { | |||
| Register[AgentCheckStorage]() | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package event | |||
| type CheckCache struct { | |||
| NodeID int64 `json:"nodeID"` | |||
| } | |||
| func NewCheckCache(nodeID int64) CheckCache { | |||
| return CheckCache{ | |||
| NodeID: nodeID, | |||
| } | |||
| } | |||
| func init() { | |||
| Register[CheckCache]() | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package event | |||
| type CheckObject struct { | |||
| ObjectIDs []int64 `json:"objectIDs"` | |||
| } | |||
| func NewCheckObject(objectIDs []int64) CheckObject { | |||
| return CheckObject{ | |||
| ObjectIDs: objectIDs, | |||
| } | |||
| } | |||
| func init() { | |||
| Register[CheckObject]() | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package event | |||
| type CheckRepCount struct { | |||
| FileHashes []string `json:"fileHashes"` | |||
| } | |||
| func NewCheckRepCount(fileHashes []string) CheckRepCount { | |||
| return CheckRepCount{ | |||
| FileHashes: fileHashes, | |||
| } | |||
| } | |||
| func init() { | |||
| Register[CheckRepCount]() | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package event | |||
| import ( | |||
| myreflect "gitlink.org.cn/cloudream/common/utils/reflect" | |||
| "gitlink.org.cn/cloudream/common/utils/serder" | |||
| ) | |||
| var typeResolver = serder.NewTypeNameResolver(true) | |||
| var serderOption = serder.TypedSerderOption{ | |||
| TypeResolver: &typeResolver, | |||
| TypeFieldName: "@type", | |||
| } | |||
| func MapToMessage(m map[string]any) (any, error) { | |||
| return serder.TypedMapToObject(m, serderOption) | |||
| } | |||
| func MessageToMap(msg any) (map[string]any, error) { | |||
| return serder.ObjectToTypedMap(msg, serderOption) | |||
| } | |||
| func Register[T any]() { | |||
| typeResolver.Register(myreflect.TypeOf[T]()) | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| type AgentService interface { | |||
| GetState(msg *agtmsg.GetState) (*agtmsg.GetStateResp, *mq.CodeMessage) | |||
| } | |||
| func init() { | |||
| Register(AgentService.GetState) | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| type IPFSService interface { | |||
| CheckIPFS(msg *agtmsg.CheckIPFS) (*agtmsg.CheckIPFSResp, *mq.CodeMessage) | |||
| } | |||
| func init() { | |||
| Register(IPFSService.CheckIPFS) | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| type ObjectService interface { | |||
| StartPinningObject(msg *agtmsg.StartPinningObject) (*agtmsg.StartPinningObjectResp, *mq.CodeMessage) | |||
| WaitPinningObject(msg *agtmsg.WaitPinningObject) (*agtmsg.WaitPinningObjectResp, *mq.CodeMessage) | |||
| } | |||
| func init() { | |||
| Register(ObjectService.StartPinningObject) | |||
| Register(ObjectService.WaitPinningObject) | |||
| } | |||
| @@ -0,0 +1,67 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| mymq "gitlink.org.cn/cloudream/storage-common/pkgs/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/mq/config" | |||
| ) | |||
| type Service interface { | |||
| ObjectService | |||
| StorageService | |||
| IPFSService | |||
| AgentService | |||
| } | |||
| type Server struct { | |||
| service Service | |||
| rabbitSvr mq.RabbitMQServer | |||
| OnError func(err error) | |||
| } | |||
| func NewServer(svc Service, id int64, cfg *config.Config) (*Server, error) { | |||
| srv := &Server{ | |||
| service: svc, | |||
| } | |||
| rabbitSvr, err := mq.NewRabbitMQServer( | |||
| cfg.MakeConnectingURL(), | |||
| mymq.MakeAgentQueueName(id), | |||
| func(msg *mq.Message) (*mq.Message, error) { | |||
| return msgDispatcher.Handle(srv.service, msg) | |||
| }, | |||
| ) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| srv.rabbitSvr = *rabbitSvr | |||
| return srv, nil | |||
| } | |||
| func (s *Server) Stop() { | |||
| s.rabbitSvr.Close() | |||
| } | |||
| func (s *Server) Serve() error { | |||
| return s.rabbitSvr.Serve() | |||
| } | |||
| var msgDispatcher mq.MessageDispatcher = mq.NewMessageDispatcher() | |||
| // Register 将Service中的一个接口函数作为指定类型消息的处理函数 | |||
| // TODO 需要约束:Service实现了TSvc接口 | |||
| func Register[TSvc any, TReq any, TResp any](svcFn func(svc TSvc, msg *TReq) (*TResp, *mq.CodeMessage)) { | |||
| mq.AddServiceFn(&msgDispatcher, svcFn) | |||
| } | |||
| // RegisterNoReply 将Service中的一个*没有返回值的*接口函数作为指定类型消息的处理函数 | |||
| // TODO 需要约束:Service实现了TSvc接口 | |||
| func RegisterNoReply[TSvc any, TReq any](svcFn func(svc TSvc, msg *TReq)) { | |||
| mq.AddNoRespServiceFn(&msgDispatcher, svcFn) | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package agent | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| agtmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/agent" | |||
| ) | |||
| type StorageService interface { | |||
| StartStorageMoveObject(msg *agtmsg.StartStorageMoveObject) (*agtmsg.StartStorageMoveObjectResp, *mq.CodeMessage) | |||
| WaitStorageMoveObject(msg *agtmsg.WaitStorageMoveObject) (*agtmsg.WaitStorageMoveObjectResp, *mq.CodeMessage) | |||
| StorageCheck(msg *agtmsg.StorageCheck) (*agtmsg.StorageCheckResp, *mq.CodeMessage) | |||
| StartStorageUploadRepObject(msg *agtmsg.StartStorageUploadRepObject) (*agtmsg.StartStorageUploadRepObjectResp, *mq.CodeMessage) | |||
| WaitStorageUploadRepObject(msg *agtmsg.WaitStorageUploadRepObject) (*agtmsg.WaitStorageUploadRepObjectResp, *mq.CodeMessage) | |||
| } | |||
| func init() { | |||
| Register(StorageService.StartStorageMoveObject) | |||
| Register(StorageService.WaitStorageMoveObject) | |||
| Register(StorageService.StorageCheck) | |||
| Register(StorageService.StartStorageUploadRepObject) | |||
| Register(StorageService.WaitStorageUploadRepObject) | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package coordinator | |||
| import coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| type AgentService interface { | |||
| TempCacheReport(msg *coormsg.TempCacheReport) | |||
| AgentStatusReport(msg *coormsg.AgentStatusReport) | |||
| } | |||
| func init() { | |||
| RegisterNoReply(AgentService.TempCacheReport) | |||
| RegisterNoReply(AgentService.AgentStatusReport) | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| ) | |||
| type BucketService interface { | |||
| GetUserBuckets(msg *coormsg.GetUserBuckets) (*coormsg.GetUserBucketsResp, *mq.CodeMessage) | |||
| GetBucketObjects(msg *coormsg.GetBucketObjects) (*coormsg.GetBucketObjectsResp, *mq.CodeMessage) | |||
| CreateBucket(msg *coormsg.CreateBucket) (*coormsg.CreateBucketResp, *mq.CodeMessage) | |||
| DeleteBucket(msg *coormsg.DeleteBucket) (*coormsg.DeleteBucketResp, *mq.CodeMessage) | |||
| } | |||
| func init() { | |||
| Register(BucketService.GetUserBuckets) | |||
| Register(BucketService.GetBucketObjects) | |||
| Register(BucketService.CreateBucket) | |||
| Register(BucketService.DeleteBucket) | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package coordinator | |||
| import ( | |||
| "testing" | |||
| . "github.com/smartystreets/goconvey/convey" | |||
| ) | |||
| func TestSerder(t *testing.T) { | |||
| Convey("输出注册的Handler", t, func() { | |||
| for k, _ := range msgDispatcher.Handlers { | |||
| t.Logf("(%s)", k) | |||
| } | |||
| }) | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| ) | |||
| type ObjectService interface { | |||
| GetObjectsByDirName(msg *coormsg.GetObjectsByDirName) (*coormsg.GetObjectsResp, *mq.CodeMessage) | |||
| PreDownloadObject(msg *coormsg.PreDownloadObject) (*coormsg.PreDownloadObjectResp, *mq.CodeMessage) | |||
| PreUploadRepObject(msg *coormsg.PreUploadRepObject) (*coormsg.PreUploadResp, *mq.CodeMessage) | |||
| CreateRepObject(msg *coormsg.CreateRepObject) (*coormsg.CreateObjectResp, *mq.CodeMessage) | |||
| PreUpdateRepObject(msg *coormsg.PreUpdateRepObject) (*coormsg.PreUpdateRepObjectResp, *mq.CodeMessage) | |||
| UpdateRepObject(msg *coormsg.UpdateRepObject) (*coormsg.UpdateRepObjectResp, *mq.CodeMessage) | |||
| PreUploadEcObject(msg *coormsg.PreUploadEcObject) (*coormsg.PreUploadEcResp, *mq.CodeMessage) | |||
| CreateEcObject(msg *coormsg.CreateEcObject) (*coormsg.CreateObjectResp, *mq.CodeMessage) | |||
| DeleteObject(msg *coormsg.DeleteObject) (*coormsg.DeleteObjectResp, *mq.CodeMessage) | |||
| } | |||
| func init() { | |||
| Register(ObjectService.GetObjectsByDirName) | |||
| Register(ObjectService.PreDownloadObject) | |||
| Register(ObjectService.PreUploadRepObject) | |||
| Register(ObjectService.CreateRepObject) | |||
| Register(ObjectService.PreUpdateRepObject) | |||
| Register(ObjectService.UpdateRepObject) | |||
| Register(ObjectService.PreUploadEcObject) | |||
| Register(ObjectService.CreateEcObject) | |||
| Register(ObjectService.DeleteObject) | |||
| } | |||
| @@ -0,0 +1,67 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| mymq "gitlink.org.cn/cloudream/storage-common/pkgs/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/mq/config" | |||
| ) | |||
| // Service 协调端接口 | |||
| type Service interface { | |||
| ObjectService | |||
| BucketService | |||
| StorageService | |||
| AgentService | |||
| } | |||
| type Server struct { | |||
| service Service | |||
| rabbitSvr mq.RabbitMQServer | |||
| OnError func(err error) | |||
| } | |||
| func NewServer(svc Service, cfg *config.Config) (*Server, error) { | |||
| srv := &Server{ | |||
| service: svc, | |||
| } | |||
| rabbitSvr, err := mq.NewRabbitMQServer( | |||
| cfg.MakeConnectingURL(), | |||
| mymq.COORDINATOR_QUEUE_NAME, | |||
| func(msg *mq.Message) (*mq.Message, error) { | |||
| return msgDispatcher.Handle(srv.service, msg) | |||
| }, | |||
| ) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| srv.rabbitSvr = *rabbitSvr | |||
| return srv, nil | |||
| } | |||
| func (s *Server) Stop() { | |||
| s.rabbitSvr.Close() | |||
| } | |||
| func (s *Server) Serve() error { | |||
| return s.rabbitSvr.Serve() | |||
| } | |||
| var msgDispatcher mq.MessageDispatcher = mq.NewMessageDispatcher() | |||
| // Register 将Service中的一个接口函数作为指定类型消息的处理函数 | |||
| // TODO 需要约束:Service实现了TSvc接口 | |||
| func Register[TSvc any, TReq any, TResp any](svcFn func(svc TSvc, msg *TReq) (*TResp, *mq.CodeMessage)) { | |||
| mq.AddServiceFn(&msgDispatcher, svcFn) | |||
| } | |||
| // RegisterNoReply 将Service中的一个*没有返回值的*接口函数作为指定类型消息的处理函数 | |||
| // TODO 需要约束:Service实现了TSvc接口 | |||
| func RegisterNoReply[TSvc any, TReq any](svcFn func(svc TSvc, msg *TReq)) { | |||
| mq.AddNoRespServiceFn(&msgDispatcher, svcFn) | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| package coordinator | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator" | |||
| ) | |||
| type StorageService interface { | |||
| GetStorageInfo(msg *coormsg.GetStorageInfo) (*coormsg.GetStorageInfoResp, *mq.CodeMessage) | |||
| PreMoveObjectToStorage(msg *coormsg.PreMoveObjectToStorage) (*coormsg.PreMoveObjectToStorageResp, *mq.CodeMessage) | |||
| MoveObjectToStorage(msg *coormsg.MoveObjectToStorage) (*coormsg.MoveObjectToStorageResp, *mq.CodeMessage) | |||
| } | |||
| func init() { | |||
| Register(StorageService.GetStorageInfo) | |||
| Register(StorageService.PreMoveObjectToStorage) | |||
| Register(StorageService.MoveObjectToStorage) | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package scanner | |||
| import scmsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/scanner" | |||
| type EventService interface { | |||
| PostEvent(event *scmsg.PostEvent) | |||
| } | |||
| func init() { | |||
| RegisterNoReply(EventService.PostEvent) | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| package scanner | |||
| import ( | |||
| "gitlink.org.cn/cloudream/common/pkg/mq" | |||
| mymq "gitlink.org.cn/cloudream/storage-common/pkgs/mq" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/mq/config" | |||
| ) | |||
| // Service 协调端接口 | |||
| type Service interface { | |||
| EventService | |||
| } | |||
| type Server struct { | |||
| service Service | |||
| rabbitSvr mq.RabbitMQServer | |||
| OnError func(err error) | |||
| } | |||
| func NewServer(svc Service, cfg *config.Config) (*Server, error) { | |||
| srv := &Server{ | |||
| service: svc, | |||
| } | |||
| rabbitSvr, err := mq.NewRabbitMQServer( | |||
| cfg.MakeConnectingURL(), | |||
| mymq.SCANNER_QUEUE_NAME, | |||
| func(msg *mq.Message) (*mq.Message, error) { | |||
| return msgDispatcher.Handle(srv.service, msg) | |||
| }, | |||
| ) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| srv.rabbitSvr = *rabbitSvr | |||
| return srv, nil | |||
| } | |||
| func (s *Server) Stop() { | |||
| s.rabbitSvr.Close() | |||
| } | |||
| func (s *Server) Serve() error { | |||
| return s.rabbitSvr.Serve() | |||
| } | |||
| var msgDispatcher mq.MessageDispatcher = mq.NewMessageDispatcher() | |||
| // Register 将Service中的一个接口函数作为指定类型消息的处理函数 | |||
| // TODO 需要约束:Service实现了TSvc接口 | |||
| func Register[TSvc any, TReq any, TResp any](svcFn func(svc TSvc, msg *TReq) (*TResp, *mq.CodeMessage)) { | |||
| mq.AddServiceFn(&msgDispatcher, svcFn) | |||
| } | |||
| // RegisterNoReply 将Service中的一个*没有返回值的*接口函数作为指定类型消息的处理函数 | |||
| // TODO 需要约束:Service实现了TSvc接口 | |||
| func RegisterNoReply[TSvc any, TReq any](svcFn func(svc TSvc, msg *TReq)) { | |||
| mq.AddNoRespServiceFn(&msgDispatcher, svcFn) | |||
| } | |||
| @@ -0,0 +1,2 @@ | |||
| protoc: | |||
| protoc --go_out=. --go-grpc_out=. .\file_transport.proto | |||
| @@ -0,0 +1,343 @@ | |||
| // 使用的语法版本 | |||
| // Code generated by protoc-gen-go. DO NOT EDIT. | |||
| // versions: | |||
| // protoc-gen-go v1.30.0 | |||
| // protoc v4.22.3 | |||
| // source: file_transport.proto | |||
| package proto | |||
| import ( | |||
| protoreflect "google.golang.org/protobuf/reflect/protoreflect" | |||
| protoimpl "google.golang.org/protobuf/runtime/protoimpl" | |||
| reflect "reflect" | |||
| sync "sync" | |||
| ) | |||
| const ( | |||
| // Verify that this generated code is sufficiently up-to-date. | |||
| _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) | |||
| // Verify that runtime/protoimpl is sufficiently up-to-date. | |||
| _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) | |||
| ) | |||
| type FileDataPacketType int32 | |||
| const ( | |||
| FileDataPacketType_Data FileDataPacketType = 0 | |||
| FileDataPacketType_EOF FileDataPacketType = 1 | |||
| ) | |||
| // Enum value maps for FileDataPacketType. | |||
| var ( | |||
| FileDataPacketType_name = map[int32]string{ | |||
| 0: "Data", | |||
| 1: "EOF", | |||
| } | |||
| FileDataPacketType_value = map[string]int32{ | |||
| "Data": 0, | |||
| "EOF": 1, | |||
| } | |||
| ) | |||
| func (x FileDataPacketType) Enum() *FileDataPacketType { | |||
| p := new(FileDataPacketType) | |||
| *p = x | |||
| return p | |||
| } | |||
| func (x FileDataPacketType) String() string { | |||
| return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) | |||
| } | |||
| func (FileDataPacketType) Descriptor() protoreflect.EnumDescriptor { | |||
| return file_file_transport_proto_enumTypes[0].Descriptor() | |||
| } | |||
| func (FileDataPacketType) Type() protoreflect.EnumType { | |||
| return &file_file_transport_proto_enumTypes[0] | |||
| } | |||
| func (x FileDataPacketType) Number() protoreflect.EnumNumber { | |||
| return protoreflect.EnumNumber(x) | |||
| } | |||
| // Deprecated: Use FileDataPacketType.Descriptor instead. | |||
| func (FileDataPacketType) EnumDescriptor() ([]byte, []int) { | |||
| return file_file_transport_proto_rawDescGZIP(), []int{0} | |||
| } | |||
| // 文件数据。注意:只在Type为Data的时候,Data字段才能有数据 | |||
| type FileDataPacket struct { | |||
| state protoimpl.MessageState | |||
| sizeCache protoimpl.SizeCache | |||
| unknownFields protoimpl.UnknownFields | |||
| Type FileDataPacketType `protobuf:"varint,1,opt,name=Type,proto3,enum=FileDataPacketType" json:"Type,omitempty"` | |||
| Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"` | |||
| } | |||
| func (x *FileDataPacket) Reset() { | |||
| *x = FileDataPacket{} | |||
| if protoimpl.UnsafeEnabled { | |||
| mi := &file_file_transport_proto_msgTypes[0] | |||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| ms.StoreMessageInfo(mi) | |||
| } | |||
| } | |||
| func (x *FileDataPacket) String() string { | |||
| return protoimpl.X.MessageStringOf(x) | |||
| } | |||
| func (*FileDataPacket) ProtoMessage() {} | |||
| func (x *FileDataPacket) ProtoReflect() protoreflect.Message { | |||
| mi := &file_file_transport_proto_msgTypes[0] | |||
| if protoimpl.UnsafeEnabled && x != nil { | |||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| if ms.LoadMessageInfo() == nil { | |||
| ms.StoreMessageInfo(mi) | |||
| } | |||
| return ms | |||
| } | |||
| return mi.MessageOf(x) | |||
| } | |||
| // Deprecated: Use FileDataPacket.ProtoReflect.Descriptor instead. | |||
| func (*FileDataPacket) Descriptor() ([]byte, []int) { | |||
| return file_file_transport_proto_rawDescGZIP(), []int{0} | |||
| } | |||
| func (x *FileDataPacket) GetType() FileDataPacketType { | |||
| if x != nil { | |||
| return x.Type | |||
| } | |||
| return FileDataPacketType_Data | |||
| } | |||
| func (x *FileDataPacket) GetData() []byte { | |||
| if x != nil { | |||
| return x.Data | |||
| } | |||
| return nil | |||
| } | |||
| type SendResp struct { | |||
| state protoimpl.MessageState | |||
| sizeCache protoimpl.SizeCache | |||
| unknownFields protoimpl.UnknownFields | |||
| FileHash string `protobuf:"bytes,1,opt,name=FileHash,proto3" json:"FileHash,omitempty"` | |||
| } | |||
| func (x *SendResp) Reset() { | |||
| *x = SendResp{} | |||
| if protoimpl.UnsafeEnabled { | |||
| mi := &file_file_transport_proto_msgTypes[1] | |||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| ms.StoreMessageInfo(mi) | |||
| } | |||
| } | |||
| func (x *SendResp) String() string { | |||
| return protoimpl.X.MessageStringOf(x) | |||
| } | |||
| func (*SendResp) ProtoMessage() {} | |||
| func (x *SendResp) ProtoReflect() protoreflect.Message { | |||
| mi := &file_file_transport_proto_msgTypes[1] | |||
| if protoimpl.UnsafeEnabled && x != nil { | |||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| if ms.LoadMessageInfo() == nil { | |||
| ms.StoreMessageInfo(mi) | |||
| } | |||
| return ms | |||
| } | |||
| return mi.MessageOf(x) | |||
| } | |||
| // Deprecated: Use SendResp.ProtoReflect.Descriptor instead. | |||
| func (*SendResp) Descriptor() ([]byte, []int) { | |||
| return file_file_transport_proto_rawDescGZIP(), []int{1} | |||
| } | |||
| func (x *SendResp) GetFileHash() string { | |||
| if x != nil { | |||
| return x.FileHash | |||
| } | |||
| return "" | |||
| } | |||
| type GetReq struct { | |||
| state protoimpl.MessageState | |||
| sizeCache protoimpl.SizeCache | |||
| unknownFields protoimpl.UnknownFields | |||
| FileHash string `protobuf:"bytes,1,opt,name=FileHash,proto3" json:"FileHash,omitempty"` | |||
| } | |||
| func (x *GetReq) Reset() { | |||
| *x = GetReq{} | |||
| if protoimpl.UnsafeEnabled { | |||
| mi := &file_file_transport_proto_msgTypes[2] | |||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| ms.StoreMessageInfo(mi) | |||
| } | |||
| } | |||
| func (x *GetReq) String() string { | |||
| return protoimpl.X.MessageStringOf(x) | |||
| } | |||
| func (*GetReq) ProtoMessage() {} | |||
| func (x *GetReq) ProtoReflect() protoreflect.Message { | |||
| mi := &file_file_transport_proto_msgTypes[2] | |||
| if protoimpl.UnsafeEnabled && x != nil { | |||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||
| if ms.LoadMessageInfo() == nil { | |||
| ms.StoreMessageInfo(mi) | |||
| } | |||
| return ms | |||
| } | |||
| return mi.MessageOf(x) | |||
| } | |||
| // Deprecated: Use GetReq.ProtoReflect.Descriptor instead. | |||
| func (*GetReq) Descriptor() ([]byte, []int) { | |||
| return file_file_transport_proto_rawDescGZIP(), []int{2} | |||
| } | |||
| func (x *GetReq) GetFileHash() string { | |||
| if x != nil { | |||
| return x.FileHash | |||
| } | |||
| return "" | |||
| } | |||
| var File_file_transport_proto protoreflect.FileDescriptor | |||
| var file_file_transport_proto_rawDesc = []byte{ | |||
| 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, | |||
| 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4d, 0x0a, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, | |||
| 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, | |||
| 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, | |||
| 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, | |||
| 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, | |||
| 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x26, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, | |||
| 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, | |||
| 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x22, 0x24, 0x0a, | |||
| 0x06, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, | |||
| 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, | |||
| 0x61, 0x73, 0x68, 0x2a, 0x27, 0x0a, 0x12, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, | |||
| 0x61, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x61, 0x74, | |||
| 0x61, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4f, 0x46, 0x10, 0x01, 0x32, 0x64, 0x0a, 0x0d, | |||
| 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2a, 0x0a, | |||
| 0x08, 0x53, 0x65, 0x6e, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0f, 0x2e, 0x46, 0x69, 0x6c, 0x65, | |||
| 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x1a, 0x09, 0x2e, 0x53, 0x65, 0x6e, | |||
| 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x28, 0x01, 0x12, 0x27, 0x0a, 0x07, 0x47, 0x65, 0x74, | |||
| 0x46, 0x69, 0x6c, 0x65, 0x12, 0x07, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, | |||
| 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x00, | |||
| 0x30, 0x01, 0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, | |||
| 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, | |||
| } | |||
| var ( | |||
| file_file_transport_proto_rawDescOnce sync.Once | |||
| file_file_transport_proto_rawDescData = file_file_transport_proto_rawDesc | |||
| ) | |||
| func file_file_transport_proto_rawDescGZIP() []byte { | |||
| file_file_transport_proto_rawDescOnce.Do(func() { | |||
| file_file_transport_proto_rawDescData = protoimpl.X.CompressGZIP(file_file_transport_proto_rawDescData) | |||
| }) | |||
| return file_file_transport_proto_rawDescData | |||
| } | |||
| var file_file_transport_proto_enumTypes = make([]protoimpl.EnumInfo, 1) | |||
| var file_file_transport_proto_msgTypes = make([]protoimpl.MessageInfo, 3) | |||
| var file_file_transport_proto_goTypes = []interface{}{ | |||
| (FileDataPacketType)(0), // 0: FileDataPacketType | |||
| (*FileDataPacket)(nil), // 1: FileDataPacket | |||
| (*SendResp)(nil), // 2: SendResp | |||
| (*GetReq)(nil), // 3: GetReq | |||
| } | |||
| var file_file_transport_proto_depIdxs = []int32{ | |||
| 0, // 0: FileDataPacket.Type:type_name -> FileDataPacketType | |||
| 1, // 1: FileTransport.SendFile:input_type -> FileDataPacket | |||
| 3, // 2: FileTransport.GetFile:input_type -> GetReq | |||
| 2, // 3: FileTransport.SendFile:output_type -> SendResp | |||
| 1, // 4: FileTransport.GetFile:output_type -> FileDataPacket | |||
| 3, // [3:5] is the sub-list for method output_type | |||
| 1, // [1:3] is the sub-list for method input_type | |||
| 1, // [1:1] is the sub-list for extension type_name | |||
| 1, // [1:1] is the sub-list for extension extendee | |||
| 0, // [0:1] is the sub-list for field type_name | |||
| } | |||
| func init() { file_file_transport_proto_init() } | |||
| func file_file_transport_proto_init() { | |||
| if File_file_transport_proto != nil { | |||
| return | |||
| } | |||
| if !protoimpl.UnsafeEnabled { | |||
| file_file_transport_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { | |||
| switch v := v.(*FileDataPacket); i { | |||
| case 0: | |||
| return &v.state | |||
| case 1: | |||
| return &v.sizeCache | |||
| case 2: | |||
| return &v.unknownFields | |||
| default: | |||
| return nil | |||
| } | |||
| } | |||
| file_file_transport_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { | |||
| switch v := v.(*SendResp); i { | |||
| case 0: | |||
| return &v.state | |||
| case 1: | |||
| return &v.sizeCache | |||
| case 2: | |||
| return &v.unknownFields | |||
| default: | |||
| return nil | |||
| } | |||
| } | |||
| file_file_transport_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { | |||
| switch v := v.(*GetReq); i { | |||
| case 0: | |||
| return &v.state | |||
| case 1: | |||
| return &v.sizeCache | |||
| case 2: | |||
| return &v.unknownFields | |||
| default: | |||
| return nil | |||
| } | |||
| } | |||
| } | |||
| type x struct{} | |||
| out := protoimpl.TypeBuilder{ | |||
| File: protoimpl.DescBuilder{ | |||
| GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | |||
| RawDescriptor: file_file_transport_proto_rawDesc, | |||
| NumEnums: 1, | |||
| NumMessages: 3, | |||
| NumExtensions: 0, | |||
| NumServices: 1, | |||
| }, | |||
| GoTypes: file_file_transport_proto_goTypes, | |||
| DependencyIndexes: file_file_transport_proto_depIdxs, | |||
| EnumInfos: file_file_transport_proto_enumTypes, | |||
| MessageInfos: file_file_transport_proto_msgTypes, | |||
| }.Build() | |||
| File_file_transport_proto = out.File | |||
| file_file_transport_proto_rawDesc = nil | |||
| file_file_transport_proto_goTypes = nil | |||
| file_file_transport_proto_depIdxs = nil | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| // 使用的语法版本 | |||
| syntax = "proto3"; | |||
| // 生成的go文件包 | |||
| option go_package = "../proto;proto";//grpc这里生效了 | |||
| enum FileDataPacketType { | |||
| Data = 0; | |||
| EOF = 1; | |||
| } | |||
| // 文件数据。注意:只在Type为Data的时候,Data字段才能有数据 | |||
| message FileDataPacket { | |||
| FileDataPacketType Type = 1; | |||
| bytes Data = 2; | |||
| } | |||
| message SendResp { | |||
| string FileHash = 1; | |||
| } | |||
| message GetReq { | |||
| string FileHash = 1; | |||
| } | |||
| service FileTransport { | |||
| rpc SendFile(stream FileDataPacket)returns(SendResp){} | |||
| rpc GetFile(GetReq)returns(stream FileDataPacket){} | |||
| } | |||
| @@ -0,0 +1,209 @@ | |||
| // 使用的语法版本 | |||
| // Code generated by protoc-gen-go-grpc. DO NOT EDIT. | |||
| // versions: | |||
| // - protoc-gen-go-grpc v1.3.0 | |||
| // - protoc v4.22.3 | |||
| // source: file_transport.proto | |||
| package proto | |||
| import ( | |||
| context "context" | |||
| grpc "google.golang.org/grpc" | |||
| codes "google.golang.org/grpc/codes" | |||
| status "google.golang.org/grpc/status" | |||
| ) | |||
| // This is a compile-time assertion to ensure that this generated file | |||
| // is compatible with the grpc package it is being compiled against. | |||
| // Requires gRPC-Go v1.32.0 or later. | |||
| const _ = grpc.SupportPackageIsVersion7 | |||
| const ( | |||
| FileTransport_SendFile_FullMethodName = "/FileTransport/SendFile" | |||
| FileTransport_GetFile_FullMethodName = "/FileTransport/GetFile" | |||
| ) | |||
| // FileTransportClient is the client API for FileTransport service. | |||
| // | |||
| // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. | |||
| type FileTransportClient interface { | |||
| SendFile(ctx context.Context, opts ...grpc.CallOption) (FileTransport_SendFileClient, error) | |||
| GetFile(ctx context.Context, in *GetReq, opts ...grpc.CallOption) (FileTransport_GetFileClient, error) | |||
| } | |||
| type fileTransportClient struct { | |||
| cc grpc.ClientConnInterface | |||
| } | |||
| func NewFileTransportClient(cc grpc.ClientConnInterface) FileTransportClient { | |||
| return &fileTransportClient{cc} | |||
| } | |||
| func (c *fileTransportClient) SendFile(ctx context.Context, opts ...grpc.CallOption) (FileTransport_SendFileClient, error) { | |||
| stream, err := c.cc.NewStream(ctx, &FileTransport_ServiceDesc.Streams[0], FileTransport_SendFile_FullMethodName, opts...) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| x := &fileTransportSendFileClient{stream} | |||
| return x, nil | |||
| } | |||
| type FileTransport_SendFileClient interface { | |||
| Send(*FileDataPacket) error | |||
| CloseAndRecv() (*SendResp, error) | |||
| grpc.ClientStream | |||
| } | |||
| type fileTransportSendFileClient struct { | |||
| grpc.ClientStream | |||
| } | |||
| func (x *fileTransportSendFileClient) Send(m *FileDataPacket) error { | |||
| return x.ClientStream.SendMsg(m) | |||
| } | |||
| func (x *fileTransportSendFileClient) CloseAndRecv() (*SendResp, error) { | |||
| if err := x.ClientStream.CloseSend(); err != nil { | |||
| return nil, err | |||
| } | |||
| m := new(SendResp) | |||
| if err := x.ClientStream.RecvMsg(m); err != nil { | |||
| return nil, err | |||
| } | |||
| return m, nil | |||
| } | |||
| func (c *fileTransportClient) GetFile(ctx context.Context, in *GetReq, opts ...grpc.CallOption) (FileTransport_GetFileClient, error) { | |||
| stream, err := c.cc.NewStream(ctx, &FileTransport_ServiceDesc.Streams[1], FileTransport_GetFile_FullMethodName, opts...) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| x := &fileTransportGetFileClient{stream} | |||
| if err := x.ClientStream.SendMsg(in); err != nil { | |||
| return nil, err | |||
| } | |||
| if err := x.ClientStream.CloseSend(); err != nil { | |||
| return nil, err | |||
| } | |||
| return x, nil | |||
| } | |||
| type FileTransport_GetFileClient interface { | |||
| Recv() (*FileDataPacket, error) | |||
| grpc.ClientStream | |||
| } | |||
| type fileTransportGetFileClient struct { | |||
| grpc.ClientStream | |||
| } | |||
| func (x *fileTransportGetFileClient) Recv() (*FileDataPacket, error) { | |||
| m := new(FileDataPacket) | |||
| if err := x.ClientStream.RecvMsg(m); err != nil { | |||
| return nil, err | |||
| } | |||
| return m, nil | |||
| } | |||
| // FileTransportServer is the server API for FileTransport service. | |||
| // All implementations must embed UnimplementedFileTransportServer | |||
| // for forward compatibility | |||
| type FileTransportServer interface { | |||
| SendFile(FileTransport_SendFileServer) error | |||
| GetFile(*GetReq, FileTransport_GetFileServer) error | |||
| mustEmbedUnimplementedFileTransportServer() | |||
| } | |||
| // UnimplementedFileTransportServer must be embedded to have forward compatible implementations. | |||
| type UnimplementedFileTransportServer struct { | |||
| } | |||
| func (UnimplementedFileTransportServer) SendFile(FileTransport_SendFileServer) error { | |||
| return status.Errorf(codes.Unimplemented, "method SendFile not implemented") | |||
| } | |||
| func (UnimplementedFileTransportServer) GetFile(*GetReq, FileTransport_GetFileServer) error { | |||
| return status.Errorf(codes.Unimplemented, "method GetFile not implemented") | |||
| } | |||
| func (UnimplementedFileTransportServer) mustEmbedUnimplementedFileTransportServer() {} | |||
| // UnsafeFileTransportServer may be embedded to opt out of forward compatibility for this service. | |||
| // Use of this interface is not recommended, as added methods to FileTransportServer will | |||
| // result in compilation errors. | |||
| type UnsafeFileTransportServer interface { | |||
| mustEmbedUnimplementedFileTransportServer() | |||
| } | |||
| func RegisterFileTransportServer(s grpc.ServiceRegistrar, srv FileTransportServer) { | |||
| s.RegisterService(&FileTransport_ServiceDesc, srv) | |||
| } | |||
| func _FileTransport_SendFile_Handler(srv interface{}, stream grpc.ServerStream) error { | |||
| return srv.(FileTransportServer).SendFile(&fileTransportSendFileServer{stream}) | |||
| } | |||
| type FileTransport_SendFileServer interface { | |||
| SendAndClose(*SendResp) error | |||
| Recv() (*FileDataPacket, error) | |||
| grpc.ServerStream | |||
| } | |||
| type fileTransportSendFileServer struct { | |||
| grpc.ServerStream | |||
| } | |||
| func (x *fileTransportSendFileServer) SendAndClose(m *SendResp) error { | |||
| return x.ServerStream.SendMsg(m) | |||
| } | |||
| func (x *fileTransportSendFileServer) Recv() (*FileDataPacket, error) { | |||
| m := new(FileDataPacket) | |||
| if err := x.ServerStream.RecvMsg(m); err != nil { | |||
| return nil, err | |||
| } | |||
| return m, nil | |||
| } | |||
| func _FileTransport_GetFile_Handler(srv interface{}, stream grpc.ServerStream) error { | |||
| m := new(GetReq) | |||
| if err := stream.RecvMsg(m); err != nil { | |||
| return err | |||
| } | |||
| return srv.(FileTransportServer).GetFile(m, &fileTransportGetFileServer{stream}) | |||
| } | |||
| type FileTransport_GetFileServer interface { | |||
| Send(*FileDataPacket) error | |||
| grpc.ServerStream | |||
| } | |||
| type fileTransportGetFileServer struct { | |||
| grpc.ServerStream | |||
| } | |||
| func (x *fileTransportGetFileServer) Send(m *FileDataPacket) error { | |||
| return x.ServerStream.SendMsg(m) | |||
| } | |||
| // FileTransport_ServiceDesc is the grpc.ServiceDesc for FileTransport service. | |||
| // It's only intended for direct use with grpc.RegisterService, | |||
| // and not to be introspected or modified (even as a copy) | |||
| var FileTransport_ServiceDesc = grpc.ServiceDesc{ | |||
| ServiceName: "FileTransport", | |||
| HandlerType: (*FileTransportServer)(nil), | |||
| Methods: []grpc.MethodDesc{}, | |||
| Streams: []grpc.StreamDesc{ | |||
| { | |||
| StreamName: "SendFile", | |||
| Handler: _FileTransport_SendFile_Handler, | |||
| ClientStreams: true, | |||
| }, | |||
| { | |||
| StreamName: "GetFile", | |||
| Handler: _FileTransport_GetFile_Handler, | |||
| ServerStreams: true, | |||
| }, | |||
| }, | |||
| Metadata: "file_transport.proto", | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| package grpc | |||
| import ( | |||
| "context" | |||
| "fmt" | |||
| "io" | |||
| myio "gitlink.org.cn/cloudream/common/utils/io" | |||
| "gitlink.org.cn/cloudream/storage-common/pkgs/proto" | |||
| ) | |||
| type fileReadCloser struct { | |||
| io.ReadCloser | |||
| stream proto.FileTransport_GetFileClient | |||
| cancelFn context.CancelFunc | |||
| readData []byte | |||
| } | |||
| func (s *fileReadCloser) Read(p []byte) (int, error) { | |||
| if s.readData == nil { | |||
| resp, err := s.stream.Recv() | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| if resp.Type == proto.FileDataPacketType_Data { | |||
| s.readData = resp.Data | |||
| } else if resp.Type == proto.FileDataPacketType_EOF { | |||
| return 0, io.EOF | |||
| } else { | |||
| return 0, fmt.Errorf("unsuppoted packt type: %v", resp.Type) | |||
| } | |||
| } | |||
| cnt := copy(p, s.readData) | |||
| if len(s.readData) == cnt { | |||
| s.readData = nil | |||
| } else { | |||
| s.readData = s.readData[cnt:] | |||
| } | |||
| return cnt, nil | |||
| } | |||
| func (s *fileReadCloser) Close() error { | |||
| s.cancelFn() | |||
| return nil | |||
| } | |||
| func GetFileAsStream(client proto.FileTransportClient, fileHash string) (io.ReadCloser, error) { | |||
| ctx, cancel := context.WithCancel(context.Background()) | |||
| stream, err := client.GetFile(ctx, &proto.GetReq{ | |||
| FileHash: fileHash, | |||
| }) | |||
| if err != nil { | |||
| cancel() | |||
| return nil, fmt.Errorf("request grpc failed, err: %w", err) | |||
| } | |||
| return &fileReadCloser{ | |||
| stream: stream, | |||
| cancelFn: cancel, | |||
| }, nil | |||
| } | |||
| type fileWriteCloser struct { | |||
| myio.PromiseWriteCloser[string] | |||
| stream proto.FileTransport_SendFileClient | |||
| } | |||
| func (s *fileWriteCloser) Write(p []byte) (int, error) { | |||
| err := s.stream.Send(&proto.FileDataPacket{ | |||
| Type: proto.FileDataPacketType_Data, | |||
| Data: p, | |||
| }) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| return len(p), nil | |||
| } | |||
| func (s *fileWriteCloser) Abort(err error) { | |||
| s.stream.CloseSend() | |||
| } | |||
| func (s *fileWriteCloser) Finish() (string, error) { | |||
| err := s.stream.Send(&proto.FileDataPacket{ | |||
| Type: proto.FileDataPacketType_EOF, | |||
| }) | |||
| if err != nil { | |||
| return "", fmt.Errorf("send EOF packet failed, err: %w", err) | |||
| } | |||
| resp, err := s.stream.CloseAndRecv() | |||
| if err != nil { | |||
| return "", fmt.Errorf("receive response failed, err: %w", err) | |||
| } | |||
| return resp.FileHash, nil | |||
| } | |||
| func SendFileAsStream(client proto.FileTransportClient) (myio.PromiseWriteCloser[string], error) { | |||
| stream, err := client.SendFile(context.Background()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return &fileWriteCloser{ | |||
| stream: stream, | |||
| }, nil | |||
| } | |||