secutorpro commited on
Commit
ef3b247
·
verified ·
1 Parent(s): b1857d5
Files changed (1) hide show
  1. server/src/routes.js +58 -62
server/src/routes.js CHANGED
@@ -1,104 +1,100 @@
1
  import { Router } from 'express';
2
  import db from './db.js';
3
 
4
- const r = Router();
5
 
6
- const MEMBER_SELECT = `
7
- SELECT m.*, g.name AS group_name, g.color AS group_color
8
  FROM members m LEFT JOIN talkgroups g ON g.id = m.group_id`;
9
- const SOS_SELECT = `
10
- SELECT a.*, mm.name AS member_name
11
- FROM sos_alerts a JOIN members mm ON mm.id = a.member_id`;
12
 
13
- r.get('/me', (req, res) => res.json({ id: req.user.sub, username: req.user.username, role: req.user.role }));
14
 
15
- // ---- talkgroups ----
16
- r.get('/groups', (req, res) => {
17
  res.json(db.prepare('SELECT * FROM talkgroups ORDER BY id').all());
18
  });
19
 
20
- r.post('/groups', (req, res) => {
21
  const { name, color = '#3b82f6' } = req.body || {};
22
- if (!name || typeof name !== 'string') return res.status(400).json({ error: 'name is required' });
23
- const info = db.prepare('INSERT INTO talkgroups (name, color) VALUES (?, ?)').run(name.trim(), color);
24
- req.app.get('hub')?.broadcastGroups();
25
- res.json(db.prepare('SELECT * FROM talkgroups WHERE id = ?').get(info.lastInsertRowid));
 
 
 
 
26
  });
27
 
28
- // ---- members ----
29
- r.get('/members', (req, res) => {
30
- res.json(db.prepare(`${MEMBER_SELECT} ORDER BY m.id`).all());
31
  });
32
 
33
- r.post('/members', (req, res) => {
34
- const { name, callsign = null, device_id, group_id = null } = req.body || {};
35
- if (!name || !device_id) return res.status(400).json({ error: 'name and device_id are required' });
 
36
  try {
37
  const info = db.prepare('INSERT INTO members (name, callsign, device_id, group_id) VALUES (?, ?, ?, ?)')
38
- .run(name.trim(), callsign, device_id.trim(), group_id);
39
- req.app.get('hub')?.broadcastMembers();
40
- res.json(db.prepare(`${MEMBER_SELECT} WHERE m.id = ?`).get(info.lastInsertRowid));
41
  } catch {
42
- res.status(400).json({ error: 'device_id already exists or group_id is invalid' });
43
  }
44
  });
45
 
46
- r.put('/members/:id', (req, res) => {
47
- const { name, callsign = null, device_id, group_id = null } = req.body || {};
48
- if (!name || !device_id) return res.status(400).json({ error: 'name and device_id are required' });
49
- const info = db.prepare('UPDATE members SET name = ?, callsign = ?, device_id = ?, group_id = ? WHERE id = ?')
50
- .run(name.trim(), callsign, device_id.trim(), group_id, req.params.id);
51
- if (!info.changes) return res.status(404).json({ error: 'member not found' });
52
- req.app.get('hub')?.broadcastMembers();
53
- res.json(db.prepare(`${MEMBER_SELECT} WHERE m.id = ?`).get(req.params.id));
54
  });
55
 
56
- r.delete('/members/:id', (req, res) => {
57
  const info = db.prepare('DELETE FROM members WHERE id = ?').run(req.params.id);
58
  if (!info.changes) return res.status(404).json({ error: 'member not found' });
59
- req.app.get('hub')?.broadcastMembers();
60
  res.json({ ok: true });
61
  });
62
 
63
- // ---- SOS ----
64
- r.get('/sos', (req, res) => {
65
- const { state } = req.query;
66
- const rows = state
67
- ? db.prepare(`${SOS_SELECT} WHERE a.state = ? ORDER BY a.created_at DESC`).all(state)
68
- : db.prepare(`${SOS_SELECT} ORDER BY a.created_at DESC LIMIT 100`).all();
69
  res.json(rows);
70
  });
71
 
72
- r.post('/sos/:id/ack', (req, res) => {
73
- db.prepare("UPDATE sos_alerts SET state = 'ack', ack_by = ?, updated_at = ? WHERE id = ?")
74
  .run(req.user.username, Date.now(), req.params.id);
75
- const alert = db.prepare(`${SOS_SELECT} WHERE a.id = ?`).get(req.params.id);
76
- if (alert) req.app.get('hub')?.broadcast({ type: 'sos_update', alert });
77
- res.json(alert || { ok: true });
 
78
  });
79
 
80
- r.post('/sos/:id/resolve', (req, res) => {
81
- db.prepare("UPDATE sos_alerts SET state = 'resolved', ack_by = COALESCE(ack_by, ?), updated_at = ? WHERE id = ?")
82
  .run(req.user.username, Date.now(), req.params.id);
83
- const alert = db.prepare(`${SOS_SELECT} WHERE a.id = ?`).get(req.params.id);
84
- if (alert) req.app.get('hub')?.broadcast({ type: 'sos_update', alert });
85
- res.json(alert || { ok: true });
 
86
  });
87
 
88
- // ---- history ----
89
- r.get('/members/:id/trail', (req, res) => {
90
  const since = Number(req.query.since) || 0;
91
- res.json(db.prepare(`SELECT lat, lng, speed, ts FROM gps_pings
92
- WHERE member_id = ? AND ts > ? ORDER BY ts LIMIT 5000`).all(req.params.id, since));
93
  });
94
 
95
- r.get('/calls', (req, res) => {
96
- res.json(db.prepare(`
97
- SELECT c.*, g.name AS group_name, COALESCE(mm.name, 'dispatcher') AS who
98
- FROM call_log c
99
- LEFT JOIN members mm ON mm.id = c.member_id
100
- LEFT JOIN talkgroups g ON g.id = c.group_id
101
- ORDER BY c.ts DESC LIMIT 100`).all());
102
  });
103
 
104
- export default r;
 
1
  import { Router } from 'express';
2
  import db from './db.js';
3
 
4
+ const router = Router();
5
 
6
+ const MEMBER_SQL = `SELECT m.*, g.name AS group_name, g.color AS group_color
 
7
  FROM members m LEFT JOIN talkgroups g ON g.id = m.group_id`;
8
+ const SOS_SQL = `SELECT a.*, m.name AS member_name FROM sos_alerts a
9
+ JOIN members m ON m.id = a.member_id`;
 
10
 
11
+ const hub = (req) => req.app.get('hub');
12
 
13
+ router.get('/groups', (req, res) => {
 
14
  res.json(db.prepare('SELECT * FROM talkgroups ORDER BY id').all());
15
  });
16
 
17
+ router.post('/groups', (req, res) => {
18
  const { name, color = '#3b82f6' } = req.body || {};
19
+ if (!name) return res.status(400).json({ error: 'name required' });
20
+ try {
21
+ const info = db.prepare('INSERT INTO talkgroups (name, color) VALUES (?, ?)').run(name, color);
22
+ hub(req)?.broadcastGroups();
23
+ res.json(db.prepare('SELECT * FROM talkgroups WHERE id = ?').get(info.lastInsertRowid));
24
+ } catch {
25
+ res.status(409).json({ error: 'group name already exists' });
26
+ }
27
  });
28
 
29
+ router.get('/members', (req, res) => {
30
+ res.json(db.prepare(`${MEMBER_SQL} ORDER BY m.id`).all());
 
31
  });
32
 
33
+ router.post('/members', (req, res) => {
34
+ const { name, callsign, device_id, group_id } = req.body || {};
35
+ if (!name) return res.status(400).json({ error: 'name required' });
36
+ if (!device_id) return res.status(400).json({ error: 'device_id required' });
37
  try {
38
  const info = db.prepare('INSERT INTO members (name, callsign, device_id, group_id) VALUES (?, ?, ?, ?)')
39
+ .run(name, callsign || null, device_id, group_id ?? null);
40
+ hub(req)?.broadcastMembers();
41
+ res.json(db.prepare(`${MEMBER_SQL} WHERE m.id = ?`).get(info.lastInsertRowid));
42
  } catch {
43
+ res.status(409).json({ error: 'device_id already exists' });
44
  }
45
  });
46
 
47
+ router.put('/members/:id', (req, res) => {
48
+ const m = db.prepare('SELECT * FROM members WHERE id = ?').get(req.params.id);
49
+ if (!m) return res.status(404).json({ error: 'member not found' });
50
+ const { name = m.name, callsign = m.callsign, group_id = m.group_id } = req.body || {};
51
+ db.prepare('UPDATE members SET name = ?, callsign = ?, group_id = ? WHERE id = ?')
52
+ .run(name, callsign, group_id, m.id);
53
+ hub(req)?.broadcastMembers();
54
+ res.json(db.prepare(`${MEMBER_SQL} WHERE m.id = ?`).get(m.id));
55
  });
56
 
57
+ router.delete('/members/:id', (req, res) => {
58
  const info = db.prepare('DELETE FROM members WHERE id = ?').run(req.params.id);
59
  if (!info.changes) return res.status(404).json({ error: 'member not found' });
60
+ hub(req)?.broadcastMembers();
61
  res.json({ ok: true });
62
  });
63
 
64
+ router.get('/sos', (req, res) => {
65
+ const rows = req.query.state
66
+ ? db.prepare(`${SOS_SQL} WHERE a.state = ? ORDER BY a.created_at DESC LIMIT 200`).all(req.query.state)
67
+ : db.prepare(`${SOS_SQL} ORDER BY a.created_at DESC LIMIT 200`).all();
 
 
68
  res.json(rows);
69
  });
70
 
71
+ router.post('/sos/:id/ack', (req, res) => {
72
+ const info = db.prepare(`UPDATE sos_alerts SET state = 'ack', ack_by = ?, updated_at = ? WHERE id = ? AND state = 'open'`)
73
  .run(req.user.username, Date.now(), req.params.id);
74
+ if (!info.changes) return res.status(409).json({ error: 'alert not open' });
75
+ const row = db.prepare(`${SOS_SQL} WHERE a.id = ?`).get(req.params.id);
76
+ hub(req)?.broadcast({ type: 'sos_update', alert: row });
77
+ res.json(row);
78
  });
79
 
80
+ router.post('/sos/:id/resolve', (req, res) => {
81
+ const info = db.prepare(`UPDATE sos_alerts SET state = 'resolved', ack_by = COALESCE(ack_by, ?), updated_at = ? WHERE id = ? AND state != 'resolved'`)
82
  .run(req.user.username, Date.now(), req.params.id);
83
+ if (!info.changes) return res.status(409).json({ error: 'alert already resolved' });
84
+ const row = db.prepare(`${SOS_SQL} WHERE a.id = ?`).get(req.params.id);
85
+ hub(req)?.broadcast({ type: 'sos_update', alert: row });
86
+ res.json(row);
87
  });
88
 
89
+ router.get('/members/:id/trail', (req, res) => {
 
90
  const since = Number(req.query.since) || 0;
91
+ res.json(db.prepare('SELECT lat, lng, speed, ts FROM gps_pings WHERE member_id = ? AND ts > ? ORDER BY ts LIMIT 5000')
92
+ .all(req.params.id, since));
93
  });
94
 
95
+ router.get('/calls', (req, res) => {
96
+ res.json(db.prepare(`SELECT c.*, m.name AS member_name FROM call_log c
97
+ LEFT JOIN members m ON m.id = c.member_id ORDER BY c.ts DESC LIMIT 100`).all());
 
 
 
 
98
  });
99
 
100
+ export default router;