Files
sendico/api/server/internal/server/accountapiimp/service.go
Stephan D 1ab7f2e7d3
Some checks failed
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
+signup +email availability check
2025-11-17 18:00:38 +01:00

216 lines
7.8 KiB
Go

package accountapiimp
import (
"context"
"fmt"
"os"
"strings"
"time"
chaingatewayclient "github.com/tech/sendico/chain/gateway/client"
api "github.com/tech/sendico/pkg/api/http"
"github.com/tech/sendico/pkg/auth"
"github.com/tech/sendico/pkg/db/account"
"github.com/tech/sendico/pkg/db/organization"
"github.com/tech/sendico/pkg/db/policy"
"github.com/tech/sendico/pkg/db/refreshtokens"
"github.com/tech/sendico/pkg/db/transaction"
"github.com/tech/sendico/pkg/domainprovider"
"github.com/tech/sendico/pkg/messaging"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/mservice"
gatewayv1 "github.com/tech/sendico/pkg/proto/chain/gateway/v1"
"github.com/tech/sendico/server/interface/accountservice"
eapi "github.com/tech/sendico/server/interface/api"
"github.com/tech/sendico/server/interface/services/fileservice"
mutil "github.com/tech/sendico/server/internal/mutil/param"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.uber.org/zap"
)
type AccountAPI struct {
logger mlogger.Logger
db account.DB
odb organization.DB
tf transaction.Factory
rtdb refreshtokens.DB
plcdb policy.DB
domain domainprovider.DomainProvider
avatars mservice.MicroService
producer messaging.Producer
pmanager auth.Manager
enf auth.Enforcer
oph mutil.ParamHelper
aph mutil.ParamHelper
tph mutil.ParamHelper
accountsPermissionRef primitive.ObjectID
accService accountservice.AccountService
chainGateway chainWalletClient
chainAsset *gatewayv1.Asset
}
type chainWalletClient interface {
CreateManagedWallet(ctx context.Context, req *gatewayv1.CreateManagedWalletRequest) (*gatewayv1.CreateManagedWalletResponse, error)
Close() error
}
func (a *AccountAPI) Name() mservice.Type {
return mservice.Accounts
}
func (a *AccountAPI) Finish(ctx context.Context) error {
if err := a.avatars.Finish(ctx); err != nil {
return err
}
if a.chainGateway != nil {
if err := a.chainGateway.Close(); err != nil {
a.logger.Warn("Failed to close chain gateway client", zap.Error(err))
}
}
return nil
}
func CreateAPI(a eapi.API) (*AccountAPI, error) {
p := new(AccountAPI)
p.logger = a.Logger().Named(p.Name())
var err error
if p.db, err = a.DBFactory().NewAccountDB(); err != nil {
p.logger.Error("Failed to create accounts database", zap.Error(err))
return nil, err
}
if p.rtdb, err = a.DBFactory().NewRefreshTokensDB(); err != nil {
p.logger.Error("Failed to create refresh tokens database", zap.Error(err))
return nil, err
}
if p.odb, err = a.DBFactory().NewOrganizationDB(); err != nil {
p.logger.Error("Failed to create organizations database", zap.Error(err))
return nil, err
}
if p.plcdb, err = a.DBFactory().NewPoliciesDB(); err != nil {
p.logger.Error("Failed to create policies database", zap.Error(err))
return nil, err
}
p.domain = a.DomainProvider()
p.producer = a.Register().Messaging().Producer()
p.tf = a.DBFactory().TransactionFactory()
p.pmanager = a.Permissions().Manager()
p.enf = a.Permissions().Enforcer()
p.oph = mutil.CreatePH(mservice.Organizations)
p.aph = mutil.CreatePH(mservice.Accounts)
p.tph = mutil.CreatePH("token")
if p.accService, err = accountservice.NewAccountService(p.logger, a.DBFactory(), p.enf, p.pmanager.Role(), &a.Config().Mw.Password); err != nil {
p.logger.Error("Failed to create account manager", zap.Error(err))
return nil, err
}
// Account related api endpoints
a.Register().Handler(mservice.Accounts, "/signup", api.Post, p.signup)
a.Register().Handler(mservice.Accounts, "/signup/availability", api.Get, p.signupAvailability)
a.Register().AccountHandler(mservice.Accounts, "", api.Put, p.updateProfile)
a.Register().AccountHandler(mservice.Accounts, "", api.Get, p.getProfile)
a.Register().AccountHandler(mservice.Accounts, p.oph.AddRef("/employee"), api.Put, p.updateEmployee)
a.Register().AccountHandler(mservice.Accounts, "/dzone", api.Get, p.dzone)
a.Register().AccountHandler(mservice.Accounts, p.oph.AddRef("/profile"), api.Delete, p.deleteProfile)
a.Register().AccountHandler(mservice.Accounts, p.oph.AddRef("/organization"), api.Delete, p.deleteOrganization)
a.Register().AccountHandler(mservice.Accounts, p.oph.AddRef("/all"), api.Delete, p.deleteAll)
a.Register().AccountHandler(mservice.Accounts, p.oph.AddRef("/list"), api.Get, p.getEmployees)
a.Register().AccountHandler(mservice.Accounts, "/password", api.Post, p.checkPassword)
a.Register().AccountHandler(mservice.Accounts, "/password", api.Patch, p.changePassword)
a.Register().Handler(mservice.Accounts, "/password", api.Put, p.forgotPassword)
a.Register().Handler(mservice.Accounts, p.tph.AddRef(p.aph.AddRef("/password/reset")), api.Post, p.resetPassword)
a.Register().Handler(mservice.Accounts, mutil.AddToken("/verify"), api.Get, p.verify)
a.Register().Handler(mservice.Accounts, "/email", api.Post, p.resendVerificationMail)
a.Register().Handler(mservice.Accounts, "/email", api.Put, p.resendVerification)
if p.avatars, err = fileservice.CreateAPI(a, p.Name()); err != nil {
p.logger.Error("Failed to create image server", zap.Error(err))
return nil, err
}
accountsPolicy, err := a.Permissions().GetPolicyDescription(context.Background(), mservice.Accounts)
if err != nil {
p.logger.Warn("Failed to fetch account permission policy description", zap.Error(err))
return nil, err
}
p.accountsPermissionRef = accountsPolicy.ID
if err := p.initChainGateway(a.Config()); err != nil {
p.logger.Error("Failed to initialize chain gateway client", zap.Error(err))
return nil, err
}
return p, nil
}
func (a *AccountAPI) initChainGateway(cfg *eapi.Config) error {
if cfg == nil || cfg.ChainGateway == nil {
return fmt.Errorf("chain gateway configuration is not provided")
}
address := strings.TrimSpace(os.Getenv(cfg.ChainGateway.AddressEnv))
if address == "" {
return fmt.Errorf("chain gateway address env %s is empty", cfg.ChainGateway.AddressEnv)
}
clientCfg := chaingatewayclient.Config{
Address: address,
DialTimeout: time.Duration(cfg.ChainGateway.DialTimeoutSeconds) * time.Second,
CallTimeout: time.Duration(cfg.ChainGateway.CallTimeoutSeconds) * time.Second,
Insecure: cfg.ChainGateway.Insecure,
}
client, err := chaingatewayclient.New(context.Background(), clientCfg)
if err != nil {
return err
}
asset, err := buildGatewayAsset(cfg.ChainGateway.DefaultAsset)
if err != nil {
_ = client.Close()
return err
}
a.chainGateway = client
a.chainAsset = asset
return nil
}
func buildGatewayAsset(cfg eapi.ChainGatewayAssetConfig) (*gatewayv1.Asset, error) {
chain, err := parseChainNetwork(cfg.Chain)
if err != nil {
return nil, err
}
tokenSymbol := strings.TrimSpace(cfg.TokenSymbol)
if tokenSymbol == "" {
return nil, fmt.Errorf("chain gateway token symbol is required")
}
return &gatewayv1.Asset{
Chain: chain,
TokenSymbol: strings.ToUpper(tokenSymbol),
ContractAddress: strings.ToLower(strings.TrimSpace(cfg.ContractAddress)),
}, nil
}
func parseChainNetwork(value string) (gatewayv1.ChainNetwork, error) {
switch strings.ToUpper(strings.TrimSpace(value)) {
case "ETHEREUM_MAINNET", "CHAIN_NETWORK_ETHEREUM_MAINNET":
return gatewayv1.ChainNetwork_CHAIN_NETWORK_ETHEREUM_MAINNET, nil
case "ARBITRUM_ONE", "CHAIN_NETWORK_ARBITRUM_ONE":
return gatewayv1.ChainNetwork_CHAIN_NETWORK_ARBITRUM_ONE, nil
case "OTHER_EVM", "CHAIN_NETWORK_OTHER_EVM":
return gatewayv1.ChainNetwork_CHAIN_NETWORK_OTHER_EVM, nil
case "", "CHAIN_NETWORK_UNSPECIFIED":
return gatewayv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED, fmt.Errorf("chain network must be specified")
default:
return gatewayv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED, fmt.Errorf("unsupported chain network %s", value)
}
}