Incidents calls
This commit is contained in:
parent
80d3377187
commit
82d2d5c340
8 changed files with 123 additions and 30 deletions
|
@ -1,3 +1,6 @@
|
||||||
|
# this is used to compose URLs, for example in M3U responses.
|
||||||
|
# set it to what users access the instance as.
|
||||||
|
baseURL: "https://stillbox.example.com/"
|
||||||
db:
|
db:
|
||||||
connect: 'postgres://postgres:password@localhost:5432/example'
|
connect: 'postgres://postgres:password@localhost:5432/example'
|
||||||
partition:
|
partition:
|
||||||
|
|
57
internal/jsontypes/url.go
Normal file
57
internal/jsontypes/url.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package jsontypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type URL url.URL
|
||||||
|
|
||||||
|
func (u *URL) URL() url.URL {
|
||||||
|
return url.URL(*u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URL) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal(b, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ur, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*u = URL(*ur)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URL) UnmarshalYAML(n *yaml.Node) error {
|
||||||
|
var s string
|
||||||
|
|
||||||
|
err := n.Decode(&s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ur, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*u = URL(*ur)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URL) UnmarshalText(t []byte) error {
|
||||||
|
ur, err := url.Parse(string(t))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*u = URL(*ur)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package jsontypes
|
package jsontypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +19,28 @@ func (u *UUIDs) UUIDs() []uuid.UUID {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UUIDs) UnmarshalJSON(b []byte) error {
|
||||||
|
var ss []string
|
||||||
|
err := json.Unmarshal(b, &ss)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
usl := make([]UUID, 0, len(ss))
|
||||||
|
for _, s := range ss {
|
||||||
|
uu, err := uuid.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
usl = append(usl, UUID(uu))
|
||||||
|
}
|
||||||
|
|
||||||
|
*u = usl
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u UUID) UUID() uuid.UUID {
|
func (u UUID) UUID() uuid.UUID {
|
||||||
return uuid.UUID(u)
|
return uuid.UUID(u)
|
||||||
}
|
}
|
||||||
|
@ -26,12 +50,12 @@ func (u *UUID) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UUID) UnmarshalJSON(b []byte) error {
|
func (u *UUID) UnmarshalJSON(b []byte) error {
|
||||||
id, err := uuid.Parse(string(b[:]))
|
id, err := uuid.Parse(string(b[:]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*u = UUID(id)
|
*u = UUID(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UUID) UnmarshalText(t []byte) error {
|
func (u *UUID) UnmarshalText(t []byte) error {
|
||||||
|
|
|
@ -16,16 +16,17 @@ type Configuration struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DB DB `yaml:"db"`
|
BaseURL jsontypes.URL `yaml:"baseURL"`
|
||||||
CORS CORS `yaml:"cors"`
|
DB DB `yaml:"db"`
|
||||||
Auth Auth `yaml:"auth"`
|
CORS CORS `yaml:"cors"`
|
||||||
Alerting Alerting `yaml:"alerting"`
|
Auth Auth `yaml:"auth"`
|
||||||
Log []Logger `yaml:"log"`
|
Alerting Alerting `yaml:"alerting"`
|
||||||
Listen string `yaml:"listen"`
|
Log []Logger `yaml:"log"`
|
||||||
Public bool `yaml:"public"`
|
Listen string `yaml:"listen"`
|
||||||
RateLimit RateLimit `yaml:"rateLimit"`
|
Public bool `yaml:"public"`
|
||||||
Notify Notify `yaml:"notify"`
|
RateLimit RateLimit `yaml:"rateLimit"`
|
||||||
Relay []Relay `yaml:"relay"`
|
Notify Notify `yaml:"notify"`
|
||||||
|
Relay []Relay `yaml:"relay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (c *Configuration) read() error {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unmarshal err: %w", err)
|
return fmt.Errorf("config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -3,6 +3,7 @@ package rest
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
|
||||||
|
|
||||||
|
@ -19,10 +20,11 @@ type API interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type api struct {
|
type api struct {
|
||||||
|
baseURL url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *api {
|
func New(baseURL url.URL) *api {
|
||||||
s := new(api)
|
s := &api{baseURL}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -33,7 +35,7 @@ func (a *api) Subrouter() http.Handler {
|
||||||
r.Mount("/talkgroup", new(talkgroupAPI).Subrouter())
|
r.Mount("/talkgroup", new(talkgroupAPI).Subrouter())
|
||||||
r.Mount("/call", new(callsAPI).Subrouter())
|
r.Mount("/call", new(callsAPI).Subrouter())
|
||||||
r.Mount("/user", new(usersAPI).Subrouter())
|
r.Mount("/user", new(usersAPI).Subrouter())
|
||||||
r.Mount("/incident", new(incidentsAPI).Subrouter())
|
r.Mount("/incident", newIncidentsAPI(&a.baseURL).Subrouter())
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"dynatron.me/x/stillbox/internal/common"
|
||||||
"dynatron.me/x/stillbox/internal/forms"
|
"dynatron.me/x/stillbox/internal/forms"
|
||||||
|
"dynatron.me/x/stillbox/internal/jsontypes"
|
||||||
"dynatron.me/x/stillbox/pkg/database"
|
"dynatron.me/x/stillbox/pkg/database"
|
||||||
"dynatron.me/x/stillbox/pkg/incidents"
|
"dynatron.me/x/stillbox/pkg/incidents"
|
||||||
"dynatron.me/x/stillbox/pkg/incidents/incstore"
|
"dynatron.me/x/stillbox/pkg/incidents/incstore"
|
||||||
|
@ -17,6 +20,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type incidentsAPI struct {
|
type incidentsAPI struct {
|
||||||
|
baseURL *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIncidentsAPI(baseURL *url.URL) API {
|
||||||
|
return &incidentsAPI{baseURL}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ia *incidentsAPI) Subrouter() http.Handler {
|
func (ia *incidentsAPI) Subrouter() http.Handler {
|
||||||
|
@ -25,13 +33,13 @@ func (ia *incidentsAPI) Subrouter() http.Handler {
|
||||||
r.Get(`/{id:[a-f0-9-]+}`, ia.getIncident)
|
r.Get(`/{id:[a-f0-9-]+}`, ia.getIncident)
|
||||||
r.Get(`/{id:[a-f0-9-]+}.m3u`, ia.getCallsM3U)
|
r.Get(`/{id:[a-f0-9-]+}.m3u`, ia.getCallsM3U)
|
||||||
|
|
||||||
r.Post(`/create`, 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)
|
||||||
|
|
||||||
r.Put(`/{id:[a-f0-9]+}`, ia.updateIncident)
|
r.Put(`/{id:[a-f0-9]+}`, ia.updateIncident)
|
||||||
|
|
||||||
r.Delete(`/{id:[a-f0-9]+}`, ia.deleteIncident)
|
r.Delete(`/{id:[a-f0-9-]+}`, ia.deleteIncident)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -148,10 +156,10 @@ func (ia *incidentsAPI) deleteIncident(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CallIncidentParams struct {
|
type CallIncidentParams struct {
|
||||||
Add []uuid.UUID `json:"add"`
|
Add jsontypes.UUIDs `json:"add"`
|
||||||
Notes json.RawMessage `json:"notes"`
|
Notes json.RawMessage `json:"notes"`
|
||||||
|
|
||||||
Remove []uuid.UUID `json:"remove"`
|
Remove jsontypes.UUIDs `json:"remove"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ia *incidentsAPI) postCalls(w http.ResponseWriter, r *http.Request) {
|
func (ia *incidentsAPI) postCalls(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -170,7 +178,7 @@ func (ia *incidentsAPI) postCalls(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = incs.AddRemoveIncidentCalls(ctx, id, p.Add, p.Notes, p.Remove)
|
err = incs.AddRemoveIncidentCalls(ctx, id, p.Add.UUIDs(), p.Notes, p.Remove.UUIDs())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wErr(w, r, autoError(err))
|
wErr(w, r, autoError(err))
|
||||||
return
|
return
|
||||||
|
@ -197,9 +205,7 @@ func (ia *incidentsAPI) getCallsM3U(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
|
|
||||||
callUrl := r.URL
|
callUrl := common.PtrTo(*ia.baseURL)
|
||||||
callUrl.RawQuery = ""
|
|
||||||
callUrl.Fragment = ""
|
|
||||||
|
|
||||||
b.WriteString("#EXTM3U\n\n")
|
b.WriteString("#EXTM3U\n\n")
|
||||||
for _, c := range inc.Calls {
|
for _, c := range inc.Calls {
|
||||||
|
@ -213,7 +219,7 @@ func (ia *incidentsAPI) getCallsM3U(w http.ResponseWriter, r *http.Request) {
|
||||||
from = fmt.Sprintf(" from %d", c.Source)
|
from = fmt.Sprintf(" from %d", c.Source)
|
||||||
}
|
}
|
||||||
|
|
||||||
callUrl.Path = "/api/call/%s" + c.ID.String()
|
callUrl.Path = "/api/call/" + c.ID.String()
|
||||||
|
|
||||||
fmt.Fprintf(w, "#EXTINF:%d,%s%s (%s)\n%s\n\n",
|
fmt.Fprintf(w, "#EXTINF:%d,%s%s (%s)\n%s\n\n",
|
||||||
c.Duration.Seconds(),
|
c.Duration.Seconds(),
|
||||||
|
|
|
@ -71,7 +71,7 @@ func New(ctx context.Context, cfg *config.Configuration) (*Server, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tgCache := tgstore.NewCache()
|
tgCache := tgstore.NewCache()
|
||||||
api := rest.New()
|
api := rest.New(cfg.BaseURL.URL())
|
||||||
|
|
||||||
srv := &Server{
|
srv := &Server{
|
||||||
auth: authenticator,
|
auth: authenticator,
|
||||||
|
|
Loading…
Reference in a new issue