birdpack 1.0.8 → 1.0.10

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 +288 -0
  2. package/lib/core.js +58 -32
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,288 @@
1
+ # 🐦 birdpack
2
+
3
+ **birdpack** คือ Web Framework สำหรับ Node.js ที่ถูกออกแบบมาให้ **เบา, เร็ว, ใช้ง่าย, รองรับ SSL หลายโดเมน** พร้อมระบบ Routing และ Static File Server
4
+
5
+ - - -
6
+
7
+ ## 📦 การติดตั้ง
8
+
9
+ ```
10
+ npm install birdpack
11
+ ```
12
+
13
+ - - -
14
+
15
+ ## 🚀 เริ่มต้นใช้งาน (Quick Start)
16
+
17
+ ```js
18
+
19
+ const fs = require('fs');
20
+ const birdpack = require('birdpack');
21
+
22
+ const app = new birdpack({
23
+ ssl: [
24
+ {
25
+ key: fs.readFileSync(__dirname + "/backend/ssl/test.dev.key"),
26
+ cert: fs.readFileSync(__dirname + "/backend/ssl/test.dev.crt"),
27
+ domain: ['test.dev'],
28
+ },
29
+ ],
30
+ traffic: '$time [$method] $path $ip',
31
+ log: 'console',
32
+ logFile: `${__dirname}/server.log`,
33
+ port: 443,
34
+ use: 'https',
35
+ });
36
+
37
+ // Routing พื้นฐาน
38
+ app.get('/', (req) => req.send('hi'))
39
+ .post('/json', (req) => req.code(400).json({ err: 400 }))
40
+ .all('/allpath', (req) => req.send('test'))
41
+ .directory('/public'); // Static files
42
+
43
+ // Routing เฉพาะ domain
44
+ let test = app.domain('test.dev');
45
+ test.next((req, next) => {
46
+ if (req.look('token')) next({ rank: 'admin' });
47
+ else next({ rank: 'user' });
48
+ })
49
+ .get('/user/:id', (req, data) => {
50
+ req.json({ userId: req.params.id, role: data.rank });
51
+ })
52
+ .get('/home', (req) => req.file('index.html'))
53
+ .routes(__dirname + '/backend/routes');
54
+
55
+ // เริ่ม server
56
+ app.listen();
57
+ ```
58
+
59
+ - - -
60
+
61
+ ## 📂 โครงสร้างโปรเจกต์แนะนำ
62
+
63
+ ```
64
+
65
+ my-app/
66
+ ├── backend/
67
+ │ ├── routes/
68
+ │ │ └── index.js
69
+ │ └── ssl/
70
+ │ ├── test.dev.crt
71
+ │ └── test.dev.key
72
+ ├── public/
73
+ ├── server.log
74
+ └── index.js
75
+ ```
76
+
77
+ - - -
78
+
79
+ ## ⚙️ การตั้งค่า (Config Options)
80
+
81
+ | Key | Type | Default | Description |
82
+ | --- | --- | --- | --- |
83
+ | ssl | Array | \[\] | SSL Certificates `{ key, cert, domain[] }` |
84
+ | traffic | string | false | รูปแบบ log เช่น `$time $method $path $ip` |
85
+ | log | string | 'console' | 'console' \| 'file' \| false |
86
+ | logFile | string | \- | path log file (ใช้เมื่อ `log:'file'`) |
87
+ | port | number | 80 | Port ที่ server listen |
88
+ | use | string | 'http' | 'http' หรือ 'https' |
89
+
90
+ - - -
91
+
92
+ ## 🔀 Routing Methods
93
+
94
+ ```js
95
+
96
+ app.get('/hello', req => req.send('Hello World'));
97
+ app.post('/data', req => req.json({ ok: 1 }));
98
+ app.all('/all', req => req.send('Match any method'));
99
+ app.directory('/public');
100
+ app.routes(__dirname + '/backend/routes');
101
+ ```
102
+
103
+ | Method | Description |
104
+ | --- | --- |
105
+ | .get(path, handler) | รับเฉพาะ GET |
106
+ | .post(path, handler) | รับเฉพาะ POST |
107
+ | .all(path, handler) | รองรับทุก Method |
108
+ | .directory(folder) | เสิร์ฟ static files |
109
+ | .routes(path) | โหลด routes จากไฟล์/โฟลเดอร์ |
110
+ | .domain(domain) | แยก routing ตามโดเมน |
111
+
112
+ - - -
113
+
114
+ ## 🧩 Request Object (req)
115
+
116
+ ### 📤 ส่ง Response
117
+
118
+ ```
119
+
120
+ req.send("text");
121
+ req.json({ hello: "world" });
122
+ req.html("<h1>Hi</h1>");
123
+ req.code(201).send("Created");
124
+ req.file("index.html");
125
+ req.redirect("https://google.com");
126
+ ```
127
+
128
+ ### 📌 Headers
129
+
130
+ ```
131
+
132
+ req.set("Content-Type", "text/plain");
133
+ let ua = req.get("user-agent");
134
+ ```
135
+
136
+ ### 🔍 Query Parameters (?id=1&user=2)
137
+
138
+ ```
139
+
140
+ req.u("id"); // "1"
141
+ req.look("id,user"); // ตรวจว่ามี id และ user
142
+ req.query; // {id:"1", user:"2"}
143
+ ```
144
+
145
+ ### 📥 Payload
146
+
147
+ ```
148
+
149
+ req.q("username");
150
+ req.check("id,user");
151
+ req.body; // payload object
152
+ ```
153
+
154
+ ### 🍪 Cookies
155
+
156
+ ```
157
+
158
+ req.scookie("token", "abc123", { httpOnly: true });
159
+ req.rcookie("token");
160
+ console.log(req.cookie);
161
+ ```
162
+
163
+ ### ⚙️ Response Config
164
+
165
+ ```
166
+
167
+ req.length(1024);
168
+ req.type("application/json");
169
+ ```
170
+
171
+ ### 📌 Properties
172
+
173
+ ```
174
+
175
+ req.ip; // IP client
176
+ req.host; // Host
177
+ req.method; // GET/POST
178
+ req.path; // Path
179
+ req.params; // เช่น /user/:id => {id:"123"}
180
+ ```
181
+
182
+ - - -
183
+
184
+ ## 🧭 Middleware + Domain Routing
185
+
186
+ ```
187
+
188
+ let dashboard = app.domain('dashboard.dev');
189
+
190
+ dashboard.next((req, next) => {
191
+ if (!req.look('token'))
192
+ return req.code(401).send('Unauthorized');
193
+ next({ user: 'admin' });
194
+ });
195
+
196
+ dashboard.get('/stats', (req, data) => {
197
+ req.json({ status: 'ok', user: data.user });
198
+ });
199
+ ```
200
+
201
+ - - -
202
+
203
+ ## 📂 การใช้งาน Routes แบบอัตโนมัติ
204
+
205
+ คุณสามารถใช้ `app.routes(folderPath)` เพื่อให้ **birdpack** โหลดเส้นทาง (routes) จากไฟล์ในโฟลเดอร์ที่กำหนด โดย **ชื่อไฟล์** จะถูกแปลงเป็น Method และ Path อัตโนมัติ
206
+
207
+ ### 📌 กฎการตั้งชื่อไฟล์
208
+
209
+ | รูปแบบชื่อไฟล์ | HTTP Method | Path ที่ได้ |
210
+ | --- | --- | --- |
211
+ | `get@user-id-$id.js` | GET | `/user/id/:id` |
212
+ | `post@user-*.js` | POST | `/user/*` |
213
+ | `all@health.js` | ALL | `/health` |
214
+
215
+ **💡 รูปแบบ:** `{method}@{path}.js`
216
+ ใช้ `$param` เพื่อกำหนด dynamic params เช่น `$id`
217
+ ใช้ `*` เพื่อ match ทุก segment ที่เหลือ
218
+
219
+ ### 📂 โครงสร้างตัวอย่าง
220
+
221
+ ```
222
+
223
+ backend/
224
+ └── routes/
225
+ ├── get@user-id-$id.js
226
+ ├── post@user-*.js
227
+ └── all@health.js
228
+ ```
229
+
230
+ ### 📌 ตัวอย่างไฟล์ route
231
+
232
+ ```js
233
+
234
+ // backend/routes/get@user-id-$id.js
235
+ module.exports = (req) => {
236
+ req.json({ userId: req.params.id });
237
+ };
238
+ ```
239
+
240
+ ```js
241
+
242
+ // backend/routes/post@user-*.js
243
+ module.exports = (req) => {
244
+ req.html('<h1>Post request matched!</h1>');
245
+ };
246
+ ```
247
+
248
+ ```js
249
+
250
+ // backend/routes/all@health.js
251
+ module.exports = (req) => {
252
+ req.send('OK');
253
+ };
254
+ ```
255
+
256
+ ### 📌 การใช้งานใน main server
257
+
258
+ ```js
259
+
260
+ const app = new birdpack({...});
261
+ app.routes(__dirname + '/backend/routes');
262
+ app.listen();
263
+ ```
264
+
265
+ - - -
266
+
267
+ ## 📝 Logging Variables
268
+
269
+ | Placeholder | Description |
270
+ | --- | --- |
271
+ | $time | เวลา |
272
+ | $method | HTTP Method |
273
+ | $path | Path ของ request |
274
+ | $ip | IP ของ client |
275
+ | $status | HTTP Status |
276
+ | $body | Payload |
277
+ | $head(key) | Header value เช่น $head(user-agent) |
278
+
279
+ ```
280
+
281
+ traffic: '$time - $ip - $method $path [$status] UA:$head(user-agent)'
282
+ ```
283
+
284
+ - - -
285
+
286
+ ## 📌 License
287
+
288
+ R938 Service
package/lib/core.js CHANGED
@@ -387,46 +387,72 @@ module.exports = class{
387
387
  this.end();
388
388
  }
389
389
  }else if(fileRange == 'file'){
390
- const totalSize = fileStat.size;
391
-
392
- const start = 0;
393
- const end = totalSize - 1;
390
+ if(options.parameter){
391
+ let data = fs.readFileSync(options.file, { encoding: 'utf8', flag: 'r' });
392
+
393
+ data = data.replace(/\{\{[a-zA-Z0-9\_]*\}\}/g, (a)=>{
394
+ let key = a.slice(2,-2);
395
+ if(key != ''){
396
+ if(options.parameter.hasOwnProperty(key)){
397
+ return options.parameter[key]
398
+ }
399
+ }
400
+ return '';
401
+ });
394
402
 
395
- const chunkSize = end - start + 1;
396
- const fileDescriptor = fs.openSync(options.file, "r");
403
+ data = Buffer.from(data, "utf8");
397
404
 
398
- const sendChunk = (position) => {
399
- if(position > end){
400
- fs.closeSync(fileDescriptor);
401
- return;
405
+ if(options.download === true){
406
+ this.set('content-disposition', `attachment; filename="${options.filename}"`);
402
407
  }
403
408
 
404
- const bytesToRead = Math.min(bufferSize, end - position + 1);
405
- const bytesRead = fs.readSync(fileDescriptor, buffer, 0, bytesToRead, position);
409
+ this.code(200)
410
+ .length(data.length)
411
+ .type(options.type);
412
+
413
+ this.send(data);
414
+ }else{
415
+ const totalSize = fileStat.size;
406
416
 
407
- if(bytesRead > 0 && this.res.writable){
408
- this[position + bytesRead > end ? 'end' : 'write'](buffer.slice(0, bytesRead), (err) => {
409
- if (err) {
410
- fs.closeSync(fileDescriptor);
411
- } else {
412
- sendChunk(position + bytesRead);
413
- }
414
- });
415
- }else{
416
- fs.closeSync(fileDescriptor);
417
- }
418
- };
417
+ const start = 0;
418
+ const end = totalSize - 1;
419
419
 
420
- this.code(200)
421
- .length(chunkSize)
422
- .type(options.type);
420
+ const chunkSize = end - start + 1;
421
+ const fileDescriptor = fs.openSync(options.file, "r");
422
+
423
+ const sendChunk = (position) => {
424
+ if(position > end){
425
+ fs.closeSync(fileDescriptor);
426
+ return;
427
+ }
423
428
 
424
- if(options.download === true){
425
- this.set('content-disposition', `attachment; filename="${options.filename}"`);
429
+ const bytesToRead = Math.min(bufferSize, end - position + 1);
430
+ const bytesRead = fs.readSync(fileDescriptor, buffer, 0, bytesToRead, position);
431
+
432
+ if(bytesRead > 0 && this.res.writable){
433
+ this[position + bytesRead > end ? 'end' : 'write'](buffer.slice(0, bytesRead), (err) => {
434
+ if (err) {
435
+ fs.closeSync(fileDescriptor);
436
+ } else {
437
+ sendChunk(position + bytesRead);
438
+ }
439
+ });
440
+ }else{
441
+ fs.closeSync(fileDescriptor);
442
+ }
443
+ };
444
+
445
+ this.code(200)
446
+ .length(chunkSize)
447
+ .type(options.type);
448
+
449
+ if(options.download === true){
450
+ this.set('content-disposition', `attachment; filename="${options.filename}"`);
451
+ }
452
+
453
+ this.writeHead();
454
+ sendChunk(start);
426
455
  }
427
-
428
- this.writeHead();
429
- sendChunk(start);
430
456
  }else{
431
457
  options.error(400, 'This file not supported.');
432
458
  }
package/package.json CHANGED
@@ -3,6 +3,6 @@
3
3
  "description": "BirdPack web framework is a tool for web server via TCP HTTP supporting websocket focusing on speed.",
4
4
  "author":"R938",
5
5
  "license":"R938",
6
- "version":"1.0.8",
6
+ "version":"1.0.10",
7
7
  "main": "index.js"
8
8
  }