@balaji003/lantransfer 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server.js +83 -41
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -165,6 +165,20 @@ const server = http.createServer(async (req, res) => {
|
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
// ── Ping endpoint for HTTP-based discovery ──
|
|
169
|
+
if (req.method === 'GET' && req.url === '/api/ping') {
|
|
170
|
+
res.writeHead(200, {
|
|
171
|
+
'Content-Type': 'application/json',
|
|
172
|
+
'Access-Control-Allow-Origin': '*',
|
|
173
|
+
});
|
|
174
|
+
res.end(JSON.stringify({
|
|
175
|
+
app: 'lantransfer',
|
|
176
|
+
device_name: deviceName,
|
|
177
|
+
tcp_port: TRANSFER_PORT,
|
|
178
|
+
}));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
168
182
|
// ── SSE stream ──
|
|
169
183
|
if (req.method === 'GET' && req.url === '/events') {
|
|
170
184
|
res.writeHead(200, {
|
|
@@ -504,52 +518,83 @@ function formatSize(bytes) {
|
|
|
504
518
|
}
|
|
505
519
|
|
|
506
520
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
507
|
-
//
|
|
521
|
+
// HTTP-based discovery (no firewall rules needed — outbound TCP only)
|
|
508
522
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
509
|
-
function
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
523
|
+
function getSubnetIPs() {
|
|
524
|
+
const results = [];
|
|
525
|
+
for (const ifaces of Object.values(os.networkInterfaces())) {
|
|
526
|
+
for (const iface of ifaces) {
|
|
527
|
+
if (iface.family === 'IPv4' && !iface.internal && iface.netmask) {
|
|
528
|
+
const ipParts = iface.address.split('.').map(Number);
|
|
529
|
+
const maskParts = iface.netmask.split('.').map(Number);
|
|
530
|
+
// Only scan /24 or smaller subnets to keep it fast
|
|
531
|
+
if (maskParts[2] === 255) {
|
|
532
|
+
const base = ipParts.slice(0, 3).join('.');
|
|
533
|
+
for (let i = 1; i < 255; i++) {
|
|
534
|
+
const ip = `${base}.${i}`;
|
|
535
|
+
if (ip !== iface.address) results.push(ip);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
521
538
|
}
|
|
522
539
|
}
|
|
540
|
+
}
|
|
541
|
+
return results;
|
|
542
|
+
}
|
|
523
543
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
exec(psCmd, { windowsHide: false, timeout: 60_000 }, (err2) => {
|
|
539
|
-
if (err2) {
|
|
540
|
-
console.log(' !! Firewall permission denied. Discovery will NOT work.');
|
|
541
|
-
console.log(' !! To fix manually:');
|
|
542
|
-
console.log(' !! 1. Open Windows Security > Firewall & network protection');
|
|
543
|
-
console.log(' !! 2. Click "Allow an app through firewall"');
|
|
544
|
-
console.log(' !! 3. Add Node.js and allow Private + Public networks');
|
|
545
|
-
console.log('');
|
|
546
|
-
} else {
|
|
547
|
-
console.log(' Firewall: Rule added! Discovery should work now.');
|
|
548
|
-
}
|
|
544
|
+
function pingHost(ip) {
|
|
545
|
+
return new Promise(resolve => {
|
|
546
|
+
const req = http.get(`http://${ip}:${HTTP_PORT}/api/ping`, { timeout: 800 }, res => {
|
|
547
|
+
let body = '';
|
|
548
|
+
res.on('data', c => body += c);
|
|
549
|
+
res.on('end', () => {
|
|
550
|
+
try {
|
|
551
|
+
const d = JSON.parse(body);
|
|
552
|
+
if (d.app === 'lantransfer' && d.device_name !== deviceName) {
|
|
553
|
+
resolve({ ip, device_name: d.device_name, tcp_port: d.tcp_port });
|
|
554
|
+
} else resolve(null);
|
|
555
|
+
} catch { resolve(null); }
|
|
556
|
+
});
|
|
549
557
|
});
|
|
558
|
+
req.on('error', () => resolve(null));
|
|
559
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
550
560
|
});
|
|
551
561
|
}
|
|
552
562
|
|
|
563
|
+
let httpScanning = false;
|
|
564
|
+
async function httpDiscoveryScan() {
|
|
565
|
+
if (!isDiscoverable || httpScanning) return;
|
|
566
|
+
httpScanning = true;
|
|
567
|
+
try {
|
|
568
|
+
const ips = getSubnetIPs();
|
|
569
|
+
// Scan in batches of 50 to avoid fd exhaustion
|
|
570
|
+
for (let i = 0; i < ips.length; i += 50) {
|
|
571
|
+
const batch = ips.slice(i, i + 50);
|
|
572
|
+
const results = await Promise.all(batch.map(pingHost));
|
|
573
|
+
for (const r of results) {
|
|
574
|
+
if (!r) continue;
|
|
575
|
+
const id = `${r.ip}:${r.tcp_port}`;
|
|
576
|
+
const isNew = !peers.has(id);
|
|
577
|
+
peers.set(id, {
|
|
578
|
+
device_name: r.device_name,
|
|
579
|
+
ip: r.ip,
|
|
580
|
+
tcp_port: r.tcp_port,
|
|
581
|
+
last_seen: Date.now(),
|
|
582
|
+
});
|
|
583
|
+
if (isNew) {
|
|
584
|
+
console.log(` Found: ${r.device_name} (${r.ip})`);
|
|
585
|
+
broadcast();
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
} catch { /* scan error, ignore */ }
|
|
590
|
+
httpScanning = false;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Run HTTP discovery scan every 5 seconds
|
|
594
|
+
setInterval(httpDiscoveryScan, 5_000);
|
|
595
|
+
// Also run immediately on start (after a short delay for server to be ready)
|
|
596
|
+
setTimeout(httpDiscoveryScan, 1_000);
|
|
597
|
+
|
|
553
598
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
554
599
|
// Start
|
|
555
600
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -570,9 +615,6 @@ server.listen(HTTP_PORT, () => {
|
|
|
570
615
|
console.log(` UI: http://localhost:${HTTP_PORT}`);
|
|
571
616
|
console.log('');
|
|
572
617
|
|
|
573
|
-
// Check firewall on Windows
|
|
574
|
-
checkWindowsFirewall();
|
|
575
|
-
|
|
576
618
|
// Auto-open browser
|
|
577
619
|
const url = `http://localhost:${HTTP_PORT}`;
|
|
578
620
|
if (process.platform === 'win32') exec(`start "" "${url}"`);
|