birdpack 1.0.9 → 1.0.11

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/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/method.js CHANGED
@@ -64,7 +64,7 @@ module.exports = {
64
64
  return this;
65
65
  }
66
66
  },
67
- websocket:(app, use, domain)=>{
67
+ websocket:(app, use, {domain, next})=>{
68
68
  return (url, a, b, c)=>{
69
69
  let typeA = typeof a == 'function';
70
70
  let typeB = typeof b == 'function';
@@ -95,7 +95,8 @@ module.exports = {
95
95
  };
96
96
 
97
97
  app.setUpgrade({
98
- domain,
98
+ domain,
99
+ next,
99
100
  url,
100
101
  callback: app.setCall(callback)
101
102
  });
package/lib/server.js CHANGED
@@ -87,7 +87,7 @@ module.exports = class{
87
87
  methodPlugin.basic(this, this, {domain:'*'});
88
88
  this.routes = methodPlugin.routes(this);
89
89
  this.directory = methodPlugin.directory(this);
90
- this.websocket = methodPlugin.websocket(this, this, '*');
90
+ this.websocket = methodPlugin.websocket(this, this, {domain:'*'});
91
91
  }
92
92
  setupSSL(){
93
93
  if(this.opt.use !== 'https'){
@@ -236,13 +236,19 @@ module.exports = class{
236
236
  }
237
237
 
238
238
  if(call && call.hasOwnProperty(ws.path)){
239
- call = call[ws.path].callback;
240
- this.traffic(ws);
241
-
242
- const run = this.getCall(call);
243
-
244
- ws.update(run);
245
- run(ws, 'start');
239
+ call = call[ws.path];
240
+
241
+ if(call.next !== false){
242
+ this.getCall(call.next)(ws, ()=>{
243
+ const run = this.getCall(call.callback);
244
+ ws.update(run);
245
+ run(ws, 'start');
246
+ });
247
+ }else{
248
+ const run = this.getCall(call.callback);
249
+ ws.update(run);
250
+ run(ws, 'start');
251
+ }
246
252
  }else{
247
253
  ws.error(404);
248
254
  }
@@ -317,6 +323,7 @@ module.exports = class{
317
323
  methodPlugin.basic(this, next, {domain, next:this.setCall(n)});
318
324
  next.routes = methodPlugin.routes(next);
319
325
  next.directory = methodPlugin.directory(next);
326
+ next.websocket = methodPlugin.websocket(this, next, {domain, next:this.setCall(n)});
320
327
 
321
328
  return next;
322
329
  }
@@ -324,7 +331,7 @@ module.exports = class{
324
331
  methodPlugin.basic(this, config, {domain});
325
332
  config.routes = methodPlugin.routes(config);
326
333
  config.directory = methodPlugin.directory(config);
327
- config.websocket = methodPlugin.websocket(this, config, domain);
334
+ config.websocket = methodPlugin.websocket(this, config, {domain});
328
335
 
329
336
  return config;
330
337
  }
@@ -377,7 +384,7 @@ module.exports = class{
377
384
  this.maps[domain][method].params[mode == 'params' ? 0 : 1].push(route);
378
385
  }
379
386
  }
380
- setUpgrade({domain, url, callback}){
387
+ setUpgrade({domain, next, url, callback}){
381
388
  if(typeof domain != 'string'){
382
389
  domain = '*';
383
390
  }
@@ -390,12 +397,17 @@ module.exports = class{
390
397
  return false;
391
398
  }
392
399
 
400
+ if(typeof next != 'number'){
401
+ next = false;
402
+ }
403
+
393
404
  if(!this.mapws.hasOwnProperty(domain)){
394
405
  this.mapws[domain] = {};
395
406
  }
396
407
 
397
408
  this.mapws[domain][url] = {
398
- callback:callback
409
+ callback,
410
+ next,
399
411
  };
400
412
  }
401
413
  setCall(callback){
@@ -408,5 +420,14 @@ module.exports = class{
408
420
  }
409
421
  return ()=>{};
410
422
  }
423
+ next(n){
424
+ let next = {};
425
+ methodPlugin.basic(this, next, {domain:'*', next:this.setCall(n)});
426
+ next.routes = methodPlugin.routes(next);
427
+ next.directory = methodPlugin.directory(next);
428
+ next.websocket = methodPlugin.websocket(this, next, {domain:'*', next:this.setCall(n)});
429
+
430
+ return next;
431
+ }
411
432
  }
412
433
 
package/lib/websocket.js CHANGED
@@ -11,6 +11,7 @@ module.exports = class{
11
11
  query = {}
12
12
  method = ''
13
13
  ip = ''
14
+ isUID = false
14
15
  callback = ()=>{}
15
16
  log = ()=>{}
16
17
  constructor({req, socket, head}){
@@ -170,6 +171,7 @@ module.exports = class{
170
171
  const head = tools.head2line(this.header);
171
172
  const statusLine = `HTTP/1.1 ${this.status} ${tools.statusText(this.status)}`;
172
173
  const headLine = `${statusLine}\r\n${head}\r\n\r\n`;
174
+ this.isUID = true;
173
175
  this.write(Buffer.from(headLine));
174
176
 
175
177
  this.log();
@@ -177,7 +179,9 @@ module.exports = class{
177
179
  return this;
178
180
  }
179
181
  write(data, callback){
180
- if(this.socket.writable){
182
+ if(!this.isUID){
183
+ this.error(400);
184
+ }else if(this.socket.writable){
181
185
  this.socket.write(data, callback);
182
186
  }else if(this.req.destroyed){
183
187
  this.req.destroy();
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.9",
6
+ "version":"1.0.11",
7
7
  "main": "index.js"
8
8
  }