package main import ( "context" "log" "net/http" "os" "path/filepath" pgxuuid "github.com/jackc/pgx-gofrs-uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "github.com/joho/godotenv" "michaelthomson.dev/mthomson/go-todos-app/handlers" "michaelthomson.dev/mthomson/go-todos-app/middleware" "michaelthomson.dev/mthomson/go-todos-app/repositories" "michaelthomson.dev/mthomson/go-todos-app/services" ) func main() { // load environment err := godotenv.Load() if err != nil { log.Printf("Error loading .env file: %v", err) } databaseUrl := os.Getenv("DATABASE_URL") // connect to Db dbconfig, err := pgxpool.ParseConfig(databaseUrl) if err != nil { log.Fatalf("Unable to parse db config: %v", err) } dbconfig.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error { pgxuuid.Register(conn.TypeMap()) return nil } dbpool, err := pgxpool.New(context.Background(), databaseUrl) if err != nil { log.Fatalf("Unable to create connection pool: %v", err) } defer dbpool.Close() // apply initial sql log.Print("Applying migrations table") content, err := os.ReadFile(filepath.Join("sql", "init.sql")) if err != nil { log.Fatal(err) os.Exit(1) } _, err = dbpool.Exec(context.Background(), string(content)) if err != nil { log.Fatal(err) os.Exit(1) } //apply migrations log.Print("Starting migrations...") files, err := os.ReadDir("./sql/migrations") if err != nil { log.Fatal(err) os.Exit(1) } for _, file := range files { path := filepath.Join("sql", "migrations", file.Name()) content, err := os.ReadFile(path) if err != nil { log.Fatal(err) os.Exit(1) } var count int err = dbpool.QueryRow(context.Background(), "SELECT COUNT(1) FROM migration WHERE name=$1", file.Name()).Scan(&count) if err != nil { log.Fatal(err) os.Exit(1) } if count == 0 { log.Printf("Running migration: %s", file.Name()) _, err = dbpool.Exec(context.Background(), string(content)) if err != nil { log.Fatal(err) os.Exit(1) } _, err = dbpool.Exec(context.Background(), "INSERT INTO migration(name) values($1)", file.Name()) if err != nil { log.Fatal(err) os.Exit(1) } } else { log.Printf("Migration already exists: %s", file.Name()) } } log.Print("Migrations complete") // set up repositories todoRepository := repositories.NewPostgresTodoRepository(dbpool) // set up services todoService := services.NewTodoService(&todoRepository) // set up handlers homeHandler := handlers.NewHomeHandler(todoService) todoHandler := handlers.NewTodoHandler(todoService) router := http.NewServeMux() // Serve static files fs := http.FileServer(http.Dir("./assets")) router.Handle("GET /assets/", http.StripPrefix("/assets/", fs)) router.HandleFunc("GET /{$}", homeHandler.Home) router.HandleFunc("POST /todos", todoHandler.Create) router.HandleFunc("DELETE /todos/{id}", todoHandler.Delete) router.HandleFunc("PATCH /todos/{id}/done", todoHandler.Done) router.HandleFunc("PATCH /todos/{id}/undone", todoHandler.Undone) server := http.Server{ Addr: ":3000", Handler: middleware.LoggingMiddleware(router), } log.Fatal(server.ListenAndServe()) }