Files
Completed/cmd/api/main.go

139 lines
3.0 KiB
Go

package main
import (
"context"
"database/sql"
"fmt"
"net/url"
"os"
"strings"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humaecho"
"github.com/golang-migrate/migrate/v4"
_ "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
_ "github.com/lib/pq"
"completed/internal/api"
"completed/internal/db"
)
func ensureDatabase(dbURL string) error {
u, err := url.Parse(dbURL)
if err != nil {
return err
}
dbName := strings.TrimPrefix(u.Path, "/")
if dbName == "" {
return fmt.Errorf("database name is empty in URL: %s", dbURL)
}
// Connect to 'postgres' database to create the new DB
u.Path = "/postgres"
db, err := sql.Open("postgres", u.String())
if err != nil {
return err
}
defer db.Close()
// Check if DB exists
var exists bool
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)", dbName).Scan(&exists)
if err != nil {
return err
}
if !exists {
fmt.Printf("Database '%s' does not exist. Creating...\n", dbName)
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE \"%s\"", dbName))
if err != nil {
return err
}
fmt.Printf("Database '%s' created successfully.\n", dbName)
} else {
fmt.Printf("Database '%s' already exists.\n", dbName)
}
return nil
}
func runMigrations(dbURL string) {
m, err := migrate.New(
"file:///app/db/migrations", // Path inside Docker container
dbURL,
)
if err != nil {
// Fallback for local development if not in /app
if os.Getenv("MIGRATION_PATH") != "" {
m, err = migrate.New(
"file://"+os.Getenv("MIGRATION_PATH"),
dbURL,
)
} else {
// Try relative path default
m, err = migrate.New(
"file://db/migrations",
dbURL,
)
}
}
if err != nil {
fmt.Fprintf(os.Stderr, "Migration initialization failed: %v\n", err)
return
}
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
fmt.Fprintf(os.Stderr, "Migration failed: %v\n", err)
} else {
fmt.Println("Migrations ran successfully")
}
}
func main() {
// 1. Database connection
dbURL := os.Getenv("DATABASE_URL")
if dbURL == "" {
dbURL = "postgres://user:password@localhost:5432/dbname?sslmode=disable"
}
// Ensure Database Exists
if err := ensureDatabase(dbURL); err != nil {
fmt.Fprintf(os.Stderr, "Failed to ensure database: %v\n", err)
}
// Run Migrations
runMigrations(dbURL)
pool, err := pgxpool.New(context.Background(), dbURL)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
os.Exit(1)
}
defer pool.Close()
queries := db.New(pool)
// 2. Setup Echo
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// 3. Setup Huma
humaApi := humaecho.New(e, huma.DefaultConfig("My API", "1.0.0"))
// 4. Register Routes
api.RegisterRoutes(humaApi, queries)
// 5. Start Server
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
e.Logger.Fatal(e.Start(":" + port))
}