package main import ( "database/sql" "fmt" "log" "net/url" "os" "time" _ "github.com/go-sql-driver/mysql" ) var db *sql.DB func InitDB() { host := os.Getenv("DB_HOST") port := os.Getenv("DB_PORT") name := os.Getenv("DB_DATABASENAME") user := os.Getenv("DB_USERNAME") pass := os.Getenv("DB_PASSWORD") if host == "" { log.Println("DB_HOST not set, skipping database initialization") return } log.Printf("[DB] Using timezone: %s (Local=%s)", os.Getenv("TZ"), time.Local.String()) // Default to UTC if TZ is empty, otherwise use the encoded TZ name tzParam := "UTC" if os.Getenv("TZ") != "" { tzParam = url.QueryEscape("'" + os.Getenv("TZ") + "'") } dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&loc=Local&time_zone=%s", user, pass, host, port, name, tzParam) var err error db, err = sql.Open("mysql", dsn) if err != nil { log.Fatalf("Error opening database: %v", err) } if err := db.Ping(); err != nil { log.Fatalf("Error connecting to database: %v", err) } log.Println("Connected to MariaDB successfully") // Verify and enforce timezones var dbNow, dbTZ string err = db.QueryRow("SELECT NOW(), @@session.time_zone").Scan(&dbNow, &dbTZ) if err == nil { log.Printf("[DB] Before setting: NOW()=%s, session.time_zone=%s", dbNow, dbTZ) } if _, err := db.Exec("SET time_zone = SYSTEM"); err != nil { log.Printf("[DB] Warning: Failed to set session time_zone to SYSTEM: %v", err) } else { db.QueryRow("SELECT NOW(), @@session.time_zone").Scan(&dbNow, &dbTZ) log.Printf("[DB] After setting: NOW()=%s, session.time_zone=%s", dbNow, dbTZ) } createTables() } func createTables() { queries := []string{ `CREATE TABLE IF NOT EXISTS users ( fitbit_user_id VARCHAR(255) PRIMARY KEY, display_name VARCHAR(255), avatar_url TEXT, created_at DATETIME )`, `CREATE TABLE IF NOT EXISTS sessions ( token VARCHAR(255) PRIMARY KEY, fitbit_user_id VARCHAR(255), created_at DATETIME, expires_at DATETIME, FOREIGN KEY (fitbit_user_id) REFERENCES users(fitbit_user_id) ON DELETE CASCADE )`, `CREATE TABLE IF NOT EXISTS fitbit_tokens ( user_id VARCHAR(255) PRIMARY KEY, access_token TEXT, refresh_token TEXT, expires_at DATETIME, FOREIGN KEY (user_id) REFERENCES users(fitbit_user_id) ON DELETE CASCADE )`, `CREATE TABLE IF NOT EXISTS kml_metadata ( id INT AUTO_INCREMENT PRIMARY KEY, filename VARCHAR(255), user_id VARCHAR(255), distance DOUBLE, is_public BOOLEAN DEFAULT FALSE, uploaded_at DATETIME, UNIQUE KEY (user_id, filename), FOREIGN KEY (user_id) REFERENCES users(fitbit_user_id) ON DELETE CASCADE )`, `CREATE TABLE IF NOT EXISTS kml_votes ( kml_id INT, user_id VARCHAR(255), vote_value INT, PRIMARY KEY (kml_id, user_id), FOREIGN KEY (kml_id) REFERENCES kml_metadata(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(fitbit_user_id) ON DELETE CASCADE )`, `CREATE TABLE IF NOT EXISTS trips ( user_id VARCHAR(255) PRIMARY KEY, start_date VARCHAR(10), start_time DATETIME, start_day_initial_steps INT, previous_total_steps INT, target_total_steps INT, last_sync_time DATETIME, next_sync_time DATETIME, FOREIGN KEY (user_id) REFERENCES users(fitbit_user_id) ON DELETE CASCADE )`, `CREATE TABLE IF NOT EXISTS daily_steps ( user_id VARCHAR(255), date VARCHAR(10), steps INT, PRIMARY KEY (user_id, date), FOREIGN KEY (user_id) REFERENCES users(fitbit_user_id) ON DELETE CASCADE )`, } for _, query := range queries { if _, err := db.Exec(query); err != nil { log.Fatalf("Error creating table: %v\nQuery: %s", err, query) } } log.Println("Database tables initialized") }