laju-server 1.0.0 → 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 +74 -60
- 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,14 +17,9 @@ function startServer(folderPath, port = 3000) {
|
|
|
16
17
|
process.exit(1);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
const server =
|
|
20
|
-
|
|
21
|
-
// Serve static files
|
|
22
|
-
server.get('/*', async (req, res) => {
|
|
23
|
-
let requestPath = req.path;
|
|
24
|
-
|
|
20
|
+
const server = http.createServer((req, res) => {
|
|
25
21
|
// Decode URL
|
|
26
|
-
requestPath = decodeURIComponent(
|
|
22
|
+
let requestPath = decodeURIComponent(req.url.split('?')[0]);
|
|
27
23
|
|
|
28
24
|
// Remove leading slash
|
|
29
25
|
if (requestPath.startsWith('/')) {
|
|
@@ -40,88 +36,105 @@ function startServer(folderPath, port = 3000) {
|
|
|
40
36
|
// Security: prevent directory traversal
|
|
41
37
|
const resolvedPath = path.resolve(filePath);
|
|
42
38
|
if (!resolvedPath.startsWith(path.resolve(folderPath))) {
|
|
43
|
-
res.
|
|
39
|
+
res.writeHead(403);
|
|
40
|
+
res.end('Forbidden');
|
|
44
41
|
return;
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
44
|
+
// Check if file exists
|
|
45
|
+
fs.stat(resolvedPath, (err, stat) => {
|
|
46
|
+
if (err) {
|
|
50
47
|
// Try adding index.html if it's a directory
|
|
51
48
|
const indexPath = path.join(resolvedPath, 'index.html');
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
+
});
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
const stat = fs.statSync(resolvedPath);
|
|
60
|
-
|
|
61
59
|
if (stat.isDirectory()) {
|
|
62
|
-
// Try serving index.html from directory
|
|
63
60
|
const indexPath = path.join(resolvedPath, 'index.html');
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
});
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
console.error('Error:', err.message);
|
|
74
|
-
res.status(500).send('Internal Server Error');
|
|
75
|
-
}
|
|
71
|
+
serveFile(resolvedPath, stat, req, res);
|
|
72
|
+
});
|
|
76
73
|
});
|
|
77
74
|
|
|
78
|
-
function serveFile(filePath,
|
|
75
|
+
function serveFile(filePath, stat, req, res) {
|
|
79
76
|
const mimeType = mime.lookup(filePath) || 'application/octet-stream';
|
|
80
|
-
const stat = fs.statSync(filePath);
|
|
81
77
|
const fileSize = stat.size;
|
|
82
|
-
|
|
83
|
-
const range = req.headers['range'];
|
|
78
|
+
const range = req.headers.range;
|
|
84
79
|
|
|
85
80
|
if (range) {
|
|
86
|
-
// Handle range requests for video streaming / resume download
|
|
87
81
|
const parts = range.replace(/bytes=/, '').split('-');
|
|
88
82
|
const start = parseInt(parts[0], 10);
|
|
89
83
|
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
|
|
90
84
|
const chunkSize = end - start + 1;
|
|
91
85
|
|
|
92
|
-
res.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
res.writeHead(206, {
|
|
87
|
+
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
|
|
88
|
+
'Accept-Ranges': 'bytes',
|
|
89
|
+
'Content-Length': chunkSize,
|
|
90
|
+
'Content-Type': mimeType,
|
|
91
|
+
});
|
|
97
92
|
|
|
98
93
|
const stream = fs.createReadStream(filePath, { start, end });
|
|
99
|
-
|
|
94
|
+
stream.pipe(res);
|
|
95
|
+
|
|
96
|
+
stream.on('error', (err) => {
|
|
97
|
+
console.error('Stream error:', err.message);
|
|
98
|
+
res.end();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
req.on('close', () => {
|
|
102
|
+
stream.destroy();
|
|
103
|
+
});
|
|
100
104
|
} else {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
res.writeHead(200, {
|
|
106
|
+
'Content-Length': fileSize,
|
|
107
|
+
'Content-Type': mimeType,
|
|
108
|
+
'Accept-Ranges': 'bytes',
|
|
109
|
+
});
|
|
105
110
|
|
|
106
111
|
const stream = fs.createReadStream(filePath);
|
|
107
|
-
|
|
112
|
+
stream.pipe(res);
|
|
113
|
+
|
|
114
|
+
stream.on('error', (err) => {
|
|
115
|
+
console.error('Stream error:', err.message);
|
|
116
|
+
res.end();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
req.on('close', () => {
|
|
120
|
+
stream.destroy();
|
|
121
|
+
});
|
|
108
122
|
}
|
|
109
123
|
}
|
|
110
124
|
|
|
111
|
-
server.listen(port, '0.0.0.0')
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
for (const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
addresses.push(net.address);
|
|
120
|
-
}
|
|
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);
|
|
121
133
|
}
|
|
122
134
|
}
|
|
135
|
+
}
|
|
123
136
|
|
|
124
|
-
|
|
137
|
+
console.log(`
|
|
125
138
|
🚀 Laju Server berjalan!
|
|
126
139
|
|
|
127
140
|
📁 Folder: ${folderPath}
|
|
@@ -130,11 +143,12 @@ function startServer(folderPath, port = 3000) {
|
|
|
130
143
|
|
|
131
144
|
Tekan Ctrl+C untuk berhenti
|
|
132
145
|
`);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
server.on('error', (err) => {
|
|
149
|
+
console.error('❌ Gagal menjalankan server:', err.message);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
});
|
|
138
152
|
}
|
|
139
153
|
|
|
140
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
|
}
|