package server import ( "context" "fmt" "log" "net/http" "time" "github.com/jackc/pgx/v5/pgxpool" "github.com/panelhosting/panel/internal/authsvc" "github.com/panelhosting/panel/internal/bootstrap" "github.com/panelhosting/panel/internal/config" "github.com/panelhosting/panel/internal/handler" "github.com/panelhosting/panel/internal/middleware" "github.com/panelhosting/panel/internal/repository" "github.com/panelhosting/panel/internal/setup" "github.com/panelhosting/panel/internal/sitesvc" ) type Server struct { httpServer *http.Server } func New(pool *pgxpool.Pool, cfg *config.Config) (*Server, error) { users := repository.NewUserRepository(pool) servers := repository.NewServerRepository(pool) sessions := repository.NewSessionRepository(pool) sites := repository.NewSiteRepository(pool) phpVers := repository.NewPHPVersionRepository(pool) setupSvc := setup.NewService(users, servers, cfg) authSvc := authsvc.NewService(users, sessions, cfg.SessionTTLHours) siteSvc := sitesvc.NewService(sites, servers, phpVers) setupHandler := handler.NewSetupHandler(setupSvc) authHandler := handler.NewAuthHandler(authSvc, cfg) siteHandler := handler.NewSiteHandler(siteSvc) authMW := middleware.NewAuth(authSvc, cfg) setupRequired, err := setupSvc.Status(context.Background()) if err != nil { return nil, fmt.Errorf("check setup status: %w", err) } if setupRequired { log.Println("setup required: open / to create first admin") } if err := bootstrap.EnsureDefaultServer(context.Background(), servers, cfg); err != nil { return nil, fmt.Errorf("bootstrap server: %w", err) } mux := http.NewServeMux() mux.HandleFunc("GET /health", handler.Health) mux.HandleFunc("GET /api/v1/setup/status", setupHandler.Status) mux.HandleFunc("POST /api/v1/setup/register", setupHandler.Register) mux.HandleFunc("POST /api/v1/auth/login", authHandler.Login) mux.HandleFunc("POST /api/v1/auth/logout", authMW.Require(authHandler.Logout)) mux.HandleFunc("GET /api/v1/auth/me", authMW.Require(authHandler.Me)) mux.HandleFunc("GET /api/v1/php-versions", siteHandler.PHPVersions) mux.HandleFunc("GET /api/v1/sites", authMW.Require(siteHandler.List)) mux.HandleFunc("POST /api/v1/sites", authMW.Require(siteHandler.Create)) mux.HandleFunc("GET /{$}", authMW.Optional(handler.IndexPage(setupSvc))) addr := fmt.Sprintf(":%s", cfg.HTTPPort) return &Server{ httpServer: &http.Server{ Addr: addr, Handler: loggingMiddleware(mux), ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, }, }, nil } func (s *Server) Start() error { log.Printf("panel listening on %s", s.httpServer.Addr) return s.httpServer.ListenAndServe() } func (s *Server) Shutdown(ctx context.Context) error { return s.httpServer.Shutdown(ctx) } func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start).Round(time.Millisecond)) }) }