1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use sqlx::types::Json;
5use sqlx::FromRow;
6
7use crate::camera_url;
8
9#[derive(Debug, Clone, FromRow)]
11pub struct Camera {
12 pub id: String,
13 pub site_id: Option<String>,
14 pub name: String,
15 pub vendor: String,
16 pub model: Option<String>,
17 pub address: Option<String>,
18 pub rtsp_port: i64,
19 pub username: Option<String>,
20 pub password: Option<String>,
21 pub main_stream_url: Option<String>,
22 pub sub_stream_url: Option<String>,
23 pub record_stream: String,
24 pub codec: Option<String>,
25 pub resolution_main: Option<String>,
26 pub resolution_sub: Option<String>,
27 pub fps_main: Option<i64>,
28 pub fps_sub: Option<i64>,
29 pub capabilities: Json<Value>,
30 pub record_enabled: bool,
31 pub segment_seconds: i64,
32 pub retention_hours: i64,
33 pub enabled: bool,
34 pub created_at: DateTime<Utc>,
35 pub updated_at: DateTime<Utc>,
36}
37
38impl Camera {
39 pub fn should_record(&self) -> bool {
41 self.enabled && self.record_enabled
42 }
43}
44
45#[derive(Debug, Clone, Serialize)]
47pub struct CameraView {
48 pub id: String,
49 pub site_id: Option<String>,
50 pub name: String,
51 pub vendor: String,
52 pub model: Option<String>,
53 pub address: Option<String>,
54 pub rtsp_port: i64,
55 pub username: Option<String>,
56 pub has_password: bool,
57 pub record_stream: String,
58 pub record_url_masked: Option<String>,
60 pub codec: Option<String>,
61 pub resolution_main: Option<String>,
62 pub resolution_sub: Option<String>,
63 pub fps_main: Option<i64>,
64 pub fps_sub: Option<i64>,
65 pub capabilities: Value,
66 pub record_enabled: bool,
67 pub segment_seconds: i64,
68 pub retention_hours: i64,
69 pub enabled: bool,
70 pub created_at: DateTime<Utc>,
71 pub updated_at: DateTime<Utc>,
72}
73
74impl From<Camera> for CameraView {
75 fn from(c: Camera) -> Self {
76 let record_url_masked = camera_url::record_url(&c).map(|u| camera_url::mask_url(&u));
77 CameraView {
78 id: c.id,
79 site_id: c.site_id,
80 name: c.name,
81 vendor: c.vendor,
82 model: c.model,
83 address: c.address,
84 rtsp_port: c.rtsp_port,
85 username: c.username,
86 has_password: c
87 .password
88 .as_deref()
89 .map(|p| !p.is_empty())
90 .unwrap_or(false),
91 record_stream: c.record_stream,
92 record_url_masked,
93 codec: c.codec,
94 resolution_main: c.resolution_main,
95 resolution_sub: c.resolution_sub,
96 fps_main: c.fps_main,
97 fps_sub: c.fps_sub,
98 capabilities: c.capabilities.0,
99 record_enabled: c.record_enabled,
100 segment_seconds: c.segment_seconds,
101 retention_hours: c.retention_hours,
102 enabled: c.enabled,
103 created_at: c.created_at,
104 updated_at: c.updated_at,
105 }
106 }
107}
108
109#[derive(Debug, Deserialize)]
111pub struct CameraCreate {
112 pub id: Option<String>,
113 pub name: String,
114 pub site_id: Option<String>,
115 #[serde(default = "default_vendor")]
116 pub vendor: String,
117 pub model: Option<String>,
118 pub address: Option<String>,
119 pub rtsp_port: Option<i64>,
120 pub username: Option<String>,
121 pub password: Option<String>,
122 pub main_stream_url: Option<String>,
123 pub sub_stream_url: Option<String>,
124 pub record_stream: Option<String>,
125 pub capabilities: Option<Value>,
126 pub record_enabled: Option<bool>,
127 pub segment_seconds: Option<i64>,
128 pub retention_hours: Option<i64>,
129 pub enabled: Option<bool>,
130}
131
132fn default_vendor() -> String {
133 "generic".to_string()
134}
135
136#[derive(Debug, Deserialize, Default)]
138pub struct CameraUpdate {
139 pub name: Option<String>,
140 pub site_id: Option<String>,
141 pub vendor: Option<String>,
142 pub model: Option<String>,
143 pub address: Option<String>,
144 pub rtsp_port: Option<i64>,
145 pub username: Option<String>,
146 pub password: Option<String>,
147 pub main_stream_url: Option<String>,
148 pub sub_stream_url: Option<String>,
149 pub record_stream: Option<String>,
150 pub capabilities: Option<Value>,
151 pub record_enabled: Option<bool>,
152 pub segment_seconds: Option<i64>,
153 pub retention_hours: Option<i64>,
154 pub enabled: Option<bool>,
155}
156
157#[derive(Debug, Clone, Serialize, FromRow)]
158pub struct Segment {
159 pub id: String,
160 pub camera_id: String,
161 pub path: String,
162 pub start_time: DateTime<Utc>,
163 pub end_time: DateTime<Utc>,
164 pub duration_s: f64,
165 pub codec: Option<String>,
166 pub width: Option<i64>,
167 pub height: Option<i64>,
168 pub size_bytes: i64,
169 pub container: String,
170 pub locked: bool,
171 pub incident_id: Option<String>,
172 pub created_at: DateTime<Utc>,
173}
174
175#[derive(Debug, Clone, Serialize, FromRow)]
176pub struct CameraStatus {
177 pub camera_id: String,
178 pub state: String,
179 pub last_segment_at: Option<DateTime<Utc>>,
180 pub last_started_at: Option<DateTime<Utc>>,
181 pub reconnect_count: i64,
182 pub segments_written: i64,
183 pub fps_observed: Option<f64>,
184 pub bitrate_kbps: Option<f64>,
185 pub last_error: Option<String>,
186 pub recorder_pid: Option<i64>,
187 pub updated_at: DateTime<Utc>,
188}
189
190#[derive(Debug, Clone, Serialize, FromRow)]
191pub struct Event {
192 pub id: String,
193 pub camera_id: Option<String>,
194 pub site_id: Option<String>,
195 pub event_type: String,
196 pub severity: String,
197 pub timestamp: DateTime<Utc>,
198 pub payload: Json<Value>,
199 pub created_at: DateTime<Utc>,
200}
201
202#[derive(Debug, Clone, Serialize, FromRow)]
206pub struct AiTask {
207 pub id: String,
208 pub camera_id: String,
209 pub task_type: String,
210 pub enabled: bool,
211 pub stream_profile: String,
212 pub fps: f64,
213 pub width: i64,
214 pub config: Json<Value>,
215 pub created_at: DateTime<Utc>,
216 pub updated_at: DateTime<Utc>,
217}
218
219#[derive(Debug, Deserialize)]
220pub struct AiTaskCreate {
221 pub task_type: String,
222 pub stream_profile: Option<String>,
223 pub fps: Option<f64>,
224 pub width: Option<i64>,
225 pub config: Option<Value>,
226 pub enabled: Option<bool>,
227}
228
229#[derive(Debug, Deserialize, Default)]
230pub struct AiTaskUpdate {
231 pub task_type: Option<String>,
232 pub stream_profile: Option<String>,
233 pub fps: Option<f64>,
234 pub width: Option<i64>,
235 pub config: Option<Value>,
236 pub enabled: Option<bool>,
237}
238
239#[derive(Debug, Clone, Serialize, FromRow)]
241pub struct Detection {
242 pub id: String,
243 pub camera_id: String,
244 pub task_type: String,
245 pub timestamp: DateTime<Utc>,
246 pub label: Option<String>,
247 pub confidence: Option<f64>,
248 pub bbox: Option<Json<Value>>,
249 pub track_id: Option<String>,
250 pub attributes: Json<Value>,
251 pub frame_id: Option<String>,
253 pub created_at: DateTime<Utc>,
254}
255
256#[derive(Debug, Deserialize)]
258pub struct DetectionIngest {
259 pub label: Option<String>,
260 pub confidence: Option<f64>,
261 pub bbox: Option<Value>,
262 pub track_id: Option<String>,
263 pub attributes: Option<Value>,
264}
265
266#[derive(Debug, Deserialize)]
268pub struct IngestEvent {
269 pub event_type: String,
270 pub severity: Option<String>,
271 pub payload: Option<Value>,
272}
273
274#[derive(Debug, Deserialize)]
276pub struct AiIngest {
277 pub camera_id: String,
278 pub task_type: String,
279 pub timestamp: Option<String>,
280 pub frame_id: Option<String>,
284 #[serde(default)]
285 pub detections: Vec<DetectionIngest>,
286 pub event: Option<IngestEvent>,
287}
288
289#[derive(Debug, Clone, Serialize, FromRow)]
293pub struct Zone {
294 pub id: String,
295 pub camera_id: String,
296 pub name: String,
297 pub kind: String,
298 pub polygon: Json<Value>,
300 pub dwell_seconds: f64,
301 pub labels: Json<Value>,
303 pub severity: String,
304 pub config: Json<Value>,
305 pub enabled: bool,
306 pub created_at: DateTime<Utc>,
307 pub updated_at: DateTime<Utc>,
308}
309
310#[derive(Debug, Deserialize)]
311pub struct ZoneCreate {
312 pub name: String,
313 pub kind: Option<String>,
314 pub polygon: Value,
315 pub dwell_seconds: Option<f64>,
316 pub labels: Option<Value>,
317 pub severity: Option<String>,
318 pub config: Option<Value>,
319 pub enabled: Option<bool>,
320}
321
322#[derive(Debug, Deserialize, Default)]
323pub struct ZoneUpdate {
324 pub name: Option<String>,
325 pub kind: Option<String>,
326 pub polygon: Option<Value>,
327 pub dwell_seconds: Option<f64>,
328 pub labels: Option<Value>,
329 pub severity: Option<String>,
330 pub config: Option<Value>,
331 pub enabled: Option<bool>,
332}
333
334#[derive(Debug, Clone, Serialize, FromRow)]
335pub struct ZoneEvent {
336 pub id: String,
337 pub camera_id: String,
338 pub zone_id: String,
339 pub zone_name: String,
340 pub track_id: Option<String>,
341 pub event_type: String,
342 pub label: Option<String>,
343 pub timestamp: DateTime<Utc>,
344 pub dwell_seconds: Option<f64>,
345 pub evidence_path: Option<String>,
346 pub created_at: DateTime<Utc>,
347}
348
349#[derive(Debug, Clone, FromRow)]
353pub struct User {
354 pub id: String,
355 pub username: String,
356 pub password_hash: String,
357 pub role: String,
358 pub display_name: Option<String>,
359 pub active: bool,
360 pub created_at: DateTime<Utc>,
361 pub updated_at: DateTime<Utc>,
362}
363
364#[derive(Debug, Clone, Serialize)]
365pub struct UserView {
366 pub id: String,
367 pub username: String,
368 pub role: String,
369 pub display_name: Option<String>,
370 pub active: bool,
371 pub created_at: DateTime<Utc>,
372 pub updated_at: DateTime<Utc>,
373}
374
375impl From<User> for UserView {
376 fn from(u: User) -> Self {
377 UserView {
378 id: u.id,
379 username: u.username,
380 role: u.role,
381 display_name: u.display_name,
382 active: u.active,
383 created_at: u.created_at,
384 updated_at: u.updated_at,
385 }
386 }
387}
388
389#[derive(Debug, Deserialize)]
390pub struct UserCreate {
391 pub username: String,
392 pub password: String,
393 pub role: Option<String>,
394 pub display_name: Option<String>,
395 pub active: Option<bool>,
396}
397
398#[derive(Debug, Deserialize, Default)]
399pub struct UserUpdate {
400 pub password: Option<String>,
401 pub role: Option<String>,
402 pub display_name: Option<String>,
403 pub active: Option<bool>,
404}
405
406#[derive(Debug, Deserialize)]
407pub struct LoginRequest {
408 pub username: String,
409 pub password: String,
410}
411
412#[derive(Debug, Clone, FromRow)]
413pub struct ApiKey {
414 pub id: String,
415 pub name: String,
416 pub key_hash: String,
418 pub key_prefix: String,
419 pub role: String,
420 pub active: bool,
421 pub last_used_at: Option<DateTime<Utc>>,
422 pub created_at: DateTime<Utc>,
423}
424
425#[derive(Debug, Clone, Serialize)]
426pub struct ApiKeyView {
427 pub id: String,
428 pub name: String,
429 pub key_prefix: String,
430 pub role: String,
431 pub active: bool,
432 pub last_used_at: Option<DateTime<Utc>>,
433 pub created_at: DateTime<Utc>,
434}
435
436impl From<ApiKey> for ApiKeyView {
437 fn from(k: ApiKey) -> Self {
438 ApiKeyView {
439 id: k.id,
440 name: k.name,
441 key_prefix: k.key_prefix,
442 role: k.role,
443 active: k.active,
444 last_used_at: k.last_used_at,
445 created_at: k.created_at,
446 }
447 }
448}
449
450#[derive(Debug, Deserialize)]
451pub struct ApiKeyCreate {
452 pub name: String,
453 pub role: Option<String>,
454}