]> git.r.bdr.sh - rbdr/mobius/blame_incremental - internal/mobius/api.go
Account for the root
[rbdr/mobius] / internal / mobius / api.go
... / ...
CommitLineData
1package mobius
2
3import (
4 "bytes"
5 "encoding/json"
6 "github.com/jhalter/mobius/hotline"
7 "io"
8 "log"
9 "log/slog"
10 "net/http"
11)
12
13type logResponseWriter struct {
14 http.ResponseWriter
15 statusCode int
16 buf bytes.Buffer
17}
18
19func NewLogResponseWriter(w http.ResponseWriter) *logResponseWriter {
20 return &logResponseWriter{w, http.StatusOK, bytes.Buffer{}}
21}
22
23func (lrw *logResponseWriter) WriteHeader(code int) {
24 lrw.statusCode = code
25 lrw.ResponseWriter.WriteHeader(code)
26}
27
28func (lrw *logResponseWriter) Write(b []byte) (int, error) {
29 lrw.buf.Write(b)
30 return lrw.ResponseWriter.Write(b)
31}
32
33type APIServer struct {
34 hlServer *hotline.Server
35 logger *slog.Logger
36 mux *http.ServeMux
37}
38
39func (srv *APIServer) logMiddleware(next http.Handler) http.Handler {
40 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
41 lrw := NewLogResponseWriter(w)
42 next.ServeHTTP(lrw, r)
43
44 srv.logger.Info("req", "method", r.Method, "url", r.URL.Path, "remoteAddr", r.RemoteAddr, "response_code", lrw.statusCode)
45 })
46}
47
48func NewAPIServer(hlServer *hotline.Server, reloadFunc func(), logger *slog.Logger) *APIServer {
49 srv := APIServer{
50 hlServer: hlServer,
51 logger: logger,
52 mux: http.NewServeMux(),
53 }
54
55 srv.mux.Handle("/api/v1/reload", srv.logMiddleware(http.HandlerFunc(srv.ReloadHandler(reloadFunc))))
56 srv.mux.Handle("/api/v1/shutdown", srv.logMiddleware(http.HandlerFunc(srv.ShutdownHandler)))
57 srv.mux.Handle("/api/v1/stats", srv.logMiddleware(http.HandlerFunc(srv.RenderStats)))
58
59 return &srv
60}
61
62func (srv *APIServer) ShutdownHandler(w http.ResponseWriter, r *http.Request) {
63 msg, err := io.ReadAll(r.Body)
64 if err != nil || len(msg) == 0 {
65 w.WriteHeader(http.StatusBadRequest)
66 return
67 }
68
69 go srv.hlServer.Shutdown(msg)
70
71 _, _ = io.WriteString(w, `{ "msg": "server shutting down" }`)
72}
73
74func (srv *APIServer) ReloadHandler(reloadFunc func()) func(w http.ResponseWriter, _ *http.Request) {
75 return func(w http.ResponseWriter, _ *http.Request) {
76 reloadFunc()
77
78 _, _ = io.WriteString(w, `{ "msg": "config reloaded" }`)
79 }
80}
81
82func (srv *APIServer) RenderStats(w http.ResponseWriter, _ *http.Request) {
83 u, err := json.Marshal(srv.hlServer.CurrentStats())
84 if err != nil {
85 panic(err)
86 }
87
88 _, _ = io.WriteString(w, string(u))
89}
90
91func (srv *APIServer) Serve(port string) {
92 err := http.ListenAndServe(port, srv.mux)
93 if err != nil {
94 log.Fatal(err)
95 }
96}