Files
styx/proxy/admin.go
2025-09-26 08:49:53 +02:00

146 lines
3.1 KiB
Go

package proxy
import (
"bytes"
"encoding/json"
"encoding/pem"
"errors"
"net/http"
"os"
"strconv"
"strings"
"time"
"git.maze.io/maze/styx/internal/log"
)
type Admin struct {
*Proxy
}
func NewAdmin(proxy *Proxy) *Admin {
a := &Admin{
Proxy: proxy,
}
return a
}
func (a *Admin) handleRequest(ses *Session) error {
var (
logger = ses.log()
err error
)
switch ses.request.URL.Path {
case "/ca.crt":
err = a.handleCACert(ses)
case "/api/v1/policy":
err = a.apiPolicy(ses)
case "/api/v1/policy/matcher":
err = a.apiPolicyMatcher(ses)
case "/api/v1/stats/log":
err = a.apiStatsLog(ses)
case "/api/v1/stats/status":
err = a.apiStatsStatus(ses)
default:
if strings.HasPrefix(ses.request.URL.Path, "/api") {
err = errors.New("invalid endpoint")
} else {
err = os.ErrNotExist
}
}
if err != nil {
logger.Warn().Err(err).Msg("admin error")
ses.response = ErrorResponse(ses.request, err)
defer log.OnCloseError(logger.Debug(), ses.response.Body)
ses.response.Close = true
return a.writeResponse(ses)
}
return err
}
func (a *Admin) handleCACert(ses *Session) error {
b := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: a.authority.Certificate().Raw,
})
ses.response = NewResponse(http.StatusOK, bytes.NewReader(b), ses.request)
defer log.OnCloseError(log.Debug(), ses.response.Body)
ses.response.Close = true
ses.response.Header.Set("Content-Type", "application/x-x509-ca-cert")
ses.response.ContentLength = int64(len(b))
return a.writeResponse(ses)
}
func (a *Admin) apiPolicy(ses *Session) error {
var (
b = new(bytes.Buffer)
e = json.NewEncoder(b)
)
e.SetIndent("", " ")
if err := e.Encode(a.config.Policy); err != nil {
return err
}
ses.response = NewJSONResponse(http.StatusOK, b, ses.request)
defer log.OnCloseError(log.Debug(), ses.response.Body)
ses.response.Close = true
return a.writeResponse(ses)
}
func (a *Admin) apiPolicyMatcher(ses *Session) error {
var (
b = new(bytes.Buffer)
e = json.NewEncoder(b)
)
e.SetIndent("", " ")
if err := e.Encode(a.config.Policy.Matchers); err != nil {
return err
}
ses.response = NewJSONResponse(http.StatusOK, b, ses.request)
defer log.OnCloseError(log.Debug(), ses.response.Body)
ses.response.Close = true
return a.writeResponse(ses)
}
func (a *Admin) apiResponse(ses *Session, v any, err error) error {
if err != nil {
return err
}
var (
b = new(bytes.Buffer)
e = json.NewEncoder(b)
)
e.SetIndent("", " ")
if err := e.Encode(v); err != nil {
return err
}
ses.response = NewJSONResponse(http.StatusOK, b, ses.request)
defer log.OnCloseError(log.Debug(), ses.response.Body)
ses.response.Close = true
return a.writeResponse(ses)
}
func (a *Admin) apiStatsLog(ses *Session) error {
var (
query = ses.request.URL.Query()
offset, _ = strconv.Atoi(query.Get("offset"))
limit, _ = strconv.Atoi(query.Get("limit"))
)
if limit > 100 {
limit = 100
}
s, err := a.stats.QueryLog(offset, limit)
return a.apiResponse(ses, s, err)
}
func (a *Admin) apiStatsStatus(ses *Session) error {
s, err := a.stats.QueryStatus(time.Time{})
return a.apiResponse(ses, s, err)
}