Files
worker/internal/webapp/handlers_checks.go
Gleb Tv 2c7a0236da feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
2026-07-13 17:55:14 +03:00

85 строки
3.3 KiB
Go

package webapp
import "net/http"
// handleChecks renders the worker's recent result rows. The
// runner's in-memory ring buffer supplies the data; Phase 1 reads
// from it directly with no extra caching.
//
// A "Run now" button is rendered on the page but stays disabled
// until the control plane accepts one-off check hints. The hint
// protocol is gated on the worker-notifier MVP plan
// (docs/plans/worker-notifier-mvp.md §N) and on
// docs/plans/separate-checks.md §11.6; once both are in place the
// RunNowEnabled flag flips to true and the handler reads
// s.deps.Runner.SubmitCheckHint(...) instead of the placeholder.
func (s *Server) handleChecks(w http.ResponseWriter, r *http.Request) {
s.renderRunnerPage(w, r, "Recent checks", "checks.html", func() any {
return checksPageData{
basePageData: s.newBasePage(r, "Recent checks", sessionFromContextOrEmpty(r)),
Rows: s.deps.Runner.RecentResults(50),
RunNowEnabled: false,
RunNowTooltip: "Run-now lands with the worker hint protocol (worker-notifier-mvp.md §N + separate-checks.md §11.6).",
}
})
}
type checksPageData struct {
basePageData
Rows []ResultRow
RunNowEnabled bool
RunNowTooltip string
}
// handleNotifications renders the worker's recent notification
// rows. Phase 1 only emits selfcheck alerts (email/telegram via the
// cached credentials), but the runner ring buffer is shape-stable
// for the main-app-issued notifications coming online in a later
// phase.
//
// A "Resend" button is rendered on the page but stays disabled
// until the worker resend protocol exists. Same gating as the
// "Run now" button on /checks.
func (s *Server) handleNotifications(w http.ResponseWriter, r *http.Request) {
s.renderRunnerPage(w, r, "Recent notifications", "notifications.html", func() any {
return notificationsPageData{
basePageData: s.newBasePage(r, "Recent notifications", sessionFromContextOrEmpty(r)),
Rows: s.deps.Runner.RecentNotifications(50),
ResendEnabled: false,
ResendTooltip: "Resend lands with the worker notification resend protocol (worker-notifier-mvp.md §N).",
}
})
}
type notificationsPageData struct {
basePageData
Rows []NotificationRow
ResendEnabled bool
ResendTooltip string
}
// renderRunnerPage is the small boilerplate-killer shared by
// handleChecks / handleNotifications / handleApps: write the
// no-store header, look up the session, build the page data via the
// caller-supplied closure, execute the template, and log + 500 on
// error. The closure receives no arguments because each handler
// already has its own copy of *Server and *http.Request in scope
// (this method is bound to *Server, so the closure captures them).
func (s *Server) renderRunnerPage(w http.ResponseWriter, _ *http.Request, logName, tmpl string, build func() any) {
writeNoStore(w)
if err := s.templates.Execute(w, tmpl, build()); err != nil {
s.deps.Logger.Printf("render %s: %v", logName, err)
http.Error(w, "template error", http.StatusInternalServerError)
}
}
// sessionFromContextOrEmpty is a thin wrapper around
// sessionFromContext that returns a nil session instead of a bool,
// for handlers that pass the session straight into a page-data
// struct (the Session zero value is harmless for template
// rendering).
func sessionFromContextOrEmpty(r *http.Request) *Session {
sess, _ := sessionFromContext(r.Context())
return sess
}