mirror of
https://github.com/emo2007/block-accounting.git
synced 2025-01-18 07:26:27 +00:00
tmp
This commit is contained in:
parent
4c17f84b7e
commit
43dbe185ea
@ -17,6 +17,7 @@ import (
|
||||
|
||||
type ParticipantsController interface {
|
||||
List(w http.ResponseWriter, r *http.Request) ([]byte, error)
|
||||
New(w http.ResponseWriter, r *http.Request) ([]byte, error)
|
||||
}
|
||||
|
||||
type participantsController struct {
|
||||
@ -92,3 +93,30 @@ func (c *participantsController) List(w http.ResponseWriter, r *http.Request) ([
|
||||
|
||||
return c.presenter.ResponseListParticipants(ctx, participants)
|
||||
}
|
||||
|
||||
func (c *participantsController) New(w http.ResponseWriter, r *http.Request) ([]byte, error) {
|
||||
req, err := presenters.CreateRequest[domain.AddEmployeeRequest](r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error build list participants request. %w", err)
|
||||
}
|
||||
|
||||
organizationID, err := ctxmeta.OrganizationId(r.Context())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetch organization id from context. %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
participant, err := c.orgInteractor.AddParticipant(ctx, organizations.AddParticipantParams{
|
||||
OrganizationID: organizationID,
|
||||
Name: req.Name,
|
||||
Position: req.Position,
|
||||
WalletAddress: req.WalletAddress,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error create new participant. %w", err)
|
||||
}
|
||||
|
||||
return c.presenter.ResponseParticipant(ctx, participant)
|
||||
}
|
||||
|
@ -16,6 +16,10 @@ type ParticipantsPresenter interface {
|
||||
ctx context.Context,
|
||||
participants []models.OrganizationParticipant,
|
||||
) ([]byte, error)
|
||||
ResponseParticipant(
|
||||
ctx context.Context,
|
||||
participant models.OrganizationParticipant,
|
||||
) ([]byte, error)
|
||||
}
|
||||
|
||||
type participantsPresenter struct{}
|
||||
@ -116,3 +120,20 @@ func (p *participantsPresenter) ResponseListParticipants(
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p *participantsPresenter) ResponseParticipant(
|
||||
ctx context.Context,
|
||||
participant models.OrganizationParticipant,
|
||||
) ([]byte, error) {
|
||||
r, err := p.responseParticipant(ctx, participant)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error map participant to hal resource. %w", err)
|
||||
}
|
||||
|
||||
out, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshal organization create response. %w", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
@ -103,9 +103,7 @@ func (s *Server) buildRouter() {
|
||||
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"))
|
||||
|
||||
// Deprecated??
|
||||
r.Route("/transactions", func(r chi.Router) {
|
||||
r.Get("/", s.handle(s.controllers.Transactions.List, "tx_list"))
|
||||
r.Post("/", s.handle(s.controllers.Transactions.New, "new_tx"))
|
||||
@ -115,8 +113,19 @@ func (s *Server) buildRouter() {
|
||||
)
|
||||
})
|
||||
|
||||
r.Route("/payout", func(r chi.Router) {
|
||||
r.Post("/", nil)
|
||||
r.Route("/payrolls", func(r chi.Router) {
|
||||
r.Get("/", nil) // list payrolls
|
||||
r.Post("/", nil) // deploy contract
|
||||
})
|
||||
|
||||
r.Route("/multisig", func(r chi.Router) {
|
||||
r.Post("/", nil) // new multisig (deploy)
|
||||
r.Get("/", nil) // list
|
||||
})
|
||||
|
||||
r.Route("/license", func(r chi.Router) {
|
||||
r.Get("/", nil) // list license
|
||||
r.Post("/", nil) // deploy contract
|
||||
})
|
||||
|
||||
// join via invite link
|
||||
@ -124,19 +133,21 @@ func (s *Server) buildRouter() {
|
||||
|
||||
r.Route("/participants", func(r chi.Router) {
|
||||
r.Get("/", s.handle(s.controllers.Participants.List, "participants_list"))
|
||||
r.Post("/", nil)
|
||||
|
||||
r.Put("/{participant_id}", nil) // update user / employee
|
||||
r.Delete("/{participant_id}", nil) // remove user / employee
|
||||
// generate new invite link
|
||||
r.Post("/invite", s.handle(s.controllers.Auth.Invite, "invite"))
|
||||
|
||||
r.Post("/", nil) // add {employee}
|
||||
|
||||
r.Post("/invite", s.handle(s.controllers.Auth.Invite, "invite")) // generate new invite link
|
||||
})
|
||||
|
||||
r.Route("/multisig", func(r chi.Router) {
|
||||
r.Post("/", nil) // new multisig
|
||||
r.Get("/", nil) // list
|
||||
r.Route("/{participant_id}", func(r chi.Router) {
|
||||
r.Put("/", nil) // update user / employee
|
||||
r.Delete("/", nil) // remove user / employee
|
||||
|
||||
r.Route("/payroll", func(r chi.Router) {
|
||||
r.Post("/", nil) // set salary
|
||||
r.Put("/", nil) // edit
|
||||
r.Get("/", nil)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,10 +1,10 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
Common CommonConfig
|
||||
Rest RestConfig
|
||||
DB DBConfig
|
||||
Eth EthConfig
|
||||
Common CommonConfig
|
||||
Rest RestConfig
|
||||
DB DBConfig
|
||||
ChainAPI ChainAPIConfig
|
||||
}
|
||||
|
||||
type CommonConfig struct {
|
||||
@ -35,6 +35,6 @@ type DBConfig struct {
|
||||
CacheSecret string
|
||||
}
|
||||
|
||||
type EthConfig struct {
|
||||
// todo
|
||||
type ChainAPIConfig struct {
|
||||
Host string
|
||||
}
|
||||
|
@ -1,24 +1,128 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/emochka2007/block-accounting/internal/pkg/config"
|
||||
"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/common"
|
||||
)
|
||||
|
||||
type ChainInteractor interface {
|
||||
NewMultisig(ctx context.Context, params NewMultisigParams) error
|
||||
}
|
||||
|
||||
type chainInteractor struct {
|
||||
log *slog.Logger
|
||||
config config.Config
|
||||
txRepository transactions.Repository
|
||||
usersInteractor users.UsersInteractor
|
||||
}
|
||||
|
||||
func NewChainInteractor(
|
||||
log *slog.Logger,
|
||||
config config.Config,
|
||||
txRepository transactions.Repository,
|
||||
usersInteractor users.UsersInteractor,
|
||||
) ChainInteractor {
|
||||
return &chainInteractor{
|
||||
log: log,
|
||||
config: config,
|
||||
txRepository: txRepository,
|
||||
usersInteractor: usersInteractor,
|
||||
}
|
||||
}
|
||||
|
||||
type NewMultisigParams struct {
|
||||
OwnersPKs []string
|
||||
OwnersPKs []string
|
||||
Confirmations int
|
||||
}
|
||||
|
||||
func (i *chainInteractor) NewMultisig(ctx context.Context) {
|
||||
func (i *chainInteractor) NewMultisig(ctx context.Context, params NewMultisigParams) error {
|
||||
deployAddr := i.config.ChainAPI.Host + "/multi-sig/deploy"
|
||||
|
||||
i.log.Debug(
|
||||
"deploy multisig",
|
||||
slog.String("endpoint", deployAddr),
|
||||
slog.Any("params", params),
|
||||
)
|
||||
|
||||
requestBody, err := json.Marshal(map[string]any{
|
||||
"owners": params.OwnersPKs,
|
||||
"confirmations": params.Confirmations,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshal request body. %w", err)
|
||||
}
|
||||
|
||||
body := bytes.NewBuffer(requestBody)
|
||||
|
||||
doneCh := make(chan struct{})
|
||||
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
resp, err := http.Post(http.MethodPost, deployAddr, body)
|
||||
if err != nil {
|
||||
i.log.Error(
|
||||
"error send deploy multisig request",
|
||||
slog.String("endpoint", deployAddr),
|
||||
slog.Any("params", params),
|
||||
)
|
||||
|
||||
errCh <- fmt.Errorf("error build new multisig request. %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
i.log.Debug(
|
||||
"deploy multisig response",
|
||||
slog.Int("code", resp.StatusCode),
|
||||
)
|
||||
|
||||
if _, ok := <-doneCh; ok {
|
||||
doneCh <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-doneCh:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (i *chainInteractor) PubKey(ctx context.Context, user *models.User) (*ecdsa.PublicKey, error) {
|
||||
hex := common.Bytes2Hex(user.Seed())
|
||||
|
||||
pubAddr := i.config.ChainAPI.Host + "/address/" + hex
|
||||
|
||||
doneCh := make(chan struct{})
|
||||
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
resp, err := http.Get(pubAddr)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-doneCh:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/emochka2007/block-accounting/internal/pkg/models"
|
||||
"github.com/emochka2007/block-accounting/internal/usecase/repository/cache"
|
||||
"github.com/emochka2007/block-accounting/internal/usecase/repository/organizations"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@ -55,22 +56,12 @@ type ParticipantsParams struct {
|
||||
}
|
||||
|
||||
type OrganizationsInteractor interface {
|
||||
Create(
|
||||
ctx context.Context,
|
||||
params CreateParams,
|
||||
) (*models.Organization, error)
|
||||
List(
|
||||
ctx context.Context,
|
||||
params ListParams,
|
||||
) (*ListResponse, error)
|
||||
Participant(
|
||||
ctx context.Context,
|
||||
params ParticipantParams,
|
||||
) (models.OrganizationParticipant, error)
|
||||
Participants(
|
||||
ctx context.Context,
|
||||
params ParticipantsParams,
|
||||
) ([]models.OrganizationParticipant, error)
|
||||
Create(ctx context.Context, params CreateParams) (*models.Organization, error)
|
||||
List(ctx context.Context, params ListParams) (*ListResponse, error)
|
||||
|
||||
Participant(ctx context.Context, params ParticipantParams) (models.OrganizationParticipant, error)
|
||||
Participants(ctx context.Context, params ParticipantsParams) ([]models.OrganizationParticipant, error)
|
||||
AddParticipant(ctx context.Context, params AddParticipantParams) (models.OrganizationParticipant, error)
|
||||
}
|
||||
|
||||
type organizationsInteractor struct {
|
||||
@ -308,3 +299,57 @@ func (i *organizationsInteractor) Participants(
|
||||
|
||||
return participants, nil
|
||||
}
|
||||
|
||||
type AddParticipantParams struct {
|
||||
OrganizationID uuid.UUID
|
||||
EmployeeUserID uuid.UUID
|
||||
Name string
|
||||
Position string
|
||||
WalletAddress string
|
||||
}
|
||||
|
||||
func (i *organizationsInteractor) AddParticipant(
|
||||
ctx context.Context,
|
||||
params AddParticipantParams,
|
||||
) (models.OrganizationParticipant, error) {
|
||||
user, err := ctxmeta.User(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetch user from context. %w", err)
|
||||
}
|
||||
|
||||
actor, err := i.Participant(ctx, ParticipantParams{
|
||||
ID: user.Id(),
|
||||
OrganizationID: params.OrganizationID,
|
||||
ActiveOnly: true,
|
||||
UsersOnly: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetch actor. %w", err)
|
||||
}
|
||||
|
||||
if !actor.IsOwner() {
|
||||
return nil, fmt.Errorf("error actor not an owner")
|
||||
}
|
||||
|
||||
if !common.IsHexAddress(params.WalletAddress) {
|
||||
return nil, fmt.Errorf("error invalid address")
|
||||
}
|
||||
|
||||
participantID := uuid.Must(uuid.NewV7())
|
||||
|
||||
empl := models.Employee{
|
||||
ID: participantID,
|
||||
EmployeeName: params.Name,
|
||||
UserID: params.EmployeeUserID,
|
||||
OrganizationId: params.OrganizationID,
|
||||
WalletAddress: common.FromHex(params.WalletAddress),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err = i.orgRepository.AddEmployee(ctx, empl); err != nil {
|
||||
return nil, fmt.Errorf("error add new employee. %w", err)
|
||||
}
|
||||
|
||||
return &empl, nil
|
||||
}
|
||||
|
@ -87,6 +87,10 @@ func (i *usersInteractor) Create(ctx context.Context, params CreateParams) (*mod
|
||||
Telegram: params.Tg,
|
||||
}
|
||||
|
||||
// TODO fetch user PK from chain-api
|
||||
|
||||
user.PK = []byte{0x01}
|
||||
|
||||
if err = i.usersRepo.Create(ctx, user); err != nil {
|
||||
return nil, fmt.Errorf("error create new user. %w", err)
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ type Repository interface {
|
||||
Participants(ctx context.Context, params ParticipantsParams) ([]models.OrganizationParticipant, error)
|
||||
CreateAndAdd(ctx context.Context, org models.Organization, user *models.User) error
|
||||
DeleteParticipant(ctx context.Context, params DeleteParticipantParams) error
|
||||
AddEmployee(ctx context.Context, employee models.Employee) error
|
||||
}
|
||||
|
||||
type repositorySQL struct {
|
||||
@ -650,3 +651,41 @@ func (r *repositorySQL) fetchEmployees(
|
||||
|
||||
return employees, nil
|
||||
}
|
||||
|
||||
func (r *repositorySQL) AddEmployee(ctx context.Context, employee models.Employee) error {
|
||||
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) error {
|
||||
query := sq.Insert("employees").Columns(
|
||||
"id",
|
||||
"user_id",
|
||||
"organization_id",
|
||||
"wallet_address",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
).Values(
|
||||
employee.ID,
|
||||
employee.UserID,
|
||||
employee.OrganizationId,
|
||||
employee.WalletAddress,
|
||||
employee.CreatedAt,
|
||||
employee.UpdatedAt,
|
||||
)
|
||||
|
||||
if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil {
|
||||
return fmt.Errorf("error add employee. %w", err)
|
||||
}
|
||||
|
||||
if err := r.AddParticipant(ctx, AddParticipantParams{
|
||||
OrganizationId: employee.OrganizationId,
|
||||
UserId: employee.UserID,
|
||||
EmployeeId: employee.ID,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error add employee to organization. %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return 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))
|
||||
|
||||
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
|
||||
query := sq.Select("u.id, u.name, u.email, u.phone, u.tg, u.seed, u.created_at, u.activated_at").
|
||||
query := sq.Select("u.id, u.name, u.email, u.phone, u.tg, u.seed, u.created_at, u.activated_at, u.public_key").
|
||||
From("users as u").
|
||||
PlaceholderFormat(sq.Dollar)
|
||||
|
||||
@ -95,6 +95,7 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us
|
||||
tg string
|
||||
|
||||
seed []byte
|
||||
pk []byte
|
||||
//isAdmin bool
|
||||
createdAt time.Time
|
||||
activatedAt sql.NullTime
|
||||
@ -109,6 +110,7 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us
|
||||
&seed,
|
||||
&createdAt,
|
||||
&activatedAt,
|
||||
&pk,
|
||||
); err != nil {
|
||||
return fmt.Errorf("error scan row. %w", err)
|
||||
}
|
||||
@ -122,6 +124,7 @@ func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Us
|
||||
Telegram: tg,
|
||||
},
|
||||
Bip39Seed: seed,
|
||||
PK: pk,
|
||||
//Admin: isAdmin,
|
||||
CreatedAt: createdAt,
|
||||
Activated: activatedAt.Valid,
|
||||
@ -138,7 +141,7 @@ 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", "name", "email", "phone", "tg", "seed", "created_at"}
|
||||
columns := []string{"id", "name", "email", "phone", "tg", "seed", "public_key", "created_at"}
|
||||
|
||||
values := []any{
|
||||
user.ID,
|
||||
@ -147,6 +150,7 @@ func (r *repositorySQL) Create(ctx context.Context, user *models.User) error {
|
||||
user.Credentails.Phone,
|
||||
user.Credentails.Telegram,
|
||||
user.Bip39Seed,
|
||||
user.PK,
|
||||
user.CreatedAt,
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ create table multisigs (
|
||||
|
||||
create table multisig_owners (
|
||||
multisig_id uuid references multisigs(id),
|
||||
owner_id uuid references organizations_users(user_id),
|
||||
owner_id uuid references users(id),
|
||||
created_at timestamp default current_timestamp,
|
||||
updated_at timestamp default current_timestamp,
|
||||
primary key (multisig_id, owner_id)
|
||||
@ -166,7 +166,7 @@ create table multisig_owners (
|
||||
|
||||
create table multisig_confirmations (
|
||||
multisig_id uuid references multisigs(id),
|
||||
owner_id uuid references organizations_users(user_id),
|
||||
owner_id uuid references users(id),
|
||||
created_at timestamp default current_timestamp,
|
||||
primary key (multisig_id, owner_id)
|
||||
);
|
Loading…
Reference in New Issue
Block a user