Checkpoint
This commit is contained in:
146
admin/admin.go
Normal file
146
admin/admin.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"git.maze.io/maze/styx/dataset"
|
||||
"git.maze.io/maze/styx/logger"
|
||||
"git.maze.io/maze/styx/proxy"
|
||||
)
|
||||
|
||||
type Admin struct {
|
||||
Storage dataset.Storage
|
||||
setupOnce sync.Once
|
||||
mux *http.ServeMux
|
||||
api *http.ServeMux
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
Code int
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err apiError) Error() string {
|
||||
return err.Err.Error()
|
||||
}
|
||||
|
||||
func (a *Admin) setup() {
|
||||
a.mux = http.NewServeMux()
|
||||
|
||||
a.api = http.NewServeMux()
|
||||
a.api.HandleFunc("GET /groups", a.apiGroups)
|
||||
a.api.HandleFunc("POST /group", a.apiGroupCreate)
|
||||
a.api.HandleFunc("GET /group/{id}", a.apiGroup)
|
||||
a.api.HandleFunc("PATCH /group/{id}", a.apiGroupUpdate)
|
||||
a.api.HandleFunc("DELETE /group/{id}", a.apiGroupDelete)
|
||||
a.api.HandleFunc("GET /clients", a.apiClients)
|
||||
a.api.HandleFunc("GET /client/{id}", a.apiClient)
|
||||
a.api.HandleFunc("POST /client", a.apiClientCreate)
|
||||
a.api.HandleFunc("PATCH /client/{id}", a.apiClientUpdate)
|
||||
a.api.HandleFunc("DELETE /client/{id}", a.apiClientDelete)
|
||||
a.api.HandleFunc("GET /lists", a.apiLists)
|
||||
a.api.HandleFunc("POST /list", a.apiListCreate)
|
||||
a.api.HandleFunc("GET /list/{id}", a.apiList)
|
||||
a.api.HandleFunc("DELETE /list/{id}", a.apiListDelete)
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
Handle(pattern string, handler http.Handler)
|
||||
}
|
||||
|
||||
func (a *Admin) Install(handler Handler) {
|
||||
a.setupOnce.Do(a.setup)
|
||||
handler.Handle("/api/v1/", http.StripPrefix("/api/v1", a.api))
|
||||
}
|
||||
|
||||
func (a *Admin) handleAPIError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
code := http.StatusBadRequest
|
||||
switch {
|
||||
case dataset.IsNotExist(err):
|
||||
code = http.StatusNotFound
|
||||
case os.IsPermission(err):
|
||||
code = http.StatusForbidden
|
||||
case errors.Is(err, apiError{}):
|
||||
if c := err.(apiError).Code; c > 0 {
|
||||
code = c
|
||||
}
|
||||
}
|
||||
|
||||
logger.StandardLog.Err(err).Values(logger.Values{
|
||||
"code": code,
|
||||
"client": r.RemoteAddr,
|
||||
"method": r.Method,
|
||||
"path": r.URL.Path,
|
||||
}).Warn("Unexpected API error encountered")
|
||||
|
||||
var data []byte
|
||||
if err, ok := err.(apiError); ok {
|
||||
data, _ = json.Marshal(struct {
|
||||
Code int `json:"code"`
|
||||
Error string `json:"error"`
|
||||
}{code, err.Error()})
|
||||
} else {
|
||||
data, _ = json.Marshal(struct {
|
||||
Code int `json:"code"`
|
||||
Error string `json:"error"`
|
||||
}{code, http.StatusText(code)})
|
||||
}
|
||||
|
||||
res := proxy.NewResponse(code, io.NopCloser(bytes.NewReader(data)), r)
|
||||
res.Header.Set(proxy.HeaderContentType, "application/json")
|
||||
|
||||
for k, vv := range res.Header {
|
||||
if len(vv) >= 1 {
|
||||
w.Header().Set(k, vv[0])
|
||||
for _, v := range vv[1:] {
|
||||
w.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.WriteHeader(code)
|
||||
io.Copy(w, res.Body)
|
||||
}
|
||||
|
||||
func (a *Admin) jsonResponse(w http.ResponseWriter, r *http.Request, value any, codes ...int) {
|
||||
var (
|
||||
code = http.StatusNoContent
|
||||
body io.ReadCloser
|
||||
size int64
|
||||
)
|
||||
if value != nil {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
a.handleAPIError(w, r, err)
|
||||
return
|
||||
}
|
||||
code = http.StatusOK
|
||||
body = io.NopCloser(bytes.NewReader(data))
|
||||
size = int64(len(data))
|
||||
}
|
||||
if len(codes) > 0 {
|
||||
code = codes[0]
|
||||
}
|
||||
|
||||
res := proxy.NewResponse(code, body, r)
|
||||
res.Close = true
|
||||
res.Header.Set(proxy.HeaderContentLength, strconv.FormatInt(size, 10))
|
||||
res.Header.Set(proxy.HeaderContentType, "application/json")
|
||||
|
||||
for k, vv := range res.Header {
|
||||
if len(vv) >= 1 {
|
||||
w.Header().Set(k, vv[0])
|
||||
for _, v := range vv[1:] {
|
||||
w.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.WriteHeader(code)
|
||||
io.Copy(w, res.Body)
|
||||
}
|
Reference in New Issue
Block a user