Skip to content

Commit 25d5b91

Browse files
Merge branch 'main' of https://github.com/joshsoftware/code-curiosity-2025 into fix/login-timestamp-type
2 parents cbd8dbf + 17d917d commit 25d5b91

File tree

5 files changed

+203
-24
lines changed

5 files changed

+203
-24
lines changed

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ require (
1212

1313
require (
1414
github.com/BurntSushi/toml v1.2.1 // indirect
15+
github.com/golang-migrate/migrate/v4 v4.18.3 // indirect
1516
github.com/google/go-cmp v0.6.0 // indirect
17+
github.com/hashicorp/errwrap v1.1.0 // indirect
18+
github.com/hashicorp/go-multierror v1.1.1 // indirect
1619
github.com/joho/godotenv v1.5.1 // indirect
1720
github.com/kr/pretty v0.3.1 // indirect
21+
go.uber.org/atomic v1.11.0 // indirect
1822
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
1923
gopkg.in/yaml.v3 v3.0.1 // indirect
2024
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect

go.sum

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4
33
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
44
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
55
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
67
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
78
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
89
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
910
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
11+
github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs=
12+
github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY=
1013
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
1114
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
15+
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
16+
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
17+
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
18+
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
19+
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
1220
github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
1321
github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
1422
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
@@ -27,8 +35,12 @@ github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
2735
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
2836
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
2937
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
30-
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
38+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3139
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
40+
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
41+
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
42+
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
43+
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
3244
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
3345
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
3446
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/db/migrate.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"log/slog"
7+
"os"
8+
"time"
9+
10+
"github.com/golang-migrate/migrate/v4"
11+
_ "github.com/golang-migrate/migrate/v4/database/postgres"
12+
_ "github.com/golang-migrate/migrate/v4/source/file"
13+
"github.com/joshsoftware/code-curiosity-2025/internal/config"
14+
)
15+
16+
var (
17+
// mainMigrationsDIR defines the directory where all migration files are located
18+
mainMigrationsDIR = "./internal/db/migrations"
19+
20+
// mainMigrationFilesPath defines path for migration files
21+
mainMigrationFilesPath = "file://" + mainMigrationsDIR
22+
)
23+
24+
// Migration used to define migrations
25+
type Migration struct {
26+
m *migrate.Migrate
27+
directoryName string
28+
filesPath string
29+
}
30+
31+
// InitMainDBMigrations used to initialize migrations
32+
func InitMainDBMigrations(config config.AppConfig) (migration Migration, er error) {
33+
var dbConnection string = fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", config.Database.User, config.Database.Password, config.Database.Host, config.Database.Port, config.Database.Name)
34+
35+
migration.directoryName = mainMigrationsDIR
36+
migration.filesPath = mainMigrationFilesPath
37+
38+
migration.m, er = migrate.New(migration.filesPath, dbConnection)
39+
// if err == migrate.ErrNoChange {
40+
41+
return
42+
}
43+
44+
// MigrationsUp used to make migrations up
45+
func (migration Migration) MigrationsUp() {
46+
err := migration.m.Up()
47+
if err != nil {
48+
if err == migrate.ErrNoChange {
49+
slog.Error("No new migrations to apply")
50+
return
51+
}
52+
slog.Error("*******" + err.Error())
53+
return
54+
}
55+
migration.MigrationVersion()
56+
slog.Info("Migration up completed")
57+
}
58+
59+
// MigrationsDown used to make migrations down
60+
func (migration Migration) MigrationsDown() {
61+
err := migration.m.Down()
62+
if err != nil {
63+
if err == migrate.ErrNoChange {
64+
slog.Info("No migrations to revert")
65+
return
66+
}
67+
68+
slog.Error(err.Error())
69+
return
70+
}
71+
migration.MigrationVersion()
72+
slog.Info("Migration down completed")
73+
}
74+
75+
// CreateMigrationFile creates new migration files
76+
func (migration Migration) CreateMigrationFile(filename string) (err error) {
77+
if len(filename) == 0 {
78+
return errors.New("filename is not provided")
79+
}
80+
81+
timeStamp := time.Now().Unix()
82+
upMigrationFilePath := fmt.Sprintf("%s/%d_%s.up.sql", migration.directoryName, timeStamp, filename)
83+
downMigrationFilePath := fmt.Sprintf("%s/%d_%s.down.sql", migration.directoryName, timeStamp, filename)
84+
85+
defer func() {
86+
if err != nil {
87+
os.Remove(upMigrationFilePath)
88+
os.Remove(downMigrationFilePath)
89+
}
90+
}()
91+
92+
err = createFile(upMigrationFilePath)
93+
if err != nil {
94+
return
95+
}
96+
97+
slog.Info(fmt.Sprintf("created %s\n", upMigrationFilePath))
98+
99+
err = createFile(downMigrationFilePath)
100+
if err != nil {
101+
return
102+
}
103+
104+
slog.Info(fmt.Sprintf("created %s\n", downMigrationFilePath))
105+
return
106+
}
107+
108+
// createFile used to create a file with specified name of versioning
109+
func createFile(filename string) (err error) {
110+
f, err := os.Create(filename)
111+
if err != nil {
112+
return
113+
}
114+
115+
err = f.Close()
116+
return
117+
}
118+
119+
// MigrationVersion prints the current migration version
120+
func (migration Migration) MigrationVersion() (err error) {
121+
version, dirty, err := migration.m.Version()
122+
if err != nil {
123+
return
124+
}
125+
126+
slog.Info(fmt.Sprintf("version: %v, dirty: %v", version, dirty))
127+
return
128+
}
129+
130+
func main() {
131+
// Setup config
132+
cfg, err := config.LoadAppConfig()
133+
if err != nil {
134+
slog.Error("error loading app config", "error", err)
135+
return
136+
}
137+
138+
if len(os.Args) < 2 {
139+
slog.Error("Missing action argument. Use 'up' or 'down' or 'create.")
140+
os.Exit(1)
141+
}
142+
143+
migration, err := InitMainDBMigrations(cfg)
144+
if err != nil {
145+
slog.Error("Error initializing migrations:", "Error", err.Error())
146+
return
147+
}
148+
149+
action := os.Args[1]
150+
switch action {
151+
case "up":
152+
migration.MigrationsUp()
153+
case "down":
154+
migration.MigrationsDown()
155+
case "create":
156+
migration.CreateMigrationFile(os.Args[2])
157+
default:
158+
slog.Info("Invalid action. Use 'up' or 'down'.")
159+
}
160+
}
File renamed without changes.

internal/migrations/000001_init.up.sql renamed to internal/db/migrations/1748862201_init.up.sql

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ CREATE TABLE "users"(
1010
"is_admin" BOOLEAN DEFAULT FALSE,
1111
"password" VARCHAR(255) DEFAULT '',
1212
"is_deleted" BOOLEAN DEFAULT FALSE,
13-
"deleted_at" BIGINT,
14-
"created_at" BIGINT NOT NULL,
15-
"updated_at" BIGINT NOT NULL
13+
"deleted_at" TIMESTAMPTZ,
14+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
15+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
1616
);
1717

1818
CREATE TABLE "leaderboard_hourly"(
@@ -22,8 +22,9 @@ CREATE TABLE "leaderboard_hourly"(
2222
"avatar_url" VARCHAR(255) NOT NULL,
2323
"current_balance" BIGINT NOT NULL,
2424
"rank" BIGINT NOT NULL,
25-
"refreshed_at" BIGINT NOT NULL,
26-
"created_at" BIGINT NOT NULL
25+
"refreshed_at" TIMESTAMPTZ NOT NULL,
26+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
27+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
2728
);
2829

2930
CREATE TABLE "contributions"(
@@ -33,9 +34,9 @@ CREATE TABLE "contributions"(
3334
"contribution_score_id" BIGINT NOT NULL,
3435
"contribution_type" VARCHAR(255) NOT NULL,
3536
"balance_change" BIGINT NOT NULL,
36-
"contributed_at" BIGINT NOT NULL,
37-
"created_at" BIGINT NOT NULL,
38-
"updated_at" BIGINT NOT NULL
37+
"contributed_at" TIMESTAMPTZ NOT NULL,
38+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
39+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
3940
);
4041

4142
CREATE TABLE "repositories"(
@@ -46,17 +47,18 @@ CREATE TABLE "repositories"(
4647
"languages_url" VARCHAR(255) NOT NULL,
4748
"repo_url" VARCHAR(255) NOT NULL,
4849
"owner_name" VARCHAR(255) NOT NULL,
49-
"update_date" BIGINT NOT NULL,
50-
"created_at" BIGINT NOT NULL,
51-
"updated_at" BIGINT NOT NULL
50+
"update_date" TIMESTAMPTZ NOT NULL,
51+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
52+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
5253
);
5354

5455
CREATE TABLE "badges"(
5556
"id" SERIAL PRIMARY KEY,
5657
"user_id" BIGINT NOT NULL,
5758
"badge_type" VARCHAR(255) NOT NULL,
58-
"earned_at" BIGINT NOT NULL,
59-
"created_at" BIGINT NOT NULL
59+
"earned_at" TIMESTAMPTZ NOT NULL,
60+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
61+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
6062
);
6163

6264
CREATE TABLE "transactions"(
@@ -66,8 +68,9 @@ CREATE TABLE "transactions"(
6668
"is_redeemed" BOOLEAN NOT NULL,
6769
"is_gained" BOOLEAN NOT NULL,
6870
"transacted_balance" BIGINT NOT NULL,
69-
"transacted_at" BIGINT NOT NULL,
70-
"created_at" BIGINT NOT NULL
71+
"transacted_at" TIMESTAMPTZ NOT NULL,
72+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
73+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
7174
);
7275

7376
CREATE TABLE "summary"(
@@ -78,24 +81,24 @@ CREATE TABLE "summary"(
7881
"badges_count" BIGINT NOT NULL,
7982
"rank" BIGINT NOT NULL,
8083
"contribution_id" BIGINT NOT NULL,
81-
"created_at" BIGINT NOT NULL,
82-
"updated_at" BIGINT NOT NULL
84+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
85+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
8386
);
8487

8588
CREATE TABLE "contribution_score"(
8689
"id" SERIAL PRIMARY KEY,
8790
"admin_id" BIGINT NOT NULL,
8891
"contribution_type" VARCHAR(255) NOT NULL,
8992
"score" BIGINT NOT NULL,
90-
"created_at" BIGINT NOT NULL,
91-
"updated_at" BIGINT NOT NULL
93+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
94+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
9295
);
9396

9497
CREATE TABLE "goal"(
9598
"id" SERIAL PRIMARY KEY,
9699
"level" VARCHAR(255) NOT NULL,
97-
"created_at" BIGINT NOT NULL,
98-
"updated_at" BIGINT NOT NULL
100+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
101+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
99102
);
100103

101104
CREATE TABLE "goal_contribution"(
@@ -105,8 +108,8 @@ CREATE TABLE "goal_contribution"(
105108
"target_count" BIGINT NOT NULL,
106109
"is_custom" BOOLEAN NOT NULL,
107110
"set_by_user_id" BIGINT NOT NULL,
108-
"created_at" BIGINT NOT NULL,
109-
"updated_at" BIGINT NOT NULL
111+
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
112+
"updated_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
110113
);
111114

112115
ALTER TABLE

0 commit comments

Comments
 (0)