laju-server 1.0.1 → 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/index.js +67 -91
- package/package.json +4 -5
package/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const http = require('http');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const mime = require('mime-types');
|
|
5
|
+
const os = require('os');
|
|
5
6
|
|
|
6
7
|
function startServer(folderPath, port = 3000) {
|
|
7
8
|
// Check if folder exists
|
|
@@ -16,25 +17,9 @@ function startServer(folderPath, port = 3000) {
|
|
|
16
17
|
process.exit(1);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
const server =
|
|
20
|
-
fast_buffers: true,
|
|
21
|
-
max_body_buffer: 64 * 1024, // 64KB buffer for streaming
|
|
22
|
-
streaming: {
|
|
23
|
-
readable: {
|
|
24
|
-
highWaterMark: 64 * 1024, // 64KB chunks
|
|
25
|
-
},
|
|
26
|
-
writable: {
|
|
27
|
-
highWaterMark: 64 * 1024, // 64KB chunks
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Serve static files
|
|
33
|
-
server.get('/*', async (req, res) => {
|
|
34
|
-
let requestPath = req.path;
|
|
35
|
-
|
|
20
|
+
const server = http.createServer((req, res) => {
|
|
36
21
|
// Decode URL
|
|
37
|
-
requestPath = decodeURIComponent(
|
|
22
|
+
let requestPath = decodeURIComponent(req.url.split('?')[0]);
|
|
38
23
|
|
|
39
24
|
// Remove leading slash
|
|
40
25
|
if (requestPath.startsWith('/')) {
|
|
@@ -51,115 +36,105 @@ function startServer(folderPath, port = 3000) {
|
|
|
51
36
|
// Security: prevent directory traversal
|
|
52
37
|
const resolvedPath = path.resolve(filePath);
|
|
53
38
|
if (!resolvedPath.startsWith(path.resolve(folderPath))) {
|
|
54
|
-
res.
|
|
39
|
+
res.writeHead(403);
|
|
40
|
+
res.end('Forbidden');
|
|
55
41
|
return;
|
|
56
42
|
}
|
|
57
43
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (
|
|
44
|
+
// Check if file exists
|
|
45
|
+
fs.stat(resolvedPath, (err, stat) => {
|
|
46
|
+
if (err) {
|
|
61
47
|
// Try adding index.html if it's a directory
|
|
62
48
|
const indexPath = path.join(resolvedPath, 'index.html');
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
49
|
+
fs.stat(indexPath, (err2, stat2) => {
|
|
50
|
+
if (!err2 && stat2.isFile()) {
|
|
51
|
+
return serveFile(indexPath, stat2, req, res);
|
|
52
|
+
}
|
|
53
|
+
res.writeHead(404);
|
|
54
|
+
res.end('File not found');
|
|
55
|
+
});
|
|
67
56
|
return;
|
|
68
57
|
}
|
|
69
58
|
|
|
70
|
-
const stat = fs.statSync(resolvedPath);
|
|
71
|
-
|
|
72
59
|
if (stat.isDirectory()) {
|
|
73
|
-
// Try serving index.html from directory
|
|
74
60
|
const indexPath = path.join(resolvedPath, 'index.html');
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
fs.stat(indexPath, (err2, stat2) => {
|
|
62
|
+
if (!err2 && stat2.isFile()) {
|
|
63
|
+
return serveFile(indexPath, stat2, req, res);
|
|
64
|
+
}
|
|
65
|
+
res.writeHead(404);
|
|
66
|
+
res.end('File not found');
|
|
67
|
+
});
|
|
79
68
|
return;
|
|
80
69
|
}
|
|
81
70
|
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
console.error('Error:', err.message);
|
|
85
|
-
res.status(500).send('Internal Server Error');
|
|
86
|
-
}
|
|
71
|
+
serveFile(resolvedPath, stat, req, res);
|
|
72
|
+
});
|
|
87
73
|
});
|
|
88
74
|
|
|
89
|
-
function serveFile(filePath,
|
|
75
|
+
function serveFile(filePath, stat, req, res) {
|
|
90
76
|
const mimeType = mime.lookup(filePath) || 'application/octet-stream';
|
|
91
|
-
const stat = fs.statSync(filePath);
|
|
92
77
|
const fileSize = stat.size;
|
|
93
|
-
|
|
94
|
-
const range = req.headers['range'];
|
|
95
|
-
|
|
96
|
-
// Use larger buffer for big files (64KB chunks)
|
|
97
|
-
const highWaterMark = 64 * 1024;
|
|
78
|
+
const range = req.headers.range;
|
|
98
79
|
|
|
99
80
|
if (range) {
|
|
100
|
-
// Handle range requests for video streaming / resume download
|
|
101
81
|
const parts = range.replace(/bytes=/, '').split('-');
|
|
102
82
|
const start = parseInt(parts[0], 10);
|
|
103
|
-
|
|
104
|
-
const maxChunk = 10 * 1024 * 1024;
|
|
105
|
-
let end = parts[1] ? parseInt(parts[1], 10) : Math.min(start + maxChunk, fileSize - 1);
|
|
106
|
-
|
|
107
|
-
// Ensure end doesn't exceed file size
|
|
108
|
-
if (end >= fileSize) end = fileSize - 1;
|
|
109
|
-
|
|
83
|
+
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
|
|
110
84
|
const chunkSize = end - start + 1;
|
|
111
85
|
|
|
112
|
-
res.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
86
|
+
res.writeHead(206, {
|
|
87
|
+
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
|
|
88
|
+
'Accept-Ranges': 'bytes',
|
|
89
|
+
'Content-Length': chunkSize,
|
|
90
|
+
'Content-Type': mimeType,
|
|
91
|
+
});
|
|
118
92
|
|
|
119
|
-
const stream = fs.createReadStream(filePath, { start, end
|
|
93
|
+
const stream = fs.createReadStream(filePath, { start, end });
|
|
94
|
+
stream.pipe(res);
|
|
120
95
|
|
|
121
96
|
stream.on('error', (err) => {
|
|
122
97
|
console.error('Stream error:', err.message);
|
|
123
|
-
|
|
124
|
-
res.status(500).send('Stream error');
|
|
125
|
-
}
|
|
98
|
+
res.end();
|
|
126
99
|
});
|
|
127
100
|
|
|
128
|
-
|
|
101
|
+
req.on('close', () => {
|
|
102
|
+
stream.destroy();
|
|
103
|
+
});
|
|
129
104
|
} else {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
105
|
+
res.writeHead(200, {
|
|
106
|
+
'Content-Length': fileSize,
|
|
107
|
+
'Content-Type': mimeType,
|
|
108
|
+
'Accept-Ranges': 'bytes',
|
|
109
|
+
});
|
|
135
110
|
|
|
136
|
-
const stream = fs.createReadStream(filePath
|
|
111
|
+
const stream = fs.createReadStream(filePath);
|
|
112
|
+
stream.pipe(res);
|
|
137
113
|
|
|
138
114
|
stream.on('error', (err) => {
|
|
139
115
|
console.error('Stream error:', err.message);
|
|
140
|
-
|
|
141
|
-
res.status(500).send('Stream error');
|
|
142
|
-
}
|
|
116
|
+
res.end();
|
|
143
117
|
});
|
|
144
118
|
|
|
145
|
-
|
|
119
|
+
req.on('close', () => {
|
|
120
|
+
stream.destroy();
|
|
121
|
+
});
|
|
146
122
|
}
|
|
147
123
|
}
|
|
148
124
|
|
|
149
|
-
server.listen(port, '0.0.0.0')
|
|
150
|
-
.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
for (const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
addresses.push(net.address);
|
|
158
|
-
}
|
|
125
|
+
server.listen(port, '0.0.0.0', () => {
|
|
126
|
+
const networkInterfaces = os.networkInterfaces();
|
|
127
|
+
const addresses = [];
|
|
128
|
+
|
|
129
|
+
for (const name of Object.keys(networkInterfaces)) {
|
|
130
|
+
for (const net of networkInterfaces[name]) {
|
|
131
|
+
if (net.family === 'IPv4' && !net.internal) {
|
|
132
|
+
addresses.push(net.address);
|
|
159
133
|
}
|
|
160
134
|
}
|
|
135
|
+
}
|
|
161
136
|
|
|
162
|
-
|
|
137
|
+
console.log(`
|
|
163
138
|
🚀 Laju Server berjalan!
|
|
164
139
|
|
|
165
140
|
📁 Folder: ${folderPath}
|
|
@@ -168,11 +143,12 @@ function startServer(folderPath, port = 3000) {
|
|
|
168
143
|
|
|
169
144
|
Tekan Ctrl+C untuk berhenti
|
|
170
145
|
`);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
server.on('error', (err) => {
|
|
149
|
+
console.error('❌ Gagal menjalankan server:', err.message);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
});
|
|
176
152
|
}
|
|
177
153
|
|
|
178
154
|
module.exports = { startServer };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "laju-server",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Instant static file server
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Instant static file server - supports large files up to 100GB+",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"laju-server": "./bin/cli.js"
|
|
@@ -12,14 +12,13 @@
|
|
|
12
12
|
"keywords": [
|
|
13
13
|
"static",
|
|
14
14
|
"server",
|
|
15
|
-
"hyper-express",
|
|
16
15
|
"file-server",
|
|
17
|
-
"cli"
|
|
16
|
+
"cli",
|
|
17
|
+
"large-files"
|
|
18
18
|
],
|
|
19
19
|
"author": "",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"hyper-express": "^6.17.3",
|
|
23
22
|
"mime-types": "^2.1.35"
|
|
24
23
|
}
|
|
25
24
|
}
|