@balaji003/lantransfer 1.0.3 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +83 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@balaji003/lantransfer",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "LAN File Transfer — peer-to-peer file sharing over local network. Zero dependencies.",
5
5
  "main": "server.js",
6
6
  "bin": {
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,54 +518,83 @@ function formatSize(bytes) {
504
518
  }
505
519
 
506
520
  // ──────────────────────────────────────────────────────────────────────────────
507
- // Windows Firewall check
521
+ // HTTP-based discovery (no firewall rules needed — outbound TCP only)
508
522
  // ──────────────────────────────────────────────────────────────────────────────
509
- function checkWindowsFirewall() {
510
- if (process.platform !== 'win32') return;
511
-
512
- const ruleName = 'LAN Transfer (lantransfer)';
513
- const nodeExe = process.execPath;
514
-
515
- // Check if a firewall rule already exists for this app
516
- exec(`netsh advfirewall firewall show rule name="${ruleName}"`, { windowsHide: true }, (err, stdout) => {
517
- if (!err && stdout.includes(ruleName)) {
518
- // Rule exists, check if it's enabled
519
- if (stdout.includes('Enabled:') && stdout.includes('Yes')) {
520
- console.log(' Firewall: Allowed');
521
- return;
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
+ }
522
538
  }
523
539
  }
540
+ }
541
+ return results;
542
+ }
524
543
 
525
- // No rule found — prompt the user to allow it
526
- console.log('');
527
- console.log(' !! Firewall rule not found for LAN Transfer.');
528
- console.log(' !! Device discovery requires UDP/TCP access through Windows Firewall.');
529
- console.log(' !! Attempting to add firewall rule (requires Admin)...');
530
- console.log('');
531
-
532
- // Try to add the rule via elevated PowerShell — this triggers a UAC prompt
533
- const addCmd = [
534
- `netsh advfirewall firewall add rule name="${ruleName}" dir=in action=allow protocol=UDP localport=${DISCOVERY_PORT} program="${nodeExe}" enable=yes`,
535
- `netsh advfirewall firewall add rule name="${ruleName}" dir=in action=allow protocol=TCP localport=${TRANSFER_PORT} program="${nodeExe}" enable=yes`,
536
- ].join(' & ');
537
-
538
- const psCmd = `powershell -Command "Start-Process cmd -ArgumentList '/c ${addCmd.replace(/"/g, '\\"')}' -Verb RunAs -Wait"`;
539
-
540
- exec(psCmd, { windowsHide: false, timeout: 60_000 }, (err2) => {
541
- if (err2) {
542
- console.log(' !! Could not add firewall rule. Discovery may not work.');
543
- console.log(' !! To fix manually:');
544
- console.log(' !! 1. Open Windows Security > Firewall & network protection');
545
- console.log(' !! 2. Click "Allow an app through firewall"');
546
- console.log(' !! 3. Add Node.js and allow Private + Public networks');
547
- console.log('');
548
- } else {
549
- console.log(' Firewall: Rule added successfully! Discovery should work now.');
550
- }
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
+ });
551
557
  });
558
+ req.on('error', () => resolve(null));
559
+ req.on('timeout', () => { req.destroy(); resolve(null); });
552
560
  });
553
561
  }
554
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
+
555
598
  // ──────────────────────────────────────────────────────────────────────────────
556
599
  // Start
557
600
  // ──────────────────────────────────────────────────────────────────────────────
@@ -572,9 +615,6 @@ server.listen(HTTP_PORT, () => {
572
615
  console.log(` UI: http://localhost:${HTTP_PORT}`);
573
616
  console.log('');
574
617
 
575
- // Check firewall on Windows
576
- checkWindowsFirewall();
577
-
578
618
  // Auto-open browser
579
619
  const url = `http://localhost:${HTTP_PORT}`;
580
620
  if (process.platform === 'win32') exec(`start "" "${url}"`);