1317ff6369
Operations are now clickable from the host detail page, linking to
/ops/{id} which shows the operation info, host link, duration, and
activity log filtered to that operation. Active operations can be
cancelled, which transitions the host to failed and releases the lock.
SSE activity events now include operation_id for real-time filtering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"io/fs"
|
|
"net/http"
|
|
|
|
"provisioning/internal/api"
|
|
"provisioning/internal/events"
|
|
"provisioning/internal/web"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
)
|
|
|
|
type Deps struct {
|
|
HostAPI *api.HostAPI
|
|
BootAPI *api.BootAPI
|
|
ImageAPI *api.ImageAPI
|
|
UI *api.UI
|
|
Hub *events.Hub
|
|
ImageDir string
|
|
}
|
|
|
|
func NewRouter(d Deps) http.Handler {
|
|
r := chi.NewRouter()
|
|
r.Use(middleware.RealIP)
|
|
r.Use(middleware.Recoverer)
|
|
r.Use(middleware.Logger)
|
|
|
|
// Static files
|
|
staticFS, _ := fs.Sub(web.Static, "static")
|
|
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
|
|
|
|
// Boot image files (kernel/initrd served from disk)
|
|
r.Handle("/images/boot/*", http.StripPrefix("/images/boot/",
|
|
http.FileServer(http.Dir(d.ImageDir))))
|
|
|
|
// SSE
|
|
r.Get("/events", d.Hub.ServeSSE)
|
|
|
|
// Dashboard UI
|
|
r.Get("/", d.UI.Dashboard)
|
|
r.Get("/hosts/new", d.UI.NewHostForm)
|
|
r.Post("/hosts", d.UI.CreateHost)
|
|
r.Get("/hosts/{id}", d.UI.HostDetail)
|
|
r.Post("/hosts/{id}/rebuild", d.UI.TriggerRebuild)
|
|
r.Post("/hosts/{id}/delete", d.UI.DeleteHost)
|
|
r.Get("/ops/{id}", d.UI.OperationDetail)
|
|
r.Post("/ops/{id}/cancel", d.UI.CancelOperation)
|
|
r.Get("/images", d.UI.ImagesPage)
|
|
r.Get("/images/new", d.UI.NewImageForm)
|
|
r.Post("/images/upload", d.UI.UploadImage)
|
|
r.Post("/images/{id}/default", d.UI.SetDefaultImage)
|
|
r.Post("/images/{id}/delete", d.UI.DeleteImage)
|
|
|
|
// Host JSON API
|
|
r.Route("/api/hosts", func(r chi.Router) {
|
|
r.Get("/", d.HostAPI.List)
|
|
r.Post("/", d.HostAPI.Create)
|
|
r.Get("/{id}", d.HostAPI.Get)
|
|
r.Delete("/{id}", d.HostAPI.Delete)
|
|
r.Post("/{id}/rebuild", d.HostAPI.Rebuild)
|
|
})
|
|
|
|
// Image JSON API
|
|
r.Route("/api/images", func(r chi.Router) {
|
|
r.Get("/", d.ImageAPI.List)
|
|
r.Post("/", d.ImageAPI.Upload)
|
|
r.Get("/{id}", d.ImageAPI.Get)
|
|
r.Delete("/{id}", d.ImageAPI.Delete)
|
|
r.Post("/{id}/default", d.ImageAPI.SetDefault)
|
|
})
|
|
|
|
// Boot / PXE endpoints
|
|
r.Get("/ipxe/{mac}", d.BootAPI.IPXEScript)
|
|
r.Post("/api/boot/answer", d.BootAPI.AnswerFile)
|
|
r.Post("/api/hosts/{id}/installed", d.BootAPI.InstallComplete)
|
|
r.Get("/api/hosts/{id}/first-boot-script", d.BootAPI.FirstBootScript)
|
|
r.Post("/api/hosts/{id}/phone-home", d.BootAPI.PhoneHome)
|
|
|
|
return r
|
|
}
|