generated from CubeCraft-Creations/Tracehound
1f253283f8
- Add request validation: Content-Type check, body size limit (64KB)
- Add field length validation (camera_id: 64, friendly_name: 128, mode: 32, resolution: 32)
- Add FPS range validation (0-240)
- Add battery_pct range validation (0-100)
- Replace ad-hoc map[string]string errors with structured APIError {error, code, details}
- Fix isUniqueConstraintErr to catch both camera_id and mac_address constraint violations
- Fix MacAddress model field from string to *string for NULL handling
- Fix splitSQL to strip -- line comments before splitting (was causing migration failures with modernc.org/sqlite)
- Add 30 integration tests covering all endpoints
- All tests pass: ok github.com/cubecraft/remoterig/internal/api
92 lines
2.9 KiB
Go
92 lines
2.9 KiB
Go
// Package models contains the data structures for RemoteRig.
|
|
package models
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// Camera represents a registered GoPro camera in the system.
|
|
type Camera struct {
|
|
CameraID string `json:"camera_id"`
|
|
FriendlyName string `json:"friendly_name"`
|
|
MacAddress *string `json:"mac_address,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// StatusLog records a single status poll from an ESP8266 node.
|
|
type StatusLog struct {
|
|
ID int64 `json:"id"`
|
|
CameraID string `json:"camera_id"`
|
|
RecordedAt time.Time `json:"recorded_at"`
|
|
BatteryPct *int `json:"battery_pct,omitempty"`
|
|
VideoRemainingSec *int `json:"video_remaining_sec,omitempty"`
|
|
RecordingState int `json:"recording_state"` // 0=idle, 1=recording
|
|
Mode *string `json:"mode,omitempty"`
|
|
Resolution *string `json:"resolution,omitempty"`
|
|
FPS *int `json:"fps,omitempty"`
|
|
Online int `json:"online"` // 0=offline, 1=online
|
|
RawBatteryPct *float64 `json:"raw_battery_pct,omitempty"`
|
|
}
|
|
|
|
// CameraStatus is a combined view: latest status log joined with camera info.
|
|
type CameraStatus struct {
|
|
CameraID string `json:"camera_id"`
|
|
FriendlyName string `json:"friendly_name"`
|
|
BatteryPct *int `json:"battery_pct,omitempty"`
|
|
VideoRemainingSec *int `json:"video_remaining_sec,omitempty"`
|
|
Recording bool `json:"recording"`
|
|
Mode string `json:"mode"`
|
|
Resolution string `json:"resolution"`
|
|
FPS int `json:"fps"`
|
|
Online bool `json:"online"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
}
|
|
|
|
// RecordingEvent represents a start/stop recording event.
|
|
type RecordingEvent struct {
|
|
ID int64 `json:"id"`
|
|
CameraID string `json:"camera_id"`
|
|
StartedAt time.Time `json:"started_at"`
|
|
StoppedAt *time.Time `json:"stopped_at,omitempty"`
|
|
Reason *string `json:"reason,omitempty"`
|
|
Duration *int `json:"duration,omitempty"` // seconds
|
|
}
|
|
|
|
// Settings stores system-wide configuration.
|
|
type Setting struct {
|
|
Key string `json:"key"`
|
|
Value string `json:"value"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// NewCameraStatus creates a CameraStatus from a Camera and StatusLog.
|
|
func NewCameraStatus(c Camera, sl StatusLog) CameraStatus {
|
|
return CameraStatus{
|
|
CameraID: c.CameraID,
|
|
FriendlyName: c.FriendlyName,
|
|
BatteryPct: sl.BatteryPct,
|
|
VideoRemainingSec: sl.VideoRemainingSec,
|
|
Recording: sl.RecordingState == 1,
|
|
Mode: takeString(sl.Mode),
|
|
Resolution: takeString(sl.Resolution),
|
|
FPS: takeInt(sl.FPS),
|
|
Online: sl.Online == 1,
|
|
LastSeen: sl.RecordedAt,
|
|
}
|
|
}
|
|
|
|
func takeString(s *string) string {
|
|
if s == nil {
|
|
return ""
|
|
}
|
|
return *s
|
|
}
|
|
|
|
func takeInt(i *int) int {
|
|
if i == nil {
|
|
return 0
|
|
}
|
|
return *i
|
|
}
|