package api import ( "database/sql" "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/syncserver/internal/auth" "github.com/syncserver/internal/config" "github.com/syncserver/internal/sshmanager" "github.com/syncserver/internal/syncengine" "github.com/syncserver/internal/webui" ) type Server struct { router *chi.Mux cfg *config.Config engine *syncengine.Engine } func NewServer(cfg *config.Config, db *sql.DB, engine *syncengine.Engine) *Server { auth.InitJWTManager(cfg.Auth.JWTSecret, cfg.Auth.JWTExpiryH) r := chi.NewRouter() r.Use(middleware.RequestID) r.Use(middleware.RealIP) r.Use(middleware.Logger) r.Use(middleware.Recoverer) s := &Server{router: r, cfg: cfg, engine: engine} authHandler := NewAuthHandler(db) machineHandler := NewMachineHandler(db) syncPairHandler := NewSyncPairHandler(db) jobHandler := NewJobHandler(db, engine) sseHandler := NewSSEHandler(engine) sshKeyHandler := NewSSHKeyHandler(db, cfg) r.Route("/api", func(r chi.Router) { r.Route("/auth", func(r chi.Router) { r.Post("/login", authHandler.Login) r.Post("/logout", authHandler.Logout) r.With(auth.RequireAuth).Get("/me", authHandler.Me) }) r.With(auth.RequireAuth).Route("/machines", func(r chi.Router) { r.Get("/", machineHandler.List) r.Post("/", machineHandler.Create) r.Get("/{id}", machineHandler.Get) r.Put("/{id}", machineHandler.Update) r.Delete("/{id}", machineHandler.Delete) }) r.With(auth.RequireAuth).Route("/sync-pairs", func(r chi.Router) { r.Get("/", syncPairHandler.List) r.Post("/", syncPairHandler.Create) r.Get("/{id}", syncPairHandler.Get) r.Put("/{id}", syncPairHandler.Update) r.Delete("/{id}", syncPairHandler.Delete) r.Post("/{id}/run", jobHandler.TriggerRun) }) r.With(auth.RequireAuth).Route("/jobs", func(r chi.Router) { r.Get("/", jobHandler.List) r.Get("/{id}", jobHandler.Get) r.Post("/{id}/cancel", jobHandler.Cancel) r.Get("/{id}/log", jobHandler.GetLog) r.Get("/{id}/log/download", jobHandler.DownloadLog) r.Get("/{id}/log/stream", sseHandler.StreamJob) }) r.With(auth.RequireAuth).Get("/jobs/stream", sseHandler.StreamAll) r.With(auth.RequireAuth).Get("/settings/pubkey", func(w http.ResponseWriter, r *http.Request) { _, _, pubKey, _ := sshmanager.EnsureServerKey(cfg.SSHDir()) w.Header().Set("Content-Type", "text/plain") w.Write([]byte(pubKey)) }) r.With(auth.RequireAuth).Route("/ssh-keys", func(r chi.Router) { r.Get("/", sshKeyHandler.List) r.Post("/", sshKeyHandler.Create) r.Get("/{id}", sshKeyHandler.Get) r.Delete("/{id}", sshKeyHandler.Delete) r.Get("/{id}/private", sshKeyHandler.DownloadPrivate) }) }) r.Get("/health", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) })) r.NotFound(func(w http.ResponseWriter, r *http.Request) { webui.ServeSPA().ServeHTTP(w, r) }) return s } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.router.ServeHTTP(w, r) }