call apikey
This commit is contained in:
parent
5bb16ecd73
commit
d94ae3b701
8 changed files with 75 additions and 27 deletions
1
go.mod
1
go.mod
|
@ -9,6 +9,7 @@ require (
|
||||||
github.com/go-chi/jwtauth/v5 v5.3.1
|
github.com/go-chi/jwtauth/v5 v5.3.1
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/golang-migrate/migrate/v4 v4.17.1
|
github.com/golang-migrate/migrate/v4 v4.17.1
|
||||||
|
github.com/google/uuid v1.4.0
|
||||||
github.com/jackc/pgx/v5 v5.6.0
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -41,6 +41,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
||||||
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
||||||
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
|
|
@ -8,6 +8,7 @@ package database
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ const setCallTranscript = `-- name: SetCallTranscript :exec
|
||||||
UPDATE calls SET transcript = $2 WHERE id = $1
|
UPDATE calls SET transcript = $2 WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) SetCallTranscript(ctx context.Context, iD pgtype.UUID, transcript pgtype.Text) error {
|
func (q *Queries) SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript pgtype.Text) error {
|
||||||
_, err := q.db.Exec(ctx, setCallTranscript, iD, transcript)
|
_, err := q.db.Exec(ctx, setCallTranscript, iD, transcript)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,11 +15,11 @@ type ApiKey struct {
|
||||||
CreatedAt pgtype.Timestamp
|
CreatedAt pgtype.Timestamp
|
||||||
Expires pgtype.Timestamp
|
Expires pgtype.Timestamp
|
||||||
Disabled pgtype.Bool
|
Disabled pgtype.Bool
|
||||||
ApiKey pgtype.UUID
|
ApiKey uuid.UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
type Call struct {
|
type Call struct {
|
||||||
ID pgtype.UUID
|
ID uuid.UUID
|
||||||
Submitter pgtype.Int4
|
Submitter pgtype.Int4
|
||||||
System int32
|
System int32
|
||||||
Talkgroup int32
|
Talkgroup int32
|
||||||
|
@ -36,7 +37,7 @@ type Call struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Incident struct {
|
type Incident struct {
|
||||||
ID pgtype.UUID
|
ID uuid.UUID
|
||||||
Name string
|
Name string
|
||||||
Description pgtype.Text
|
Description pgtype.Text
|
||||||
StartTime pgtype.Timestamp
|
StartTime pgtype.Timestamp
|
||||||
|
@ -46,8 +47,8 @@ type Incident struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type IncidentsCall struct {
|
type IncidentsCall struct {
|
||||||
IncidentID pgtype.UUID
|
IncidentID uuid.UUID
|
||||||
CallID pgtype.UUID
|
CallID uuid.UUID
|
||||||
Notes []byte
|
Notes []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ package database
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ const deleteAPIKey = `-- name: DeleteAPIKey :exec
|
||||||
DELETE FROM api_keys WHERE api_key = $1
|
DELETE FROM api_keys WHERE api_key = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) DeleteAPIKey(ctx context.Context, apiKey pgtype.UUID) error {
|
func (q *Queries) DeleteAPIKey(ctx context.Context, apiKey uuid.UUID) error {
|
||||||
_, err := q.db.Exec(ctx, deleteAPIKey, apiKey)
|
_, err := q.db.Exec(ctx, deleteAPIKey, apiKey)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -90,6 +91,24 @@ func (q *Queries) DeleteUser(ctx context.Context, username string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAPIKey = `-- name: GetAPIKey :one
|
||||||
|
SELECT id, owner, created_at, expires, disabled, api_key FROM api_keys WHERE api_key = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetAPIKey(ctx context.Context, apiKey uuid.UUID) (ApiKey, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getAPIKey, apiKey)
|
||||||
|
var i ApiKey
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Owner,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.Expires,
|
||||||
|
&i.Disabled,
|
||||||
|
&i.ApiKey,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getUserByID = `-- name: GetUserByID :one
|
const getUserByID = `-- name: GetUserByID :one
|
||||||
SELECT id, username, password, email, is_admin, prefs FROM users
|
SELECT id, username, password, email, is_admin, prefs FROM users
|
||||||
WHERE id = $1 LIMIT 1
|
WHERE id = $1 LIMIT 1
|
||||||
|
|
|
@ -8,9 +8,10 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
|
||||||
|
|
||||||
//const timeFormat = "2006-01-02T15:04:05.999Z07:00"
|
"dynatron.me/x/stillbox/pkg/gordio/database"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
type callUploadRequest struct {
|
type callUploadRequest struct {
|
||||||
Audio []byte `form:"audio"`
|
Audio []byte `form:"audio"`
|
||||||
|
@ -21,9 +22,9 @@ type callUploadRequest struct {
|
||||||
Frequency int `form:"frequency"`
|
Frequency int `form:"frequency"`
|
||||||
Key string `form:"key"`
|
Key string `form:"key"`
|
||||||
Patches []string `form:"patches"`
|
Patches []string `form:"patches"`
|
||||||
Source string `form:"source"`
|
Source int `form:"source"`
|
||||||
Sources []string `form:"sources"`
|
Sources []string `form:"sources"`
|
||||||
System string `form:"system"`
|
System int `form:"system"`
|
||||||
SystemLabel string `form:"systemLabel"`
|
SystemLabel string `form:"systemLabel"`
|
||||||
Talkgroup int `form:"talkgroup"`
|
Talkgroup int `form:"talkgroup"`
|
||||||
TalkgroupGroup string `form:"talkgroupGroup"`
|
TalkgroupGroup string `form:"talkgroupGroup"`
|
||||||
|
@ -32,8 +33,35 @@ type callUploadRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) routeCallUpload(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) routeCallUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
apik, err := s.db.GetAPIKey(r.Context(), keyUuid)
|
||||||
|
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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
call := new(callUploadRequest)
|
call := new(callUploadRequest)
|
||||||
err := call.fill(r)
|
err = call.fill(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "cannot bind upload "+err.Error(), 500)
|
http.Error(w, "cannot bind upload "+err.Error(), 500)
|
||||||
return
|
return
|
||||||
|
@ -43,11 +71,6 @@ func (s *Server) routeCallUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (car *callUploadRequest) fill(r *http.Request) error {
|
func (car *callUploadRequest) fill(r *http.Request) error {
|
||||||
err := r.ParseMultipartForm(1024 * 1024 * 2) // 2MB
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("multipart parse: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rv := reflect.ValueOf(car).Elem()
|
rv := reflect.ValueOf(car).Elem()
|
||||||
rt := rv.Type()
|
rt := rv.Type()
|
||||||
|
|
||||||
|
@ -73,7 +96,7 @@ func (car *callUploadRequest) fill(r *http.Request) error {
|
||||||
return fmt.Errorf("parse time: %w", err)
|
return fmt.Errorf("parse time: %w", err)
|
||||||
}
|
}
|
||||||
f.Set(reflect.ValueOf(t))
|
f.Set(reflect.ValueOf(t))
|
||||||
case "frequencies":
|
case "frequencies", "patches", "sources":
|
||||||
val := strings.Trim(r.Form.Get(ff), "[]")
|
val := strings.Trim(r.Form.Get(ff), "[]")
|
||||||
if val == "" {
|
if val == "" {
|
||||||
continue
|
continue
|
||||||
|
@ -87,20 +110,12 @@ func (car *callUploadRequest) fill(r *http.Request) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.Set(reflect.ValueOf(ar))
|
f.Set(reflect.ValueOf(ar))
|
||||||
case "frequency", "talkgroup":
|
case "frequency", "talkgroup", "system", "source":
|
||||||
val, err := strconv.Atoi(r.Form.Get(ff))
|
val, err := strconv.Atoi(r.Form.Get(ff))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("atoi('%s'): %w", ff, err)
|
return fmt.Errorf("atoi('%s'): %w", ff, err)
|
||||||
}
|
}
|
||||||
f.SetInt(int64(val))
|
f.SetInt(int64(val))
|
||||||
case "patches", "sources":
|
|
||||||
val := strings.Trim(r.Form.Get(ff), "[]")
|
|
||||||
if val == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
vals := strings.Split(val, ",")
|
|
||||||
f.Set(reflect.ValueOf(vals))
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
f.SetString(r.Form.Get(ff))
|
f.SetString(r.Form.Get(ff))
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,3 +40,6 @@ RETURNING *;
|
||||||
|
|
||||||
-- name: DeleteAPIKey :exec
|
-- name: DeleteAPIKey :exec
|
||||||
DELETE FROM api_keys WHERE api_key = $1;
|
DELETE FROM api_keys WHERE api_key = $1;
|
||||||
|
|
||||||
|
-- name: GetAPIKey :one
|
||||||
|
SELECT * FROM api_keys WHERE api_key = $1;
|
||||||
|
|
|
@ -9,3 +9,9 @@ sql:
|
||||||
out: "../pkg/gordio/database"
|
out: "../pkg/gordio/database"
|
||||||
sql_package: "pgx/v5"
|
sql_package: "pgx/v5"
|
||||||
query_parameter_limit: 3
|
query_parameter_limit: 3
|
||||||
|
overrides:
|
||||||
|
- db_type: "uuid"
|
||||||
|
go_type:
|
||||||
|
import: "github.com/google/uuid"
|
||||||
|
type: "UUID"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue