Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ go 1.24.1
require (
github.com/Masterminds/squirrel v1.5.4
github.com/fatih/color v1.18.0
github.com/georgysavva/scany/v2 v2.1.4
github.com/jackc/pgx/v5 v5.7.4
github.com/joho/godotenv v1.5.1
github.com/pkg/errors v0.9.1
golang.org/x/crypto v0.37.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.36.6
Expand Down
16 changes: 14 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs=
github.com/cockroachdb/cockroach-go/v2 v2.2.0/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/georgysavva/scany/v2 v2.1.4 h1:nrzHEJ4oQVRoiKmocRqA1IyGOmM/GQOEsg9UjMR5Ip4=
github.com/georgysavva/scany/v2 v2.1.4/go.mod h1:fqp9yHZzM/PFVa3/rYEC57VmDx+KDch0LoqrJzkvtos=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
Expand All @@ -21,19 +27,25 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
Expand Down
1 change: 1 addition & 0 deletions internal/api/user/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Service interface {
Update(ctx context.Context, id int64, name *string, email *string) error
Delete(ctx context.Context, id int64) error
}

type Implementation struct {
desc.UnimplementedAuthV1Server
userService Service
Expand Down
6 changes: 3 additions & 3 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,19 @@ func (a *App) initGRPC(ctx context.Context) error {
reflection.Register(a.grpcServer)

desc.RegisterAuthV1Server(a.grpcServer, a.serviceProvider.UserAPIImpl(ctx))
a.serviceProvider.cls.Add(func() error {
a.serviceProvider.GetCloser().Add(func() error {
a.grpcServer.GracefulStop()
return nil
})
return nil
}

func (a *App) runGRPCServer() error {
a.serviceProvider.Log().Info("server listening", slog.String("Addr", a.serviceProvider.GRPCConfig().Address()))
a.serviceProvider.GetLog().Info("server listening", slog.String("Addr", a.serviceProvider.GRPCConfig().Address()))

l, err := net.Listen("tcp", a.serviceProvider.GRPCConfig().Address())
if err != nil {
a.serviceProvider.Log().Error("failed to listen", sl.Err(err))
a.serviceProvider.GetLog().Error("failed to listen", sl.Err(err))
return err
}

Expand Down
94 changes: 54 additions & 40 deletions internal/app/service_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import (
"os"
"syscall"

pgClient "github.com/Str1m/auth/internal/client/db/postgres"
"github.com/Str1m/auth/internal/client/db/transaction"
modelService "github.com/Str1m/auth/internal/model"
"github.com/Str1m/auth/internal/service/user"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"

userAPI "github.com/Str1m/auth/internal/api/user"
"github.com/Str1m/auth/internal/closer"
"github.com/Str1m/auth/internal/config/env"
"github.com/Str1m/auth/internal/lib/logger/handlers/slogpretty"
modelService "github.com/Str1m/auth/internal/model"
"github.com/Str1m/auth/internal/service/user"
"github.com/Str1m/auth/internal/storage/users/postgres"
"github.com/jackc/pgx/v5/pgxpool"
)

const (
Expand All @@ -23,11 +27,12 @@ const (
envProd = "prod"
)

type Storage interface {
Create(ctx context.Context, info *modelService.UserInfo, hashedPassword []byte) (int64, error)
Get(ctx context.Context, id int64) (*modelService.User, error)
Update(ctx context.Context, id int64, name, email *string) error
Delete(ctx context.Context, id int64) error
type StorageConfig interface {
DSN() string
}

type GRPCConfig interface {
Address() string
}

type Service interface {
Expand All @@ -37,23 +42,31 @@ type Service interface {
Delete(ctx context.Context, id int64) error
}

type StorageConfig interface {
DSN() string
type Storage interface {
Create(ctx context.Context, info *modelService.UserInfo, hashedPassword []byte) (int64, error)
Get(ctx context.Context, id int64) (*modelService.User, error)
Update(ctx context.Context, id int64, name, email *string) error
Delete(ctx context.Context, id int64) error
}

type GRPCConfig interface {
Address() string
type Transactor interface {
BeginTx(ctx context.Context, txOptions pgx.TxOptions) (pgx.Tx, error)
}

type TxManager interface {
ReadCommitted(ctx context.Context, f pgClient.Handler) error
}

type ServiceProvider struct {
cls *closer.Closer
log *slog.Logger

repoConfig StorageConfig
grpcConfig GRPCConfig
storageConfig StorageConfig
grpcConfig GRPCConfig

pgPool *pgxpool.Pool
userRepository Storage
dbClient *pgClient.ClientPG
txManager *transaction.TxManager
dbLayer Storage

userService Service
userAPI *userAPI.Implementation
Expand All @@ -63,15 +76,15 @@ func newServiceProvider() *ServiceProvider {
return &ServiceProvider{}
}

func (s *ServiceProvider) Closer() *closer.Closer {
func (s *ServiceProvider) GetCloser() *closer.Closer {
if s.cls == nil {
s.cls = closer.New(os.Interrupt, syscall.SIGTERM)
}

return s.cls
}

func (s *ServiceProvider) Log() *slog.Logger {
func (s *ServiceProvider) GetLog() *slog.Logger {
if s.log == nil {
env := os.Getenv("ENV")
switch env {
Expand All @@ -90,15 +103,15 @@ func (s *ServiceProvider) Log() *slog.Logger {
return s.log
}

func (s *ServiceProvider) RepoConfig() StorageConfig {
if s.repoConfig == nil {
func (s *ServiceProvider) GetStorageConfig() StorageConfig {
if s.storageConfig == nil {
cfg, err := env.NewPGConfig()
if err != nil {
log.Fatalf("failed to get pg config: %s", err.Error())
}
s.repoConfig = cfg
s.storageConfig = cfg
}
return s.repoConfig
return s.storageConfig
}

func (s *ServiceProvider) GRPCConfig() GRPCConfig {
Expand All @@ -112,35 +125,36 @@ func (s *ServiceProvider) GRPCConfig() GRPCConfig {
return s.grpcConfig
}

func (s *ServiceProvider) PGPool(ctx context.Context) *pgxpool.Pool {
if s.pgPool == nil {
pool, err := pgxpool.New(ctx, s.RepoConfig().DSN())
func (s *ServiceProvider) GetDBClient(ctx context.Context) *pgClient.ClientPG {
if s.dbClient == nil {
p, err := pgxpool.New(ctx, s.GetStorageConfig().DSN())
if err != nil {
log.Fatalf("failed to connect to database: %s", err.Error())
}
if err = pool.Ping(ctx); err != nil {
log.Fatalf("ping error: %s", err.Error())
log.Fatalln("err")
}
s.Closer().Add(func() error {
pool.Close()
return nil
})
s.pgPool = pool

s.dbClient = pgClient.NewClient(p)
}
return s.pgPool

return s.dbClient
}

func (s *ServiceProvider) UserRepository(ctx context.Context) Storage {
if s.userRepository == nil {
s.userRepository = postgres.NewRepository(s.PGPool(ctx))
func (s *ServiceProvider) GetTxManager(ctx context.Context) *transaction.TxManager {
if s.txManager == nil {
s.txManager = transaction.NewTransactionManager(s.GetDBClient(ctx))
}
return s.txManager
}

return s.userRepository
func (s *ServiceProvider) GetDBLayer(ctx context.Context) Storage {
if s.dbLayer == nil {
s.dbLayer = postgres.NewStoragePG(s.GetDBClient(ctx))
}
return s.dbLayer
}

func (s *ServiceProvider) UserService(ctx context.Context) Service {
if s.userService == nil {
s.userService = user.NewService(s.Log(), s.UserRepository(ctx))
s.userService = user.NewService(s.GetLog(), s.GetDBLayer(ctx), s.GetTxManager(ctx))
}
return s.userService
}
Expand Down
88 changes: 88 additions & 0 deletions internal/client/db/postgres/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package postgres

import (
"context"

"github.com/georgysavva/scany/v2/pgxscan"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
)

type Handler func(ctx context.Context) error

type Query struct {
Name string
QueryRaw string
}
type key string

const (
TxKey key = "tx"
)

type ClientPG struct {
db *pgxpool.Pool
}

func NewClient(db *pgxpool.Pool) *ClientPG {
return &ClientPG{db: db}
}

func (p *ClientPG) GetDB() *pgxpool.Pool {
return p.db
}

func (p *ClientPG) ExecContext(ctx context.Context, q Query, args ...any) (pgconn.CommandTag, error) {
tx, ok := ctx.Value(TxKey).(pgx.Tx)
if ok {
return tx.Exec(ctx, q.QueryRaw, args...)
}
return p.db.Exec(ctx, q.QueryRaw, args...)
}

func (p *ClientPG) QueryContext(ctx context.Context, q Query, args ...any) (pgx.Rows, error) {
tx, ok := ctx.Value(TxKey).(pgx.Tx)
if ok {
return tx.Query(ctx, q.QueryRaw, args...)
}
return p.db.Query(ctx, q.QueryRaw, args...)
}

func (p *ClientPG) QueryRowContext(ctx context.Context, q Query, args ...any) pgx.Row {
tx, ok := ctx.Value(TxKey).(pgx.Tx)
if ok {
return tx.QueryRow(ctx, q.QueryRaw, args...)
}

return p.db.QueryRow(ctx, q.QueryRaw, args...)
}

func (p *ClientPG) Ping(ctx context.Context) error {
return p.db.Ping(ctx)
}

func (p *ClientPG) Close() {
p.db.Close()
}

func (p *ClientPG) ScanOneContext(ctx context.Context, dest any, q Query, args ...any) error {
row, err := p.QueryContext(ctx, q, args...)
if err != nil {
return err
}
return pgxscan.ScanOne(dest, row)
}

func (p *ClientPG) ScanAllContext(ctx context.Context, dest any, q Query, args ...any) error {
row, err := p.QueryContext(ctx, q, args...)
if err != nil {
return err
}

return pgxscan.ScanAll(dest, row)
}

func MakeContextTx(ctx context.Context, tx pgx.Tx) context.Context {
return context.WithValue(ctx, TxKey, tx)
}
Loading