Skip to content

Commit 9230411

Browse files
authored
refactor(sponsor-panel): migrate from raw SQL to GORM (#1183)
* refactor(sponsor-panel): rewrite models with GORM tags and helpers Replace raw pgxpool structs and SQL helper functions with GORM-tagged models. Rename User to PanelUser to avoid collision with internal/models. All DB helpers now take *gorm.DB instead of context + *pgxpool.Pool. Add TableName() methods and PanelModels() for AutoMigrate. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * refactor(sponsor-panel): remove raw SQL migrations GORM AutoMigrate replaces hand-written CREATE TABLE statements and ALTER TABLE migrations. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * refactor(sponsor-panel): swap pgxpool for gorm.DB in server Replace pgxpool.Pool with gorm.DB on Server struct. Use slog-gorm for structured logging and gorm-prometheus for database metrics. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * refactor(sponsor-panel): update oauth handlers to use GORM Replace s.pool with s.db, update function signatures, rename User to PanelUser, and cast user.ID to int for session compatibility. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * refactor(sponsor-panel): update patreon oauth to use GORM Replace s.pool with s.db, rename User to PanelUser, cast user.ID to int for session storage. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * refactor(sponsor-panel): update handlers to use GORM Replace s.pool with s.db for createLogoSubmission call. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * refactor(sponsor-panel): update sync sponsors to use GORM Replace pgxpool.Pool with gorm.DB in syncSponsors and startSyncLoop. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(sponsor-panel): add index drop tool for GORM migration One-shot tool to drop old hand-created indexes and constraints so GORM AutoMigrate can recreate them with its own naming scheme. Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * build(deps): add slog-gorm and gorm-prometheus dependencies Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: update templ generated code to v0.3.1001 Signed-off-by: Xe Iaso <me@xeiaso.net> * docs(sponsor-panel): add GORM migration implementation plan Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net>
1 parent 504058e commit 9230411

13 files changed

Lines changed: 1518 additions & 392 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"flag"
6+
"fmt"
7+
"log"
8+
"os"
9+
10+
"github.com/facebookgo/flagenv"
11+
_ "github.com/jackc/pgx/v5/stdlib"
12+
_ "github.com/joho/godotenv/autoload"
13+
)
14+
15+
var databaseURL = flag.String("database-url", "", "Database URL")
16+
17+
func main() {
18+
flagenv.Parse()
19+
flag.Parse()
20+
21+
if *databaseURL == "" {
22+
fmt.Fprintln(os.Stderr, "database-url is required")
23+
os.Exit(1)
24+
}
25+
26+
db, err := sql.Open("pgx", *databaseURL)
27+
if err != nil {
28+
log.Fatalf("failed to connect: %v", err)
29+
}
30+
defer db.Close()
31+
32+
if err := db.Ping(); err != nil {
33+
log.Fatalf("failed to ping: %v", err)
34+
}
35+
36+
statements := []string{
37+
// Users table: drop constraints (unique indexes backed by constraints)
38+
`ALTER TABLE users DROP CONSTRAINT IF EXISTS users_github_id_key`,
39+
`ALTER TABLE users DROP CONSTRAINT IF EXISTS users_patreon_id_key`,
40+
// Users table: drop plain indexes
41+
`DROP INDEX IF EXISTS idx_users_github_id`,
42+
`DROP INDEX IF EXISTS idx_users_login`,
43+
`DROP INDEX IF EXISTS idx_users_patreon_id`,
44+
`DROP INDEX IF EXISTS idx_users_provider_login`,
45+
// Sponsor usernames table: drop constraint
46+
`ALTER TABLE github_sponsor_usernames DROP CONSTRAINT IF EXISTS github_sponsor_usernames_username_key`,
47+
// Sponsor usernames table: drop plain indexes
48+
`DROP INDEX IF EXISTS idx_sponsor_active`,
49+
`DROP INDEX IF EXISTS idx_sponsor_usernames`,
50+
}
51+
52+
for _, stmt := range statements {
53+
fmt.Printf(" %s\n", stmt)
54+
if _, err := db.Exec(stmt); err != nil {
55+
log.Fatalf("failed: %v", err)
56+
}
57+
}
58+
59+
fmt.Println("done — GORM AutoMigrate will recreate indexes on next startup")
60+
}

cmd/sponsor-panel/handlers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func (s *Server) logoHandler(w http.ResponseWriter, r *http.Request) {
281281
GitHubIssueURL: createdIssue.GetHTMLURL(),
282282
GitHubIssueNumber: createdIssue.GetNumber(),
283283
}
284-
if err := createLogoSubmission(r.Context(), s.pool, submission); err != nil {
284+
if err := createLogoSubmission(s.db, submission); err != nil {
285285
slog.Error("logoHandler: failed to store submission", "err", err, "user_id", user.ID, "issue_number", createdIssue.GetNumber())
286286
} else {
287287
slog.Debug("logoHandler: submission stored in database", "user_id", user.ID, "issue_number", createdIssue.GetNumber())

cmd/sponsor-panel/main.go

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import (
1919
"github.com/facebookgo/flagenv"
2020
gh "github.com/google/go-github/v82/github"
2121
"github.com/gorilla/sessions"
22-
"github.com/jackc/pgx/v5/pgxpool"
22+
slogGorm "github.com/orandin/slog-gorm"
23+
"gorm.io/driver/postgres"
24+
"gorm.io/gorm"
25+
gormPrometheus "gorm.io/plugin/prometheus"
2326
_ "github.com/joho/godotenv/autoload"
2427
patreon "gopkg.in/mxpv/patreon-go.v1"
2528
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -60,7 +63,7 @@ var (
6063

6164
// Server holds the application dependencies.
6265
type Server struct {
63-
pool *pgxpool.Pool
66+
db *gorm.DB
6467
ghClient *gh.Client
6568
oauth *oauth2.Config
6669
patreonOAuth *oauth2.Config // nil if Patreon not configured
@@ -144,34 +147,37 @@ func main() {
144147
os.Exit(1)
145148
}
146149

147-
// Connect to database
148-
slog.Debug("main: connecting to database")
149-
ctx := context.Background()
150-
pool, err := pgxpool.New(ctx, *databaseURL)
150+
// Connect to database via GORM
151+
slog.Debug("main: connecting to database via GORM")
152+
db, err := gorm.Open(postgres.Open(*databaseURL), &gorm.Config{
153+
Logger: slogGorm.New(
154+
slogGorm.WithErrorField("err"),
155+
slogGorm.WithRecordNotFoundError(),
156+
),
157+
})
151158
if err != nil {
152-
slog.Error("failed to create connection pool", "err", err)
159+
slog.Error("failed to connect to database", "err", err)
153160
os.Exit(1)
154161
}
155-
defer pool.Close()
156162

157-
if err := pool.Ping(ctx); err != nil {
158-
slog.Error("failed to ping database", "err", err)
159-
os.Exit(1)
160-
}
163+
db.Use(gormPrometheus.New(gormPrometheus.Config{
164+
DBName: "sponsor_panel",
165+
}))
166+
161167
slog.Info("main: database connection established")
162168

163-
// Run migrations
164-
slog.Debug("main: running migrations")
165-
if err := runMigrations(ctx, pool); err != nil {
166-
slog.Error("failed to run migrations", "err", err)
169+
// Run GORM AutoMigrate
170+
slog.Debug("main: running GORM auto-migration")
171+
if err := db.AutoMigrate(PanelModels()...); err != nil {
172+
slog.Error("failed to auto-migrate", "err", err)
167173
os.Exit(1)
168174
}
169-
slog.Info("main: migrations completed")
175+
slog.Info("main: auto-migration completed")
170176

171177
// Start sponsor sync loop in background
172178
syncCtx, syncCancel := context.WithCancel(context.Background())
173179
defer syncCancel()
174-
go startSyncLoop(syncCtx, pool, *githubToken)
180+
go startSyncLoop(syncCtx, db, *githubToken)
175181
slog.Info("main: sponsor sync loop started")
176182

177183
// Create GitHub client
@@ -255,7 +261,7 @@ func main() {
255261
}
256262

257263
server := &Server{
258-
pool: pool,
264+
db: db,
259265
ghClient: ghClient,
260266
oauth: oauthConfig,
261267
patreonOAuth: patreonConfig,

cmd/sponsor-panel/migrations.go

Lines changed: 0 additions & 103 deletions
This file was deleted.

0 commit comments

Comments
 (0)