f4d1c3cf28
- Add statistics.sql with 8 SQL queries for play count statistics - Generate repository code via sqlc - Add backend/statistics.go with business logic - Add server/statistics_handler.go with Echo handlers - Register protected routes under /api/v1/statistics/ with token auth - Endpoints: games/most-played, games/least-played, games/never-played, games/last-played, games/oldest-played, songs/most-played, songs/least-played, summary Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
122 lines
3.2 KiB
Go
122 lines
3.2 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"music-server/internal/backend"
|
|
"music-server/internal/db"
|
|
"music-server/internal/logging"
|
|
"net/http"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type Server struct {
|
|
port int
|
|
db *db.Database
|
|
tokenHandler *TokenHandler
|
|
statisticsHandler *StatisticsHandler
|
|
httpServer *http.Server
|
|
}
|
|
|
|
var (
|
|
host = os.Getenv("DB_HOST")
|
|
dbPort = os.Getenv("DB_PORT")
|
|
dbName = os.Getenv("DB_NAME")
|
|
username = os.Getenv("DB_USERNAME")
|
|
password = os.Getenv("DB_PASSWORD")
|
|
musicPath = os.Getenv("MUSIC_PATH")
|
|
charactersPath = os.Getenv("CHARACTERS_PATH")
|
|
logLevel = os.Getenv("LOG_LEVEL")
|
|
logJSON = os.Getenv("LOG_JSON") == "true"
|
|
)
|
|
|
|
// NewServerInstance creates a new Server instance with all dependencies initialized.
|
|
// Use this for dependency injection and proper lifecycle management.
|
|
func NewServerInstance() *Server {
|
|
// Initialize logger
|
|
if logLevel == "" {
|
|
logLevel = "info"
|
|
}
|
|
logging.Init(logLevel, logJSON)
|
|
|
|
logger := logging.GetLogger()
|
|
|
|
port, _ := strconv.Atoi(os.Getenv("PORT"))
|
|
|
|
// Validate required environment variables
|
|
if host == "" || dbPort == "" || username == "" || password == "" || dbName == "" || musicPath == "" || charactersPath == "" {
|
|
logging.GetLogger().Fatal("Invalid settings - missing required environment variables")
|
|
}
|
|
|
|
// Create database instance
|
|
database, err := db.NewDatabase(host, dbPort, username, password, dbName)
|
|
if err != nil {
|
|
logging.GetLogger().Fatal("Failed to initialize database", zap.String("error", err.Error()))
|
|
}
|
|
|
|
// Run migrations using the new method
|
|
if err := database.RunMigrations(); err != nil {
|
|
logging.GetLogger().Fatal("Migration failed", zap.String("error", err.Error()))
|
|
}
|
|
|
|
// Initialize backend package with database pool
|
|
backend.InitBackend(database.Pool)
|
|
|
|
// Initialize token handler with database pool
|
|
tokenHandler := NewTokenHandler(database.Pool)
|
|
|
|
// Initialize statistics handler
|
|
statisticsHandler := NewStatisticsHandler()
|
|
|
|
// Create the server instance
|
|
appServer := &Server{
|
|
port: port,
|
|
db: database,
|
|
tokenHandler: tokenHandler,
|
|
statisticsHandler: statisticsHandler,
|
|
}
|
|
|
|
// Create the HTTP server
|
|
appServer.httpServer = &http.Server{
|
|
Addr: fmt.Sprintf(":%d", port),
|
|
Handler: appServer.RegisterRoutes(),
|
|
IdleTimeout: time.Minute,
|
|
ReadTimeout: 10 * time.Second,
|
|
WriteTimeout: 30 * time.Second,
|
|
}
|
|
|
|
logger.Info("Starting server",
|
|
zap.String("host", host),
|
|
zap.String("dbPort", dbPort),
|
|
zap.String("username", username),
|
|
zap.String("dbName", dbName),
|
|
)
|
|
|
|
logger.Info("Paths",
|
|
zap.String("musicPath", musicPath),
|
|
zap.String("charactersPath", charactersPath),
|
|
)
|
|
|
|
return appServer
|
|
}
|
|
|
|
// HTTPServer returns the underlying http.Server for serving HTTP requests.
|
|
func (s *Server) HTTPServer() *http.Server {
|
|
return s.httpServer
|
|
}
|
|
|
|
// DB returns the database instance for dependency injection.
|
|
func (s *Server) DB() *db.Database {
|
|
return s.db
|
|
}
|
|
|
|
// NewServer creates a new HTTP server (deprecated, use NewServerInstance instead).
|
|
// This function is kept for backward compatibility.
|
|
func NewServer() *http.Server {
|
|
return NewServerInstance().HTTPServer()
|
|
}
|