Skip to content

Commit 2e624e8

Browse files
committed
feat: base app config
1 parent f73892b commit 2e624e8

File tree

11 files changed

+186
-43
lines changed

11 files changed

+186
-43
lines changed

cmd/server/config.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package main
2+
3+
import (
4+
"connectrpc.com/connect"
5+
"context"
6+
configurationv1 "github.com/ft-t/go-money-pb/gen/gomoneypb/configuration/v1"
7+
"github.com/ft-t/go-money-pb/gen/gomoneypb/configuration/v1/accountsv1connect"
8+
"github.com/ft-t/go-money/pkg/boilerplate"
9+
)
10+
11+
type ConfigApi struct {
12+
configSvc ConfigSvc
13+
}
14+
15+
func NewConfigApi(
16+
mux *boilerplate.DefaultGrpcServer,
17+
userSvc ConfigSvc,
18+
) (*ConfigApi, error) {
19+
res := &ConfigApi{
20+
configSvc: userSvc,
21+
}
22+
23+
mux.GetMux().Handle(
24+
accountsv1connect.NewConfigurationServiceHandler(res, mux.GetDefaultHandlerOptions()...),
25+
)
26+
27+
return res, nil
28+
}
29+
30+
func (a *ConfigApi) GetConfiguration(
31+
ctx context.Context,
32+
c *connect.Request[configurationv1.GetConfigurationRequest],
33+
) (*connect.Response[configurationv1.GetConfigurationResponse], error) {
34+
res, err := a.configSvc.GetConfiguration(ctx, c.Msg)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
return connect.NewResponse(res), nil
40+
}

cmd/server/interfaces.go

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"context"
5+
configurationv1 "github.com/ft-t/go-money-pb/gen/gomoneypb/configuration/v1"
56
usersv1 "github.com/ft-t/go-money-pb/gen/gomoneypb/users/v1"
67
)
78

@@ -10,4 +11,16 @@ type UserSvc interface {
1011
ctx context.Context,
1112
req *usersv1.LoginRequest,
1213
) (*usersv1.LoginResponse, error)
14+
15+
Create(
16+
ctx context.Context,
17+
req *usersv1.CreateRequest,
18+
) (*usersv1.CreateResponse, error)
19+
}
20+
21+
type ConfigSvc interface {
22+
GetConfiguration(
23+
ctx context.Context,
24+
_ *configurationv1.GetConfigurationRequest,
25+
) (*configurationv1.GetConfigurationResponse, error)
1326
}

cmd/server/main.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"context"
5+
"github.com/ft-t/go-money/pkg/appcfg"
56
"github.com/ft-t/go-money/pkg/boilerplate"
67
"github.com/ft-t/go-money/pkg/configuration"
78
"github.com/ft-t/go-money/pkg/jwt"
@@ -46,18 +47,28 @@ func main() {
4647
log.Logger.Fatal().Err(err).Msg("failed to create user handler")
4748
}
4849

50+
_, err = NewConfigApi(grpcServer, appcfg.NewService(&appcfg.ServiceConfig{
51+
UserSvc: userService,
52+
}))
53+
if err != nil {
54+
log.Logger.Fatal().Err(err).Msg("failed to create config handler")
55+
}
56+
4957
go func() {
5058
grpcServer.ServeAsync(config.GrpcPort)
51-
sg := <-sig
5259

53-
log.Logger.Info().Msgf("GOT SIGNAL %v", sg.String())
54-
log.Logger.Info().Msgf("[Graceful Shutdown] GOT SIGNAL %v", sg.String())
60+
log.Logger.Info().Msgf("server started on port %v", config.GrpcPort)
61+
}()
5562

56-
log.Logger.Info().Msgf("[Graceful Shutdown] Shutting down webservers")
63+
sg := <-sig
5764

58-
cancel()
59-
_ = grpcServer.Shutdown(context.TODO())
65+
log.Logger.Info().Msgf("GOT SIGNAL %v", sg.String())
66+
log.Logger.Info().Msgf("[Graceful Shutdown] GOT SIGNAL %v", sg.String())
6067

61-
log.Logger.Info().Msg("[Graceful Shutdown] Exit")
62-
}()
68+
log.Logger.Info().Msgf("[Graceful Shutdown] Shutting down webservers")
69+
70+
cancel()
71+
_ = grpcServer.Shutdown(context.TODO())
72+
73+
log.Logger.Info().Msg("[Graceful Shutdown] Exit")
6374
}

cmd/server/user.go

+21-12
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,6 @@ type UserApi struct {
1212
userSvc UserSvc
1313
}
1414

15-
func (u *UserApi) Login(
16-
ctx context.Context,
17-
c *connect.Request[usersv1.LoginRequest],
18-
) (*connect.Response[usersv1.LoginResponse], error) {
19-
resp, err := u.userSvc.Login(ctx, c.Msg)
20-
if err != nil {
21-
return nil, err
22-
}
23-
24-
return connect.NewResponse(resp), nil
25-
}
26-
2715
func NewUserApi(
2816
mux *boilerplate.DefaultGrpcServer,
2917
userSvc UserSvc,
@@ -38,3 +26,24 @@ func NewUserApi(
3826

3927
return res, nil
4028
}
29+
30+
func (u *UserApi) Create(ctx context.Context, c *connect.Request[usersv1.CreateRequest]) (*connect.Response[usersv1.CreateResponse], error) {
31+
resp, err := u.userSvc.Create(ctx, c.Msg)
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
return connect.NewResponse(resp), nil
37+
}
38+
39+
func (u *UserApi) Login(
40+
ctx context.Context,
41+
c *connect.Request[usersv1.LoginRequest],
42+
) (*connect.Response[usersv1.LoginResponse], error) {
43+
resp, err := u.userSvc.Login(ctx, c.Msg)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
return connect.NewResponse(resp), nil
49+
}

compose/.env.example

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ GRPC_PORT=52055
33
PUBLIC_PORT=52055
44

55
## APP
6-
DB_HOST=postgres
6+
DB_HOST=db
77
DB_PORT=5432
88
DB_USER=money
99
DB_PASSWORD=XcteUSxT2wP68ZW$

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
connectrpc.com/grpcreflect v1.3.0
88
github.com/cockroachdb/errors v1.11.3
99
github.com/dgrijalva/jwt-go v3.2.0+incompatible
10-
github.com/ft-t/go-money-pb v0.0.0-20250223145139-c11de6abad88
10+
github.com/ft-t/go-money-pb v0.0.0-20250223204332-c94ffb0ded76
1111
github.com/golang-jwt/jwt/v5 v5.2.1
1212
github.com/hashicorp/golang-lru/v2 v2.0.7
1313
github.com/jackc/pgx/v5 v5.5.5

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
1515
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1616
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
1717
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
18-
github.com/ft-t/go-money-pb v0.0.0-20250223145139-c11de6abad88 h1:ibW3U5QQOZJhRef50Kq5GD2hCbqWNLXEZTLPyyI86mQ=
19-
github.com/ft-t/go-money-pb v0.0.0-20250223145139-c11de6abad88/go.mod h1:PI8ulxDJ0HamtFdQhvfOTJDla0blE6tJX9EuMw1IrzU=
18+
github.com/ft-t/go-money-pb v0.0.0-20250223204332-c94ffb0ded76 h1:RA9M/xgDWq4p7lf800g9G/36kzz53A1h4jVkxOtJkIg=
19+
github.com/ft-t/go-money-pb v0.0.0-20250223204332-c94ffb0ded76/go.mod h1:PI8ulxDJ0HamtFdQhvfOTJDla0blE6tJX9EuMw1IrzU=
2020
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
2121
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
2222
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=

pkg/appcfg/interfaces.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package appcfg
2+
3+
import "context"
4+
5+
type UserSvc interface {
6+
ShouldCreateAdmin(ctx context.Context) (bool, error)
7+
}

pkg/appcfg/service.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package appcfg
2+
3+
import (
4+
"context"
5+
configurationv1 "github.com/ft-t/go-money-pb/gen/gomoneypb/configuration/v1"
6+
)
7+
8+
type Service struct {
9+
cfg *ServiceConfig
10+
}
11+
12+
type ServiceConfig struct {
13+
UserSvc UserSvc
14+
}
15+
16+
func NewService(
17+
cfg *ServiceConfig,
18+
) *Service {
19+
return &Service{
20+
cfg: cfg,
21+
}
22+
}
23+
24+
func (s *Service) GetConfiguration(
25+
ctx context.Context,
26+
_ *configurationv1.GetConfigurationRequest,
27+
) (*configurationv1.GetConfigurationResponse, error) {
28+
shouldCreatedAdmin, err := s.cfg.UserSvc.ShouldCreateAdmin(ctx)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
return &configurationv1.GetConfigurationResponse{
34+
ShouldCreateAdmin: shouldCreatedAdmin,
35+
}, nil
36+
}

pkg/boilerplate/grpc.go

+1-19
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"golang.org/x/net/http2"
1212
"golang.org/x/net/http2/h2c"
1313
"net/http"
14-
"net/http/pprof"
15-
"strings"
1614
"time"
1715
)
1816

@@ -90,20 +88,6 @@ func (d *DefaultGrpcServer) GetDefaultHandlerOptions() []connect.HandlerOption {
9088
return options
9189
}
9290

93-
func (d *DefaultGrpcServer) addProfiler() {
94-
mux := d.GetMux()
95-
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
96-
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
97-
mux.HandleFunc("/debug/pprof/", func(w http.ResponseWriter, r *http.Request) {
98-
name := strings.TrimPrefix(r.URL.Path, "/debug/pprof/")
99-
if handler := pprof.Handler(name); handler != nil {
100-
handler.ServeHTTP(w, r)
101-
} else {
102-
http.NotFound(w, r)
103-
}
104-
})
105-
}
106-
10791
func (d *DefaultGrpcServer) ServeAsync(grpcPort int) {
10892
grpcWebAddress := fmt.Sprintf("0.0.0.0:%d", grpcPort)
10993

@@ -116,9 +100,7 @@ func (d *DefaultGrpcServer) ServeAsync(grpcPort int) {
116100
grpcreflect.NewStaticReflector(d.reflectionServices...),
117101
))
118102
}
119-
120-
d.addProfiler()
121-
103+
122104
go func() {
123105
d.srv = &http.Server{
124106
Addr: grpcWebAddress,

pkg/users/users.go

+45
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,51 @@ func NewService(
2727
}
2828
}
2929

30+
func (s *Service) ShouldCreateAdmin(ctx context.Context) (bool, error) {
31+
db := database.FromContext(ctx, database.GetDb(database.DbTypeReadonly))
32+
33+
var count int64
34+
if err := db.Model(&database.User{}).Count(&count).Error; err != nil {
35+
return false, err
36+
}
37+
38+
return count == 0, nil
39+
}
40+
41+
func (s *Service) Create(
42+
ctx context.Context,
43+
req *usersv1.CreateRequest,
44+
) (*usersv1.CreateResponse, error) {
45+
db := database.FromContext(ctx, database.GetDb(database.DbTypeMaster))
46+
47+
shouldCreate, err := s.ShouldCreateAdmin(ctx)
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
if !shouldCreate {
53+
return nil, errors.New("admin already exists")
54+
}
55+
56+
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), 5)
57+
if err != nil {
58+
return nil, err
59+
}
60+
61+
targetUser := &database.User{
62+
Login: req.Login,
63+
Password: string(hashedPassword),
64+
}
65+
66+
if err = db.Create(targetUser).Error; err != nil {
67+
return nil, err
68+
}
69+
70+
return &usersv1.CreateResponse{
71+
Id: targetUser.ID,
72+
}, nil
73+
}
74+
3075
func (s *Service) Login(
3176
ctx context.Context,
3277
req *usersv1.LoginRequest,

0 commit comments

Comments
 (0)