mirror of
https://github.com/emo2007/block-accounting.git
synced 2025-04-12 08:56:28 +00:00
tmp
This commit is contained in:
parent
e800b9e572
commit
1f680142f0
@ -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:
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user