2025-01-18 17:22:08 -05:00
|
|
|
package rest
|
|
|
|
|
|
|
|
import (
|
2025-01-19 21:51:39 -05:00
|
|
|
"errors"
|
2025-01-18 17:22:08 -05:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2025-01-20 16:31:43 -05:00
|
|
|
"strings"
|
2025-01-19 21:51:39 -05:00
|
|
|
"time"
|
2025-01-18 17:22:08 -05:00
|
|
|
|
|
|
|
"dynatron.me/x/stillbox/internal/forms"
|
2025-01-19 21:51:39 -05:00
|
|
|
"dynatron.me/x/stillbox/pkg/rbac"
|
|
|
|
"dynatron.me/x/stillbox/pkg/shares"
|
2025-01-18 17:22:08 -05:00
|
|
|
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
)
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
var (
|
|
|
|
ErrBadShare = errors.New("bad share request type")
|
|
|
|
)
|
|
|
|
|
|
|
|
type ShareRequestType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
ShareRequestCall ShareRequestType = "call"
|
|
|
|
ShareRequestIncident ShareRequestType = "incident"
|
|
|
|
ShareRequestIncidentM3U ShareRequestType = "m3u"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (rt ShareRequestType) IsValid() bool {
|
|
|
|
switch rt {
|
|
|
|
case ShareRequestCall, ShareRequestIncident, ShareRequestIncidentM3U:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
type ShareHandlers map[shares.EntityType]http.Handler
|
2025-01-18 17:22:08 -05:00
|
|
|
type shareAPI struct {
|
|
|
|
baseURL *url.URL
|
2025-01-20 16:31:43 -05:00
|
|
|
}
|
2025-01-19 21:51:39 -05:00
|
|
|
|
2025-01-20 16:31:43 -05:00
|
|
|
type ShareAPI interface {
|
|
|
|
API
|
|
|
|
ShareMiddleware() func(http.Handler) http.Handler
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-20 16:31:43 -05:00
|
|
|
func newShareAPI(baseURL *url.URL) ShareAPI {
|
2025-01-19 21:51:39 -05:00
|
|
|
return &shareAPI{
|
|
|
|
baseURL: baseURL,
|
2025-01-20 16:31:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2025-01-19 21:51:39 -05:00
|
|
|
}
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
func (sa *shareAPI) Subrouter() http.Handler {
|
2025-01-18 17:22:08 -05:00
|
|
|
r := chi.NewMux()
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
r.Post(`/create`, sa.createShare)
|
|
|
|
r.Delete(`/{id:[A-Za-z0-9_-]{20,}}`, sa.deleteShare)
|
2025-01-18 17:22:08 -05:00
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2025-01-20 16:31:43 -05:00
|
|
|
//func (sa *shareAPI) PublicRoutes(r chi.Router) http.Handler {
|
|
|
|
// r.Get(`/{type}/{id:[A-Za-z0-9_-]{20,}}`, sa.getShare)
|
|
|
|
// r.Get(`/{type}/{id:[A-Za-z0-9_-]{20,}}*`, sa.getShare)
|
|
|
|
//}
|
2025-01-18 17:22:08 -05:00
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
func (sa *shareAPI) createShare(w http.ResponseWriter, r *http.Request) {
|
2025-01-18 17:22:08 -05:00
|
|
|
ctx := r.Context()
|
2025-01-19 21:51:39 -05:00
|
|
|
shs := shares.FromCtx(ctx)
|
|
|
|
|
|
|
|
p := shares.CreateShareParams{}
|
2025-01-18 17:22:08 -05:00
|
|
|
|
|
|
|
err := forms.Unmarshal(r, &p, forms.WithTag("json"), forms.WithAcceptBlank(), forms.WithOmitEmpty())
|
|
|
|
if err != nil {
|
|
|
|
wErr(w, r, badRequest(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
sh, err := shs.NewShare(ctx, p)
|
2025-01-18 17:22:08 -05:00
|
|
|
if err != nil {
|
|
|
|
wErr(w, r, autoError(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
respond(w, r, sh)
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
func (sa *shareAPI) deleteShare(w http.ResponseWriter, r *http.Request) {
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-20 16:31:43 -05:00
|
|
|
func (sa *shareAPI) getShare(r *http.Request) (*http.Request, error) {
|
2025-01-18 17:22:08 -05:00
|
|
|
ctx := r.Context()
|
2025-01-19 21:51:39 -05:00
|
|
|
shs := shares.FromCtx(ctx)
|
2025-01-18 17:22:08 -05:00
|
|
|
|
2025-01-20 16:31:43 -05:00
|
|
|
params := struct {
|
|
|
|
Type string `param:"type"`
|
|
|
|
ID string `param:"shareId"`
|
|
|
|
}{}
|
|
|
|
|
|
|
|
err := decodeParams(¶ms, r)
|
2025-01-18 17:22:08 -05:00
|
|
|
if err != nil {
|
2025-01-20 16:31:43 -05:00
|
|
|
return nil, err
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-20 16:31:43 -05:00
|
|
|
rType := ShareRequestType(params.Type)
|
|
|
|
id := params.ID
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
if !rType.IsValid() {
|
2025-01-20 16:31:43 -05:00
|
|
|
return nil, ErrBadShare
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
sh, err := shs.GetShare(ctx, id)
|
2025-01-18 17:22:08 -05:00
|
|
|
if err != nil {
|
2025-01-20 16:31:43 -05:00
|
|
|
return nil, err
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
if sh.Expiration != nil && sh.Expiration.Time().Before(time.Now()) {
|
2025-01-20 16:31:43 -05:00
|
|
|
return nil, shares.ErrNoShare
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
ctx = rbac.CtxWithSubject(ctx, sh)
|
|
|
|
r = r.WithContext(ctx)
|
2025-01-18 17:22:08 -05:00
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
switch rType {
|
|
|
|
case ShareRequestCall, ShareRequestIncident:
|
2025-01-20 16:31:43 -05:00
|
|
|
r.URL.Path += fmt.Sprintf("/%s/%s", rType, sh.EntityID.String())
|
2025-01-19 21:51:39 -05:00
|
|
|
case ShareRequestIncidentM3U:
|
|
|
|
r.URL.Path = fmt.Sprintf("/incident/%s.m3u", sh.EntityID.String())
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-20 16:31:43 -05:00
|
|
|
return r, nil
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
// idOnlyParam checks for a sole URL parameter, id, and writes an errorif this fails.
|
|
|
|
func shareParams(w http.ResponseWriter, r *http.Request) (rType ShareRequestType, rID string, err error) {
|
|
|
|
params := struct {
|
|
|
|
Type string `param:"type"`
|
|
|
|
ID string `param:"id"`
|
|
|
|
}{}
|
2025-01-18 17:22:08 -05:00
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
err = decodeParams(¶ms, r)
|
2025-01-18 17:22:08 -05:00
|
|
|
if err != nil {
|
2025-01-19 21:51:39 -05:00
|
|
|
wErr(w, r, badRequest(err))
|
|
|
|
return "", "", err
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|
|
|
|
|
2025-01-19 21:51:39 -05:00
|
|
|
return ShareRequestType(params.Type), params.ID, nil
|
2025-01-18 17:22:08 -05:00
|
|
|
}
|