epress-cli 1.0.0

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 (3) hide show
  1. package/README.md +97 -0
  2. package/cli.js +146 -0
  3. package/package.json +44 -0
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # epress - Fast File Sharing CLI
2
+
3
+ A super fast CLI tool to upload and download compressed files across machines.
4
+
5
+ ## Quick Start
6
+
7
+ ### 1. Setup Backend (Next.js)
8
+
9
+ ```bash
10
+ cd backend
11
+ npm install
12
+ npm run dev
13
+ ```
14
+
15
+ The backend will run on `http://localhost:3000`
16
+
17
+ ### 2. Setup Vercel Blob Storage
18
+
19
+ 1. Go to [Vercel Dashboard](https://vercel.com) → Your Team → Settings → Storage
20
+ 2. Create a new Blob Store (or use existing)
21
+ 3. Copy the `BLOB_READ_WRITE_TOKEN`
22
+ 4. Add it as an environment variable in Vercel:
23
+ - Go to your project → Settings → Environment Variables
24
+ - Add: `BLOB_READ_WRITE_TOKEN` = your token
25
+
26
+ ### 3. Deploy Backend (Vercel - Recommended)
27
+
28
+ ```bash
29
+ cd backend
30
+ npm install
31
+ vercel
32
+ ```
33
+
34
+ After deployment, set the API URL:
35
+ ```bash
36
+ export EPRESS_API_URL=https://your-app.vercel.app/api
37
+ ```
38
+
39
+ ### 4. Use CLI Tool
40
+
41
+ #### Upload a file or folder:
42
+ ```bash
43
+ npx epress post ./my-folder
44
+ npx epress post ./file.txt
45
+ npx epress post ./my-folder --name "my-project"
46
+ ```
47
+
48
+ #### Download by ID:
49
+ ```bash
50
+ npx epress pull <id>
51
+ npx epress pull <id> --output ./downloads
52
+ ```
53
+
54
+ ## Development
55
+
56
+ ### Local Development
57
+
58
+ 1. Start backend:
59
+ ```bash
60
+ cd backend
61
+ npm run dev
62
+ ```
63
+
64
+ 2. Test CLI locally:
65
+ ```bash
66
+ node cli.js post ./test-folder
67
+ node cli.js pull <id>
68
+ ```
69
+
70
+ ### Publishing CLI to npm
71
+
72
+ 1. Update package.json with your npm username
73
+ 2. Login to npm: `npm login`
74
+ 3. Publish: `npm publish`
75
+
76
+ ## Environment Variables
77
+
78
+ ### CLI
79
+ - `EPRESS_API_URL` - Backend API URL (default: http://localhost:3000/api)
80
+
81
+ ### Backend (Vercel)
82
+ - `BLOB_READ_WRITE_TOKEN` - Vercel Blob storage token (required for production)
83
+
84
+ ## Features
85
+
86
+ - ✅ Upload files/folders as compressed zip
87
+ - ✅ Download and extract files by ID
88
+ - ✅ Works cross-platform (Windows, Mac, Linux)
89
+ - ✅ Fast deployment on Vercel
90
+ - ✅ Persistent storage with Vercel Blob
91
+ - ✅ No database needed
92
+
93
+ ## Notes
94
+
95
+ - Files are stored in Vercel Blob storage (persistent and fast)
96
+ - Add authentication if needed
97
+ - Set up file expiration/cleanup for old uploads
package/cli.js ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const FormData = require('form-data');
7
+ const axios = require('axios');
8
+ const archiver = require('archiver');
9
+ const extract = require('extract-zip');
10
+ const chalk = require('chalk');
11
+
12
+ // Get API URL from environment or use default
13
+ const API_URL = process.env.EPRESS_API_URL || 'https://espress-two.vercel.app/api';
14
+
15
+ program
16
+ .name('epress')
17
+ .description('CLI tool to upload and download compressed files')
18
+ .version('1.0.0');
19
+
20
+ program
21
+ .command('post <path>')
22
+ .description('Upload a file or folder as compressed zip')
23
+ .option('-n, --name <name>', 'Custom ID for the upload (e.g., hd1234)')
24
+ .action(async (filePath, options) => {
25
+ try {
26
+ console.log(chalk.blue('📦 Compressing...'));
27
+
28
+ const fullPath = path.resolve(filePath);
29
+ if (!fs.existsSync(fullPath)) {
30
+ console.error(chalk.red('❌ File or folder not found:', filePath));
31
+ process.exit(1);
32
+ }
33
+
34
+ const stats = fs.statSync(fullPath);
35
+ const isDirectory = stats.isDirectory();
36
+
37
+ // Create zip file
38
+ const zipPath = path.join(__dirname, 'temp-upload.zip');
39
+ const output = fs.createWriteStream(zipPath);
40
+ const archive = archiver('zip', { zlib: { level: 9 } });
41
+
42
+ return new Promise((resolve, reject) => {
43
+ output.on('close', async () => {
44
+ try {
45
+ console.log(chalk.blue('📤 Uploading...'));
46
+
47
+ const form = new FormData();
48
+ form.append('file', fs.createReadStream(zipPath));
49
+ if (options.name) {
50
+ form.append('name', options.name);
51
+ }
52
+
53
+ const response = await axios.post(`${API_URL}/upload`, form, {
54
+ headers: form.getHeaders(),
55
+ maxContentLength: Infinity,
56
+ maxBodyLength: Infinity,
57
+ });
58
+
59
+ // Clean up temp file
60
+ fs.unlinkSync(zipPath);
61
+
62
+ console.log(chalk.green('✅ Upload successful!'));
63
+ console.log(chalk.cyan('📋 ID:'), response.data.id);
64
+ console.log(chalk.cyan('🔗 URL:'), `${API_URL.replace('/api', '')}/api/download/${response.data.id}`);
65
+ console.log(chalk.yellow('\n💡 Use this command to download:'));
66
+ console.log(chalk.white(` npx epress pull ${response.data.id}`));
67
+
68
+ resolve();
69
+ } catch (error) {
70
+ fs.unlinkSync(zipPath);
71
+ console.error(chalk.red('❌ Upload failed:'), error.response?.data?.error || error.message);
72
+ reject(error);
73
+ }
74
+ });
75
+
76
+ archive.on('error', (err) => {
77
+ console.error(chalk.red('❌ Compression failed:'), err);
78
+ reject(err);
79
+ });
80
+
81
+ archive.pipe(output);
82
+
83
+ if (isDirectory) {
84
+ archive.directory(fullPath, false);
85
+ } else {
86
+ archive.file(fullPath, { name: path.basename(fullPath) });
87
+ }
88
+
89
+ archive.finalize();
90
+ });
91
+ } catch (error) {
92
+ console.error(chalk.red('❌ Error:'), error.message);
93
+ process.exit(1);
94
+ }
95
+ });
96
+
97
+ program
98
+ .command('pull <id>')
99
+ .description('Download and extract a file by ID')
100
+ .option('-o, --output <dir>', 'Output directory (default: current directory)')
101
+ .action(async (id, options) => {
102
+ try {
103
+ const outputDir = options.output ? path.resolve(options.output) : process.cwd();
104
+
105
+ if (!fs.existsSync(outputDir)) {
106
+ fs.mkdirSync(outputDir, { recursive: true });
107
+ }
108
+
109
+ console.log(chalk.blue('📥 Downloading...'));
110
+
111
+ const response = await axios.get(`${API_URL}/download/${id}`, {
112
+ responseType: 'stream',
113
+ });
114
+
115
+ const zipPath = path.join(__dirname, `temp-${id}.zip`);
116
+ const writer = fs.createWriteStream(zipPath);
117
+
118
+ response.data.pipe(writer);
119
+
120
+ await new Promise((resolve, reject) => {
121
+ writer.on('finish', async () => {
122
+ try {
123
+ console.log(chalk.blue('📦 Extracting...'));
124
+ await extract(zipPath, { dir: outputDir });
125
+ fs.unlinkSync(zipPath);
126
+ console.log(chalk.green(`✅ Extracted to: ${outputDir}`));
127
+ resolve();
128
+ } catch (error) {
129
+ fs.unlinkSync(zipPath);
130
+ console.error(chalk.red('❌ Extraction failed:'), error.message);
131
+ reject(error);
132
+ }
133
+ });
134
+ writer.on('error', reject);
135
+ });
136
+ } catch (error) {
137
+ if (error.response?.status === 404) {
138
+ console.error(chalk.red('❌ File not found. Invalid ID.'));
139
+ } else {
140
+ console.error(chalk.red('❌ Download failed:'), error.response?.data?.error || error.message);
141
+ }
142
+ process.exit(1);
143
+ }
144
+ });
145
+
146
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "epress-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool to upload and download compressed files across machines",
5
+ "main": "cli.js",
6
+ "bin": {
7
+ "epress": "./cli.js"
8
+ },
9
+ "files": [
10
+ "cli.js",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "dev": "cd backend && npm run dev",
15
+ "build": "cd backend && npm run build",
16
+ "start": "cd backend && npm start"
17
+ },
18
+ "keywords": [
19
+ "cli",
20
+ "file-sharing",
21
+ "upload",
22
+ "download",
23
+ "file-transfer",
24
+ "zip",
25
+ "compress"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/yourusername/epress.git"
32
+ },
33
+ "engines": {
34
+ "node": ">=14.0.0"
35
+ },
36
+ "dependencies": {
37
+ "commander": "^11.1.0",
38
+ "form-data": "^4.0.0",
39
+ "axios": "^1.6.2",
40
+ "archiver": "^6.0.1",
41
+ "extract-zip": "^2.0.1",
42
+ "chalk": "^4.1.2"
43
+ }
44
+ }