2024-07-24 08:38:18 -04:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-07-24 14:07:24 -04:00
|
|
|
"io"
|
2024-07-24 08:38:18 -04:00
|
|
|
"net/http"
|
2024-07-24 14:07:24 -04:00
|
|
|
"reflect"
|
2024-07-24 18:18:04 -04:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2024-07-24 08:38:18 -04:00
|
|
|
"time"
|
|
|
|
|
2024-07-24 22:42:30 -04:00
|
|
|
"dynatron.me/x/stillbox/pkg/gordio/database"
|
|
|
|
"github.com/google/uuid"
|
2024-07-25 09:37:27 -04:00
|
|
|
"github.com/jackc/pgtype"
|
|
|
|
"github.com/rs/zerolog/log"
|
2024-07-24 22:42:30 -04:00
|
|
|
)
|
2024-07-24 14:07:24 -04:00
|
|
|
|
2024-07-24 08:38:18 -04:00
|
|
|
type callUploadRequest struct {
|
|
|
|
Audio []byte `form:"audio"`
|
|
|
|
AudioName string `form:"audioName"`
|
2024-07-24 14:07:24 -04:00
|
|
|
AudioType string `form:"audioType"`
|
|
|
|
DateTime time.Time `form:"dateTime"`
|
2024-07-24 08:38:18 -04:00
|
|
|
Frequencies []int `form:"frequencies"`
|
|
|
|
Frequency int `form:"frequency"`
|
|
|
|
Key string `form:"key"`
|
2024-07-24 22:49:42 -04:00
|
|
|
Patches []int `form:"patches"`
|
2024-07-24 22:42:30 -04:00
|
|
|
Source int `form:"source"`
|
2024-07-24 22:49:42 -04:00
|
|
|
Sources []int `form:"sources"`
|
2024-07-24 22:42:30 -04:00
|
|
|
System int `form:"system"`
|
2024-07-24 08:38:18 -04:00
|
|
|
SystemLabel string `form:"systemLabel"`
|
|
|
|
Talkgroup int `form:"talkgroup"`
|
|
|
|
TalkgroupGroup string `form:"talkgroupGroup"`
|
|
|
|
TalkgroupLabel string `form:"talkgroupLabel"`
|
|
|
|
TalkgroupTag string `form:"talkgroupTag"`
|
|
|
|
}
|
|
|
|
|
2024-07-25 09:37:27 -04:00
|
|
|
type AddCallParams struct {
|
|
|
|
Submitter pgtype.Int4 `json:"submitter"`
|
|
|
|
System int32 `json:"system"`
|
|
|
|
Talkgroup int32 `json:"talkgroup"`
|
|
|
|
CallDate pgtype.Timestamp `json:"call_date"`
|
|
|
|
AudioName pgtype.Text `json:"audio_name"`
|
|
|
|
AudioBlob []byte `json:"audio_blob"`
|
|
|
|
AudioType pgtype.Text `json:"audio_type"`
|
|
|
|
AudioUrl pgtype.Text `json:"audio_url"`
|
|
|
|
Frequency pgtype.Int4 `json:"frequency"`
|
|
|
|
Frequencies []byte `json:"frequencies"`
|
|
|
|
Patches []byte `json:"patches"`
|
|
|
|
TgLabel pgtype.Text `json:"tg_label"`
|
|
|
|
Source pgtype.Text `json:"source"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (car *callUploadRequest) ToAddCallParams(submitter int) database.AddCallParams {
|
|
|
|
return database.AddCallParams{
|
|
|
|
Submitter: submitter,
|
|
|
|
System: car.System,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-24 08:38:18 -04:00
|
|
|
func (s *Server) routeCallUpload(w http.ResponseWriter, r *http.Request) {
|
2024-07-24 22:42:30 -04:00
|
|
|
err := r.ParseMultipartForm(1024 * 1024 * 2) // 2MB
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, "cannot parse form "+err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
keyUuid, err := uuid.Parse(r.Form.Get("key"))
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, "cannot parse key "+err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
2024-07-25 09:37:27 -04:00
|
|
|
db := database.FromCtx(r.Context())
|
|
|
|
apik, err := db.GetAPIKey(r.Context(), keyUuid)
|
2024-07-24 22:42:30 -04:00
|
|
|
if err != nil {
|
|
|
|
if database.IsNoRows(err) {
|
|
|
|
http.Error(w, "bad key", http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if apik.Disabled.Bool || (apik.Expires.Valid && time.Now().After(apik.Expires.Time)) {
|
|
|
|
http.Error(w, "disabled", http.StatusUnauthorized)
|
2024-07-25 09:37:27 -04:00
|
|
|
log.Error().Str("key", apik.ApiKey.String()).Msg("key disabled")
|
2024-07-24 22:42:30 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-24 14:07:24 -04:00
|
|
|
call := new(callUploadRequest)
|
2024-07-24 22:42:30 -04:00
|
|
|
err = call.fill(r)
|
2024-07-24 08:38:18 -04:00
|
|
|
if err != nil {
|
2024-07-24 18:18:04 -04:00
|
|
|
http.Error(w, "cannot bind upload "+err.Error(), 500)
|
2024-07-24 08:38:18 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-25 09:37:27 -04:00
|
|
|
dbCall, err := db.AddCall(r.Context(), call.ToAddCallParams())
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
|
|
|
log.Error().Err(err).Msg("add call")
|
|
|
|
return
|
|
|
|
}
|
2024-07-24 08:38:18 -04:00
|
|
|
}
|
2024-07-24 14:07:24 -04:00
|
|
|
|
2024-07-24 18:18:04 -04:00
|
|
|
func (car *callUploadRequest) fill(r *http.Request) error {
|
|
|
|
rv := reflect.ValueOf(car).Elem()
|
2024-07-24 14:07:24 -04:00
|
|
|
rt := rv.Type()
|
|
|
|
|
|
|
|
for i := 0; i < rv.NumField(); i++ {
|
|
|
|
f := rv.Field(i)
|
2024-07-24 22:49:42 -04:00
|
|
|
fi := f.Interface()
|
|
|
|
formField := rt.Field(i).Tag.Get("form")
|
|
|
|
switch v := fi.(type) {
|
|
|
|
case []byte:
|
|
|
|
file, _, err := r.FormFile(formField)
|
2024-07-24 14:07:24 -04:00
|
|
|
if err != nil {
|
2024-07-24 18:18:04 -04:00
|
|
|
return fmt.Errorf("get form file: %w", err)
|
2024-07-24 14:07:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
audioBytes, err := io.ReadAll(file)
|
|
|
|
if err != nil {
|
2024-07-24 18:18:04 -04:00
|
|
|
return fmt.Errorf("file read: %w", err)
|
2024-07-24 14:07:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
f.SetBytes(audioBytes)
|
2024-07-24 22:49:42 -04:00
|
|
|
case time.Time:
|
|
|
|
t, err := time.Parse(time.RFC3339, r.Form.Get(formField))
|
2024-07-24 14:07:24 -04:00
|
|
|
if err != nil {
|
2024-07-24 18:18:04 -04:00
|
|
|
return fmt.Errorf("parse time: %w", err)
|
2024-07-24 14:07:24 -04:00
|
|
|
}
|
|
|
|
f.Set(reflect.ValueOf(t))
|
2024-07-24 22:49:42 -04:00
|
|
|
case []int:
|
|
|
|
val := strings.Trim(r.Form.Get(formField), "[]")
|
2024-07-24 18:18:04 -04:00
|
|
|
if val == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
vals := strings.Split(val, ",")
|
|
|
|
ar := make([]int, 0, len(vals))
|
|
|
|
for _, v := range vals {
|
|
|
|
i, err := strconv.Atoi(v)
|
|
|
|
if err == nil {
|
|
|
|
ar = append(ar, i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f.Set(reflect.ValueOf(ar))
|
2024-07-24 22:49:42 -04:00
|
|
|
case int:
|
|
|
|
val, err := strconv.Atoi(r.Form.Get(formField))
|
2024-07-24 18:18:04 -04:00
|
|
|
if err != nil {
|
2024-07-24 22:49:42 -04:00
|
|
|
return fmt.Errorf("atoi('%s'): %w", formField, err)
|
2024-07-24 18:18:04 -04:00
|
|
|
}
|
|
|
|
f.SetInt(int64(val))
|
2024-07-24 22:49:42 -04:00
|
|
|
case string:
|
|
|
|
f.SetString(r.Form.Get(formField))
|
2024-07-24 14:07:24 -04:00
|
|
|
default:
|
2024-07-24 22:49:42 -04:00
|
|
|
panic(fmt.Errorf("unsupported type %T", v))
|
2024-07-24 14:07:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|