aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2024-07-18 16:15:08 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2024-07-18 16:21:19 -0700
commitb6e3be945680d017874967ae72ef86ee4235dcc2 (patch)
tree1dfe96f69563021d83132d0c18452c0870f9d673 /internal
parentfd740bc499ebc6d3a381479316f74cdc736d02de (diff)
Add initial HTTP API endpoints
Diffstat (limited to 'internal')
-rw-r--r--internal/mobius/api.go96
1 files changed, 96 insertions, 0 deletions
diff --git a/internal/mobius/api.go b/internal/mobius/api.go
new file mode 100644
index 0000000..31755b8
--- /dev/null
+++ b/internal/mobius/api.go
@@ -0,0 +1,96 @@
+package mobius
+
+import (
+ "bytes"
+ "encoding/json"
+ "github.com/jhalter/mobius/hotline"
+ "io"
+ "log"
+ "log/slog"
+ "net/http"
+)
+
+type logResponseWriter struct {
+ http.ResponseWriter
+ statusCode int
+ buf bytes.Buffer
+}
+
+func NewLogResponseWriter(w http.ResponseWriter) *logResponseWriter {
+ return &logResponseWriter{w, http.StatusOK, bytes.Buffer{}}
+}
+
+func (lrw *logResponseWriter) WriteHeader(code int) {
+ lrw.statusCode = code
+ lrw.ResponseWriter.WriteHeader(code)
+}
+
+func (lrw *logResponseWriter) Write(b []byte) (int, error) {
+ lrw.buf.Write(b)
+ return lrw.ResponseWriter.Write(b)
+}
+
+type APIServer struct {
+ hlServer *hotline.Server
+ logger *slog.Logger
+ mux *http.ServeMux
+}
+
+func (srv *APIServer) logMiddleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ lrw := NewLogResponseWriter(w)
+ next.ServeHTTP(lrw, r)
+
+ srv.logger.Info("req", "method", r.Method, "url", r.URL.Path, "remoteAddr", r.RemoteAddr, "response_code", lrw.statusCode)
+ })
+}
+
+func NewAPIServer(hlServer *hotline.Server, reloadFunc func(), logger *slog.Logger) *APIServer {
+ srv := APIServer{
+ hlServer: hlServer,
+ logger: logger,
+ mux: http.NewServeMux(),
+ }
+
+ srv.mux.Handle("/api/v1/reload", srv.logMiddleware(http.HandlerFunc(srv.ReloadHandler(reloadFunc))))
+ srv.mux.Handle("/api/v1/shutdown", srv.logMiddleware(http.HandlerFunc(srv.ShutdownHandler)))
+ srv.mux.Handle("/api/v1/stats", srv.logMiddleware(http.HandlerFunc(srv.RenderStats)))
+
+ return &srv
+}
+
+func (srv *APIServer) ShutdownHandler(w http.ResponseWriter, r *http.Request) {
+ msg, err := io.ReadAll(r.Body)
+ if err != nil || len(msg) == 0 {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ go srv.hlServer.Shutdown(msg)
+
+ _, _ = io.WriteString(w, `{ "msg": "server shutting down" }`)
+}
+
+func (srv *APIServer) ReloadHandler(reloadFunc func()) func(w http.ResponseWriter, _ *http.Request) {
+ return func(w http.ResponseWriter, _ *http.Request) {
+ reloadFunc()
+
+ _, _ = io.WriteString(w, `{ "msg": "config reloaded" }`)
+ }
+}
+
+func (srv *APIServer) RenderStats(w http.ResponseWriter, _ *http.Request) {
+ u, err := json.Marshal(srv.hlServer.CurrentStats())
+ if err != nil {
+ panic(err)
+ }
+
+ _, _ = io.WriteString(w, string(u))
+}
+
+func (srv *APIServer) Serve(port string) {
+ err := http.ListenAndServe(port, srv.mux)
+ if err != nil {
+ log.Fatal(err)
+ }
+}