gohorsejobs/backend/cmd/api/main.go

106 lines
3 KiB
Go
Executable file

package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"strings"
"github.com/joho/godotenv"
"github.com/rede5/gohorsejobs/backend/docs"
"github.com/rede5/gohorsejobs/backend/internal/database"
"github.com/rede5/gohorsejobs/backend/internal/router"
"github.com/rede5/gohorsejobs/backend/internal/services"
)
// @title GoHorseJobs API
// @version 1.0
// @description API for GoHorseJobs recruitment platform.
// @BasePath /
func main() {
// Load .env file
if err := godotenv.Load(); err != nil {
log.Println("No .env file found or error loading it")
}
// Validate JWT_SECRET strength (must be at least 32 characters / 256 bits)
if err := ValidateJWT(os.Getenv("JWT_SECRET"), os.Getenv("ENV")); err != nil {
if strings.HasPrefix(err.Error(), "FATAL") {
log.Fatal(err)
}
log.Println(err)
}
database.InitDB()
database.RunMigrations()
// Configure Swagger Host dynamically
apiHost := os.Getenv("BACKEND_HOST")
if apiHost == "" {
apiHost = "localhost:8521"
}
finalHost, schemes := ConfigureSwagger(apiHost)
docs.SwaggerInfo.Host = finalHost
docs.SwaggerInfo.Schemes = schemes
apiHost = finalHost // Update for logging
// Bootstrap Credentials from Env to DB
// This ensures smooth migration from .env to Database configuration
go func() {
// Initialize temporary credentials service for bootstrapping
credService := services.NewCredentialsService(database.DB)
ctx := context.Background()
if err := credService.BootstrapCredentials(ctx); err != nil {
// Log error but don't crash, it might be transient DB issue
fmt.Printf("Error bootstrapping credentials: %v\n", err)
}
}()
handler := router.NewRouter()
port := os.Getenv("BACKEND_PORT")
if port == "" {
port = "8521"
}
log.Println("Starting server on :" + port)
log.Println("API Host configured to:", apiHost)
if err := http.ListenAndServe(":"+port, handler); err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}
func ValidateJWT(secret, env string) error {
if secret == "" || len(secret) < 32 {
msg := "⚠️ WARNING: JWT_SECRET is empty or too short (< 32 chars). Use a strong secret in production!"
if env == "production" {
return fmt.Errorf("FATAL: Cannot start in production without strong JWT_SECRET")
}
// Logic was: log warning. Here we return error with message to be logged/handled.
// To match exact logic:
// warning is always returned if short.
// fatal error is returned if production.
// Caller handles logging.
return fmt.Errorf("%s", msg)
}
return nil
}
func ConfigureSwagger(apiHost string) (string, []string) {
schemes := []string{"http", "https"} // default to both
if strings.HasPrefix(apiHost, "https://") {
schemes = []string{"https"}
} else if strings.HasPrefix(apiHost, "http://") {
schemes = []string{"http"}
}
// Strip protocol schemes to ensure clean host for Swagger
apiHost = strings.TrimPrefix(apiHost, "http://")
apiHost = strings.TrimPrefix(apiHost, "https://")
return apiHost, schemes
}