Add device-certificate mTLS auth, live position tracking, and API docs
Introduces a CA/PKI module so field devices can authenticate to Mosquitto over TLS (8883) with per-device client certificates (CN = serial number) instead of a shared password, with matching Devices/MQTT-Certs UI. Adds live transmitter position tracking alongside logged points, an MQTTS transport option in the simulator for exercising the real cert-auth path, and Swagger API docs at /api/docs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,9 +18,11 @@ interface SentEntry {
|
||||
lat: number;
|
||||
lng: number;
|
||||
depth: number;
|
||||
transport: Transport;
|
||||
}
|
||||
|
||||
type MessageType = 'log' | 'status';
|
||||
type Transport = 'relay' | 'mqtts';
|
||||
|
||||
const UTILITIES = ['GAS', 'WATER', 'ELECTRIC', 'SEWER', 'TELECOM', 'CATV', 'FIBER', 'STEAM', 'UNKNOWN'];
|
||||
const FIX_TYPES = ['FIXED_RTK', 'FLOAT_RTK', 'DGPS', 'AUTONOMOUS', 'NONE'];
|
||||
@@ -51,6 +53,7 @@ export default function SimulatorPage() {
|
||||
const [newTitle, setNewTitle] = useState('');
|
||||
|
||||
const [messageType, setMessageType] = useState<MessageType>('log');
|
||||
const [transport, setTransport] = useState<Transport>('relay');
|
||||
const [serial, setSerial] = useState('');
|
||||
const [utility, setUtility] = useState('GAS');
|
||||
const [fixType, setFixType] = useState('FIXED_RTK');
|
||||
@@ -137,6 +140,7 @@ export default function SimulatorPage() {
|
||||
await api.post(`/api/orgs/${orgId}/sim/publish`, {
|
||||
type: messageType,
|
||||
serial,
|
||||
transport,
|
||||
jobId,
|
||||
lat: pos.lat,
|
||||
lng: pos.lng,
|
||||
@@ -160,7 +164,15 @@ export default function SimulatorPage() {
|
||||
setCount(seq);
|
||||
setSent((prev) =>
|
||||
[
|
||||
{ at: new Date().toLocaleTimeString(), seq, type: messageType, lat: pos.lat, lng: pos.lng, depth: pointDepth },
|
||||
{
|
||||
at: new Date().toLocaleTimeString(),
|
||||
seq,
|
||||
type: messageType,
|
||||
lat: pos.lat,
|
||||
lng: pos.lng,
|
||||
depth: pointDepth,
|
||||
transport,
|
||||
},
|
||||
...prev,
|
||||
].slice(0, 25),
|
||||
);
|
||||
@@ -216,7 +228,7 @@ export default function SimulatorPage() {
|
||||
<main style={{ maxWidth: 720, margin: '0 auto', padding: '1.5rem' }}>
|
||||
<p style={{ color: '#666' }}>
|
||||
Simulates a locator receiver publishing GPS + telemetry to the MQTT broker at{' '}
|
||||
<code>devices/{serial || '<serial>'}/log</code>, exactly as a real device would.
|
||||
<code>devices/{serial || '<serial>'}/log</code>.
|
||||
</p>
|
||||
{error && <p style={{ color: '#c62828' }}>{error}</p>}
|
||||
|
||||
@@ -232,6 +244,24 @@ export default function SimulatorPage() {
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset style={{ marginBottom: '1rem', border: '1px solid #ddd', borderRadius: 8, padding: '1rem' }}>
|
||||
<legend>Connection</legend>
|
||||
<label style={{ marginRight: '1.5rem' }}>
|
||||
<input type="radio" checked={transport === 'relay'} onChange={() => setTransport('relay')} /> HTTP relay{' '}
|
||||
<span style={{ color: '#888' }}>— backend forwards it on the broker connection it already holds</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" checked={transport === 'mqtts'} onChange={() => setTransport('mqtts')} /> MQTTS (device
|
||||
certificate) <span style={{ color: '#888' }}>— connects to port 8883 and authenticates as this serial's own client cert</span>
|
||||
</label>
|
||||
{transport === 'mqtts' && (
|
||||
<p style={{ color: '#999', fontSize: '0.85rem', margin: '0.5rem 0 0' }}>
|
||||
Requires a device with this exact serial number to already exist in this org and have a certificate
|
||||
issued from <a href="/settings/devices">Devices</a>.
|
||||
</p>
|
||||
)}
|
||||
</fieldset>
|
||||
|
||||
<fieldset style={{ marginBottom: '1rem', border: '1px solid #ddd', borderRadius: 8, padding: '1rem' }}>
|
||||
<legend>Job</legend>
|
||||
{!creatingJob ? (
|
||||
@@ -424,6 +454,7 @@ export default function SimulatorPage() {
|
||||
<th>Lat</th>
|
||||
<th>Lng</th>
|
||||
<th>Depth</th>
|
||||
<th>Via</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -435,11 +466,12 @@ export default function SimulatorPage() {
|
||||
<td>{s.lat.toFixed(7)}</td>
|
||||
<td>{s.lng.toFixed(7)}</td>
|
||||
<td>{s.depth} m</td>
|
||||
<td style={{ color: s.transport === 'mqtts' ? '#2e7d32' : '#888' }}>{s.transport}</td>
|
||||
</tr>
|
||||
))}
|
||||
{sent.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={6} style={{ padding: '1.5rem', textAlign: 'center', color: '#888' }}>
|
||||
<td colSpan={7} style={{ padding: '1.5rem', textAlign: 'center', color: '#888' }}>
|
||||
No points sent yet.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user