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

View File

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

View File

@ -34,6 +34,9 @@ func main() {
Name: "log-add-source",
Value: true,
},
&cli.StringFlag{
Name: "jwt-secret",
},
// rest
&cli.StringFlag{
@ -77,6 +80,7 @@ func main() {
LogLocal: c.Bool("log-local"),
LogFile: c.String("log-file"),
LogAddSource: c.Bool("log-add-source"),
JWTSecret: []byte(c.String("jwt-secret")),
},
Rest: config.RestConfig{
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"
"os"
"github.com/google/wire"
"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/presenters"
"github.com/emochka2007/block-accounting/internal/pkg/config"
"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"
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 {
@ -36,20 +45,31 @@ func provideLogger(c config.Config) *slog.Logger {
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(
log *slog.Logger,
usersRepo urepo.Repository,
authController controllers.AuthController,
) *controllers.RootController {
return &controllers.RootController{
Ping: controllers.NewPingController(log.WithGroup("ping-controller")),
Auth: controllers.NewAuthController(
log.WithGroup("auth-controller"),
presenters.NewAuthPresenter(),
users.NewUsersInteractor(
log.WithGroup("users-interactor"),
usersRepo,
),
),
Auth: authController,
}
}

View File

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

View File

@ -19,7 +19,11 @@ func ProvideService(c config.Config) (service.Service, func(), error) {
if err != nil {
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)
serviceService := service.NewService(logger, server)
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/pkg/bip32"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/jwt"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
)
@ -28,6 +29,7 @@ type authController struct {
log *slog.Logger
presenter presenters.AuthPresenter
usersInteractor users.UsersInteractor
jwtInteractor jwt.JWTInteractor
}
func NewAuthController(
@ -45,27 +47,32 @@ func NewAuthController(
func (c *authController) Join(w http.ResponseWriter, req *http.Request) error {
request, err := c.presenter.CreateJoinRequest(req)
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))
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)
defer cancel()
if _, err = c.usersInteractor.Create(ctx, users.CreateParams{
user, err := c.usersInteractor.Create(ctx, users.CreateParams{
Mnemonic: request.Mnemonic,
IsAdmin: 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 {

View File

@ -9,6 +9,27 @@ type JoinRequest struct {
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) {
var req T

View File

@ -5,19 +5,28 @@ import (
"fmt"
"io"
"net/http"
"time"
"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 {
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 {
return &authPresenter{}
func NewAuthPresenter(
jwtInteractor jwt.JWTInteractor,
) AuthPresenter {
return &authPresenter{
jwtInteractor: jwtInteractor,
}
}
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
}
// func (p *authPresenter) ResponseJoin(w http.ResponseWriter, mnemonic string) error {
// out, err := json.Marshal(domain.JoinResponse{
// Mnemonic: mnemonic,
// })
// if err != nil {
// return fmt.Errorf("error marshal join response. %w", err)
// }
func (p *authPresenter) ResponseJoin(w http.ResponseWriter, user *models.User, err error) error {
resp := new(domain.JoinResponse)
// if _, err = w.Write(out); err != nil {
// return fmt.Errorf("error write response. %w", err)
// }
if err != nil {
// 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
LogFile string
LogAddSource bool
JWTSecret []byte
}
type RestConfig struct {

View File

@ -6,7 +6,6 @@ import (
"github.com/emochka2007/block-accounting/internal/pkg/models"
"github.com/emochka2007/block-accounting/internal/usecase/interactors/users"
"github.com/emochka2007/block-accounting/internal/usecase/repository/transactions"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/google/uuid"
)
@ -32,5 +31,7 @@ type smartContractInteractor struct {
// todo
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"
"database/sql"
"fmt"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/emochka2007/block-accounting/internal/pkg/models"
@ -21,7 +22,7 @@ type GetParams struct {
type Repository interface {
Get(ctx context.Context, params GetParams) (*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
Delete(ctx context.Context, id string) error
}
@ -89,8 +90,19 @@ func (r *repositorySQL) Create(ctx context.Context, user *models.User) error {
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 {
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
}); err != nil {