]>
Commit | Line | Data |
---|---|---|
1 | package mobius | |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | "encoding/json" | |
6 | "github.com/jhalter/mobius/hotline" | |
7 | "io" | |
8 | "log" | |
9 | "log/slog" | |
10 | "net/http" | |
11 | ) | |
12 | ||
13 | type logResponseWriter struct { | |
14 | http.ResponseWriter | |
15 | statusCode int | |
16 | buf bytes.Buffer | |
17 | } | |
18 | ||
19 | func NewLogResponseWriter(w http.ResponseWriter) *logResponseWriter { | |
20 | return &logResponseWriter{w, http.StatusOK, bytes.Buffer{}} | |
21 | } | |
22 | ||
23 | func (lrw *logResponseWriter) WriteHeader(code int) { | |
24 | lrw.statusCode = code | |
25 | lrw.ResponseWriter.WriteHeader(code) | |
26 | } | |
27 | ||
28 | func (lrw *logResponseWriter) Write(b []byte) (int, error) { | |
29 | lrw.buf.Write(b) | |
30 | return lrw.ResponseWriter.Write(b) | |
31 | } | |
32 | ||
33 | type APIServer struct { | |
34 | hlServer *hotline.Server | |
35 | logger *slog.Logger | |
36 | mux *http.ServeMux | |
37 | } | |
38 | ||
39 | func (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 | ||
48 | func 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 | ||
62 | func (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 | ||
74 | func (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 | ||
82 | func (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 | ||
91 | func (srv *APIServer) Serve(port string) { | |
92 | err := http.ListenAndServe(port, srv.mux) | |
93 | if err != nil { | |
94 | log.Fatal(err) | |
95 | } | |
96 | } |