Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions rig-listener/rig-listener.js
Original file line number Diff line number Diff line change
Expand Up @@ -700,10 +700,15 @@ async function initTci(cfg) {
tciSocket.addEventListener('error', (evt) => {
// 'error' fires before 'close' — just log it, reconnect happens on 'close'
const err = evt.error || evt;
if (err.code === 'ECONNREFUSED') {
console.error(`[TCI] Connection refused — is Thetis/ExpertSDR running with TCI enabled?`);
const msg = (err && err.message) || '';
if (err && err.code === 'ECONNREFUSED') {
console.error(`[TCI] Connection refused — is the SDR app running with TCI enabled?`);
} else if (msg.toLowerCase().includes('sec-websocket-accept') || msg.toLowerCase().includes('incorrect hash')) {
console.error('[TCI] WebSocket handshake rejected (invalid Sec-WebSocket-Accept).');
console.error("[TCI] This usually means the SDR app's TCI server has a non-standard WebSocket implementation.");
console.error('[TCI] Try updating your SDR software, or check that TCI is enabled (not just CAT).');
} else {
console.error(`[TCI] Error: ${err.message || 'connection error'}`);
console.error(`[TCI] Error: ${msg || 'connection error'}`);
}
});

Expand Down
2 changes: 2 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ const staticOptions = {
setHeaders: (res, filePath) => {
if (filePath.endsWith('index.html') || filePath.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('CDN-Cache-Control', 'no-store'); // Cloudflare-specific: never cache at edge
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
}
Expand Down Expand Up @@ -267,6 +268,7 @@ app.get('*', (req, res) => {
const publicIndex = path.join(ROOT_DIR, 'public', 'index.html');
const indexPath = fs.existsSync(distIndex) ? distIndex : publicIndex;
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('CDN-Cache-Control', 'no-store'); // Cloudflare: never cache at edge
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
res.sendFile(indexPath);
Expand Down
13 changes: 13 additions & 0 deletions server/routes/presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ module.exports = function (app, ctx) {
res.json({ ok: true, active: activeUsers.size });
});

// POST /api/presence/leave — user closing tab
app.post('/api/presence/leave', (req, res) => {
const { callsign } = req.body || {};
if (!callsign) return res.status(400).json({ error: 'callsign required' });
const call = String(callsign)
.toUpperCase()
.replace(/[^A-Z0-9/\-]/g, '');
if (activeUsers.delete(call)) {
logDebug(`[Presence] ${call} left`);
}
res.json({ ok: true });
});

// GET /api/presence — get all active users
app.get('/api/presence', (req, res) => {
const cutoff = Date.now() - PRESENCE_TTL;
Expand Down
9 changes: 9 additions & 0 deletions server/routes/satellites.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@ module.exports = function (app, ctx) {
},

// ── Digipeaters ────────────────────────────────────────────────
'IO-86': {
norad: 40931,
name: 'IO-86 (LAPAN-A2/ORARI)',
color: '#33ccaa',
priority: 2,
mode: 'APRS Digipeater',
downlink: '145.825 MHz',
uplink: '145.825 MHz',
},
'IO-117': {
norad: 53106,
name: 'IO-117 (GreenCube)',
Expand Down
16 changes: 7 additions & 9 deletions server/routes/wsjtx.js
Original file line number Diff line number Diff line change
Expand Up @@ -1323,15 +1323,13 @@ module.exports = function (app, ctx) {
'',
':: Run relay',
'%NODE_EXE% "%TEMP%\\ohc-relay.js" --url "' +
safeServerURL +
'" --key "' +
safeRelayKey +
'" --session "' +
safeSessionId +
'"' +
multicastAddress
? ' --multicast "' + safeMulticastAddress + "'"
: '',
safeServerURL +
'" --key "' +
safeRelayKey +
'" --session "' +
safeSessionId +
'"' +
(multicastAddress ? ' --multicast "' + safeMulticastAddress + '"' : ''),
'',
'echo.',
'echo Relay stopped.',
Expand Down
14 changes: 13 additions & 1 deletion src/hooks/app/usePresence.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ export default function usePresence({ callsign, locator }) {

sendHeartbeat();
const interval = setInterval(sendHeartbeat, HEARTBEAT_INTERVAL);
return () => clearInterval(interval);

// Remove presence immediately when the tab closes
const handleUnload = () => {
// navigator.sendBeacon is fire-and-forget — works even during unload
const payload = JSON.stringify({ callsign });
navigator.sendBeacon('/api/presence/leave', payload);
};
window.addEventListener('beforeunload', handleUnload);

return () => {
clearInterval(interval);
window.removeEventListener('beforeunload', handleUnload);
};
}, [callsign, locator]);
}
Loading