wip
This commit is contained in:
parent
3116874247
commit
a9f64f74fb
6 changed files with 104 additions and 51 deletions
|
@ -18,52 +18,64 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type APIRoot interface {
|
||||||
|
API
|
||||||
|
Shares() ShareAPI
|
||||||
|
ShareSubroutes(chi.Router)
|
||||||
|
}
|
||||||
|
|
||||||
type API interface {
|
type API interface {
|
||||||
Subrouter() http.Handler
|
Subrouter() http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublicAPI interface {
|
type ShareableAPI interface {
|
||||||
API
|
API
|
||||||
PublicRoutes(r chi.Router)
|
GETSubroutes(chi.Router)
|
||||||
}
|
}
|
||||||
|
|
||||||
type api struct {
|
type api struct {
|
||||||
baseURL url.URL
|
baseURL *url.URL
|
||||||
share publicAPI
|
shares ShareAPI
|
||||||
|
tgs API
|
||||||
|
calls ShareableAPI
|
||||||
|
users API
|
||||||
|
incidents ShareableAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
type publicAPI interface {
|
func (a *api) Shares() ShareAPI {
|
||||||
API
|
return a.shares
|
||||||
PublicRouter() http.Handler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(baseURL url.URL) *api {
|
func New(baseURL url.URL) *api {
|
||||||
s := &api{
|
s := &api{
|
||||||
baseURL: baseURL,
|
baseURL: &baseURL,
|
||||||
|
shares: newShareAPI(&baseURL),
|
||||||
|
tgs: new(talkgroupAPI),
|
||||||
|
calls: new(callsAPI),
|
||||||
|
incidents: newIncidentsAPI(&baseURL),
|
||||||
|
users: new(usersAPI),
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) PublicRoutes(r chi.Router) {
|
|
||||||
r.Mount("/share", a.share.PublicRouter())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) Subrouter() http.Handler {
|
func (a *api) Subrouter() http.Handler {
|
||||||
r := chi.NewMux()
|
r := chi.NewMux()
|
||||||
|
|
||||||
r.Mount("/talkgroup", new(talkgroupAPI).Subrouter())
|
r.Mount("/talkgroup", a.tgs.Subrouter())
|
||||||
r.Mount("/user", new(usersAPI).Subrouter())
|
r.Mount("/user", a.users.Subrouter())
|
||||||
r.Mount("/call", new(callsAPI).Subrouter())
|
r.Mount("/call", a.calls.Subrouter())
|
||||||
r.Mount("/incident", newIncidentsAPI(&a.baseURL).Subrouter())
|
r.Mount("/incident", a.incidents.Subrouter())
|
||||||
|
r.Mount("/share", a.shares.Subrouter())
|
||||||
a.share = newShareAPI(&a.baseURL, r)
|
|
||||||
|
|
||||||
r.Mount("/share", a.share.Subrouter())
|
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *api) ShareSubroutes(r chi.Router) {
|
||||||
|
r.Route("/calls", a.calls.GETSubroutes)
|
||||||
|
r.Route("/incidents", a.incidents.GETSubroutes)
|
||||||
|
}
|
||||||
|
|
||||||
type errResponse struct {
|
type errResponse struct {
|
||||||
Err error `json:"-"`
|
Err error `json:"-"`
|
||||||
Code int `json:"-"`
|
Code int `json:"-"`
|
||||||
|
|
|
@ -30,14 +30,17 @@ type callsAPI struct {
|
||||||
func (ca *callsAPI) Subrouter() http.Handler {
|
func (ca *callsAPI) Subrouter() http.Handler {
|
||||||
r := chi.NewMux()
|
r := chi.NewMux()
|
||||||
|
|
||||||
r.Get(`/{call:[a-f0-9-]+}`, ca.getAudio)
|
ca.GETSubroutes(r)
|
||||||
r.Get(`/{call:[a-f0-9-]+}/{download:download}`, ca.getAudio)
|
|
||||||
|
|
||||||
r.Post(`/`, ca.listCalls)
|
r.Post(`/`, ca.listCalls)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ca *callsAPI) GETSubroutes(r chi.Router) {
|
||||||
|
r.Get(`/{call:[a-f0-9-]+}`, ca.getAudio)
|
||||||
|
r.Get(`/{call:[a-f0-9-]+}/{download:download}`, ca.getAudio)
|
||||||
|
}
|
||||||
|
|
||||||
func (ca *callsAPI) getAudio(w http.ResponseWriter, r *http.Request) {
|
func (ca *callsAPI) getAudio(w http.ResponseWriter, r *http.Request) {
|
||||||
p := struct {
|
p := struct {
|
||||||
CallID *uuid.UUID `param:"call"`
|
CallID *uuid.UUID `param:"call"`
|
||||||
|
|
|
@ -22,16 +22,14 @@ type incidentsAPI struct {
|
||||||
baseURL *url.URL
|
baseURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIncidentsAPI(baseURL *url.URL) API {
|
func newIncidentsAPI(baseURL *url.URL) ShareableAPI {
|
||||||
return &incidentsAPI{baseURL}
|
return &incidentsAPI{baseURL}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ia *incidentsAPI) Subrouter() http.Handler {
|
func (ia *incidentsAPI) Subrouter() http.Handler {
|
||||||
r := chi.NewMux()
|
r := chi.NewMux()
|
||||||
|
|
||||||
r.Get(`/{id:[a-f0-9-]+}`, ia.getIncident)
|
ia.GETSubroutes(r)
|
||||||
r.Get(`/{id:[a-f0-9-]+}.m3u`, ia.getCallsM3U)
|
|
||||||
|
|
||||||
r.Post(`/new`, ia.createIncident)
|
r.Post(`/new`, ia.createIncident)
|
||||||
r.Post(`/`, ia.listIncidents)
|
r.Post(`/`, ia.listIncidents)
|
||||||
r.Post(`/{id:[a-f0-9-]+}/calls`, ia.postCalls)
|
r.Post(`/{id:[a-f0-9-]+}/calls`, ia.postCalls)
|
||||||
|
@ -43,6 +41,11 @@ func (ia *incidentsAPI) Subrouter() http.Handler {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ia *incidentsAPI) GETSubroutes(r chi.Router) {
|
||||||
|
r.Get(`/{id:[a-f0-9-]+}`, ia.getIncident)
|
||||||
|
r.Get(`/{id:[a-f0-9-]+}.m3u`, ia.getCallsM3U)
|
||||||
|
}
|
||||||
|
|
||||||
func (ia *incidentsAPI) listIncidents(w http.ResponseWriter, r *http.Request) {
|
func (ia *incidentsAPI) listIncidents(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
incs := incstore.FromCtx(ctx)
|
incs := incstore.FromCtx(ctx)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dynatron.me/x/stillbox/internal/forms"
|
"dynatron.me/x/stillbox/internal/forms"
|
||||||
|
@ -38,14 +39,36 @@ func (rt ShareRequestType) IsValid() bool {
|
||||||
type ShareHandlers map[shares.EntityType]http.Handler
|
type ShareHandlers map[shares.EntityType]http.Handler
|
||||||
type shareAPI struct {
|
type shareAPI struct {
|
||||||
baseURL *url.URL
|
baseURL *url.URL
|
||||||
|
|
||||||
router http.Handler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newShareAPI(baseURL *url.URL, hand http.Handler) publicAPI {
|
type ShareAPI interface {
|
||||||
|
API
|
||||||
|
ShareMiddleware() func(http.Handler) http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func newShareAPI(baseURL *url.URL) ShareAPI {
|
||||||
return &shareAPI{
|
return &shareAPI{
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
router: hand,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *shareAPI) ShareMiddleware() func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !strings.HasPrefix(r.URL.Path, "/share/") {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nr, err := a.getShare(r)
|
||||||
|
if err != nil {
|
||||||
|
wErr(w, r, autoError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, nr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,14 +81,10 @@ func (sa *shareAPI) Subrouter() http.Handler {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sa *shareAPI) PublicRouter() http.Handler {
|
//func (sa *shareAPI) PublicRoutes(r chi.Router) http.Handler {
|
||||||
r := chi.NewMux()
|
// r.Get(`/{type}/{id:[A-Za-z0-9_-]{20,}}`, sa.getShare)
|
||||||
|
// r.Get(`/{type}/{id:[A-Za-z0-9_-]{20,}}*`, sa.getShare)
|
||||||
r.Get(`/{type}/{id:[A-Za-z0-9_-]{20,}}`, sa.getShare)
|
//}
|
||||||
r.Get(`/{type}/{id:[A-Za-z0-9_-]{20,}}*`, sa.getShare)
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sa *shareAPI) createShare(w http.ResponseWriter, r *http.Request) {
|
func (sa *shareAPI) createShare(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
@ -91,28 +110,34 @@ func (sa *shareAPI) createShare(w http.ResponseWriter, r *http.Request) {
|
||||||
func (sa *shareAPI) deleteShare(w http.ResponseWriter, r *http.Request) {
|
func (sa *shareAPI) deleteShare(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sa *shareAPI) getShare(w http.ResponseWriter, r *http.Request) {
|
func (sa *shareAPI) getShare(r *http.Request) (*http.Request, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
shs := shares.FromCtx(ctx)
|
shs := shares.FromCtx(ctx)
|
||||||
|
|
||||||
rType, id, err := shareParams(w, r)
|
params := struct {
|
||||||
|
Type string `param:"type"`
|
||||||
|
ID string `param:"shareId"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := decodeParams(¶ms, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rType := ShareRequestType(params.Type)
|
||||||
|
id := params.ID
|
||||||
|
|
||||||
if !rType.IsValid() {
|
if !rType.IsValid() {
|
||||||
wErr(w, r, autoError(ErrBadShare))
|
return nil, ErrBadShare
|
||||||
}
|
}
|
||||||
|
|
||||||
sh, err := shs.GetShare(ctx, id)
|
sh, err := shs.GetShare(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wErr(w, r, autoError(err))
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sh.Expiration != nil && sh.Expiration.Time().Before(time.Now()) {
|
if sh.Expiration != nil && sh.Expiration.Time().Before(time.Now()) {
|
||||||
wErr(w, r, autoError(shares.ErrNoShare))
|
return nil, shares.ErrNoShare
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = rbac.CtxWithSubject(ctx, sh)
|
ctx = rbac.CtxWithSubject(ctx, sh)
|
||||||
|
@ -120,12 +145,12 @@ func (sa *shareAPI) getShare(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
switch rType {
|
switch rType {
|
||||||
case ShareRequestCall, ShareRequestIncident:
|
case ShareRequestCall, ShareRequestIncident:
|
||||||
r.URL.Path = fmt.Sprintf("/%s/%s", rType, sh.EntityID.String())
|
r.URL.Path += fmt.Sprintf("/%s/%s", rType, sh.EntityID.String())
|
||||||
case ShareRequestIncidentM3U:
|
case ShareRequestIncidentM3U:
|
||||||
r.URL.Path = fmt.Sprintf("/incident/%s.m3u", sh.EntityID.String())
|
r.URL.Path = fmt.Sprintf("/incident/%s.m3u", sh.EntityID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.router.ServeHTTP(w, r)
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// idOnlyParam checks for a sole URL parameter, id, and writes an errorif this fails.
|
// idOnlyParam checks for a sole URL parameter, id, and writes an errorif this fails.
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
@ -30,6 +31,7 @@ func (s *Server) setupRoutes() {
|
||||||
|
|
||||||
s.installPprof()
|
s.installPprof()
|
||||||
|
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
// authenticated routes
|
// authenticated routes
|
||||||
r.Use(s.auth.VerifyMiddleware(), s.auth.AuthMiddleware())
|
r.Use(s.auth.VerifyMiddleware(), s.auth.AuthMiddleware())
|
||||||
|
@ -39,6 +41,11 @@ func (s *Server) setupRoutes() {
|
||||||
r.Mount("/api", s.rest.Subrouter())
|
r.Mount("/api", s.rest.Subrouter())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.Route("/share/{type}/{shareId:[A-Za-z0-9_-]{20,}}", func(r chi.Router) {
|
||||||
|
r.Use(s.rest.Shares().ShareMiddleware())
|
||||||
|
s.rest.ShareSubroutes(r)
|
||||||
|
})
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
s.rateLimit(r)
|
s.rateLimit(r)
|
||||||
r.Use(render.SetContentType(render.ContentTypeJSON))
|
r.Use(render.SetContentType(render.ContentTypeJSON))
|
||||||
|
@ -51,7 +58,6 @@ func (s *Server) setupRoutes() {
|
||||||
s.rateLimit(r)
|
s.rateLimit(r)
|
||||||
r.Use(render.SetContentType(render.ContentTypeJSON))
|
r.Use(render.SetContentType(render.ContentTypeJSON))
|
||||||
s.auth.PublicRoutes(r)
|
s.auth.PublicRoutes(r)
|
||||||
s.rest.PublicRoutes(r)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
|
@ -62,6 +68,10 @@ func (s *Server) setupRoutes() {
|
||||||
|
|
||||||
s.clientRoute(r, clientRoot)
|
s.clientRoute(r, clientRoot)
|
||||||
})
|
})
|
||||||
|
chi.Walk(r, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
|
||||||
|
fmt.Printf("[%s]: '%s' has %d middlewares\n", method, route, len(middlewares))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithCtxStores is a middleware that installs all stores in the request context.
|
// WithCtxStores is a middleware that installs all stores in the request context.
|
||||||
|
|
|
@ -45,7 +45,7 @@ type Server struct {
|
||||||
notifier notify.Notifier
|
notifier notify.Notifier
|
||||||
hup chan os.Signal
|
hup chan os.Signal
|
||||||
tgs tgstore.Store
|
tgs tgstore.Store
|
||||||
rest rest.PublicAPI
|
rest rest.APIRoot
|
||||||
partman partman.PartitionManager
|
partman partman.PartitionManager
|
||||||
users users.Store
|
users users.Store
|
||||||
calls callstore.Store
|
calls callstore.Store
|
||||||
|
|
Loading…
Reference in a new issue