Spaces:
Paused
Paused
rest api
Browse files- 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
|
| 5 |
|
| 6 |
-
const
|
| 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
|
| 10 |
-
|
| 11 |
-
FROM sos_alerts a JOIN members mm ON mm.id = a.member_id`;
|
| 12 |
|
| 13 |
-
|
| 14 |
|
| 15 |
-
/
|
| 16 |
-
r.get('/groups', (req, res) => {
|
| 17 |
res.json(db.prepare('SELECT * FROM talkgroups ORDER BY id').all());
|
| 18 |
});
|
| 19 |
|
| 20 |
-
|
| 21 |
const { name, color = '#3b82f6' } = req.body || {};
|
| 22 |
-
if (!name
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
});
|
| 27 |
|
| 28 |
-
/
|
| 29 |
-
|
| 30 |
-
res.json(db.prepare(`${MEMBER_SELECT} ORDER BY m.id`).all());
|
| 31 |
});
|
| 32 |
|
| 33 |
-
|
| 34 |
-
const { name, callsign
|
| 35 |
-
if (!name
|
|
|
|
| 36 |
try {
|
| 37 |
const info = db.prepare('INSERT INTO members (name, callsign, device_id, group_id) VALUES (?, ?, ?, ?)')
|
| 38 |
-
.run(name
|
| 39 |
-
|
| 40 |
-
res.json(db.prepare(`${
|
| 41 |
} catch {
|
| 42 |
-
res.status(
|
| 43 |
}
|
| 44 |
});
|
| 45 |
|
| 46 |
-
|
| 47 |
-
const
|
| 48 |
-
if (!
|
| 49 |
-
const
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
res.json(db.prepare(`${
|
| 54 |
});
|
| 55 |
|
| 56 |
-
|
| 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 |
-
|
| 60 |
res.json({ ok: true });
|
| 61 |
});
|
| 62 |
|
| 63 |
-
/
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 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 |
-
|
| 73 |
-
db.prepare(
|
| 74 |
.run(req.user.username, Date.now(), req.params.id);
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
| 78 |
});
|
| 79 |
|
| 80 |
-
|
| 81 |
-
db.prepare(
|
| 82 |
.run(req.user.username, Date.now(), req.params.id);
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
|
|
|
| 86 |
});
|
| 87 |
|
| 88 |
-
//
|
| 89 |
-
r.get('/members/:id/trail', (req, res) => {
|
| 90 |
const since = Number(req.query.since) || 0;
|
| 91 |
-
res.json(db.prepare(
|
| 92 |
-
|
| 93 |
});
|
| 94 |
|
| 95 |
-
|
| 96 |
-
res.json(db.prepare(`
|
| 97 |
-
|
| 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
|
|
|
|
| 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;
|