feat(cli): add file sharing CLI using Transfer.coffee
Introduced a command-line interface that allows users to upload and download files using WebTorrent. Files can be shared using a generated mnemonic phrase for easy retrieval. The CLI supports one-time uploads and continuous seeding options. Added configuration files to manage dependencies and environmental variables, including `.gitignore` for ignoring the `node_modules`. This feature enables a decentralized file sharing mechanism and leverages mnemonic phrases for simpler sharing.
This commit is contained in:
commit
d176a8f89b
4 changed files with 1465 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
111
cli.mjs
Executable file
111
cli.mjs
Executable file
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env node
|
||||
import { Command } from 'commander';
|
||||
import WebTorrent from 'webtorrent';
|
||||
import bip39 from 'bip39';
|
||||
import dotenv from 'dotenv';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const app = new Command();
|
||||
const client = new WebTorrent();
|
||||
const trackerUrl = process.env.TRACKER_URL || "wss://tracker.transfer.coffee";
|
||||
|
||||
// Upload file and generate mnemonic
|
||||
app
|
||||
.command('upload <filePath>')
|
||||
.description('Upload a file and get a mnemonic to share')
|
||||
.option('-o, --one-time', 'Exit once the file has been downloaded once')
|
||||
.action((filePath, options) => {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.error('File does not exist.');
|
||||
process.exit(1);
|
||||
}
|
||||
const opts = {
|
||||
announce: [trackerUrl],
|
||||
};
|
||||
|
||||
console.log(`Connecting to tracker: ${trackerUrl}`);
|
||||
|
||||
client.seed(filePath, opts, (torrent) => {
|
||||
console.log(`File is seeding. InfoHash: ${torrent.infoHash}`);
|
||||
try {
|
||||
const mnemonic = bip39.entropyToMnemonic(torrent.infoHash);
|
||||
console.log(`Mnemonic for sharing: ${mnemonic}`);
|
||||
|
||||
if (options.oneTime) {
|
||||
torrent.on('upload', () => {
|
||||
const completedPeer = torrent.wires.some(wire => wire.uploaded === torrent.length);
|
||||
if (completedPeer) {
|
||||
console.log('A peer has completed the download. Exiting...');
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error generating mnemonic: ${error.message}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Download file using mnemonic
|
||||
app
|
||||
.command('download [words...]')
|
||||
.description('Download a file using a mnemonic')
|
||||
.option('-s, --seed', 'Continue seeding after download is complete')
|
||||
.action((words, options) => {
|
||||
try {
|
||||
const mnemonic = words.join(' ');
|
||||
if (!bip39.validateMnemonic(mnemonic)) {
|
||||
throw new Error('Invalid mnemonic');
|
||||
}
|
||||
const infoHash = bip39.mnemonicToEntropy(mnemonic);
|
||||
|
||||
console.log(`Connecting to tracker: ${trackerUrl}`);
|
||||
console.log('Searching for seeds...');
|
||||
|
||||
const opts = {
|
||||
announce: [trackerUrl],
|
||||
};
|
||||
|
||||
client.add(infoHash, opts, (torrent) => {
|
||||
const file = torrent.files[0];
|
||||
const output = path.join(process.cwd(), file.name);
|
||||
const stream = fs.createWriteStream(output);
|
||||
|
||||
console.log(`Downloading file: ${file.name}`);
|
||||
console.log('Size:', file.length, 'bytes');
|
||||
console.log('');
|
||||
|
||||
file.createReadStream().pipe(stream);
|
||||
stream.on('finish', () => {
|
||||
console.log("\nDownload complete:", output);
|
||||
if (!options.seed) {
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log('Continuing to seed...');
|
||||
}
|
||||
});
|
||||
|
||||
torrent.on('download', () => {
|
||||
const progress = (torrent.downloaded / torrent.length * 100).toFixed(2);
|
||||
process.stdout.write(`\rProgress: ${progress}%`);
|
||||
});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error downloading file: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
app
|
||||
.command('help')
|
||||
.description('Display help information')
|
||||
.action(() => {
|
||||
app.help();
|
||||
});
|
||||
|
||||
app.parse(process.argv);
|
21
package.json
Normal file
21
package.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "transfer-coffee-cli",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.7",
|
||||
"bip39": "^3.1.0",
|
||||
"commander": "^12.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"webtorrent": "^2.5.1"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue