laju-server 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 (4) hide show
  1. package/README.md +45 -0
  2. package/bin/cli.js +25 -0
  3. package/index.js +140 -0
  4. package/package.json +25 -0
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # laju-server
2
+
3
+ Instant static file server menggunakan hyper-express. Cepat dan ringan!
4
+
5
+ ## Instalasi
6
+
7
+ ```bash
8
+ npm install -g laju-server
9
+ ```
10
+
11
+ ## Penggunaan
12
+
13
+ ```bash
14
+ # Serve folder saat ini
15
+ npx laju-server
16
+
17
+ # Serve folder tertentu
18
+ npx laju-server ./public
19
+
20
+ # Serve dengan port custom
21
+ npx laju-server ./dist -p 8080
22
+ npx laju-server ./dist --port 8080
23
+ ```
24
+
25
+ ## Fitur
26
+
27
+ - ⚡ Super cepat dengan hyper-express
28
+ - 📁 Serve folder statis
29
+ - 🔒 Proteksi directory traversal
30
+ - 📄 Auto serve index.html
31
+ - 🎨 Auto detect MIME types
32
+
33
+ ## Contoh
34
+
35
+ ```bash
36
+ # Serve folder build React
37
+ npx laju-server ./build
38
+
39
+ # Serve folder dist Vite
40
+ npx laju-server ./dist -p 4000
41
+ ```
42
+
43
+ ## License
44
+
45
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path');
4
+ const { startServer } = require('../index.js');
5
+
6
+ const args = process.argv.slice(2);
7
+
8
+ // Parse arguments
9
+ let folder = '.';
10
+ let port = 3000;
11
+
12
+ for (let i = 0; i < args.length; i++) {
13
+ if (args[i] === '-p' || args[i] === '--port') {
14
+ port = parseInt(args[i + 1], 10) || 3000;
15
+ i++;
16
+ } else if (!args[i].startsWith('-')) {
17
+ folder = args[i];
18
+ }
19
+ }
20
+
21
+ // Resolve folder path
22
+ const folderPath = path.resolve(process.cwd(), folder);
23
+
24
+ // Start server
25
+ startServer(folderPath, port);
package/index.js ADDED
@@ -0,0 +1,140 @@
1
+ const HyperExpress = require('hyper-express');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const mime = require('mime-types');
5
+
6
+ function startServer(folderPath, port = 3000) {
7
+ // Check if folder exists
8
+ if (!fs.existsSync(folderPath)) {
9
+ console.error(`❌ Folder tidak ditemukan: ${folderPath}`);
10
+ process.exit(1);
11
+ }
12
+
13
+ const stats = fs.statSync(folderPath);
14
+ if (!stats.isDirectory()) {
15
+ console.error(`❌ Path bukan folder: ${folderPath}`);
16
+ process.exit(1);
17
+ }
18
+
19
+ const server = new HyperExpress.Server();
20
+
21
+ // Serve static files
22
+ server.get('/*', async (req, res) => {
23
+ let requestPath = req.path;
24
+
25
+ // Decode URL
26
+ requestPath = decodeURIComponent(requestPath);
27
+
28
+ // Remove leading slash
29
+ if (requestPath.startsWith('/')) {
30
+ requestPath = requestPath.slice(1);
31
+ }
32
+
33
+ // Default to index.html if path is empty or ends with /
34
+ if (requestPath === '' || requestPath.endsWith('/')) {
35
+ requestPath = path.join(requestPath, 'index.html');
36
+ }
37
+
38
+ const filePath = path.join(folderPath, requestPath);
39
+
40
+ // Security: prevent directory traversal
41
+ const resolvedPath = path.resolve(filePath);
42
+ if (!resolvedPath.startsWith(path.resolve(folderPath))) {
43
+ res.status(403).send('Forbidden');
44
+ return;
45
+ }
46
+
47
+ try {
48
+ // Check if file exists
49
+ if (!fs.existsSync(resolvedPath)) {
50
+ // Try adding index.html if it's a directory
51
+ const indexPath = path.join(resolvedPath, 'index.html');
52
+ if (fs.existsSync(indexPath) && fs.statSync(indexPath).isFile()) {
53
+ return serveFile(indexPath, res, req);
54
+ }
55
+ res.status(404).send('File not found');
56
+ return;
57
+ }
58
+
59
+ const stat = fs.statSync(resolvedPath);
60
+
61
+ if (stat.isDirectory()) {
62
+ // Try serving index.html from directory
63
+ const indexPath = path.join(resolvedPath, 'index.html');
64
+ if (fs.existsSync(indexPath)) {
65
+ return serveFile(indexPath, res, req);
66
+ }
67
+ res.status(404).send('File not found');
68
+ return;
69
+ }
70
+
71
+ return serveFile(resolvedPath, res, req);
72
+ } catch (err) {
73
+ console.error('Error:', err.message);
74
+ res.status(500).send('Internal Server Error');
75
+ }
76
+ });
77
+
78
+ function serveFile(filePath, res, req) {
79
+ const mimeType = mime.lookup(filePath) || 'application/octet-stream';
80
+ const stat = fs.statSync(filePath);
81
+ const fileSize = stat.size;
82
+
83
+ const range = req.headers['range'];
84
+
85
+ if (range) {
86
+ // Handle range requests for video streaming / resume download
87
+ const parts = range.replace(/bytes=/, '').split('-');
88
+ const start = parseInt(parts[0], 10);
89
+ const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
90
+ const chunkSize = end - start + 1;
91
+
92
+ res.status(206);
93
+ res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
94
+ res.setHeader('Accept-Ranges', 'bytes');
95
+ res.setHeader('Content-Length', chunkSize);
96
+ res.setHeader('Content-Type', mimeType);
97
+
98
+ const stream = fs.createReadStream(filePath, { start, end });
99
+ res.stream(stream);
100
+ } else {
101
+ // Stream full file
102
+ res.setHeader('Content-Length', fileSize);
103
+ res.setHeader('Content-Type', mimeType);
104
+ res.setHeader('Accept-Ranges', 'bytes');
105
+
106
+ const stream = fs.createReadStream(filePath);
107
+ res.stream(stream);
108
+ }
109
+ }
110
+
111
+ server.listen(port, '0.0.0.0')
112
+ .then(() => {
113
+ const networkInterfaces = require('os').networkInterfaces();
114
+ const addresses = [];
115
+
116
+ for (const name of Object.keys(networkInterfaces)) {
117
+ for (const net of networkInterfaces[name]) {
118
+ if (net.family === 'IPv4' && !net.internal) {
119
+ addresses.push(net.address);
120
+ }
121
+ }
122
+ }
123
+
124
+ console.log(`
125
+ 🚀 Laju Server berjalan!
126
+
127
+ 📁 Folder: ${folderPath}
128
+ 🌐 Local: http://localhost:${port}
129
+ 🌐 Network: ${addresses.map(ip => `http://${ip}:${port}`).join('\n 🌐 Network: ') || 'tidak tersedia'}
130
+
131
+ Tekan Ctrl+C untuk berhenti
132
+ `);
133
+ })
134
+ .catch((err) => {
135
+ console.error('❌ Gagal menjalankan server:', err.message);
136
+ process.exit(1);
137
+ });
138
+ }
139
+
140
+ module.exports = { startServer };
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "laju-server",
3
+ "version": "1.0.0",
4
+ "description": "Instant static file server using hyper-express",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "laju-server": "./bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node bin/cli.js"
11
+ },
12
+ "keywords": [
13
+ "static",
14
+ "server",
15
+ "hyper-express",
16
+ "file-server",
17
+ "cli"
18
+ ],
19
+ "author": "",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "hyper-express": "^6.17.3",
23
+ "mime-types": "^2.1.35"
24
+ }
25
+ }