@balaji003/lantransfer 1.0.9 → 1.0.10
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/public/index.html +61 -0
- package/server.js +66 -0
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -186,6 +186,28 @@
|
|
|
186
186
|
}
|
|
187
187
|
.btn-change-dir:hover { background: var(--surface-hover); border-color: var(--accent); }
|
|
188
188
|
|
|
189
|
+
/* ── Update banner ── */
|
|
190
|
+
.update-banner {
|
|
191
|
+
display: none; align-items: center; justify-content: center; gap: 12px;
|
|
192
|
+
padding: 8px 24px; background: rgba(74, 158, 255, 0.1);
|
|
193
|
+
border-bottom: 1px solid var(--accent); font-size: 13px; flex-shrink: 0;
|
|
194
|
+
}
|
|
195
|
+
.update-banner.show { display: flex; }
|
|
196
|
+
.update-banner span { color: var(--text); }
|
|
197
|
+
.update-banner .version-new { color: var(--green); font-weight: 600; }
|
|
198
|
+
.update-banner .version-old { color: var(--text-dim); }
|
|
199
|
+
.btn-update {
|
|
200
|
+
background: var(--accent); color: #fff; border: none; border-radius: 4px;
|
|
201
|
+
padding: 3px 12px; font-size: 12px; font-weight: 500; cursor: pointer;
|
|
202
|
+
transition: background 0.12s;
|
|
203
|
+
}
|
|
204
|
+
.btn-update:hover { background: var(--accent-dim); }
|
|
205
|
+
.btn-dismiss {
|
|
206
|
+
background: none; border: none; color: var(--text-dim); cursor: pointer;
|
|
207
|
+
font-size: 16px; padding: 0 4px; line-height: 1;
|
|
208
|
+
}
|
|
209
|
+
.btn-dismiss:hover { color: var(--text); }
|
|
210
|
+
|
|
189
211
|
/* ── Transfers ── */
|
|
190
212
|
.transfers-section { flex: 1; overflow-y: auto; padding: 16px 24px; }
|
|
191
213
|
.transfer {
|
|
@@ -284,6 +306,13 @@
|
|
|
284
306
|
</div>
|
|
285
307
|
</div>
|
|
286
308
|
|
|
309
|
+
<!-- Update banner -->
|
|
310
|
+
<div class="update-banner" id="update-banner">
|
|
311
|
+
<span>Update available: <span class="version-old" id="ver-current"></span> → <span class="version-new" id="ver-latest"></span></span>
|
|
312
|
+
<button class="btn-update" id="update-btn" onclick="doUpdate()">Update Now</button>
|
|
313
|
+
<button class="btn-dismiss" onclick="dismissUpdate()" title="Dismiss">×</button>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
287
316
|
<!-- Download location -->
|
|
288
317
|
<div class="download-dir">
|
|
289
318
|
<span>Save to:</span>
|
|
@@ -419,6 +448,28 @@
|
|
|
419
448
|
api('open-folder', { transferId: transferId });
|
|
420
449
|
}
|
|
421
450
|
|
|
451
|
+
let updateDismissed = false;
|
|
452
|
+
|
|
453
|
+
function doUpdate() {
|
|
454
|
+
const btn = document.getElementById('update-btn');
|
|
455
|
+
btn.textContent = 'Updating...';
|
|
456
|
+
btn.disabled = true;
|
|
457
|
+
api('do-update');
|
|
458
|
+
// Server will restart — poll until it's back
|
|
459
|
+
setTimeout(function poll() {
|
|
460
|
+
fetch('/api/ping').then(() => {
|
|
461
|
+
window.location.reload();
|
|
462
|
+
}).catch(() => {
|
|
463
|
+
setTimeout(poll, 1000);
|
|
464
|
+
});
|
|
465
|
+
}, 3000);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function dismissUpdate() {
|
|
469
|
+
updateDismissed = true;
|
|
470
|
+
document.getElementById('update-banner').classList.remove('show');
|
|
471
|
+
}
|
|
472
|
+
|
|
422
473
|
function doShutdown() {
|
|
423
474
|
if (confirm('Stop the server? This will close LAN Transfer.')) {
|
|
424
475
|
api('shutdown');
|
|
@@ -450,6 +501,16 @@
|
|
|
450
501
|
}
|
|
451
502
|
document.getElementById('local-ip').textContent = state.localIP || '';
|
|
452
503
|
|
|
504
|
+
// Update banner
|
|
505
|
+
const banner = document.getElementById('update-banner');
|
|
506
|
+
if (!updateDismissed && state.latestVersion && state.currentVersion && state.latestVersion !== state.currentVersion) {
|
|
507
|
+
document.getElementById('ver-current').textContent = 'v' + state.currentVersion;
|
|
508
|
+
document.getElementById('ver-latest').textContent = 'v' + state.latestVersion;
|
|
509
|
+
banner.classList.add('show');
|
|
510
|
+
} else {
|
|
511
|
+
banner.classList.remove('show');
|
|
512
|
+
}
|
|
513
|
+
|
|
453
514
|
// Download dir
|
|
454
515
|
const dirEl = document.getElementById('download-dir');
|
|
455
516
|
dirEl.textContent = state.downloadDir || '';
|
package/server.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const http = require('http');
|
|
16
|
+
const https = require('https');
|
|
16
17
|
const fs = require('fs');
|
|
17
18
|
const path = require('path');
|
|
18
19
|
const crypto = require('crypto');
|
|
@@ -30,6 +31,10 @@ const HTTP_PORT = 3000;
|
|
|
30
31
|
const PEER_TIMEOUT = 10_000; // ms
|
|
31
32
|
const BROADCAST_INTERVAL = 3_000; // ms
|
|
32
33
|
const ECDH_CURVE = 'prime256v1'; // NIST P-256
|
|
34
|
+
const PKG = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'));
|
|
35
|
+
const CURRENT_VERSION = PKG.version;
|
|
36
|
+
const PKG_NAME = PKG.name;
|
|
37
|
+
let latestVersion = null; // filled by update check
|
|
33
38
|
|
|
34
39
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
35
40
|
// Persistent config (device name)
|
|
@@ -101,6 +106,8 @@ function serializeState() {
|
|
|
101
106
|
isDiscoverable,
|
|
102
107
|
deviceName,
|
|
103
108
|
downloadDir,
|
|
109
|
+
currentVersion: CURRENT_VERSION,
|
|
110
|
+
latestVersion,
|
|
104
111
|
localIP: getLocalIP(),
|
|
105
112
|
peers: [...peers.entries()].map(([id, p]) => ({
|
|
106
113
|
id, name: p.device_name, ip: p.ip,
|
|
@@ -354,6 +361,34 @@ const server = http.createServer(async (req, res) => {
|
|
|
354
361
|
console.log(` [open] Failed — transferId: ${transferId}, found: ${!!t}, savePath: ${t ? t.savePath : 'N/A'}`);
|
|
355
362
|
}
|
|
356
363
|
}
|
|
364
|
+
else if (req.url === '/api/check-update') {
|
|
365
|
+
checkForUpdate();
|
|
366
|
+
}
|
|
367
|
+
else if (req.url === '/api/do-update') {
|
|
368
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
369
|
+
res.end('ok');
|
|
370
|
+
console.log(` [update] Running: npm install -g ${PKG_NAME}`);
|
|
371
|
+
exec(`npm install -g ${PKG_NAME}`, { timeout: 120_000 }, (err, stdout, stderr) => {
|
|
372
|
+
if (err) {
|
|
373
|
+
console.error(` [update] Failed: ${err.message}`);
|
|
374
|
+
console.error(stderr);
|
|
375
|
+
} else {
|
|
376
|
+
console.log(` [update] Success! Restarting...`);
|
|
377
|
+
console.log(stdout);
|
|
378
|
+
// Restart the process
|
|
379
|
+
setTimeout(() => {
|
|
380
|
+
const args = process.argv.slice(1);
|
|
381
|
+
const child = require('child_process').spawn(process.execPath, args, {
|
|
382
|
+
detached: true,
|
|
383
|
+
stdio: 'ignore',
|
|
384
|
+
});
|
|
385
|
+
child.unref();
|
|
386
|
+
process.exit(0);
|
|
387
|
+
}, 500);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
357
392
|
else if (req.url === '/api/shutdown') {
|
|
358
393
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
359
394
|
res.end('ok');
|
|
@@ -717,6 +752,33 @@ async function sendFile(peer, file) {
|
|
|
717
752
|
}
|
|
718
753
|
}
|
|
719
754
|
|
|
755
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
756
|
+
// Update checker
|
|
757
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
758
|
+
function checkForUpdate() {
|
|
759
|
+
const url = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
760
|
+
console.log(` [update] Checking ${url}`);
|
|
761
|
+
https.get(url, { timeout: 5000 }, res => {
|
|
762
|
+
let body = '';
|
|
763
|
+
res.on('data', c => body += c);
|
|
764
|
+
res.on('end', () => {
|
|
765
|
+
try {
|
|
766
|
+
const data = JSON.parse(body);
|
|
767
|
+
latestVersion = data.version || null;
|
|
768
|
+
console.log(` [update] Current: ${CURRENT_VERSION}, Latest: ${latestVersion}`);
|
|
769
|
+
if (latestVersion && latestVersion !== CURRENT_VERSION) {
|
|
770
|
+
console.log(` [update] Update available! Run: npm install -g ${PKG_NAME}`);
|
|
771
|
+
}
|
|
772
|
+
broadcast();
|
|
773
|
+
} catch (e) {
|
|
774
|
+
console.error(` [update] Parse error: ${e.message}`);
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}).on('error', e => {
|
|
778
|
+
console.error(` [update] Check failed: ${e.message}`);
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
720
782
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
721
783
|
// Utility
|
|
722
784
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -826,9 +888,13 @@ server.listen(HTTP_PORT, () => {
|
|
|
826
888
|
console.log(` Discovery UDP :${DISCOVERY_PORT} (fallback)`);
|
|
827
889
|
console.log(` Transfer TCP :${TRANSFER_PORT}`);
|
|
828
890
|
console.log(` Encryption ECDH + AES-256-CTR (E2E)`);
|
|
891
|
+
console.log(` Version ${CURRENT_VERSION}`);
|
|
829
892
|
console.log(` UI: http://localhost:${HTTP_PORT}`);
|
|
830
893
|
console.log('');
|
|
831
894
|
|
|
895
|
+
// Check for updates on startup
|
|
896
|
+
checkForUpdate();
|
|
897
|
+
|
|
832
898
|
// Auto-open browser
|
|
833
899
|
const url = `http://localhost:${HTTP_PORT}`;
|
|
834
900
|
if (process.platform === 'win32') exec(`start "" "${url}"`);
|