11package main
22
33import (
4+ "fmt"
45 "log"
56 "net/http"
7+ "os"
8+ "time"
69
710 "github.com/gin-gonic/gin"
8- // "gorm.io/driver/mysql" // DB接続時にコメント解除
9- // "gorm.io/gorm" // DB接続時にコメント解除
11+ "github.com/go-playground/validator/v10"
12+ "github.com/golang-migrate/migrate/v4"
13+ _ "github.com/golang-migrate/migrate/v4/database/mysql" // MySQL driver for migrate
14+ _ "github.com/golang-migrate/migrate/v4/source/file" // File source for migrate
15+ "gorm.io/driver/mysql"
16+ "gorm.io/gorm"
17+ "gorm.io/gorm/logger"
1018)
1119
12- // CreateUserInput : ユーザー作成リクエストのバリデーション用構造体
13- type CreateUserInput struct {
14- Username string `json:"username" binding:"required,min=3,max=30"`
15- Email string `json:"email" binding:"required,email"`
20+ // Global Variables
21+ var (
22+ db * gorm.DB
23+ validate * validator.Validate
24+ )
25+
26+ // Models (仮定義: 実際には models/ ディレクトリなどに分離推奨)
27+ // OpenAPIのスキーマに基づいてGORMモデルを定義します。
28+ // 例:
29+ // type Complex struct {
30+ // ID uint `gorm:"primarykey" json:"id"`
31+ // UserID string `json:"user_id" gorm:"type:varchar(36);not null;index"` // UUID
32+ // Content string `json:"content" gorm:"not null" validate:"required"`
33+ // Category string `json:"category" gorm:"not null" validate:"required"`
34+ // CreatedAt time.Time `json:"created_at"`
35+ // UpdatedAt time.Time `json:"updated_at"`
36+ // }
37+ // type Goal struct { ... }
38+ // type Action struct { ... }
39+
40+ // ErrorResponse Helper
41+ type ErrorResponse struct {
42+ Code int `json:"code"`
43+ Message string `json:"message"`
44+ }
45+
46+ // NewErrorResponse Helper
47+ func NewErrorResponse (code int , message string ) ErrorResponse {
48+ return ErrorResponse {Code : code , Message : message }
49+ }
50+
51+ // AuthMiddleware provides a simple authentication mechanism for the MVP.
52+ // It checks for an 'X-User-ID' header. If the header is not present,
53+ // it sets a default test user ID. This is a temporary measure and should be
54+ // replaced with a proper token-based authentication system for production.
55+ func AuthMiddleware () gin.HandlerFunc {
56+ return func (c * gin.Context ) {
57+ userID := c .GetHeader ("X-User-ID" )
58+ if userID == "" {
59+ // テスト用のダミーユーザーID (実際の認証基盤実装時に置き換える)
60+ userID = "test-user-uuid-12345"
61+ log .Printf ("Warning: X-User-ID header not found, using default test user ID: %s" , userID )
62+ }
63+ c .Set ("userID" , userID )
64+ c .Next ()
65+ }
1666}
1767
68+ // PingHandler handles the health check endpoint.
69+ func PingHandler (c * gin.Context ) {
70+ c .JSON (http .StatusOK , gin.H {"message" : "pong" })
71+ }
72+
73+ // Complex Handlers (スタブ)
74+ // func CreateComplexHandler(c *gin.Context) { /* ... */ }
75+ // func GetComplexesHandler(c *gin.Context) { /* ... */ }
76+ // func GetComplexHandler(c *gin.Context) { /* ... */ }
77+ // func UpdateComplexHandler(c *gin.Context) { /* ... */ }
78+ // func DeleteComplexHandler(c *gin.Context) { /* ... */ }
79+
80+ // Goal Handlers (スタブ)
81+ // func CreateGoalHandler(c *gin.Context) { /* ... */ }
82+ // func GetGoalsHandler(c *gin.Context) { /* ... */ }
83+ // func GetGoalHandler(c *gin.Context) { /* ... */ }
84+ // func UpdateGoalHandler(c *gin.Context) { /* ... */ }
85+ // func DeleteGoalHandler(c *gin.Context) { /* ... */ }
86+
87+ // Action Handlers (スタブ)
88+ // func CreateActionHandler(c *gin.Context) { /* ... */ }
89+
1890func main () {
19- // データベース接続 (後で設定)
20- // dsn := "user:pass@tcp(db:3306)/refuel_db?charset=utf8mb4&parseTime=True&loc=Local"
21- // _, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
22- // if err != nil {
23- // log.Fatalf("Failed to connect to database: %v", err)
24- // }
25- // log.Println("Database connected")
91+ // --- 環境変数からの設定読み込み (例) ---
92+ dbUser := os .Getenv ("DB_USER" )
93+ dbPassword := os .Getenv ("DB_PASSWORD" )
94+ dbHost := os .Getenv ("DB_HOST" )
95+ dbPort := os .Getenv ("DB_PORT" )
96+ dbName := os .Getenv ("DB_NAME" )
97+ migrationPath := os .Getenv ("MIGRATION_PATH" ) // e.g., "file://./db/migrations"
98+ serverPort := os .Getenv ("SERVER_PORT" )
99+ if serverPort == "" {
100+ serverPort = "8080" // デフォルトポート
101+ }
26102
27- r := gin .Default ()
103+ // 必須環境変数のチェック
104+ requiredEnvVars := map [string ]string {
105+ "DB_USER" : dbUser ,
106+ "DB_PASSWORD" : dbPassword ,
107+ "DB_HOST" : dbHost ,
108+ "DB_PORT" : dbPort ,
109+ "DB_NAME" : dbName ,
110+ }
111+ for key , value := range requiredEnvVars {
112+ if value == "" {
113+ log .Fatalf ("🚨 Missing required environment variable: %s" , key )
114+ }
115+ }
116+
117+ // --- Validatorの初期化 ---
118+ validate = validator .New ()
119+
120+ // --- データベース接続 (GORM) ---
121+ dsn := fmt .Sprintf ("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local" ,
122+ dbUser , dbPassword , dbHost , dbPort , dbName ,
123+ )
124+
125+ gormLogger := logger .New (
126+ log .New (os .Stdout , "\r \n " , log .LstdFlags ),
127+ logger.Config {
128+ SlowThreshold : time .Second ,
129+ LogLevel : logger .Info , // 開発中はInfo, 本番ではWarnなど
130+ IgnoreRecordNotFoundError : true ,
131+ Colorful : true ,
132+ },
133+ )
28134
29- r .GET ("/ping" , func (c * gin.Context ) {
30- c .JSON (http .StatusOK , gin.H {"message" : "pong from backend" })
135+ var err error
136+ db , err = gorm .Open (mysql .Open (dsn ), & gorm.Config {
137+ Logger : gormLogger ,
31138 })
139+ if err != nil {
140+ log .Fatalf ("🚨 Failed to connect to database: %v" , err )
141+ }
142+ log .Println ("🎉 Database connected successfully!" )
32143
33- r .POST ("/users" , func (c * gin.Context ) {
34- var input CreateUserInput
35- if err := c .ShouldBindJSON (& input ); err != nil {
36- c .JSON (http .StatusBadRequest , gin.H {"error" : err .Error ()})
37- return
144+ // --- データベースマイグレーション (golang-migrate/migrate) ---
145+ if migrationPath != "" {
146+ dbURL := fmt .Sprintf ("mysql://%s:%s@tcp(%s:%s)/%s" ,
147+ dbUser , dbPassword , dbHost , dbPort , dbName ,
148+ )
149+ m , err := migrate .New (migrationPath , dbURL )
150+ if err != nil { // マイグレーションの初期化失敗は致命的エラーとする場合
151+ log .Fatalf ("🚨 Failed to initialize migrations: %v." , err )
38152 }
39- c .JSON (http .StatusOK , gin.H {"message" : "User created (simulated)" , "user" : input })
40- })
153+ if err := m .Up (); err != nil && err != migrate .ErrNoChange {
154+ log .Fatalf ("🚨 Failed to run migrations: %v" , err )
155+ }
156+ log .Println ("🎉 Migrations applied successfully or no changes." )
157+ srcErr , dbErr := m .Close ()
158+ if srcErr != nil {
159+ log .Printf ("⚠️ Error closing migration source: %v" , srcErr )
160+ }
161+ if dbErr != nil {
162+ log .Printf ("⚠️ Error closing migration database connection: %v" , dbErr )
163+ }
164+ } else {
165+ log .Println ("ℹ️ MIGRATION_PATH not set, skipping automated migrations. Ensure DB schema is up to date." )
166+ // 開発初期段階ではGORMのAutoMigrateも便利です
167+ // log.Println("Running GORM AutoMigrate for initial schema setup...")
168+ // db.AutoMigrate(&Complex{}, &Goal{}, &Action{}) // ここにモデルを追加
169+ }
41170
42- log .Println ("Backend server starting on port 8080" )
43- if err := r .Run (":8080" ); err != nil {
44- log .Fatalf ("Failed to run server: %v" , err )
171+ // --- Ginルーターの初期化 ---
172+ // gin.SetMode(gin.ReleaseMode) // 本番環境ではReleaseModeに
173+ r := gin .Default ()
174+
175+ // --- ミドルウェア ---
176+ r .Use (gin .Logger ()) // 標準のロガー
177+ r .Use (gin .Recovery ()) // パニックリカバリ
178+ // CORS設定 (必要に応じてカスタマイズ)
179+ // r.Use(cors.Default())
180+ r .Use (AuthMiddleware ()) // 認証ミドルウェア
181+
182+ // --- ルーティング ---
183+ // APIのベースパスを `/api/v1` とします (openapi.yamlのservers.urlに合わせる)
184+ apiV1 := r .Group ("/api/v1" )
185+ {
186+ apiV1 .GET ("/ping" , PingHandler )
187+
188+ // Complexes
189+ // complexesGroup := apiV1.Group("/complexes")
190+ // {
191+ // // complexesGroup.POST("", CreateComplexHandler)
192+ // // complexesGroup.GET("", GetComplexesHandler)
193+ // // complexesGroup.GET("/:complexId", GetComplexHandler)
194+ // // complexesGroup.PUT("/:complexId", UpdateComplexHandler)
195+ // // complexesGroup.DELETE("/:complexId", DeleteComplexHandler)
196+ // }
197+
198+ // Goals
199+ // goalsGroup := apiV1.Group("/goals")
200+ // {
201+ // // goalsGroup.POST("", CreateGoalHandler)
202+ // // goalsGroup.GET("", GetGoalsHandler)
203+ // // goalsGroup.GET("/:goalId", GetGoalHandler)
204+ // // goalsGroup.PUT("/:goalId", UpdateGoalHandler)
205+ // // goalsGroup.DELETE("/:goalId", DeleteGoalHandler)
206+ // }
207+
208+ // Actions
209+ // actionsGroup := apiV1.Group("/actions")
210+ // {
211+ // // actionsGroup.POST("", CreateActionHandler)
212+ // }
213+ }
214+
215+ // --- サーバー起動 ---
216+ log .Printf ("🚀 Server starting on port %s" , serverPort )
217+ if err := r .Run (":" + serverPort ); err != nil {
218+ log .Fatalf ("🚨 Failed to run server: %v" , err )
45219 }
46- }
220+ }
0 commit comments