package api import ( "encoding/json" "html/template" "net/http" webui "jukebox_maker/web" "jukebox_maker/internal/config" "jukebox_maker/internal/copier" "jukebox_maker/internal/task" "jukebox_maker/internal/watcher" ) type Deps struct { Config *config.Config ConfigPath string Version string Watcher *watcher.Watcher Copier *copier.Copier Tasks *task.Store MediaPath string MountPath string // OnDiskInit вызывается при ручной инициализации диска через UI. OnDiskInit func(mountPath, diskID string) } type Server struct { deps Deps dashboard *template.Template settings *template.Template mux *http.ServeMux } func New(deps Deps) (*Server, error) { dash, err := template.ParseFS(webui.FS, "templates/layout.html", "templates/dashboard.html") if err != nil { return nil, err } sett, err := template.ParseFS(webui.FS, "templates/layout.html", "templates/settings.html") if err != nil { return nil, err } s := &Server{deps: deps, dashboard: dash, settings: sett, mux: http.NewServeMux()} s.routes() return s, nil } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.mux.ServeHTTP(w, r) } func (s *Server) routes() { s.mux.Handle("GET /static/", http.FileServerFS(webui.FS)) s.mux.HandleFunc("GET /", s.handleDashboard) s.mux.HandleFunc("GET /settings", s.handleSettings) s.mux.HandleFunc("GET /health", s.handleHealth) s.mux.HandleFunc("GET /api/disks", s.handleDiskStatus) s.mux.HandleFunc("POST /api/disks/init", s.handleDiskInit) s.mux.HandleFunc("GET /api/sources", s.handleSources) s.mux.HandleFunc("GET /api/config", s.handleGetConfig) s.mux.HandleFunc("PUT /api/config", s.handlePutConfig) s.mux.HandleFunc("POST /api/disks/{diskID}/copy/start", s.handleCopyStart) s.mux.HandleFunc("POST /api/disks/{diskID}/copy/cancel", s.handleCopyCancel) s.mux.HandleFunc("GET /api/tasks/{id}", s.handleTaskGet) } func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } s.render(w, s.dashboard, map[string]any{"Title": "Dashboard", "Page": "dashboard"}) } func (s *Server) handleSettings(w http.ResponseWriter, r *http.Request) { s.render(w, s.settings, map[string]any{"Title": "Settings", "Page": "settings"}) } func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { jsonOK(w, map[string]string{"status": "ok"}) } func (s *Server) render(w http.ResponseWriter, tmpl *template.Template, data any) { w.Header().Set("Content-Type", "text/html; charset=utf-8") payload := map[string]any{"Version": s.deps.Version} if incoming, ok := data.(map[string]any); ok { for k, v := range incoming { payload[k] = v } } if err := tmpl.ExecuteTemplate(w, "layout", payload); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func jsonOK(w http.ResponseWriter, v any) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(v) } func jsonErr(w http.ResponseWriter, code int, msg string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) json.NewEncoder(w).Encode(map[string]string{"error": msg}) }