139 lines
3.0 KiB
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))
|
|
}
|