Skip to content

Commit f25b2f0

Browse files
committed
feat: users
1 parent 2e624e8 commit f25b2f0

File tree

14 files changed

+462
-100
lines changed

14 files changed

+462
-100
lines changed

go.mod

+7-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ require (
88
github.com/cockroachdb/errors v1.11.3
99
github.com/dgrijalva/jwt-go v3.2.0+incompatible
1010
github.com/ft-t/go-money-pb v0.0.0-20250223204332-c94ffb0ded76
11+
github.com/go-gormigrate/gormigrate/v2 v2.1.3
1112
github.com/golang-jwt/jwt/v5 v5.2.1
13+
github.com/golang/mock v1.6.0
1214
github.com/hashicorp/golang-lru/v2 v2.0.7
1315
github.com/jackc/pgx/v5 v5.5.5
1416
github.com/jinzhu/now v1.1.5
@@ -20,19 +22,20 @@ require (
2022
github.com/samber/lo v1.49.1
2123
github.com/sethvargo/go-envconfig v1.1.1
2224
github.com/shopspring/decimal v1.4.0
25+
github.com/stretchr/testify v1.10.0
2326
golang.org/x/crypto v0.34.0
2427
golang.org/x/net v0.23.0
2528
google.golang.org/protobuf v1.36.5
2629
gorm.io/driver/postgres v1.5.11
27-
gorm.io/gorm v1.25.10
30+
gorm.io/gorm v1.25.12
2831
)
2932

3033
require (
3134
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
3235
github.com/cockroachdb/redact v1.1.5 // indirect
36+
github.com/davecgh/go-spew v1.1.1 // indirect
3337
github.com/getsentry/sentry-go v0.27.0 // indirect
3438
github.com/gogo/protobuf v1.3.2 // indirect
35-
github.com/golang/mock v1.6.0 // indirect
3639
github.com/jackc/pgpassfile v1.0.0 // indirect
3740
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
3841
github.com/jackc/puddle/v2 v2.2.1 // indirect
@@ -41,8 +44,10 @@ require (
4144
github.com/kr/text v0.2.0 // indirect
4245
github.com/mattn/go-colorable v0.1.13 // indirect
4346
github.com/mattn/go-isatty v0.0.19 // indirect
47+
github.com/pmezard/go-difflib v1.0.0 // indirect
4448
github.com/rogpeppe/go-internal v1.13.1 // indirect
4549
golang.org/x/sync v0.11.0 // indirect
4650
golang.org/x/sys v0.30.0 // indirect
4751
golang.org/x/text v0.22.0 // indirect
52+
gopkg.in/yaml.v3 v3.0.1 // indirect
4853
)

go.sum

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK
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=
2323
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
24+
github.com/go-gormigrate/gormigrate/v2 v2.1.3 h1:ei3Vq/rpPI/jCJY9mRHJAKg5vU+EhZyWhBAkaAomQuw=
25+
github.com/go-gormigrate/gormigrate/v2 v2.1.3/go.mod h1:VJ9FIOBAur+NmQ8c4tDVwOuiJcgupTG105FexPFrXzA=
2426
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
2527
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
2628
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -144,5 +146,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
144146
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
145147
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
146148
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
147-
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
148-
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
149+
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
150+
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=

pkg/boilerplate/types.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package boilerplate
22

33
type DbConfig struct {
4-
Host string `env:"HOST"`
4+
Host string `env:"HOST, default=localhost"`
55
Port int `env:"PORT, default=5432"`
6-
Db string `env:"DB"`
7-
User string `env:"USER"`
6+
Db string `env:"DB, default=money"`
7+
User string `env:"USER, default=money"`
88
Password string `env:"PASSWORD"`
99
MaxIdleConnections int `env:"MAX_IDLE_CONNECTIONS"`
1010
MaxConnectionLifetimeSec int `env:"MAX_CONNECTION_LIFETIME_SEC"`

pkg/configuration/types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import "github.com/ft-t/go-money/pkg/boilerplate"
55
type Configuration struct {
66
Db boilerplate.DbConfig `env:", prefix=DB_"`
77
ReadOnlyDb boilerplate.DbConfig `env:", prefix=READONLY_DB_"`
8-
GrpcPort int `env:"GRPC_PORT"`
8+
GrpcPort int `env:"GRPC_PORT, default=52055"`
99
JwtPrivateKey string `env:"JWT_PRIVATE_KEY"`
1010
}

pkg/database/db.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import (
44
"context"
55
"github.com/ft-t/go-money/pkg/boilerplate"
66
"github.com/ft-t/go-money/pkg/configuration"
7-
"github.com/ft-t/go-money/pkg/testing"
7+
"github.com/ft-t/go-money/pkg/testingutils"
8+
"github.com/go-gormigrate/gormigrate/v2"
89
"github.com/rs/zerolog/log"
910
"gorm.io/gorm"
1011
)
@@ -27,7 +28,7 @@ func init() {
2728
log.Info().Msg("setup postgres database")
2829

2930
if boilerplate.GetCurrentEnvironment() == boilerplate.Ci {
30-
if err := testing.EnsurePostgresDbExists(config.Db); err != nil {
31+
if err := testingutils.EnsurePostgresDbExists(config.Db); err != nil {
3132
panic(err)
3233
}
3334
}
@@ -41,13 +42,13 @@ func init() {
4142
masterGormDb = mainDb
4243
readonlyGormDb = masterGormDb
4344

44-
//migrations := GetMigrations()
45-
//if len(migrations) > 0 {
46-
// log.Info().Msg("[Db] start migrations")
47-
// if err = gormigrate.New(mainDb, gormigrate.DefaultOptions, migrations).Migrate(); err != nil {
48-
// panic(err)
49-
// }
50-
//}
45+
migrations := getMigrations()
46+
if len(migrations) > 0 {
47+
log.Info().Msg("[Db] start migrations")
48+
if err = gormigrate.New(mainDb, gormigrate.DefaultOptions, migrations).Migrate(); err != nil {
49+
panic(err)
50+
}
51+
}
5152
}
5253

5354
func GetDb(t DbType) *gorm.DB {

pkg/database/migrations.go

+31
Original file line numberDiff line numberDiff line change
@@ -1 +1,32 @@
11
package database
2+
3+
import (
4+
"github.com/ft-t/go-money/pkg/boilerplate"
5+
"github.com/go-gormigrate/gormigrate/v2"
6+
"gorm.io/gorm"
7+
)
8+
9+
func getMigrations() []*gormigrate.Migration {
10+
return []*gormigrate.Migration{
11+
{
12+
ID: "2025-02-24-InitialUsers",
13+
Migrate: func(db *gorm.DB) error {
14+
return boilerplate.ExecuteSql(db,
15+
`create table if not exists users
16+
(
17+
id serial not null
18+
constraint users_pk
19+
primary key,
20+
login text not null,
21+
password text not null,
22+
created_at timestamp not null,
23+
deleted_at timestamp
24+
);`,
25+
`create unique index if not exists users_login_uindex
26+
on public.users (login)
27+
where deleted_at is null;`,
28+
)
29+
},
30+
},
31+
}
32+
}

pkg/database/user.go

+8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
package database
22

3+
import (
4+
"gorm.io/gorm"
5+
"time"
6+
)
7+
38
type User struct {
49
ID int32
510
Login string
611
Password string
12+
13+
CreatedAt time.Time
14+
DeletedAt gorm.DeletedAt
715
}

pkg/testing/gorm.go

-65
This file was deleted.

pkg/testingutils/gorm.go

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package testingutils
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/ft-t/go-money/pkg/boilerplate"
7+
"github.com/jackc/pgx/v5"
8+
"github.com/juju/fslock"
9+
"github.com/rs/zerolog/log"
10+
"os"
11+
"path"
12+
"strings"
13+
)
14+
15+
func getLock() *fslock.Lock {
16+
return fslock.New(path.Join(os.TempDir(), "go_fs_lock"))
17+
}
18+
19+
func FlushAllTables(config boilerplate.DbConfig) error {
20+
return flushInternal(config, nil)
21+
}
22+
23+
func ensureThatItsLocal(config boilerplate.DbConfig) {
24+
allowedHosts := []string{"localhost", "127.0.0.1", "postgres"}
25+
26+
for _, h := range allowedHosts {
27+
if strings.EqualFold(h, config.Host) {
28+
return
29+
}
30+
}
31+
32+
if strings.HasPrefix(config.Host, "192.168.") { // home network
33+
return
34+
}
35+
36+
panic("host is not allowed, please check config for postgres")
37+
}
38+
39+
func flushInternal(config boilerplate.DbConfig, tables []string) error {
40+
ensureThatItsLocal(config)
41+
42+
rawStr, _ := boilerplate.GetDbConnectionString(config)
43+
44+
conn, err := pgx.Connect(context.Background(), rawStr)
45+
46+
if err != nil {
47+
return err
48+
}
49+
50+
defer conn.Close(context.Background())
51+
52+
res, err := conn.Query(context.Background(), "SELECT table_schema, table_name FROM information_schema.tables where table_schema != 'pg_catalog' and table_schema != 'information_schema';")
53+
54+
if err != nil {
55+
return err
56+
}
57+
58+
existing := map[string]bool{}
59+
60+
for res.Next() {
61+
values := res.RawValues()
62+
63+
resultPath := fmt.Sprintf("%v.%v", string(values[0]), string(values[1]))
64+
existing[resultPath] = true
65+
}
66+
67+
builder := strings.Builder{}
68+
69+
builder.WriteString("BEGIN TRANSACTION; ")
70+
71+
if tables != nil {
72+
for _, table := range tables {
73+
if strings.ToLower(table) == "public.migrations" {
74+
continue
75+
}
76+
77+
if _, ok := existing[strings.ToLower(table)]; ok {
78+
79+
builder.WriteString(fmt.Sprintf(" truncate table %v CASCADE; ", strings.ToLower(table)))
80+
} else {
81+
log.Warn().Msgf("table %v does not exists", table)
82+
}
83+
}
84+
} else {
85+
for name := range existing {
86+
if strings.ToLower(name) == "public.migrations" {
87+
continue
88+
}
89+
builder.WriteString(fmt.Sprintf(" truncate table %v CASCADE; ", strings.ToLower(name)))
90+
}
91+
}
92+
93+
builder.WriteString(" COMMIT;")
94+
95+
_, err = conn.Exec(context.Background(), builder.String())
96+
97+
if err != nil {
98+
return err
99+
}
100+
101+
return nil
102+
}
103+
104+
func EnsurePostgresDbExists(config boilerplate.DbConfig) error {
105+
lock := getLock()
106+
107+
if err := lock.Lock(); err != nil {
108+
return err
109+
}
110+
111+
defer func() {
112+
_ = lock.Unlock()
113+
}()
114+
115+
oldDbName := config.Db
116+
117+
config.Db = "postgres"
118+
rawStr, _ := boilerplate.GetDbConnectionString(config)
119+
120+
conn, err := pgx.Connect(context.Background(), rawStr)
121+
config.Db = oldDbName
122+
123+
if err != nil {
124+
return err
125+
}
126+
127+
defer conn.Close(context.Background())
128+
129+
r, err := conn.Query(context.Background(), fmt.Sprintf("SELECT * FROM pg_database WHERE datname='%v'", oldDbName))
130+
131+
if err != nil {
132+
return err
133+
}
134+
135+
r.Next()
136+
137+
exists := true
138+
139+
if len(r.RawValues()) == 0 {
140+
exists = false
141+
}
142+
143+
if !exists {
144+
_, err = conn.Exec(context.Background(), fmt.Sprintf("CREATE DATABASE %v;", oldDbName))
145+
146+
if err != nil {
147+
return err
148+
}
149+
}
150+
151+
return nil
152+
}

pkg/users/interfaces.go

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"github.com/ft-t/go-money/pkg/database"
66
)
77

8+
//go:generate mockgen -destination interfaces_mocks_test.go -package users_test -source=interfaces.go
9+
810
type JwtSvc interface {
911
GenerateToken(
1012
_ context.Context,

0 commit comments

Comments
 (0)