From 1f680142f04152ed70727a2ee3dd66605b694dd2 Mon Sep 17 00:00:00 2001 From: optclblast Date: Sat, 11 May 2024 13:45:53 +0300 Subject: [PATCH] tmp --- backend/README.md | 6 +- backend/internal/factory/interface.go | 2 + backend/internal/factory/wire_gen.go | 2 +- .../interface/rest/controllers/auth.go | 53 ++++++-------- .../rest/controllers/organization.go | 23 +++++- .../interface/rest/controllers/ping.go | 8 +- .../interface/rest/controllers/root.go | 7 +- backend/internal/interface/rest/domain/dto.go | 6 ++ .../interface/rest/presenters/auth.go | 28 +++---- backend/internal/interface/rest/server.go | 29 ++++++-- backend/internal/pkg/models/organization.go | 10 ++- backend/internal/service/service.go | 4 +- .../interactors/organizations/interactor.go | 73 +++++++++++++++++++ .../usecase/interactors/users/interactor.go | 12 ++- .../repository/organizations/repository.go | 68 ++++++++++++++++- .../usecase/repository/users/repository.go | 35 +++++++-- backend/migrations/blockd.sql | 45 ++++++++---- 17 files changed, 314 insertions(+), 97 deletions(-) diff --git a/backend/README.md b/backend/README.md index c0cf5e1..79c4055 100644 --- a/backend/README.md +++ b/backend/README.md @@ -68,7 +68,7 @@ mnemonic (string, **required**) ### Example Request: ``` bash -curl --location 'http://localhost:8081/login' \ +curl --location 'http://localhost:8081/join' \ --header 'Content-Type: application/json' \ --data-raw '{ "name": "Bladee The Grand Drainer", @@ -109,11 +109,11 @@ Response: } ``` -## POST **/organization** +## POST **/organizations** ### Request body: name (string, **required**) address (string, optional) -// org wallet address maybe?? +wallet_mnemonic (string, optional. *if not provided, creators mnemonic will me used*) ### Example Request: diff --git a/backend/internal/factory/interface.go b/backend/internal/factory/interface.go index b8a479d..ca3a593 100644 --- a/backend/internal/factory/interface.go +++ b/backend/internal/factory/interface.go @@ -79,10 +79,12 @@ func provideRestServer( log *slog.Logger, controllers *controllers.RootController, c config.Config, + jwt jwt.JWTInteractor, ) *rest.Server { return rest.NewServer( log.WithGroup("rest"), c.Rest, controllers, + jwt, ) } diff --git a/backend/internal/factory/wire_gen.go b/backend/internal/factory/wire_gen.go index 88e2ebd..c4f64d6 100644 --- a/backend/internal/factory/wire_gen.go +++ b/backend/internal/factory/wire_gen.go @@ -24,7 +24,7 @@ func ProvideService(c config.Config) (service.Service, func(), error) { authPresenter := provideAuthPresenter(jwtInteractor) authController := provideAuthController(logger, usersInteractor, authPresenter, jwtInteractor) rootController := provideControllers(logger, authController) - server := provideRestServer(logger, rootController, c) + server := provideRestServer(logger, rootController, c, jwtInteractor) serviceService := service.NewService(logger, server) return serviceService, func() { cleanup() diff --git a/backend/internal/interface/rest/controllers/auth.go b/backend/internal/interface/rest/controllers/auth.go index 6bfe069..866f007 100644 --- a/backend/internal/interface/rest/controllers/auth.go +++ b/backend/internal/interface/rest/controllers/auth.go @@ -6,7 +6,6 @@ import ( "fmt" "log/slog" "net/http" - "strings" "time" "github.com/emochka2007/block-accounting/internal/interface/rest/domain" @@ -23,10 +22,10 @@ var ( ) type AuthController interface { - Join(w http.ResponseWriter, req *http.Request) error - JoinWithInvite(w http.ResponseWriter, req *http.Request) error - Login(w http.ResponseWriter, req *http.Request) error - Invite(w http.ResponseWriter, req *http.Request) error + Join(w http.ResponseWriter, req *http.Request) ([]byte, error) + JoinWithInvite(w http.ResponseWriter, req *http.Request) ([]byte, error) + Login(w http.ResponseWriter, req *http.Request) ([]byte, error) + Invite(w http.ResponseWriter, req *http.Request) ([]byte, error) } type authController struct { @@ -50,28 +49,31 @@ func NewAuthController( } } -func (c *authController) Join(w http.ResponseWriter, req *http.Request) error { +func (c *authController) Join(w http.ResponseWriter, req *http.Request) ([]byte, error) { request, err := presenters.CreateRequest[domain.JoinRequest](req) if err != nil { - return fmt.Errorf("error create join request. %w", err) + return nil, fmt.Errorf("error create join request. %w", err) } c.log.Debug("join request", slog.String("mnemonic", request.Mnemonic)) if !bip39.IsMnemonicValid(request.Mnemonic) { - return fmt.Errorf("error invalid mnemonic. %w", ErrorAuthInvalidMnemonic) + return nil, fmt.Errorf("error invalid mnemonic. %w", ErrorAuthInvalidMnemonic) } ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second) defer cancel() user, err := c.usersInteractor.Create(ctx, users.CreateParams{ + Name: request.Name, + Email: request.Credentals.Email, + Phone: request.Credentals.Phone, + Tg: request.Credentals.Telegram, Mnemonic: request.Mnemonic, - IsAdmin: true, Activate: true, }) if err != nil { - return fmt.Errorf("error create new user. %w", err) + return nil, fmt.Errorf("error create new user. %w", err) } c.log.Debug("join request", slog.String("user id", user.ID.String())) @@ -80,10 +82,10 @@ func (c *authController) Join(w http.ResponseWriter, req *http.Request) error { } // NIT: wrap with idempotent action handler -func (c *authController) Login(w http.ResponseWriter, req *http.Request) error { +func (c *authController) Login(w http.ResponseWriter, req *http.Request) ([]byte, error) { request, err := presenters.CreateRequest[domain.LoginRequest](req) if err != nil { - return fmt.Errorf("error create login request. %w", err) + return nil, fmt.Errorf("error create login request. %w", err) } c.log.Debug("login request", slog.String("mnemonic", request.Mnemonic)) @@ -93,18 +95,18 @@ func (c *authController) Login(w http.ResponseWriter, req *http.Request) error { seed, err := hdwallet.NewSeedFromMnemonic(request.Mnemonic) if err != nil { - return fmt.Errorf("error create seed from mnemonic. %w", err) + return nil, fmt.Errorf("error create seed from mnemonic. %w", err) } users, err := c.usersInteractor.Get(ctx, users.GetParams{ Seed: seed, }) if err != nil { - return fmt.Errorf("error fetch user by seed. %w", err) + return nil, fmt.Errorf("error fetch user by seed. %w", err) } if len(users) == 0 { - return fmt.Errorf("error empty users set") + return nil, fmt.Errorf("error empty users set") } c.log.Debug("login request", slog.String("user id", users[0].ID.String())) @@ -114,24 +116,11 @@ func (c *authController) Login(w http.ResponseWriter, req *http.Request) error { // const mnemonicEntropyBitSize int = 256 -func (c *authController) Invite(w http.ResponseWriter, req *http.Request) error { - tokenStringRaw := req.Header.Get("Authorization") - if tokenStringRaw == "" { - return fmt.Errorf("error token requeired. %w", ErrorTokenRequired) - } +func (c *authController) Invite(w http.ResponseWriter, req *http.Request) ([]byte, error) { - tokenString := strings.Split(tokenStringRaw, " ")[1] - - user, err := c.jwtInteractor.User(tokenString) - if err != nil { - return fmt.Errorf("error fetch user from token. %w", err) - } - - c.log.Debug("auth token", slog.Any("user", user)) - - return nil + return nil, nil } -func (c *authController) JoinWithInvite(w http.ResponseWriter, req *http.Request) error { - return nil // implement +func (c *authController) JoinWithInvite(w http.ResponseWriter, req *http.Request) ([]byte, error) { + return nil, nil // implement } diff --git a/backend/internal/interface/rest/controllers/organization.go b/backend/internal/interface/rest/controllers/organization.go index e064db8..a3c086f 100644 --- a/backend/internal/interface/rest/controllers/organization.go +++ b/backend/internal/interface/rest/controllers/organization.go @@ -1,10 +1,29 @@ package controllers -import "log/slog" +import ( + "fmt" + "log/slog" + "net/http" + + "github.com/emochka2007/block-accounting/internal/interface/rest/domain" + "github.com/emochka2007/block-accounting/internal/interface/rest/presenters" + "github.com/emochka2007/block-accounting/internal/usecase/interactors/organizations" +) type OrganizationsController interface { + NewOrganization(w http.ResponseWriter, r *http.Request) ([]byte, error) } type organizationsController struct { - log *slog.Logger + log *slog.Logger + orgInteractor organizations.OrganizationsInteractor +} + +func (c *organizationsController) NewOrganization(w http.ResponseWriter, r *http.Request) ([]byte, error) { + _, err := presenters.CreateRequest[domain.NewOrganizationRequest](r) + if err != nil { + return nil, fmt.Errorf("error build request. %w", err) + } + + return nil, nil } diff --git a/backend/internal/interface/rest/controllers/ping.go b/backend/internal/interface/rest/controllers/ping.go index 8932ad1..07c3674 100644 --- a/backend/internal/interface/rest/controllers/ping.go +++ b/backend/internal/interface/rest/controllers/ping.go @@ -6,7 +6,7 @@ import ( ) type PingController interface { - Ping(w http.ResponseWriter, req *http.Request) error + Ping(w http.ResponseWriter, req *http.Request) ([]byte, error) } type pingController struct { @@ -21,8 +21,6 @@ func NewPingController( } } -func (c *pingController) Ping(w http.ResponseWriter, req *http.Request) error { - _, err := w.Write([]byte("pong")) - - return err +func (c *pingController) Ping(w http.ResponseWriter, req *http.Request) ([]byte, error) { + return []byte("pong"), nil } diff --git a/backend/internal/interface/rest/controllers/root.go b/backend/internal/interface/rest/controllers/root.go index 1d5b182..daf43c0 100644 --- a/backend/internal/interface/rest/controllers/root.go +++ b/backend/internal/interface/rest/controllers/root.go @@ -1,16 +1,19 @@ package controllers type RootController struct { - Ping PingController - Auth AuthController + Ping PingController + Auth AuthController + Organizations OrganizationsController } func NewRootController( ping PingController, auth AuthController, + // organizations OrganizationsController, ) *RootController { return &RootController{ Ping: ping, Auth: auth, + // Organizations: organizations, } } diff --git a/backend/internal/interface/rest/domain/dto.go b/backend/internal/interface/rest/domain/dto.go index da16644..8823591 100644 --- a/backend/internal/interface/rest/domain/dto.go +++ b/backend/internal/interface/rest/domain/dto.go @@ -28,6 +28,12 @@ type LoginResponse struct { Token string `json:"token"` } +type NewOrganizationRequest struct { + Name string `json:"name"` + Address string `json:"address"` + WalletMnemonic string `json:"wallet_mnemonic,omitempty"` +} + func BuildRequest[T any](data []byte) (*T, error) { var req T diff --git a/backend/internal/interface/rest/presenters/auth.go b/backend/internal/interface/rest/presenters/auth.go index 03d987c..e4b5c5e 100644 --- a/backend/internal/interface/rest/presenters/auth.go +++ b/backend/internal/interface/rest/presenters/auth.go @@ -12,8 +12,8 @@ import ( ) type AuthPresenter interface { - ResponseJoin(w http.ResponseWriter, user *models.User) error - ResponseLogin(w http.ResponseWriter, user *models.User) error + ResponseJoin(w http.ResponseWriter, user *models.User) ([]byte, error) + ResponseLogin(w http.ResponseWriter, user *models.User) ([]byte, error) } type authPresenter struct { @@ -28,46 +28,38 @@ func NewAuthPresenter( } } -func (p *authPresenter) ResponseJoin(w http.ResponseWriter, user *models.User) error { +func (p *authPresenter) ResponseJoin(w http.ResponseWriter, user *models.User) ([]byte, error) { resp := new(domain.JoinResponse) token, err := p.jwtInteractor.NewToken(user, 24*time.Hour) if err != nil { - return fmt.Errorf("error create access token. %w", err) + return nil, fmt.Errorf("error create access token. %w", err) } resp.Token = token out, err := json.Marshal(resp) if err != nil { - return fmt.Errorf("error marshal join response. %w", err) + return nil, fmt.Errorf("error marshal join response. %w", err) } - if _, err = w.Write(out); err != nil { - return fmt.Errorf("error write response. %w", err) - } - - return nil + return out, nil } -func (p *authPresenter) ResponseLogin(w http.ResponseWriter, user *models.User) error { +func (p *authPresenter) ResponseLogin(w http.ResponseWriter, user *models.User) ([]byte, error) { resp := new(domain.LoginResponse) token, err := p.jwtInteractor.NewToken(user, 24*time.Hour) if err != nil { - return fmt.Errorf("error create access token. %w", err) + return nil, fmt.Errorf("error create access token. %w", err) } resp.Token = token out, err := json.Marshal(resp) if err != nil { - return fmt.Errorf("error marshal login response. %w", err) + return nil, fmt.Errorf("error marshal login response. %w", err) } - if _, err = w.Write(out); err != nil { - return fmt.Errorf("error write response. %w", err) - } - - return nil + return out, nil } diff --git a/backend/internal/interface/rest/server.go b/backend/internal/interface/rest/server.go index 03254e8..af93b41 100644 --- a/backend/internal/interface/rest/server.go +++ b/backend/internal/interface/rest/server.go @@ -42,12 +42,14 @@ func NewServer( log *slog.Logger, conf config.RestConfig, controllers *controllers.RootController, + jwt jwt.JWTInteractor, ) *Server { s := &Server{ log: log, addr: conf.Address, tls: conf.TLS, controllers: controllers, + jwt: jwt, } s.buildRouter() @@ -87,6 +89,7 @@ func (s *Server) buildRouter() { router.Use(mw.Recoverer) router.Use(mw.RequestID) router.Use(s.handleMw) + router.Use(render.SetContentType(render.ContentTypeJSON)) router.Get("/ping", s.handle(s.controllers.Ping.Ping, "ping")) @@ -94,12 +97,16 @@ func (s *Server) buildRouter() { router.Post("/join", s.handle(s.controllers.Auth.Join, "join")) router.Post("/login", s.handle(s.controllers.Auth.Login, "login")) - router.Route("/organization", func(r chi.Router) { - r.With(s.withAuthorization) + router.Route("/organizations", func(r chi.Router) { + r = r.With(s.withAuthorization) - r.Get("/", s.handle(s.controllers.Auth.Invite, "organization")) + // r.Get("/", s.handle(s.controllers.Auth.Invite, "list_organizations")) + // r.Post("/", s.handle(s.controllers.Organizations.NewOrganization, "new_organization")) r.Route("/{organization_id}", func(r chi.Router) { + // r.Put("/", s.handle(s.controllers.Organizations.NewOrganization, "update_organization")) + // r.Delete("/", s.handle(s.controllers.Organizations.NewOrganization, "delete_organization")) + r.Route("/transactions", func(r chi.Router) { r.Get("/", nil) // list r.Post("/", nil) // add @@ -122,7 +129,7 @@ func (s *Server) buildRouter() { } func (s *Server) handle( - h func(w http.ResponseWriter, req *http.Request) error, + h func(w http.ResponseWriter, req *http.Request) ([]byte, error), method_name string, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -138,7 +145,8 @@ func (s *Server) handle( ) }() - if err := h(w, r); err != nil { + out, err := h(w, r) + if err != nil { s.log.Error( "http error", slog.String("method_name", method_name), @@ -147,6 +155,17 @@ func (s *Server) handle( s.responseError(w, err) } + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + if _, err = w.Write(out); err != nil { + s.log.Error( + "error write http response", + slog.String("method_name", method_name), + logger.Err(err), + ) + } } } diff --git a/backend/internal/pkg/models/organization.go b/backend/internal/pkg/models/organization.go index 4967c4f..bbebb04 100644 --- a/backend/internal/pkg/models/organization.go +++ b/backend/internal/pkg/models/organization.go @@ -7,8 +7,10 @@ import ( ) type Organization struct { - ID uuid.UUID - Name string - CreatedAt time.Time - UpdatedAt time.Time + ID uuid.UUID + Name string + Address string + WalletSeed []byte + CreatedAt time.Time + UpdatedAt time.Time } diff --git a/backend/internal/service/service.go b/backend/internal/service/service.go index b8af814..e69c54b 100644 --- a/backend/internal/service/service.go +++ b/backend/internal/service/service.go @@ -29,6 +29,8 @@ func NewService( } func (s *ServiceImpl) Run(ctx context.Context) error { + s.log.Info("starting blockd service 0w0") + errch := make(chan error) defer s.rest.Close() @@ -52,5 +54,5 @@ func (s *ServiceImpl) Run(ctx context.Context) error { } func (s *ServiceImpl) Stop() { - + s.log.Info(">w< bye bye! :3") } diff --git a/backend/internal/usecase/interactors/organizations/interactor.go b/backend/internal/usecase/interactors/organizations/interactor.go index 7870530..5faeb68 100644 --- a/backend/internal/usecase/interactors/organizations/interactor.go +++ b/backend/internal/usecase/interactors/organizations/interactor.go @@ -1,7 +1,80 @@ package organizations +import ( + "context" + "fmt" + "log/slog" + "time" + + "github.com/emochka2007/block-accounting/internal/pkg/ctxmeta" + "github.com/emochka2007/block-accounting/internal/pkg/hdwallet" + "github.com/emochka2007/block-accounting/internal/pkg/models" + "github.com/emochka2007/block-accounting/internal/usecase/repository/organizations" + "github.com/google/uuid" +) + +type CreateParams struct { + Name string + Address string + WalletMnemonic string +} + type OrganizationsInteractor interface { + Create( + ctx context.Context, + params CreateParams, + ) (*models.Organization, error) } type organizationsInteractor struct { + log *slog.Logger + orgRepository organizations.Repository +} + +func NewOrganizationsInteractor( + log *slog.Logger, + orgRepository organizations.Repository, +) OrganizationsInteractor { + return &organizationsInteractor{ + log: log, + orgRepository: orgRepository, + } +} + +func (i *organizationsInteractor) Create( + ctx context.Context, + params CreateParams, +) (*models.Organization, error) { + var walletSeed []byte + + user, err := ctxmeta.User(ctx) + if err != nil { + return nil, fmt.Errorf("error fetch user from context. %w", err) + } + + if params.WalletMnemonic == "" { + walletSeed = user.Seed() + } else { + seed, err := hdwallet.NewSeedFromMnemonic(params.WalletMnemonic) + if err != nil { + return nil, fmt.Errorf("error convert organization wallet mnemonic into a seed. %w", err) + } + + walletSeed = seed + } + + org := models.Organization{ + ID: uuid.New(), + Name: params.Name, + Address: params.Address, + WalletSeed: walletSeed, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + if err := i.orgRepository.CreateAndAdd(ctx, org, user); err != nil { + return nil, fmt.Errorf("error create new organization. %w", err) + } + + return &org, nil } diff --git a/backend/internal/usecase/interactors/users/interactor.go b/backend/internal/usecase/interactors/users/interactor.go index eda11a3..8a39498 100644 --- a/backend/internal/usecase/interactors/users/interactor.go +++ b/backend/internal/usecase/interactors/users/interactor.go @@ -18,8 +18,11 @@ var ( ) type CreateParams struct { + Name string + Email string + Phone string + Tg string Mnemonic string - IsAdmin bool Activate bool } @@ -66,7 +69,7 @@ func NewUsersInteractor( func (i *usersInteractor) Create(ctx context.Context, params CreateParams) (*models.User, error) { seed, err := hdwallet.NewSeedFromMnemonic(params.Mnemonic) if err != nil { - return nil, fmt.Errorf("error convert mnemonic into a bytes. %w", err) + return nil, fmt.Errorf("error convert mnemonic into a seed. %w", err) } user := models.NewUser( @@ -76,6 +79,11 @@ func (i *usersInteractor) Create(ctx context.Context, params CreateParams) (*mod time.Now(), ) + user.Name = params.Name + user.Credentails.Email = params.Email + user.Credentails.Phone = params.Phone + user.Credentails.Telegram = params.Tg + if err = i.usersRepo.Create(ctx, user); err != nil { return nil, fmt.Errorf("error create new user. %w", err) } diff --git a/backend/internal/usecase/repository/organizations/repository.go b/backend/internal/usecase/repository/organizations/repository.go index 3ff6134..c42bfa2 100644 --- a/backend/internal/usecase/repository/organizations/repository.go +++ b/backend/internal/usecase/repository/organizations/repository.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "time" sq "github.com/Masterminds/squirrel" "github.com/emochka2007/block-accounting/internal/pkg/models" @@ -15,11 +16,20 @@ type GetParams struct { Ids uuid.UUIDs } +type AddParticipantParams struct { + OrganizationId uuid.UUID + UserId uuid.UUID + EmployeeId uuid.UUID + IsAdmin bool +} + type Repository interface { Create(ctx context.Context, org models.Organization) error Get(ctx context.Context, params GetParams) ([]*models.Organization, error) Update(ctx context.Context, org models.Organization) error Delete(ctx context.Context, id uuid.UUID) error + AddParticipant(ctx context.Context, params AddParticipantParams) error + CreateAndAdd(ctx context.Context, org models.Organization, user *models.User) error } type repositorySQL struct { @@ -43,10 +53,14 @@ func (s *repositorySQL) Conn(ctx context.Context) sqltools.DBTX { func (r *repositorySQL) Create(ctx context.Context, org models.Organization) error { if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) { query := sq.Insert("organizations").Columns( - "id, name", + "id, name, address, wallet_seed, created_at, updated_at", ).Values( org.ID, org.Name, + org.Address, + org.WalletSeed, + org.CreatedAt, + org.UpdatedAt, ) if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil { @@ -78,3 +92,55 @@ func (r *repositorySQL) Delete(ctx context.Context, id uuid.UUID) error { return nil } + +func (r *repositorySQL) AddParticipant(ctx context.Context, params AddParticipantParams) error { + if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) { + query := sq.Insert("organizations_users").Columns( + "organization_id", + "user_id", + "employee_id", + "added_at", + "updated_at", + "is_admin", + ).Values( + params.OrganizationId, + params.UserId, + params.EmployeeId, + time.Now(), + time.Now(), + params.IsAdmin, + ) + + if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil { + return fmt.Errorf("error add new participant to organization. %w", err) + } + + return nil + }); err != nil { + return fmt.Errorf("error execute transactional operation. %w", err) + } + + return nil +} + +func (r *repositorySQL) CreateAndAdd(ctx context.Context, org models.Organization, user *models.User) error { + if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) { + if err := r.Create(ctx, org); err != nil { + return fmt.Errorf("error create organization. %w", err) + } + + if err := r.AddParticipant(ctx, AddParticipantParams{ + OrganizationId: org.ID, + UserId: user.Id(), + IsAdmin: true, + }); err != nil { + return fmt.Errorf("error add user to newly created organization. %w", err) + } + + return nil + }); err != nil { + return fmt.Errorf("error execute transactional operation. %w", err) + } + + return nil +} diff --git a/backend/internal/usecase/repository/users/repository.go b/backend/internal/usecase/repository/users/repository.go index b060ae4..4c5cc3a 100644 --- a/backend/internal/usecase/repository/users/repository.go +++ b/backend/internal/usecase/repository/users/repository.go @@ -50,7 +50,7 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us var users []*models.User = make([]*models.User, 0, len(params.Ids)) if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) { - query := sq.Select("id, seed, created_at, activated_at"). + query := sq.Select("id, name, email, phone, tg, seed, created_at, activated_at"). From("users"). PlaceholderFormat(sq.Dollar) @@ -81,19 +81,40 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us for rows.Next() { var ( - id uuid.UUID + id uuid.UUID + + name string + email string + phone string + tg string + seed []byte //isAdmin bool createdAt time.Time activatedAt sql.NullTime ) - if err = rows.Scan(&id, &seed, &createdAt, &activatedAt); err != nil { + if err = rows.Scan( + &id, + &name, + &email, + &phone, + &tg, + &seed, + &createdAt, + &activatedAt, + ); err != nil { return fmt.Errorf("error scan row. %w", err) } users = append(users, &models.User{ - ID: id, + ID: id, + Name: name, + Credentails: models.UserCredentials{ + Email: email, + Phone: phone, + Telegram: tg, + }, Bip39Seed: seed, //Admin: isAdmin, CreatedAt: createdAt, @@ -111,10 +132,14 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us func (r *repositorySQL) Create(ctx context.Context, user *models.User) error { if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) error { - columns := []string{"id", "seed", "created_at"} + columns := []string{"id", "name", "email", "phone", "tg", "seed", "created_at"} values := []any{ user.ID, + user.Name, + user.Credentails.Email, + user.Credentails.Phone, + user.Credentails.Telegram, user.Bip39Seed, user.CreatedAt, } diff --git a/backend/migrations/blockd.sql b/backend/migrations/blockd.sql index b23f545..704a12b 100644 --- a/backend/migrations/blockd.sql +++ b/backend/migrations/blockd.sql @@ -1,15 +1,17 @@ create table if not exists users ( - id uuid not null, + id uuid primary key , name varchar(250), email varchar(200), phone varchar(16), tg varchar(200), seed bytea not null unique, created_at timestamp default current_timestamp, - activated_at timestamp default null, - primary key (id, seed) + activated_at timestamp default null ); +create index if not exists index_users_seed + on users using hash (seed); + create index if not exists index_users_name on users using hash (name); @@ -23,9 +25,10 @@ create index if not exists index_users_seed on users using hash (seed); create table if not exists organizations ( - id uuid primary key, + id uuid primary key unique, name varchar(300) default 'My Organization' not null, - address varchar(750) default "", + address varchar(750) not null, + wallet_seed bytea not null, created_at timestamp default current_timestamp, updated_at timestamp default current_timestamp ); @@ -33,11 +36,28 @@ create table if not exists organizations ( create index if not exists index_organizations_id on organizations (id); +create table employees ( + id uuid primary key, + user_id uuid references users(id), + organization_id uuid not null references organizations(id), + wallet_address text not null, + created_at timestamp default current_timestamp, + updated_at timestamp default current_timestamp +); + +create index if not exists index_employees_id_organization_id + on employees (id, organization_id); + +create index if not exists index_user_id_organization_id + on employees (user_id, organization_id); + create table organizations_users ( - organization_id uuid not null, - user_id uuid not null, + organization_id uuid not null references organizations(id), + user_id uuid not null references users(id), + employee_id uuid references employees(id) default null, added_at timestamp default current_timestamp, updated_at timestamp default current_timestamp, + deleted_at timestamp default null, is_admin bool default false, primary key(organization_id, user_id) ); @@ -48,16 +68,9 @@ create index if not exists index_organizations_users_organization_id_user_id_is_ create index if not exists index_organizations_users_organization_id_user_id on organizations_users (organization_id, user_id); -create table employees ( - id uuid primary key, - organization_id uuid not null, - wallet_address text not null, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp -); +create index if not exists index_organizations_users_organization_id_employee_id + on organizations_users (organization_id, employee_id); -create index if not exists index_employees_id_organization_id - on employees (id, organization_id); create table if not exists transactions ( id uuid primary key,