Files
SimpleFinanceDash/backend/cmd/server/main.go
2026-03-06 19:42:15 +00:00

102 lines
2.4 KiB
Go

package main
import (
"context"
"embed"
"fmt"
"io/fs"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"simplefinancedash/backend/internal/api"
"simplefinancedash/backend/internal/db"
)
// These directories are populated at build time:
// - frontend_dist/ is copied from frontend/dist by the Dockerfile
// - migrations/ is symlinked or copied from backend/migrations/
//go:embed frontend_dist
var frontendFiles embed.FS
//go:embed migrations
var migrationsFiles embed.FS
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
databaseURL := getEnv("DATABASE_URL", "postgres://simplefin:simplefin@localhost:5432/simplefindb?sslmode=disable")
sessionSecret := getEnv("SESSION_SECRET", "change-me-in-production")
port := getEnv("PORT", "8080")
// Connect to database
pool, err := db.Connect(ctx, databaseURL)
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer pool.Close()
// Run migrations
migrationsFS, err := fs.Sub(migrationsFiles, "migrations")
if err != nil {
log.Fatalf("Failed to setup migrations filesystem: %v", err)
}
if err := db.RunMigrations(ctx, pool, migrationsFS); err != nil {
log.Fatalf("Failed to run migrations: %v", err)
}
log.Println("Migrations completed")
// Setup frontend filesystem
frontendFS, err := fs.Sub(frontendFiles, "frontend_dist")
if err != nil {
log.Fatalf("Failed to setup frontend filesystem: %v", err)
}
// Create router
queries := db.NewQueries(pool)
router := api.NewRouter(queries, sessionSecret, frontendFS)
// Start server
server := &http.Server{
Addr: ":" + port,
Handler: router,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
go func() {
log.Printf("Server starting on :%s", port)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err)
}
}()
// Graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
shutdownCtx, shutdownCancel := context.WithTimeout(ctx, 10*time.Second)
defer shutdownCancel()
if err := server.Shutdown(shutdownCtx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
fmt.Println("Server stopped")
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}