This commit is contained in:
r8zavetr8v 2024-05-08 21:44:36 +03:00
parent a482e9d3f3
commit 36daecdd25
13 changed files with 158 additions and 41 deletions

View File

@ -5,7 +5,7 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "debug", "name": "blockd",
"type": "go", "type": "go",
"request": "launch", "request": "launch",
"mode": "auto", "mode": "auto",
@ -14,6 +14,7 @@
"-log-level=debug", "-log-level=debug",
"-log-local=true", "-log-local=true",
"-log-add-source=true", "-log-add-source=true",
"-jwt-secret=local_jwt_secret",
"-rest-address=localhost:8080", "-rest-address=localhost:8080",
"-db-host=localhost:8432", "-db-host=localhost:8432",

View File

@ -30,7 +30,8 @@ run.local: bin.build
-db-database=blockd \ -db-database=blockd \
-db-user=blockd \ -db-user=blockd \
-db-secret=blockd \ -db-secret=blockd \
-db-enable-tls=false -db-enable-tls=false \
-jwt-secret=local_jwt_secret
.PHONY: run.debug .PHONY: run.debug
run.debug: bin.build run.debug: bin.build

View File

@ -34,6 +34,9 @@ func main() {
Name: "log-add-source", Name: "log-add-source",
Value: true, Value: true,
}, },
&cli.StringFlag{
Name: "jwt-secret",
},
// rest // rest
&cli.StringFlag{ &cli.StringFlag{
@ -77,6 +80,7 @@ func main() {
LogLocal: c.Bool("log-local"), LogLocal: c.Bool("log-local"),
LogFile: c.String("log-file"), LogFile: c.String("log-file"),
LogAddSource: c.Bool("log-add-source"), LogAddSource: c.Bool("log-add-source"),
JWTSecret: []byte(c.String("jwt-secret")),
}, },
Rest: config.RestConfig{ Rest: config.RestConfig{
Address: c.String("rest-address"), Address: c.String("rest-address"),

View File

@ -0,0 +1,21 @@
package factory
import (
"log/slog"
"github.com/emochka2007/block-accounting/internal/pkg/config"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/jwt"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
urepo "github.com/emochka2007/block-accounting/internal/usecase/repository/users"
)
func provideUsersInteractor(
log *slog.Logger,
usersRepo urepo.Repository,
) users.UsersInteractor {
return users.NewUsersInteractor(log.WithGroup("users-interactor"), usersRepo)
}
func provideJWTInteractor(c config.Config) jwt.JWTInteractor {
return jwt.NewWardenJWT(c.Common.JWTSecret)
}

View File

@ -4,13 +4,22 @@ import (
"log/slog" "log/slog"
"os" "os"
"github.com/google/wire"
"github.com/emochka2007/block-accounting/internal/interface/rest" "github.com/emochka2007/block-accounting/internal/interface/rest"
"github.com/emochka2007/block-accounting/internal/interface/rest/controllers" "github.com/emochka2007/block-accounting/internal/interface/rest/controllers"
"github.com/emochka2007/block-accounting/internal/interface/rest/presenters" "github.com/emochka2007/block-accounting/internal/interface/rest/presenters"
"github.com/emochka2007/block-accounting/internal/pkg/config" "github.com/emochka2007/block-accounting/internal/pkg/config"
"github.com/emochka2007/block-accounting/internal/pkg/logger" "github.com/emochka2007/block-accounting/internal/pkg/logger"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/jwt"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users" "github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
urepo "github.com/emochka2007/block-accounting/internal/usecase/repository/users" )
var interfaceSet wire.ProviderSet = wire.NewSet(
provideAuthController,
provideControllers,
provideAuthPresenter,
) )
func provideLogger(c config.Config) *slog.Logger { func provideLogger(c config.Config) *slog.Logger {
@ -36,20 +45,31 @@ func provideLogger(c config.Config) *slog.Logger {
return lb.Build() return lb.Build()
} }
func provideAuthPresenter(
jwtInteractor jwt.JWTInteractor,
) presenters.AuthPresenter {
return presenters.NewAuthPresenter(jwtInteractor)
}
func provideAuthController(
log *slog.Logger,
usersInteractor users.UsersInteractor,
authPresenter presenters.AuthPresenter,
) controllers.AuthController {
return controllers.NewAuthController(
log.WithGroup("auth-controller"),
authPresenter,
usersInteractor,
)
}
func provideControllers( func provideControllers(
log *slog.Logger, log *slog.Logger,
usersRepo urepo.Repository, authController controllers.AuthController,
) *controllers.RootController { ) *controllers.RootController {
return &controllers.RootController{ return &controllers.RootController{
Ping: controllers.NewPingController(log.WithGroup("ping-controller")), Ping: controllers.NewPingController(log.WithGroup("ping-controller")),
Auth: controllers.NewAuthController( Auth: authController,
log.WithGroup("auth-controller"),
presenters.NewAuthPresenter(),
users.NewUsersInteractor(
log.WithGroup("users-interactor"),
usersRepo,
),
),
} }
} }

View File

@ -11,9 +11,11 @@ import (
func ProvideService(c config.Config) (service.Service, func(), error) { func ProvideService(c config.Config) (service.Service, func(), error) {
wire.Build( wire.Build(
provideUsersRepository,
provideLogger, provideLogger,
provideControllers, provideUsersRepository,
provideUsersInteractor,
provideJWTInteractor,
interfaceSet,
provideRestServer, provideRestServer,
service.NewService, service.NewService,
) )

View File

@ -19,7 +19,11 @@ func ProvideService(c config.Config) (service.Service, func(), error) {
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
rootController := provideControllers(logger, repository) usersInteractor := provideUsersInteractor(logger, repository)
jwtInteractor := provideJWTInteractor(c)
authPresenter := provideAuthPresenter(jwtInteractor)
authController := provideAuthController(logger, usersInteractor, authPresenter)
rootController := provideControllers(logger, authController)
server := provideRestServer(logger, rootController, c) server := provideRestServer(logger, rootController, c)
serviceService := service.NewService(logger, server) serviceService := service.NewService(logger, server)
return serviceService, func() { return serviceService, func() {

View File

@ -10,6 +10,7 @@ import (
"github.com/emochka2007/block-accounting/internal/interface/rest/presenters" "github.com/emochka2007/block-accounting/internal/interface/rest/presenters"
"github.com/emochka2007/block-accounting/internal/pkg/bip32" "github.com/emochka2007/block-accounting/internal/pkg/bip32"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/jwt"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users" "github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
) )
@ -28,6 +29,7 @@ type authController struct {
log *slog.Logger log *slog.Logger
presenter presenters.AuthPresenter presenter presenters.AuthPresenter
usersInteractor users.UsersInteractor usersInteractor users.UsersInteractor
jwtInteractor jwt.JWTInteractor
} }
func NewAuthController( func NewAuthController(
@ -45,27 +47,32 @@ func NewAuthController(
func (c *authController) Join(w http.ResponseWriter, req *http.Request) error { func (c *authController) Join(w http.ResponseWriter, req *http.Request) error {
request, err := c.presenter.CreateJoinRequest(req) request, err := c.presenter.CreateJoinRequest(req)
if err != nil { if err != nil {
return fmt.Errorf("error create join request. %w", err) return c.presenter.ResponseJoin(
w, 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 !bip32.IsMnemonicValid(request.Mnemonic) { if !bip32.IsMnemonicValid(request.Mnemonic) {
return fmt.Errorf("error invalid mnemonic. %w", ErrorAuthInvalidMnemonic) return c.presenter.ResponseJoin(
w, 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()
if _, err = c.usersInteractor.Create(ctx, users.CreateParams{ user, err := c.usersInteractor.Create(ctx, users.CreateParams{
Mnemonic: request.Mnemonic, Mnemonic: request.Mnemonic,
IsAdmin: true, IsAdmin: true,
Activate: true, Activate: true,
}); err != nil { })
return fmt.Errorf("error create new user. %w", err) if err != nil {
return c.presenter.ResponseJoin(w, nil, fmt.Errorf("error create new user. %w", err))
} }
return nil return c.presenter.ResponseJoin(w, user, nil)
} }
func (c *authController) JoinWithInvite(w http.ResponseWriter, req *http.Request) error { func (c *authController) JoinWithInvite(w http.ResponseWriter, req *http.Request) error {

View File

@ -9,6 +9,27 @@ type JoinRequest struct {
Mnemonic string `json:"mnemonic"` Mnemonic string `json:"mnemonic"`
} }
type JoinResponse struct {
Ok bool `json:"ok"`
Token string `json:"token,omitempty"`
Error *Error `json:"error,omitempty"`
}
type LoginRequest struct {
Mnemonc string `json:"mnemonic"`
}
type LoginResponse struct {
Ok bool `json:"ok"`
Token string `json:"token,omitempty"`
Error *Error `json:"error,omitempty"`
}
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
func BuildRequest[T any](data []byte) (*T, error) { func BuildRequest[T any](data []byte) (*T, error) {
var req T var req T

View File

@ -5,19 +5,28 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"time"
"github.com/emochka2007/block-accounting/internal/interface/rest/domain" "github.com/emochka2007/block-accounting/internal/interface/rest/domain"
"github.com/emochka2007/block-accounting/internal/pkg/models"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/jwt"
) )
type AuthPresenter interface { type AuthPresenter interface {
CreateJoinRequest(r *http.Request) (*domain.JoinRequest, error) CreateJoinRequest(r *http.Request) (*domain.JoinRequest, error)
// ResponseJoin(w http.ResponseWriter, mnemonic string) error ResponseJoin(w http.ResponseWriter, user *models.User, err error) error
} }
type authPresenter struct{} type authPresenter struct {
jwtInteractor jwt.JWTInteractor
}
func NewAuthPresenter() AuthPresenter { func NewAuthPresenter(
return &authPresenter{} jwtInteractor jwt.JWTInteractor,
) AuthPresenter {
return &authPresenter{
jwtInteractor: jwtInteractor,
}
} }
func (p *authPresenter) CreateJoinRequest(r *http.Request) (*domain.JoinRequest, error) { func (p *authPresenter) CreateJoinRequest(r *http.Request) (*domain.JoinRequest, error) {
@ -37,17 +46,29 @@ func (p *authPresenter) CreateJoinRequest(r *http.Request) (*domain.JoinRequest,
return &request, nil return &request, nil
} }
// func (p *authPresenter) ResponseJoin(w http.ResponseWriter, mnemonic string) error { func (p *authPresenter) ResponseJoin(w http.ResponseWriter, user *models.User, err error) error {
// out, err := json.Marshal(domain.JoinResponse{ resp := new(domain.JoinResponse)
// Mnemonic: mnemonic,
// })
// if err != nil {
// return fmt.Errorf("error marshal join response. %w", err)
// }
// if _, err = w.Write(out); err != nil { if err != nil {
// return fmt.Errorf("error write response. %w", err) // todo map error
// } } else {
token, err := p.jwtInteractor.NewToken(user, 24*time.Hour)
if err != nil {
return fmt.Errorf("error create access token. %w", err)
}
// return nil resp.Ok = true
// } resp.Token = token
}
out, err := json.Marshal(resp)
if err != nil {
return 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
}

View File

@ -12,6 +12,8 @@ type CommonConfig struct {
LogLocal bool LogLocal bool
LogFile string LogFile string
LogAddSource bool LogAddSource bool
JWTSecret []byte
} }
type RestConfig struct { type RestConfig struct {

View File

@ -6,7 +6,6 @@ import (
"github.com/emochka2007/block-accounting/internal/pkg/models" "github.com/emochka2007/block-accounting/internal/pkg/models"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users" "github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
"github.com/emochka2007/block-accounting/internal/usecase/repository/transactions" "github.com/emochka2007/block-accounting/internal/usecase/repository/transactions"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -32,5 +31,7 @@ type smartContractInteractor struct {
// todo // todo
func (s *smartContractInteractor) SignTransaction(ctx context.Context, params SignTransactionParams) error { func (s *smartContractInteractor) SignTransaction(ctx context.Context, params SignTransactionParams) error {
s.client.CallContract(ctx, ethereum.CallMsg{}, nil) // s.client.CallContract(ctx, ethereum.CallMsg{}, nil)
return nil
} }

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"
@ -21,7 +22,7 @@ type GetParams struct {
type Repository interface { type Repository interface {
Get(ctx context.Context, params GetParams) (*models.User, error) Get(ctx context.Context, params GetParams) (*models.User, error)
Create(ctx context.Context, user *models.User) error Create(ctx context.Context, user *models.User) error
Activate(ctx context.Context, id string) error Activate(ctx context.Context, id uuid.UUID) error
Update(ctx context.Context, user *models.User) error Update(ctx context.Context, user *models.User) error
Delete(ctx context.Context, id string) error Delete(ctx context.Context, id string) error
} }
@ -89,8 +90,19 @@ func (r *repositorySQL) Create(ctx context.Context, user *models.User) error {
return nil return nil
} }
func (r *repositorySQL) Activate(ctx context.Context, id string) error { func (r *repositorySQL) Activate(ctx context.Context, id uuid.UUID) 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 {
query := sq.Update("users").
SetMap(sq.Eq{
"activated_at": time.Now(),
}).
Where(sq.Eq{
"id": id,
})
if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil {
return fmt.Errorf("error mark user as activated in database. %w", err)
}
return nil return nil
}); err != nil { }); err != nil {