This commit is contained in:
r8zavetr8v 2024-05-11 13:45:53 +03:00
parent e800b9e572
commit 1f680142f0
17 changed files with 314 additions and 97 deletions

View File

@ -68,7 +68,7 @@ mnemonic (string, **required**)
### Example ### Example
Request: Request:
``` bash ``` bash
curl --location 'http://localhost:8081/login' \ curl --location 'http://localhost:8081/join' \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--data-raw '{ --data-raw '{
"name": "Bladee The Grand Drainer", "name": "Bladee The Grand Drainer",
@ -109,11 +109,11 @@ Response:
} }
``` ```
## POST **/organization** ## POST **/organizations**
### Request body: ### Request body:
name (string, **required**) name (string, **required**)
address (string, optional) address (string, optional)
// org wallet address maybe?? wallet_mnemonic (string, optional. *if not provided, creators mnemonic will me used*)
### Example ### Example
Request: Request:

View File

@ -79,10 +79,12 @@ func provideRestServer(
log *slog.Logger, log *slog.Logger,
controllers *controllers.RootController, controllers *controllers.RootController,
c config.Config, c config.Config,
jwt jwt.JWTInteractor,
) *rest.Server { ) *rest.Server {
return rest.NewServer( return rest.NewServer(
log.WithGroup("rest"), log.WithGroup("rest"),
c.Rest, c.Rest,
controllers, controllers,
jwt,
) )
} }

View File

@ -24,7 +24,7 @@ func ProvideService(c config.Config) (service.Service, func(), error) {
authPresenter := provideAuthPresenter(jwtInteractor) authPresenter := provideAuthPresenter(jwtInteractor)
authController := provideAuthController(logger, usersInteractor, authPresenter, jwtInteractor) authController := provideAuthController(logger, usersInteractor, authPresenter, jwtInteractor)
rootController := provideControllers(logger, authController) rootController := provideControllers(logger, authController)
server := provideRestServer(logger, rootController, c) server := provideRestServer(logger, rootController, c, jwtInteractor)
serviceService := service.NewService(logger, server) serviceService := service.NewService(logger, server)
return serviceService, func() { return serviceService, func() {
cleanup() cleanup()

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/emochka2007/block-accounting/internal/interface/rest/domain" "github.com/emochka2007/block-accounting/internal/interface/rest/domain"
@ -23,10 +22,10 @@ var (
) )
type AuthController interface { type AuthController interface {
Join(w http.ResponseWriter, req *http.Request) error Join(w http.ResponseWriter, req *http.Request) ([]byte, error)
JoinWithInvite(w http.ResponseWriter, req *http.Request) error JoinWithInvite(w http.ResponseWriter, req *http.Request) ([]byte, error)
Login(w http.ResponseWriter, req *http.Request) error Login(w http.ResponseWriter, req *http.Request) ([]byte, error)
Invite(w http.ResponseWriter, req *http.Request) error Invite(w http.ResponseWriter, req *http.Request) ([]byte, error)
} }
type authController struct { 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) request, err := presenters.CreateRequest[domain.JoinRequest](req)
if err != nil { 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)) c.log.Debug("join request", slog.String("mnemonic", request.Mnemonic))
if !bip39.IsMnemonicValid(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) ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second)
defer cancel() defer cancel()
user, err := c.usersInteractor.Create(ctx, users.CreateParams{ 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, Mnemonic: request.Mnemonic,
IsAdmin: true,
Activate: true, Activate: true,
}) })
if err != nil { 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())) 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 // 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) request, err := presenters.CreateRequest[domain.LoginRequest](req)
if err != nil { 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)) 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) seed, err := hdwallet.NewSeedFromMnemonic(request.Mnemonic)
if err != nil { 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{ users, err := c.usersInteractor.Get(ctx, users.GetParams{
Seed: seed, Seed: seed,
}) })
if err != nil { 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 { 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())) 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 // const mnemonicEntropyBitSize int = 256
func (c *authController) Invite(w http.ResponseWriter, req *http.Request) error { func (c *authController) Invite(w http.ResponseWriter, req *http.Request) ([]byte, error) {
tokenStringRaw := req.Header.Get("Authorization")
if tokenStringRaw == "" {
return fmt.Errorf("error token requeired. %w", ErrorTokenRequired)
}
tokenString := strings.Split(tokenStringRaw, " ")[1] return nil, nil
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
} }
func (c *authController) JoinWithInvite(w http.ResponseWriter, req *http.Request) error { func (c *authController) JoinWithInvite(w http.ResponseWriter, req *http.Request) ([]byte, error) {
return nil // implement return nil, nil // implement
} }

View File

@ -1,10 +1,29 @@
package controllers 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 { type OrganizationsController interface {
NewOrganization(w http.ResponseWriter, r *http.Request) ([]byte, error)
} }
type organizationsController struct { 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
} }

View File

@ -6,7 +6,7 @@ import (
) )
type PingController interface { type PingController interface {
Ping(w http.ResponseWriter, req *http.Request) error Ping(w http.ResponseWriter, req *http.Request) ([]byte, error)
} }
type pingController struct { type pingController struct {
@ -21,8 +21,6 @@ func NewPingController(
} }
} }
func (c *pingController) Ping(w http.ResponseWriter, req *http.Request) error { func (c *pingController) Ping(w http.ResponseWriter, req *http.Request) ([]byte, error) {
_, err := w.Write([]byte("pong")) return []byte("pong"), nil
return err
} }

View File

@ -1,16 +1,19 @@
package controllers package controllers
type RootController struct { type RootController struct {
Ping PingController Ping PingController
Auth AuthController Auth AuthController
Organizations OrganizationsController
} }
func NewRootController( func NewRootController(
ping PingController, ping PingController,
auth AuthController, auth AuthController,
// organizations OrganizationsController,
) *RootController { ) *RootController {
return &RootController{ return &RootController{
Ping: ping, Ping: ping,
Auth: auth, Auth: auth,
// Organizations: organizations,
} }
} }

View File

@ -28,6 +28,12 @@ type LoginResponse struct {
Token string `json:"token"` 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) { func BuildRequest[T any](data []byte) (*T, error) {
var req T var req T

View File

@ -12,8 +12,8 @@ import (
) )
type AuthPresenter interface { type AuthPresenter interface {
ResponseJoin(w http.ResponseWriter, user *models.User) error ResponseJoin(w http.ResponseWriter, user *models.User) ([]byte, error)
ResponseLogin(w http.ResponseWriter, user *models.User) error ResponseLogin(w http.ResponseWriter, user *models.User) ([]byte, error)
} }
type authPresenter struct { 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) resp := new(domain.JoinResponse)
token, err := p.jwtInteractor.NewToken(user, 24*time.Hour) token, err := p.jwtInteractor.NewToken(user, 24*time.Hour)
if err != nil { 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 resp.Token = token
out, err := json.Marshal(resp) out, err := json.Marshal(resp)
if err != nil { 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 out, nil
return fmt.Errorf("error write response. %w", err)
}
return 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) resp := new(domain.LoginResponse)
token, err := p.jwtInteractor.NewToken(user, 24*time.Hour) token, err := p.jwtInteractor.NewToken(user, 24*time.Hour)
if err != nil { 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 resp.Token = token
out, err := json.Marshal(resp) out, err := json.Marshal(resp)
if err != nil { 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 out, nil
return fmt.Errorf("error write response. %w", err)
}
return nil
} }

View File

@ -42,12 +42,14 @@ func NewServer(
log *slog.Logger, log *slog.Logger,
conf config.RestConfig, conf config.RestConfig,
controllers *controllers.RootController, controllers *controllers.RootController,
jwt jwt.JWTInteractor,
) *Server { ) *Server {
s := &Server{ s := &Server{
log: log, log: log,
addr: conf.Address, addr: conf.Address,
tls: conf.TLS, tls: conf.TLS,
controllers: controllers, controllers: controllers,
jwt: jwt,
} }
s.buildRouter() s.buildRouter()
@ -87,6 +89,7 @@ func (s *Server) buildRouter() {
router.Use(mw.Recoverer) router.Use(mw.Recoverer)
router.Use(mw.RequestID) router.Use(mw.RequestID)
router.Use(s.handleMw) router.Use(s.handleMw)
router.Use(render.SetContentType(render.ContentTypeJSON)) router.Use(render.SetContentType(render.ContentTypeJSON))
router.Get("/ping", s.handle(s.controllers.Ping.Ping, "ping")) 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("/join", s.handle(s.controllers.Auth.Join, "join"))
router.Post("/login", s.handle(s.controllers.Auth.Login, "login")) router.Post("/login", s.handle(s.controllers.Auth.Login, "login"))
router.Route("/organization", func(r chi.Router) { router.Route("/organizations", func(r chi.Router) {
r.With(s.withAuthorization) 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.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.Route("/transactions", func(r chi.Router) {
r.Get("/", nil) // list r.Get("/", nil) // list
r.Post("/", nil) // add r.Post("/", nil) // add
@ -122,7 +129,7 @@ func (s *Server) buildRouter() {
} }
func (s *Server) handle( 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, method_name string,
) http.HandlerFunc { ) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { 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( s.log.Error(
"http error", "http error",
slog.String("method_name", method_name), slog.String("method_name", method_name),
@ -147,6 +155,17 @@ func (s *Server) handle(
s.responseError(w, err) 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),
)
}
} }
} }

View File

@ -7,8 +7,10 @@ import (
) )
type Organization struct { type Organization struct {
ID uuid.UUID ID uuid.UUID
Name string Name string
CreatedAt time.Time Address string
UpdatedAt time.Time WalletSeed []byte
CreatedAt time.Time
UpdatedAt time.Time
} }

View File

@ -29,6 +29,8 @@ func NewService(
} }
func (s *ServiceImpl) Run(ctx context.Context) error { func (s *ServiceImpl) Run(ctx context.Context) error {
s.log.Info("starting blockd service 0w0")
errch := make(chan error) errch := make(chan error)
defer s.rest.Close() defer s.rest.Close()
@ -52,5 +54,5 @@ func (s *ServiceImpl) Run(ctx context.Context) error {
} }
func (s *ServiceImpl) Stop() { func (s *ServiceImpl) Stop() {
s.log.Info(">w< bye bye! :3")
} }

View File

@ -1,7 +1,80 @@
package organizations 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 { type OrganizationsInteractor interface {
Create(
ctx context.Context,
params CreateParams,
) (*models.Organization, error)
} }
type organizationsInteractor struct { 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
} }

View File

@ -18,8 +18,11 @@ var (
) )
type CreateParams struct { type CreateParams struct {
Name string
Email string
Phone string
Tg string
Mnemonic string Mnemonic string
IsAdmin bool
Activate bool Activate bool
} }
@ -66,7 +69,7 @@ func NewUsersInteractor(
func (i *usersInteractor) Create(ctx context.Context, params CreateParams) (*models.User, error) { func (i *usersInteractor) Create(ctx context.Context, params CreateParams) (*models.User, error) {
seed, err := hdwallet.NewSeedFromMnemonic(params.Mnemonic) seed, err := hdwallet.NewSeedFromMnemonic(params.Mnemonic)
if err != nil { 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( user := models.NewUser(
@ -76,6 +79,11 @@ func (i *usersInteractor) Create(ctx context.Context, params CreateParams) (*mod
time.Now(), 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 { if err = i.usersRepo.Create(ctx, user); err != nil {
return nil, fmt.Errorf("error create new user. %w", err) return nil, fmt.Errorf("error create new user. %w", err)
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"time"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/emochka2007/block-accounting/internal/pkg/models" "github.com/emochka2007/block-accounting/internal/pkg/models"
@ -15,11 +16,20 @@ type GetParams struct {
Ids uuid.UUIDs Ids uuid.UUIDs
} }
type AddParticipantParams struct {
OrganizationId uuid.UUID
UserId uuid.UUID
EmployeeId uuid.UUID
IsAdmin bool
}
type Repository interface { type Repository interface {
Create(ctx context.Context, org models.Organization) error Create(ctx context.Context, org models.Organization) error
Get(ctx context.Context, params GetParams) ([]*models.Organization, error) Get(ctx context.Context, params GetParams) ([]*models.Organization, error)
Update(ctx context.Context, org models.Organization) error Update(ctx context.Context, org models.Organization) error
Delete(ctx context.Context, id uuid.UUID) 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 { 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 { func (r *repositorySQL) Create(ctx context.Context, org models.Organization) error {
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) { if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
query := sq.Insert("organizations").Columns( query := sq.Insert("organizations").Columns(
"id, name", "id, name, address, wallet_seed, created_at, updated_at",
).Values( ).Values(
org.ID, org.ID,
org.Name, org.Name,
org.Address,
org.WalletSeed,
org.CreatedAt,
org.UpdatedAt,
) )
if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil { 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 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
}

View File

@ -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)) var users []*models.User = make([]*models.User, 0, len(params.Ids))
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) { 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"). From("users").
PlaceholderFormat(sq.Dollar) PlaceholderFormat(sq.Dollar)
@ -81,19 +81,40 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us
for rows.Next() { for rows.Next() {
var ( var (
id uuid.UUID id uuid.UUID
name string
email string
phone string
tg string
seed []byte seed []byte
//isAdmin bool //isAdmin bool
createdAt time.Time createdAt time.Time
activatedAt sql.NullTime 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) return fmt.Errorf("error scan row. %w", err)
} }
users = append(users, &models.User{ users = append(users, &models.User{
ID: id, ID: id,
Name: name,
Credentails: models.UserCredentials{
Email: email,
Phone: phone,
Telegram: tg,
},
Bip39Seed: seed, Bip39Seed: seed,
//Admin: isAdmin, //Admin: isAdmin,
CreatedAt: createdAt, 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 { func (r *repositorySQL) Create(ctx context.Context, user *models.User) error {
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) 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{ values := []any{
user.ID, user.ID,
user.Name,
user.Credentails.Email,
user.Credentails.Phone,
user.Credentails.Telegram,
user.Bip39Seed, user.Bip39Seed,
user.CreatedAt, user.CreatedAt,
} }

View File

@ -1,15 +1,17 @@
create table if not exists users ( create table if not exists users (
id uuid not null, id uuid primary key ,
name varchar(250), name varchar(250),
email varchar(200), email varchar(200),
phone varchar(16), phone varchar(16),
tg varchar(200), tg varchar(200),
seed bytea not null unique, seed bytea not null unique,
created_at timestamp default current_timestamp, created_at timestamp default current_timestamp,
activated_at timestamp default null, activated_at timestamp default null
primary key (id, seed)
); );
create index if not exists index_users_seed
on users using hash (seed);
create index if not exists index_users_name create index if not exists index_users_name
on users using hash (name); on users using hash (name);
@ -23,9 +25,10 @@ create index if not exists index_users_seed
on users using hash (seed); on users using hash (seed);
create table if not exists organizations ( create table if not exists organizations (
id uuid primary key, id uuid primary key unique,
name varchar(300) default 'My Organization' not null, 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, created_at timestamp default current_timestamp,
updated_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 create index if not exists index_organizations_id
on 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 ( create table organizations_users (
organization_id uuid not null, organization_id uuid not null references organizations(id),
user_id uuid not null, user_id uuid not null references users(id),
employee_id uuid references employees(id) default null,
added_at timestamp default current_timestamp, added_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp, updated_at timestamp default current_timestamp,
deleted_at timestamp default null,
is_admin bool default false, is_admin bool default false,
primary key(organization_id, user_id) 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 create index if not exists index_organizations_users_organization_id_user_id
on organizations_users (organization_id, user_id); on organizations_users (organization_id, user_id);
create table employees ( create index if not exists index_organizations_users_organization_id_employee_id
id uuid primary key, on organizations_users (organization_id, employee_id);
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_employees_id_organization_id
on employees (id, organization_id);
create table if not exists transactions ( create table if not exists transactions (
id uuid primary key, id uuid primary key,