Files
estus-shots/cmd/luxtools/main.go
2025-10-15 10:01:47 +02:00

120 lines
2.4 KiB
Go

package main
import (
"context"
"encoding/json"
"errors"
"flag"
"log"
"net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"estus-shots/internal/server"
)
type appConfig struct {
MediaDir string `json:"media_dir"`
}
func main() {
cfg := resolveConfig()
srv, err := server.New(server.Config{MediaDir: cfg.MediaDir})
if err != nil {
log.Fatalf("failed to configure server: %v", err)
}
httpServer := &http.Server{
Addr: srv.Address(),
Handler: srv.Router(),
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Printf(
"Estus Shots web server running on http://127.0.0.1%s (media: %s)",
srv.Address(),
cfg.MediaDir,
)
go func() {
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("server error: %v", err)
}
}()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := httpServer.Shutdown(shutdownCtx); err != nil {
log.Printf("graceful shutdown failed: %v", err)
} else {
log.Println("server stopped")
}
}
func resolveConfig() appConfig {
mediaDirFlag := flag.String("media-dir", "", "path to the media directory served at /media")
configPath := flag.String("config", "", "optional path to a JSON config file")
flag.Parse()
cfg := appConfig{}
if *configPath != "" {
fileCfg, err := loadConfig(*configPath)
if err != nil {
log.Fatalf("failed to read config file: %v", err)
}
cfg = fileCfg
}
if *mediaDirFlag != "" {
cfg.MediaDir = *mediaDirFlag
}
if cfg.MediaDir == "" {
cfg.MediaDir = defaultMediaDir()
}
return cfg
}
func loadConfig(path string) (appConfig, error) {
data, err := os.ReadFile(path)
if err != nil {
return appConfig{}, err
}
cfg := appConfig{}
if err := json.Unmarshal(data, &cfg); err != nil {
return appConfig{}, err
}
return cfg, nil
}
func defaultMediaDir() string {
if cwd, err := os.Getwd(); err == nil {
candidate := filepath.Join(cwd, "media")
if info, err := os.Stat(candidate); err == nil && info.IsDir() {
return candidate
}
}
if exe, err := os.Executable(); err == nil {
candidate := filepath.Join(filepath.Dir(exe), "media")
if info, err := os.Stat(candidate); err == nil && info.IsDir() {
return candidate
}
}
return filepath.Join(".", "media")
}