@balaji003/lantransfer 1.0.2 → 1.0.3
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/bin/cli.js +3 -3
- package/package.json +1 -1
- package/public/index.html +26 -3
- package/server.js +79 -2
package/bin/cli.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
require('../server.js');
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -65,6 +65,17 @@
|
|
|
65
65
|
}
|
|
66
66
|
.toggle.active .toggle-dot { background: var(--green); }
|
|
67
67
|
|
|
68
|
+
.header-right { display: flex; align-items: center; gap: 10px; }
|
|
69
|
+
|
|
70
|
+
.btn-shutdown {
|
|
71
|
+
display: flex; align-items: center; gap: 6px;
|
|
72
|
+
padding: 6px 16px; border-radius: 20px; border: 1px solid transparent;
|
|
73
|
+
background: rgba(248, 113, 113, 0.12); color: var(--red);
|
|
74
|
+
font-size: 13px; font-weight: 500; cursor: pointer;
|
|
75
|
+
transition: all 0.2s; user-select: none;
|
|
76
|
+
}
|
|
77
|
+
.btn-shutdown:hover { border-color: var(--red); background: rgba(248, 113, 113, 0.22); }
|
|
78
|
+
|
|
68
79
|
/* ── Main layout ── */
|
|
69
80
|
.main { flex: 1; display: flex; overflow: hidden; }
|
|
70
81
|
|
|
@@ -207,9 +218,14 @@
|
|
|
207
218
|
<div class="logo">⇌ LAN Transfer</div>
|
|
208
219
|
<div class="device-info" id="device-info"></div>
|
|
209
220
|
</div>
|
|
210
|
-
<div class="
|
|
211
|
-
<div class="toggle
|
|
212
|
-
|
|
221
|
+
<div class="header-right">
|
|
222
|
+
<div class="toggle" id="toggle" onclick="api('toggle')">
|
|
223
|
+
<div class="toggle-dot"></div>
|
|
224
|
+
<span id="toggle-text">Hidden</span>
|
|
225
|
+
</div>
|
|
226
|
+
<button class="btn-shutdown" onclick="doShutdown()" title="Stop the server">
|
|
227
|
+
⏻ Stop Server
|
|
228
|
+
</button>
|
|
213
229
|
</div>
|
|
214
230
|
</div>
|
|
215
231
|
|
|
@@ -303,6 +319,13 @@
|
|
|
303
319
|
api('respond', { requestId: requestId, accepted: accepted });
|
|
304
320
|
}
|
|
305
321
|
|
|
322
|
+
function doShutdown() {
|
|
323
|
+
if (confirm('Stop the server? This will close LAN Transfer.')) {
|
|
324
|
+
api('shutdown');
|
|
325
|
+
document.body.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100vh;color:#6b6b8d;font-size:18px;">Server stopped. You can close this tab.</div>';
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
306
329
|
// ──────────────────────────────────────────────────────────────────────────
|
|
307
330
|
// Render
|
|
308
331
|
// ──────────────────────────────────────────────────────────────────────────
|
package/server.js
CHANGED
|
@@ -56,6 +56,22 @@ function getLocalIP() {
|
|
|
56
56
|
return '127.0.0.1';
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/** Compute subnet broadcast address from local IP and netmask. */
|
|
60
|
+
function getBroadcastAddresses() {
|
|
61
|
+
const addrs = [];
|
|
62
|
+
for (const ifaces of Object.values(os.networkInterfaces())) {
|
|
63
|
+
for (const iface of ifaces) {
|
|
64
|
+
if (iface.family === 'IPv4' && !iface.internal && iface.netmask) {
|
|
65
|
+
const ipParts = iface.address.split('.').map(Number);
|
|
66
|
+
const maskParts = iface.netmask.split('.').map(Number);
|
|
67
|
+
const broadcast = ipParts.map((octet, i) => (octet | (~maskParts[i] & 255))).join('.');
|
|
68
|
+
addrs.push(broadcast);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return addrs.length > 0 ? addrs : ['255.255.255.255'];
|
|
73
|
+
}
|
|
74
|
+
|
|
59
75
|
function serializeState() {
|
|
60
76
|
return JSON.stringify({
|
|
61
77
|
isDiscoverable,
|
|
@@ -193,6 +209,13 @@ const server = http.createServer(async (req, res) => {
|
|
|
193
209
|
pendingRequests.delete(data.requestId);
|
|
194
210
|
}
|
|
195
211
|
}
|
|
212
|
+
else if (req.url === '/api/shutdown') {
|
|
213
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
214
|
+
res.end('ok');
|
|
215
|
+
console.log('\n Server stopped by user.');
|
|
216
|
+
setTimeout(() => process.exit(0), 200);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
196
219
|
|
|
197
220
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
198
221
|
res.end('ok');
|
|
@@ -256,11 +279,13 @@ udp.on('error', err => {
|
|
|
256
279
|
|
|
257
280
|
udp.bind(DISCOVERY_PORT);
|
|
258
281
|
|
|
259
|
-
// Broadcaster — send beacon every 3 s
|
|
282
|
+
// Broadcaster — send beacon every 3 s (to subnet broadcast addresses)
|
|
260
283
|
setInterval(() => {
|
|
261
284
|
if (!isDiscoverable) return;
|
|
262
285
|
const msg = JSON.stringify({ device_name: deviceName, tcp_port: TRANSFER_PORT });
|
|
263
|
-
|
|
286
|
+
for (const addr of getBroadcastAddresses()) {
|
|
287
|
+
udp.send(msg, DISCOVERY_PORT, addr, () => {});
|
|
288
|
+
}
|
|
264
289
|
}, BROADCAST_INTERVAL);
|
|
265
290
|
|
|
266
291
|
// Prune stale peers
|
|
@@ -478,6 +503,55 @@ function formatSize(bytes) {
|
|
|
478
503
|
return (bytes / 1073741824).toFixed(1) + ' GB';
|
|
479
504
|
}
|
|
480
505
|
|
|
506
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
507
|
+
// Windows Firewall check
|
|
508
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
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;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
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
|
+
}
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
481
555
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
482
556
|
// Start
|
|
483
557
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -498,6 +572,9 @@ server.listen(HTTP_PORT, () => {
|
|
|
498
572
|
console.log(` UI: http://localhost:${HTTP_PORT}`);
|
|
499
573
|
console.log('');
|
|
500
574
|
|
|
575
|
+
// Check firewall on Windows
|
|
576
|
+
checkWindowsFirewall();
|
|
577
|
+
|
|
501
578
|
// Auto-open browser
|
|
502
579
|
const url = `http://localhost:${HTTP_PORT}`;
|
|
503
580
|
if (process.platform === 'win32') exec(`start "" "${url}"`);
|