diff --git a/app/app.go b/app/app.go index c774d8d..5897709 100644 --- a/app/app.go +++ b/app/app.go @@ -97,7 +97,7 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co zap.String("SvcVersion", ver.Release), zap.String("SvcBuildTime", ver.BuildTime), ) - + logger := hlog.New(zapLogger) logger.Emit(InfoSvcStarted{}) @@ -192,11 +192,21 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co Addr: fmt.Sprintf(":%s", options.NumberPortLocal), } + // Account Server + accountService := service.NewAccount(database) + accountServer := NewAccountHTTP(database, zapLogger) + + accountServer.Register(accountService.GetRoutes()...) + go func() { defer func() { if err := server.Shutdown(ctx); err != nil { errChan <- err } + + if err := accountServer.Stop(); err != nil { + errChan <- err + } }() logger.Emit(InfoSvcStarted{}) @@ -214,11 +224,25 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co }) errChan <- err } + + go func() { + if err := accountServer.StartTLS("", tmplCrt, tmplKey); err != nil { + logger.Emit(ErrorCanNotServe{Err: err}) + errChan <- err + } + }() } else { fmt.Println("startserver" + options.NumberPortLocal) if err := server.ListenAndServe(); err != nil { errChan <- err } + + go func() { + if err := accountServer.Start(""); err != nil { + logger.Emit(ErrorCanNotServe{Err: err}) + errChan <- err + } + }() } }() diff --git a/app/app_account.go b/app/app_account.go new file mode 100644 index 0000000..6a015db --- /dev/null +++ b/app/app_account.go @@ -0,0 +1,52 @@ +package app + +import ( + "github.com/gofiber/contrib/fiberzap" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v2/middleware/recover" + "go.uber.org/zap" + "heruvym/dal/mongo" + "heruvym/middleware" + "heruvym/service" +) + +type AccountHTTP struct { + fiber *fiber.App +} + +func NewAccountHTTP(dal *mongo.DAL, logger *zap.Logger) *AccountHTTP { + srv := fiber.New(fiber.Config{ + AppName: "Heryvum Account", + ErrorHandler: fiber.DefaultErrorHandler, + }) + + srv.Use( + recover.New(recover.Config{EnableStackTrace: true}), + fiberzap.New(fiberzap.Config{Logger: logger}), + middleware.Jwt, + cors.New(cors.ConfigDefault), + ) + + return &AccountHTTP{fiber: srv} +} + +// StartTLS - запускает http сервер +func (srv *AccountHTTP) StartTLS(address, certFile, keyFile string) error { + return srv.fiber.ListenTLS(address, certFile, keyFile) +} + +func (srv *AccountHTTP) Start(address string) error { + return srv.fiber.Listen(address) +} + +func (srv *AccountHTTP) Register(routes ...service.Route) { + for _, route := range routes { + srv.fiber.Add(route.Method, route.Path, route.Handler).Name(route.Name) + } +} + +// Stop - останавливает http сервер +func (srv *AccountHTTP) Stop() error { + return srv.fiber.Shutdown() +} diff --git a/dal/mongo/dal.go b/dal/mongo/dal.go index 24a52da..5e2414d 100644 --- a/dal/mongo/dal.go +++ b/dal/mongo/dal.go @@ -2,6 +2,7 @@ package mongo import ( "context" + "errors" "fmt" "heruvym/model" "time" @@ -18,12 +19,13 @@ import ( const ( collMessages = "messages" collTickets = "tickets" + collAccount = "account" ) type DAL struct { - logger hlog.Logger - colMsg, colTck *mongo.Collection - client *mongo.Client + logger hlog.Logger + colMsg, colTck, colAcc *mongo.Collection + client *mongo.Client } type ErrorConnectToDB struct { @@ -76,6 +78,7 @@ func New( client: client, colMsg: client.Database(database).Collection(collMessages), colTck: client.Database(database).Collection(collTickets), + colAcc: client.Database(database).Collection(collAccount), logger: log.Module("DAL"), } @@ -94,6 +97,11 @@ func New( return nil, err } + err = dal.CreateAccountIndex(ctx) + if err != nil { + return nil, err + } + fmt.Println("dal3") return dal, nil } @@ -135,6 +143,20 @@ func (d *DAL) CreateTicketIndex(ctx context.Context) error { return err } +func (d *DAL) CreateAccountIndex(ctx context.Context) error { + keys := bson.D{{"$**", "text"}} + + opts := options.Index().SetDefaultLanguage("russian") + + _, err := d.colAcc.Indexes().CreateOne(ctx, mongo.IndexModel{Keys: keys, Options: opts}) + + if err != nil { + d.logger.Emit(ErrorCreateIndex{err}) + } + + return err +} + func (d *DAL) PutMessage( ctx context.Context, message, userID, sessionID, ticketID string, @@ -785,3 +807,242 @@ func (d *DAL) GetAdditionalData(ctx context.Context, id string) (*Additional, er result.Uid = u.DisplayID return &result, nil } + +func (d *DAL) InsertAccount(ctx context.Context, record *model.Account) (*model.Account, error) { + now := time.Now() + record.CreatedAt, record.UpdatedAt = now, now + record.ID = xid.New().String() + + if record.Avatar == "" { + record.Avatar = "/media/avatar/default-avatar.jpg" + } + + if record.Nickname == "" { + record.Nickname = "Unknown" + } + + if record.Role == "" { + record.Role = "user" + } + + _, err := d.colAcc.InsertOne(ctx, record) + + if err != nil { + d.logger.Emit(ErrorInsertAccount{err, record}) + return nil, err + } + + return record, nil +} + +type ErrorInsertAccount struct { + Err error + Account *model.Account +} + +func (d *DAL) GetAccount(ctx context.Context, id string) (*model.Account, error) { + if id == "" { + err := errors.New("id cannot be empty") + d.logger.Emit(ErrorGetAccount{err, id}) + return nil, err + } + + var result model.Account + + err := d.colAcc.FindOne(ctx, bson.M{"_id": id}).Decode(&result) + if err != nil { + d.logger.Emit(ErrorGetAccount{err, id}) + return nil, err + } + + return &result, nil +} + +type ErrorGetAccount struct { + Err error + ID string +} + +func (d *DAL) GetAccountByUserID(ctx context.Context, userId string) (*model.Account, error) { + if userId == "" { + err := errors.New("userId cannot be empty") + d.logger.Emit(ErrorGetAccount{err, userId}) + return nil, err + } + + var result model.Account + + err := d.colAcc.FindOne(ctx, bson.M{"userId": userId}).Decode(&result) + if err != nil { + d.logger.Emit(ErrorGetAccount{err, userId}) + return nil, err + } + + return &result, nil +} + +func (d *DAL) GetAccountPage(ctx context.Context, search string, offset, limit int64) ([]model.Account, error) { + var query bson.M + if search != "" { + query = bson.M{ + "$text": bson.M{ + "$search": search, + }, + } + } + + sort := bson.D{{"CreatedAt", -1}} + + cur, err := d.colAcc.Find(ctx, query, options.Find().SetLimit(limit).SetSkip(limit*offset).SetSort(sort)) + if err != nil { + return nil, err + } + + var result []model.Account + if err := cur.All(ctx, &result); err != nil { + return nil, err + } + + return result, nil +} + +func (d *DAL) SetAccountRole(ctx context.Context, userId, role string) (*model.Account, error) { + if userId == "" { + err := errors.New("userId cannot be empty") + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + if role == "" { + err := errors.New("role cannot be empty") + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + filter := bson.M{"userId": userId} + update := bson.M{"role": role, "updatedAt": time.Now()} + + var result model.Account + + opts := options.FindOneAndUpdate().SetReturnDocument(options.After) + err := d.colAcc.FindOneAndUpdate(ctx, filter, bson.M{"$set": update}, opts).Decode(&result) + if err != nil { + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + return &result, nil +} + +func (d *DAL) SetAccountNickname(ctx context.Context, userId, nickname string) (*model.Account, error) { + if userId == "" { + err := errors.New("userId cannot be empty") + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + if nickname == "" { + err := errors.New("nickname cannot be empty") + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + filter := bson.M{"userId": userId} + update := bson.M{"nickname": nickname, "updatedAt": time.Now()} + + var result model.Account + + opts := options.FindOneAndUpdate().SetReturnDocument(options.After) + err := d.colAcc.FindOneAndUpdate(ctx, filter, bson.M{"$set": update}, opts).Decode(&result) + if err != nil { + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + return &result, nil +} + +func (d *DAL) SetAccountAvatar(ctx context.Context, userId, avatar string) (*model.Account, error) { + if userId == "" { + err := errors.New("userId cannot be empty") + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + if avatar == "" { + err := errors.New("avatar cannot be empty") + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + filter := bson.M{"userId": userId} + update := bson.M{"avatar": avatar, "updatedAt": time.Now()} + + var result model.Account + + opts := options.FindOneAndUpdate().SetReturnDocument(options.After) + err := d.colAcc.FindOneAndUpdate(ctx, filter, bson.M{"$set": update}, opts).Decode(&result) + if err != nil { + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + return &result, nil +} + +func (d *DAL) SetAccountDelete(ctx context.Context, userId string, isDeleted bool) (*model.Account, error) { + if userId == "" { + err := errors.New("userId cannot be empty") + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + filter := bson.M{"userId": userId} + update := bson.M{"isDeleted": isDeleted, "updatedAt": time.Now()} + + var result model.Account + + opts := options.FindOneAndUpdate().SetReturnDocument(options.After) + err := d.colAcc.FindOneAndUpdate(ctx, filter, bson.M{"$set": update}, opts).Decode(&result) + if err != nil { + d.logger.Emit(ErrorSetAccount{err, userId}) + return nil, err + } + + return &result, nil +} + +type ErrorSetAccount struct { + Err error + UserID string +} + +func (d *DAL) DeleteAccount(ctx context.Context, userId string) (*model.Account, error) { + if userId == "" { + err := errors.New("userId cannot be empty") + d.logger.Emit(ErrorDeleteAccount{err, userId}) + return nil, err + } + + filter := bson.M{"userId": userId} + var acc model.Account + + err := d.colAcc.FindOne(ctx, filter).Decode(&acc) + if err != nil { + d.logger.Emit(ErrorDeleteAccount{err, userId}) + return nil, err + } + + _, err = d.colAcc.DeleteOne(ctx, filter) + if err != nil { + d.logger.Emit(ErrorDeleteAccount{err, userId}) + return nil, err + } + + return &acc, err +} + +type ErrorDeleteAccount struct { + Err error + UserID string +} diff --git a/dal/mongo/dal_test.go b/dal/mongo/dal_test.go new file mode 100644 index 0000000..ab58def --- /dev/null +++ b/dal/mongo/dal_test.go @@ -0,0 +1,102 @@ +package mongo + +import ( + "context" + "github.com/stretchr/testify/suite" + "github.com/themakers/hlog" + "go.uber.org/zap" + "heruvym/model" + "testing" +) + +var mongoUri = "mongodb://localmongo1:30001,localmongo2:30002,localmongo3:30003/?replicaSet=my-rs" + +type AccountTestSuite struct { + args *model.Account + dal *DAL + suite.Suite +} + +func TestAccountTestSuite(t *testing.T) { + suite.Run(t, new(AccountTestSuite)) +} + +func (s *AccountTestSuite) SetupSuite() { + s.args = &model.Account{ + UserID: "fhka41hakfh41z", + Nickname: "SomeNick", + Role: "manager", + } + + logger := hlog.New(zap.NewNop()) + dal, err := New(context.Background(), mongoUri, "support", logger) + + if s.NoError(err) { + s.dal = dal + result, err := s.dal.InsertAccount(context.Background(), s.args) + if s.NoError(err) { + s.args = result + } + } +} + +func (s *AccountTestSuite) TestGet() { + r, err := s.dal.GetAccount(context.Background(), s.args.ID) + s.NoError(err) + s.NotNil(r) +} + +func (s *AccountTestSuite) TestGetByUserID() { + r, err := s.dal.GetAccountByUserID(context.Background(), s.args.UserID) + s.NoError(err) + s.NotNil(r) +} + +func (s *AccountTestSuite) TestGetAccountPage() { + r, err := s.dal.GetAccountPage(context.Background(), "", 0, 10) + s.NoError(err) + s.NotNil(r) +} + +func (s *AccountTestSuite) TestSetAccountRole() { + arg := "admin" + r, err := s.dal.SetAccountRole(context.Background(), s.args.UserID, arg) + s.NoError(err) + if s.NotNil(r) { + s.Equal(arg, r.Role) + } +} + +func (s *AccountTestSuite) TestSetAccountNickname() { + arg := "NewNickname" + r, err := s.dal.SetAccountNickname(context.Background(), s.args.UserID, arg) + s.NoError(err) + if s.NotNil(r) { + s.Equal(arg, r.Nickname) + } +} + +func (s *AccountTestSuite) TestSetAccountAvatar() { + arg := "/new/avatar.jpeg" + r, err := s.dal.SetAccountAvatar(context.Background(), s.args.UserID, arg) + s.NoError(err) + if s.NotNil(r) { + s.Equal(arg, r.Avatar) + } + +} + +func (s *AccountTestSuite) TestSetAccountDelete() { + arg := true + r, err := s.dal.SetAccountDelete(context.Background(), s.args.UserID, arg) + s.NoError(err) + if s.NotNil(r) { + s.Equal(arg, r.IsDeleted) + } +} + +func (s *AccountTestSuite) TearDownSuite() { + r, err := s.dal.DeleteAccount(context.Background(), s.args.UserID) + s.NoError(err) + s.NotNil(r) +} diff --git a/go.mod b/go.mod index c6e7dd9..ede9df5 100644 --- a/go.mod +++ b/go.mod @@ -4,25 +4,23 @@ go 1.16 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/go-playground/validator/v10 v10.13.0 + github.com/gofiber/contrib/fiberzap v1.0.2 + github.com/gofiber/fiber/v2 v2.45.0 github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/mux v1.8.0 - github.com/klauspost/compress v1.15.12 // indirect github.com/klauspost/cpuid/v2 v2.1.2 // indirect github.com/minio/minio-go/v7 v7.0.43 github.com/montanaflynn/stats v0.6.6 // indirect github.com/pkg/errors v0.9.1 github.com/rs/xid v1.4.0 github.com/skeris/appInit v1.0.2 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.2 github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.mongodb.org/mongo-driver v1.10.3 - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.23.0 - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/sync v0.1.0 // indirect + go.uber.org/zap v1.24.0 gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tucnak/telebot.v2 v2.5.0 ) diff --git a/go.sum b/go.sum index 3a921a8..1559adb 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/danilsolovyov/validator v0.0.8/go.mod h1:2jwh4O+5hyKFNRQQTo395fW+l2gyRKPtv+V+uMzAbGM= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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= @@ -12,266 +14,197 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= +github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= +github.com/gofiber/contrib/fiberzap v1.0.2 h1:EQwhggtszVfIdBeXxN9Xrmld71es34Ufs+ef8VMqZxc= +github.com/gofiber/contrib/fiberzap v1.0.2/go.mod h1:jGO8BHU4gRI9U0JtM6zj2CIhYfgVmW5JxziN8NTgVwE= +github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc= +github.com/gofiber/fiber/v2 v2.45.0 h1:p4RpkJT9GAW6parBSbcNFH2ApnAuW3OzaQzbOCoDu+s= +github.com/gofiber/fiber/v2 v2.45.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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 v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= -github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.1.2 h1:XhdX4fqAJUA0yj+kUwMavO0hHrSPAecYdYf1ZmxHvak= github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= +github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo= github.com/minio/minio-go/v7 v7.0.43 h1:14Q4lwblqTdlAmba05oq5xL0VBLHi06zS4yLnIkz6hI= github.com/minio/minio-go/v7 v7.0.43/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v64GQ= github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.15.1 h1:DsXNrKujDlkMS9Rsxmd+Fg7S6Kc5lhE+qX8tY6laOxc= -github.com/onsi/ginkgo v1.15.1/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug= -github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= +github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeris/appInit v1.0.2 h1:Hr4KbXYd6kolTVq4cXGqDpgnpmaauiOiKizA1+Ep4KQ= github.com/skeris/appInit v1.0.2/go.mod h1:4ElEeXWVGzU3dlYq/eMWJ/U5hd+LKisc1z3+ySh1XmY= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA= github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E= github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf h1:TJJm6KcBssmbWzplF5lzixXl1RBAi/ViPs1GaSOkhwo= github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf/go.mod h1:1FsorU3vnXO9xS9SrhUp8fRb/6H/Zfll0rPt1i4GWaA= -github.com/themakers/identity v0.0.0-20200703212242-9142bb6b35e1 h1:DuzLgIqC0AxW3Gy+Mhwn15R9KvFe8nifO6Pbe3j07Ng= -github.com/themakers/identity v0.0.0-20200703212242-9142bb6b35e1/go.mod h1:lHYuLs7VL+KVZpEzfXnrv8YwlGXcuZgUZjV5pSRb+Fc= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= +github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA= go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -281,61 +214,53 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc 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.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po= gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/middleware/fiber_middleware.go b/middleware/fiber_middleware.go new file mode 100644 index 0000000..01e4ebd --- /dev/null +++ b/middleware/fiber_middleware.go @@ -0,0 +1,69 @@ +package middleware + +import ( + "github.com/gofiber/fiber/v2" + "github.com/rs/xid" + "heruvym/jwt_adapter" + "strings" + "time" +) + +func JwtPlug(c *fiber.Ctx) error { + adapter := &jwt_adapter.JwtAdapter{Id: "604b79aced1d431b9e911f56"} + adapter.SetUserID("604b79aced1d431b9e911f56") + + c.Locals("adapter", adapter) + c.Locals("role", "admin") + return c.Next() +} + +func Jwt(c *fiber.Ctx) error { + var ( + token, role string + adapter *jwt_adapter.JwtAdapter + err error + ) + + switch c.Get("Referer") { + case "admin.pena.digital": + role = "admin" + default: + role = "user" + } + + c.Locals(jwt_adapter.RoleKey, role) + + token = c.Cookies(jwt_adapter.DefaultHeaderKey) + + if token == "" { + token = strings.Replace(c.Get(jwt_adapter.DefaultHeaderKey), "Bearer ", "", -1) + } + + // Если jwt не обнаружен, ищем sessionKey + token = c.Get(sessionKey) + if token == "" { + session := c.Cookies(sessionKey) + + if token == "" { + id := xid.New().String() + adapter = &jwt_adapter.JwtAdapter{Id: id} + c.Cookie(&fiber.Cookie{ + Name: sessionKey, + Value: id, + Expires: time.Now().Add(time.Hour * 24 * 30), + }) + } else { + adapter = &jwt_adapter.JwtAdapter{Id: session} + } + } else { + adapter, err = jwt_adapter.Decode(token) + } + + if err != nil { + return c.SendStatus(fiber.StatusUnauthorized) + } + + c.Locals("adapter", adapter) + + return c.Next() +} diff --git a/model/model.go b/model/model.go index 3004178..c08f373 100644 --- a/model/model.go +++ b/model/model.go @@ -8,10 +8,10 @@ type Message struct { UserID string `bson:"UserID" json:"user_id"` SessionID string `bson:"SessionID" json:"session_id"` - Message string `bson:"Message" json:"message"` - Files []string `bson:"Files" json:"files"` - Shown map[string]int `bson:"Shown" json:"shown"` - RequestScreenshot string `bson:"RequestScreenshot" json:"request_screenshot"` + Message string `bson:"Message" json:"message"` + Files []string `bson:"Files" json:"files"` + Shown map[string]int `bson:"Shown" json:"shown"` + RequestScreenshot string `bson:"RequestScreenshot" json:"request_screenshot"` CreatedAt time.Time `bson:"CreatedAt" json:"created_at"` } @@ -39,3 +39,20 @@ type Ticket struct { Rate int `bson:"Rate" json:"rate"` } + +type Account struct { + ID string `json:"_id" bson:"_id"` + UserID string `json:"userId" bson:"userId"` + Nickname string `json:"nickname" bson:"nickname"` + Avatar string `json:"avatar" bson:"avatar"` + Role string `json:"role" bson:"role"` + IsDeleted bool `json:"isDeleted" bson:"isDeleted"` + CreatedAt time.Time `json:"createdAt" bson:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` +} + +type RespErrorValidate struct { + Field string `json:"field"` + Tag string `json:"tag"` + Value string `json:"value"` +} diff --git a/openapi.yaml b/openapi.yaml index d15179f..556c5b1 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -446,6 +446,1362 @@ paths: $ref: "#/components/responses/BadRequest" '401': $ref: "#/components/responses/Unauthorized" + /account/pagination: + get: + summary: Получение информации об аккаунтах + tags: + - account + description: Получение список аккаунтов с пагинацией из БД + parameters: + - schema: + type: number + in: query + name: page + required: false + description: номер страницы + - schema: + type: number + in: query + name: limit + required: false + description: Лимит количества аккаунтов (больше 100 не обрабатывается) + responses: + '200': + description: Список аккаунтов + content: + application/json: + schema: + description: Список аккаунтов + type: object + properties: + accounts: + type: array + description: Массив аккаунтов + items: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + - _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + - _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + totalPages: + type: number + example: + totalPages: 10 + accounts: + - _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + - _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + /account/{userId}: + get: + summary: Получение информации об аккаунте + tags: + - account + description: Получение аккаунта по ID + parameters: + - schema: + type: string + in: path + name: userId + required: true + description: ID пользователя + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + delete: + summary: Удаление аккаунта по ID + tags: + - account + description: Помечает аккаунт удалённым, но не удаляет его из БД + parameters: + - schema: + type: string + in: path + name: userId + required: true + description: ID пользователя + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + /account/: + get: + summary: Получение информации об аккаунте + tags: + - account + description: Получение информации об аккаунте через токен доступа + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + post: + summary: Создание аккаунта + tags: + - account + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + '409': + description: Conflict + content: + application/json: + schema: + type: object + description: Conflict + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 409 + error: Conflict + message: account already exist + delete: + summary: Удаление аккаунта + tags: + - account + description: Помечает аккаунт удалённым, но не удаляет его из БД + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + /account/restore: + post: + summary: Восстановление аккаунта + tags: + - account + description: Восстанавливает аккаунт, который не был удалён окончательно + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + /account/role: + patch: + summary: Присвоение роли пользователя + tags: + - account + requestBody: + content: + application/json: + schema: + type: object + required: + - userId + - role + properties: + userId: + type: string + description: ID пользователя + role: + type: string + description: название роли + required: true + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + /account/avatar: + patch: + summary: Присвоение аватарки пользователя + tags: + - account + requestBody: + content: + application/json: + schema: + type: object + required: + - userId + - avatar + properties: + userId: + type: string + description: ID пользователя + avatar: + type: string + description: путь до аватарки + required: true + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + /account/nickname: + patch: + summary: Присвоение никнейма пользователя + tags: + - account + requestBody: + content: + application/json: + schema: + type: object + required: + - userId + - role + properties: + userId: + type: string + description: ID пользователя + nickname: + type: string + description: никнейм + required: true + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + /account/delete: + delete: + summary: Удаление аккаунта + tags: + - account + description: Удаляет аккаунт из БД окончательно + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found + /account/delete/{userId}: + delete: + summary: Удаление аккаунта по ID + tags: + - account + description: Удаляет аккаунт из БД окончательно + parameters: + - schema: + type: string + in: path + name: userId + required: true + description: ID пользователя + security: + - bearer: [ ] + responses: + '200': + description: Аккаунт + content: + application/json: + schema: + description: Аккаунт + type: object + properties: + _id: + type: string + userId: + type: string + nickname: + type: string + avatar: + type: string + role: + type: string + isDeleted: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + deletedAt: + type: string + format: date-time + examples: + example1: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: false + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2017-07-21T17:32:28Z' + example2: + value: + _id: 807f1f77bcf81cd799439011 + userId: 507f1f77bcf86cd799439011 + nickname: Ivanov Ivan Ivanovich + avatar: /media/avatar/default-avatar.jpg + role: user + isDeleted: true + createdAt: '2017-07-21T17:32:28Z' + updatedAt: '2019-04-14T15:32:15Z' + deletedAt: '2021-08-17T13:23:44Z' + '400': + description: Bad Request + content: + application/json: + schema: + type: object + description: Bad Request + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 400 + error: Bad Request + message: invalid user id + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + description: Unauthorized + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 401 + error: Unauthorized + message: invalid token + '404': + description: Not Found + content: + application/json: + schema: + type: object + description: Not Found + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + example: + statusCode: 404 + error: Not Found + message: user not found components: securitySchemes: bearer_in_cookie: diff --git a/service/service_account.go b/service/service_account.go new file mode 100644 index 0000000..9af6d88 --- /dev/null +++ b/service/service_account.go @@ -0,0 +1,481 @@ +package service + +import ( + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "heruvym/dal/mongo" + "heruvym/jwt_adapter" + "heruvym/model" + "reflect" +) + +type Account struct { + dal *mongo.DAL +} + +var validate = validator.New() + +// validateStruct - возвращает строку с ошибкой, если структура не прошла валидацию +func validateStruct(s interface{}) []*model.RespErrorValidate { + err := validate.Struct(s) + + var errorsValidate []*model.RespErrorValidate + if err != nil { + for _, err := range err.(validator.ValidationErrors) { + field := err.Field() + + r, _ := reflect.TypeOf(s).Elem().FieldByName(err.Field()) + if queryTag := r.Tag.Get("query"); queryTag != "" { + field = queryTag + } + if jsonTag := r.Tag.Get("json"); jsonTag != "" { + field = jsonTag + } + + errorsValidate = append(errorsValidate, &model.RespErrorValidate{ + Field: field, + Tag: err.Tag(), + Value: err.Param(), + }) + } + } + + return errorsValidate +} + +type Route struct { + Method string + Path string + Name string + Handler fiber.Handler +} + +func NewAccount(dal *mongo.DAL) *Account { + return &Account{ + dal: dal, + } +} + +func (a *Account) GetRoutes() []Route { + return []Route{ + {"GET", "/account/pagination", "AccountPagination", a.AccountPagination}, + {"GET", "/account/:userId", "GetAccount", a.GetAccount}, + {"POST", "/account", "CreateAccount", a.CreateAccount}, + {"DELETE", "/account/:userId", "SetAccountDelete", a.SetAccountDelete}, + {"POST", "/account/restore/:userId", "RestoreAccount", a.RestoreAccount}, + {"PATCH", "/account/role", "SetAccountRole", a.SetAccountRole}, + {"PATCH", "/account/nickname", "SetAccountNickname", a.SetAccountNickname}, + {"PATCH", "/account/avatar", "SetAccountAvatar", a.SetAccountAvatar}, + {"DELETE", "/account/delete/:userId", "DeleteAccount", a.DeleteAccount}, + } +} + +type ReqAccountPagination struct { + Search string `json:"search"` + Offset int64 `json:"offset"` + Limit int64 `json:"limit" validate:"min=1"` +} + +// AccountPagination - GET метод для получения страницы пагинации. +// +// Request: ReqAccountPagination +// +// Responses: +// Success +// Status 200 +// Body: {model.Account, ...} +// +// Bad request - parsing error +// Status: 400 +// Body: error +// +// Bad request - validation error +// Status: 400 +// Body: { {field: string, tag: string, value: string}, ... } +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) AccountPagination(c *fiber.Ctx) error { + var req ReqAccountPagination + + err := c.BodyParser(&req) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + errValidate := validateStruct(&req) + if errValidate != nil { + return c.Status(fiber.StatusBadRequest).JSON(errValidate) + } + + resp, err := a.dal.GetAccountPage(c.Context(), req.Search, req.Offset, req.Limit) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +// GetAccount - GET метод для получения аккаунта. Если параметр пустой, возьмет userId из jwt +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - validation error +// Status: 400 +// Body: error +// +// Unauthorized +// Status: 401 +// Body: nil +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) GetAccount(c *fiber.Ctx) error { + userId := c.Params("userId") + + if userId == "" { + session, ok := c.Locals("adapter").(*jwt_adapter.JwtAdapter) + if !ok { + return fiber.NewError(fiber.StatusUnauthorized) + } + userId = session.GetUserID() + } + + if userId == "" { + return fiber.NewError(fiber.StatusBadRequest, "userId cannot be empty") + } + + resp, err := a.dal.GetAccountByUserID(c.Context(), userId) + + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +type ReqCreateAccount struct { + Nickname string `json:"nickname"` + Avatar string `json:"avatar"` +} + +// CreateAccount - POST метод для создания аккаунта +// +// Request: ReqCreateAccount +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - parsing error +// Status: 400 +// Body: error +// +// Bad request - validation error +// Status: 400 +// Body: { {field: string, tag: string, value: string}, ... } +// +// Unauthorized +// Status: 401 +// Body: nil +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) CreateAccount(c *fiber.Ctx) error { + var req ReqCreateAccount + + session, ok := c.Locals("adapter").(*jwt_adapter.JwtAdapter) + if !ok { + return fiber.NewError(fiber.StatusUnauthorized) + } + + role, ok := c.Locals("role").(string) + if !ok { + role = "user" + } + + err := c.BodyParser(&req) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + errValidate := validateStruct(&req) + if errValidate != nil { + return c.Status(fiber.StatusBadRequest).JSON(errValidate) + } + + resp, err := a.dal.InsertAccount(c.Context(), &model.Account{ + UserID: session.GetUserID(), + Nickname: req.Nickname, + Avatar: req.Avatar, + Role: role, + }) + + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +// SetAccountDelete - DELETE метод для пометки аккаунта как удаленный +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - validation error +// Status: 400 +// Body: error +// +// Unauthorized +// Status: 401 +// Body: nil +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) SetAccountDelete(c *fiber.Ctx) error { + userId := c.Params("userId") + + if userId == "" { + session := c.Locals("adapter").(*jwt_adapter.JwtAdapter) + if session == nil { + return fiber.NewError(fiber.StatusUnauthorized) + } + userId = session.GetUserID() + } + + if userId == "" { + return fiber.NewError(fiber.StatusBadRequest, "userId cannot be empty") + } + + resp, err := a.dal.SetAccountDelete(c.Context(), userId, true) + + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +// RestoreAccount - POST метод для пометки аккаунта как не удаленный +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - validation error +// Status: 400 +// Body: error +// +// Unauthorized +// Status: 401 +// Body: nil +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) RestoreAccount(c *fiber.Ctx) error { + userId := c.Params("userId") + + if userId == "" { + session := c.Locals("adapter").(*jwt_adapter.JwtAdapter) + if session == nil { + return fiber.NewError(fiber.StatusUnauthorized) + } + userId = session.GetUserID() + } + + if userId == "" { + return fiber.NewError(fiber.StatusBadRequest, "userId cannot be empty") + } + + resp, err := a.dal.SetAccountDelete(c.Context(), userId, false) + + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +type ReqSetAccountRole struct { + UserID string `json:"userId" validate:"required"` + Role string `json:"role" validate:"required"` +} + +// SetAccountRole - PATCH метод для присвоения роли аккаунту +// +// Request: ReqSetAccountRole +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - parsing error +// Status: 400 +// Body: error +// +// Bad request - validation error +// Status: 400 +// Body: { {field: string, tag: string, value: string}, ... } +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) SetAccountRole(c *fiber.Ctx) error { + var req ReqSetAccountRole + + err := c.BodyParser(&req) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + errValidate := validateStruct(&req) + if errValidate != nil { + return c.Status(fiber.StatusBadRequest).JSON(errValidate) + } + + resp, err := a.dal.SetAccountRole(c.Context(), req.UserID, req.Role) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +type ReqSetAccountNickname struct { + UserID string `json:"userId" validate:"required"` + Nickname string `json:"nickname" validate:"required"` +} + +// SetAccountNickname - PATCH метод для присвоения никнейма аккаунту +// +// Request: ReqSetAccountNickname +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - parsing error +// Status: 400 +// Body: error +// +// Bad request - validation error +// Status: 400 +// Body: { {field: string, tag: string, value: string}, ... } +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) SetAccountNickname(c *fiber.Ctx) error { + var req ReqSetAccountNickname + + err := c.BodyParser(&req) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + errValidate := validateStruct(&req) + if errValidate != nil { + return c.Status(fiber.StatusBadRequest).JSON(errValidate) + } + + resp, err := a.dal.SetAccountRole(c.Context(), req.UserID, req.Nickname) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +type ReqSetAccountAvatar struct { + UserID string `json:"userId" validate:"required"` + Avatar string `json:"avatar" validate:"required"` +} + +// SetAccountAvatar - PATCH метод для присвоения никнейма аккаунту +// +// Request: ReqSetAccountAvatar +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - parsing error +// Status: 400 +// Body: error +// +// Bad request - validation error +// Status: 400 +// Body: { {field: string, tag: string, value: string}, ... } +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) SetAccountAvatar(c *fiber.Ctx) error { + var req ReqSetAccountAvatar + + err := c.BodyParser(&req) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + errValidate := validateStruct(&req) + if errValidate != nil { + return c.Status(fiber.StatusBadRequest).JSON(errValidate) + } + + resp, err := a.dal.SetAccountRole(c.Context(), req.UserID, req.Avatar) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} + +// DeleteAccount - DELETE метод для полного удаления аккаунта +// +// Responses: +// Success +// Status 200 +// Body: model.Account +// +// Bad request - parsing error +// Status: 400 +// Body: error +// +// Internal server error - dal error +// Status: 500 +// Body: error +func (a *Account) DeleteAccount(c *fiber.Ctx) error { + userId := c.Params("userId") + + if userId == "" { + session := c.Locals("adapter").(*jwt_adapter.JwtAdapter) + if session == nil { + return fiber.NewError(fiber.StatusUnauthorized) + } + userId = session.GetUserID() + } + + resp, err := a.dal.DeleteAccount(c.Context(), userId) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.Status(fiber.StatusOK).JSON(resp) +} diff --git a/service/service_account_test.go b/service/service_account_test.go new file mode 100644 index 0000000..e8fba79 --- /dev/null +++ b/service/service_account_test.go @@ -0,0 +1,341 @@ +package service + +import ( + "bytes" + "context" + "encoding/json" + "github.com/gofiber/fiber/v2" + "github.com/stretchr/testify/suite" + "github.com/themakers/hlog" + "go.uber.org/zap" + "heruvym/dal/mongo" + "heruvym/middleware" + "heruvym/model" + "net/http" + "testing" +) + +var mongoUri = "mongodb://localmongo1:30001,localmongo2:30002,localmongo3:30003/?replicaSet=my-rs" + +type AccountTestSuite struct { + svc *Account + dal *mongo.DAL + app *fiber.App + arg *model.Account + suite.Suite +} + +func TestAccountTestSuite(t *testing.T) { + suite.Run(t, new(AccountTestSuite)) +} + +func (s *AccountTestSuite) SetupSuite() { + logger := hlog.New(zap.NewNop()) + dal, err := mongo.New(context.Background(), mongoUri, "support", logger) + + if s.NoError(err) { + s.dal = dal + } + + s.svc = NewAccount(dal) + + app := fiber.New() + + app.Use( + middleware.JwtPlug, + ) + s.app = app + + for _, route := range s.svc.GetRoutes() { + app.Add(route.Method, route.Path, route.Handler).Name(route.Name) + } + + b, err := json.Marshal(ReqCreateAccount{ + Nickname: "SomeNickname", + Avatar: "/some/avatar.jpeg", + }) + + s.NoError(err) + + req, err := http.NewRequest("POST", "/account", bytes.NewBuffer(b)) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := app.Test(req, 10) + s.NoError(err) + + var result model.Account + err = json.NewDecoder(resp.Body).Decode(&result) + s.NoError(err) + + s.arg = &result + + s.Equal(200, resp.StatusCode) +} + +func (s *AccountTestSuite) TearDownSuite() { + req, err := http.NewRequest("DELETE", "/account/delete/"+s.arg.UserID, nil) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + s.NoError(err) + + s.Equal(200, resp.StatusCode) +} + +func (s *AccountTestSuite) TestAccountPagination() { + tests := []struct { + name string + args *ReqAccountPagination + want int + }{ + { + name: "status 200", + args: &ReqAccountPagination{ + Search: "", + Offset: 0, + Limit: 10, + }, + want: fiber.StatusOK, + }, + { + name: "status 400", + args: &ReqAccountPagination{ + Offset: 0, + Limit: 0, + }, + want: fiber.StatusBadRequest, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + + b, err := json.Marshal(tt.args) + s.NoError(err) + + req, err := http.NewRequest("GET", "/account/pagination", bytes.NewBuffer(b)) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + + s.NoError(err) + + s.Equal(tt.want, resp.StatusCode) + }) + } +} + +func (s *AccountTestSuite) TestGetAccount() { + tests := []struct { + name string + args string + want int + }{ + { + name: "status 200", + args: s.arg.UserID, + want: 200, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + req, err := http.NewRequest("GET", "/account/"+tt.args, nil) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + + s.NoError(err) + + s.Equal(tt.want, resp.StatusCode) + }) + } +} + +func (s *AccountTestSuite) TestSetAccountDelete() { + tests := []struct { + name string + args string + want int + }{ + { + name: "status 200", + args: s.arg.UserID, + want: 200, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + req, err := http.NewRequest("DELETE", "/account/"+tt.args, nil) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + + s.NoError(err) + + s.Equal(tt.want, resp.StatusCode) + }) + } +} + +func (s *AccountTestSuite) TestAccountRestore() { + tests := []struct { + name string + args string + want int + }{ + { + name: "status 200", + args: s.arg.UserID, + want: 200, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + req, err := http.NewRequest("POST", "/account/restore/"+tt.args, nil) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + + s.NoError(err) + + s.Equal(tt.want, resp.StatusCode) + }) + } +} + +func (s *AccountTestSuite) TestSetAccountRole() { + tests := []struct { + name string + args *ReqSetAccountRole + want int + }{ + { + name: "status 200", + args: &ReqSetAccountRole{ + UserID: s.arg.UserID, + Role: "test", + }, + want: fiber.StatusOK, + }, + { + name: "status 400", + args: &ReqSetAccountRole{}, + want: fiber.StatusBadRequest, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + + b, err := json.Marshal(tt.args) + s.NoError(err) + + req, err := http.NewRequest("PATCH", "/account/role", bytes.NewBuffer(b)) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + + s.NoError(err) + + s.Equal(tt.want, resp.StatusCode) + }) + } +} + +func (s *AccountTestSuite) TestSetAccountNickname() { + tests := []struct { + name string + args *ReqSetAccountNickname + want int + }{ + { + name: "status 200", + args: &ReqSetAccountNickname{ + UserID: s.arg.UserID, + Nickname: "test", + }, + want: fiber.StatusOK, + }, + { + name: "status 400", + args: &ReqSetAccountNickname{}, + want: fiber.StatusBadRequest, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + + b, err := json.Marshal(tt.args) + s.NoError(err) + + req, err := http.NewRequest("PATCH", "/account/nickname", bytes.NewBuffer(b)) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + + s.NoError(err) + + s.Equal(tt.want, resp.StatusCode) + }) + } +} + +func (s *AccountTestSuite) TestSetAccountAvatar() { + tests := []struct { + name string + args *ReqSetAccountAvatar + want int + }{ + { + name: "status 200", + args: &ReqSetAccountAvatar{ + UserID: s.arg.UserID, + Avatar: "/test.jpeg", + }, + want: fiber.StatusOK, + }, + { + name: "status 400", + args: &ReqSetAccountAvatar{}, + want: fiber.StatusBadRequest, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + + b, err := json.Marshal(tt.args) + s.NoError(err) + + req, err := http.NewRequest("PATCH", "/account/avatar", bytes.NewBuffer(b)) + s.NoError(err) + + req.Header.Set("Content-Type", "application/json") + + resp, err := s.app.Test(req, 10) + + s.NoError(err) + + s.Equal(tt.want, resp.StatusCode) + }) + } +}