keypointjs 1.1.0 → 1.2.2
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 +108 -676
- package/package.json +21 -13
- package/src/core/BPE.js +47 -0
- package/src/core/GrpcEngine.js +209 -0
- package/src/core/HttpEngine.js +107 -0
- package/src/core/PA.js +169 -0
- package/src/core/ProtocolEngine.js +566 -29
- package/src/core/WsEngine.js +333 -0
- package/src/keypointJS.js +562 -211
- package/src/plugins/transformer/TransformerPlugin.js +2 -2
package/README.md
CHANGED
|
@@ -8,26 +8,38 @@
|
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
<p align="center">
|
|
11
|
-
<img alt="
|
|
12
|
-
<img alt="
|
|
11
|
+
<img alt="License" src="https://img.shields.io/github/license/anasbex-dev/keypointjs?color=blue">
|
|
12
|
+
<img alt="Version" src="https://img.shields.io/npm/v/keypointjs">
|
|
13
|
+
<img alt="Downloads" src="https://img.shields.io/npm/dm/keypointjs?style=for-the-badge">
|
|
13
14
|
<img alt="Node.js" src="https://img.shields.io/badge/Node.js-%3E%3D18.0.0-green">
|
|
14
15
|
<img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-Ready-blue">
|
|
15
16
|
<img alt="Tests" src="https://img.shields.io/badge/tests-100%25%20passing-brightgreen">
|
|
16
17
|
</p>
|
|
18
|
+
|
|
17
19
|
**A Modern, Extensible Authentication & Authorization Framework for Node.js**
|
|
18
20
|
|
|
19
|
-
[
|
|
21
|
+
[Quick Start](#quick-start) • [Documentation](#documentation) • [Examples](#examples) • [Contributing](./CONTRIBUTING.md)
|
|
22
|
+
[Philosophy](./philosophyEN.md)
|
|
20
23
|
|
|
21
24
|
</div>
|
|
22
25
|
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Project Overview
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
KeypointJS is a layered authentication and authorization framework for Node.js, featuring:
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
* Secure, production-ready authentication & authorization
|
|
33
|
+
* Plugin architecture for extensibility
|
|
34
|
+
* Real-time WebSocket support
|
|
35
|
+
* Audit logging and monitoring
|
|
36
|
+
* Built-in policy engine and scope management
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
---
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
## Architecture
|
|
41
|
+
|
|
42
|
+
### Layered Middleware System
|
|
31
43
|
|
|
32
44
|
```
|
|
33
45
|
┌─────────────────────────────────┐
|
|
@@ -48,181 +60,102 @@ Layered Middleware System
|
|
|
48
60
|
│ Layer 7: Response Processing │
|
|
49
61
|
└─────────────────────────────────┘
|
|
50
62
|
```
|
|
51
|
-
# File Structure & Responsibilities
|
|
52
|
-
|
|
53
|
-
## Core Components (core/)
|
|
54
|
-
|
|
55
|
-
- Context.js - Request Context Base Class
|
|
56
|
-
|
|
57
|
-
- Request/Response wrapper
|
|
58
|
-
- State management
|
|
59
|
-
- Plugin data storage
|
|
60
|
-
- Helper methods for JSON, text, HTML
|
|
61
|
-
responses
|
|
62
|
-
- Header and query parameter accessors
|
|
63
|
-
|
|
64
|
-
## ProtocolEngine.js - Protocol Detection & Processing
|
|
65
|
-
|
|
66
|
-
- HTTP/HTTPS/WebSocket protocol detection
|
|
67
|
-
- Request body parsing (JSON, form data)
|
|
68
|
-
- IP extraction from headers
|
|
69
|
-
- Body size limiting
|
|
70
|
-
- Protocol validation
|
|
71
|
-
|
|
72
|
-
# Keypoint System (keypoint/)
|
|
73
|
-
|
|
74
|
-
## Keypoint.js - Keypoint Entity
|
|
75
|
-
|
|
76
|
-
- Keypoint data model (keyId, secret, scopes, protocols)
|
|
77
|
-
- Scope validation methods
|
|
78
|
-
- Expiration checking
|
|
79
|
-
- Origin and protocol validation
|
|
80
|
-
- Rate limit configuration
|
|
81
|
-
|
|
82
|
-
## KeypointContext.js - Enhanced Context
|
|
83
|
-
|
|
84
|
-
- Extends base Context class
|
|
85
|
-
- Keypoint-specific methods (scope checking, rate limiting)
|
|
86
|
-
- Access logging
|
|
87
|
-
- Security validation (origin, protocol)
|
|
88
|
-
- Authentication state management
|
|
89
|
-
|
|
90
|
-
## KeypointStorage.js - Storage Abstraction
|
|
91
63
|
|
|
92
|
-
|
|
93
|
-
- File-based storage option
|
|
94
|
-
- CRUD operations with indexing by secret, name, scope
|
|
95
|
-
- Cleanup of expired keypoints
|
|
96
|
-
- List operations with filtering
|
|
64
|
+
---
|
|
97
65
|
|
|
98
|
-
##
|
|
66
|
+
## File Structure & Responsibilities
|
|
99
67
|
|
|
100
|
-
|
|
101
|
-
- Validates keypoint existence and expiration
|
|
102
|
-
- Secret verification
|
|
103
|
-
- Context attachment
|
|
68
|
+
### Core Components (`core/`)
|
|
104
69
|
|
|
105
|
-
|
|
70
|
+
* **Context.js**: Base request context
|
|
71
|
+
* Request/Response wrapper
|
|
72
|
+
* State management
|
|
73
|
+
* Plugin data storage
|
|
74
|
+
* JSON, text, HTML helpers
|
|
75
|
+
* Header & query accessors
|
|
106
76
|
|
|
107
|
-
|
|
108
|
-
- Inheritance system
|
|
109
|
-
- Scope validation and expansion
|
|
110
|
-
- Pattern matching for wildcard scopes
|
|
111
|
-
- Scope tree generation
|
|
77
|
+
### Protocol Engine (`ProtocolEngine.js`)
|
|
112
78
|
|
|
113
|
-
|
|
79
|
+
* HTTP/HTTPS/WebSocket detection
|
|
80
|
+
* Body parsing (JSON, form data)
|
|
81
|
+
* IP extraction & validation
|
|
82
|
+
* Request size limiting
|
|
114
83
|
|
|
115
|
-
|
|
84
|
+
### Keypoint System (`keypoint/`)
|
|
116
85
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
|
|
86
|
+
* **Keypoint.js**: Keypoint entity, scopes, protocols, expiration
|
|
87
|
+
* **KeypointContext.js**: Context extension with scope checking, rate limiting, logging
|
|
88
|
+
* **KeypointStorage.js**: In-memory & file-based storage with indexing
|
|
89
|
+
* **KeypointValidator.js**: Extracts & validates keypoints
|
|
90
|
+
* **ScopeManager.js**: Manages scopes, hierarchy, wildcard patterns
|
|
121
91
|
|
|
122
|
-
|
|
92
|
+
### Policy Engine (`policy/`)
|
|
123
93
|
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
- Priority and enablement controls
|
|
94
|
+
* **PolicyEngine.js**: Rule-based access control
|
|
95
|
+
* **PolicyRule.js**: Built-in & custom rules (method, origin, IP, rate, scope)
|
|
96
|
+
* **AccessDecision.js**: Aggregates rule results
|
|
128
97
|
|
|
129
|
-
|
|
98
|
+
### Plugin System (`plugins/`)
|
|
130
99
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
- Debug information generation
|
|
100
|
+
* **PluginManager.js**: Plugin registration, lifecycle, hooks
|
|
101
|
+
* **AuditLogger.js**: Request/response logging with rotation
|
|
102
|
+
* **RateLimiter.js**: Keypoint-based rate limiting
|
|
103
|
+
* **WebSocketGuard.js**: Secure WebSocket connections
|
|
136
104
|
|
|
137
|
-
|
|
105
|
+
### Router (`router/`)
|
|
138
106
|
|
|
139
|
-
|
|
107
|
+
* **MinimalRouter.js**: Simple HTTP router with method/path matching
|
|
140
108
|
|
|
141
|
-
|
|
142
|
-
- Middleware chain composition
|
|
143
|
-
- Event and hook system
|
|
144
|
-
- Built-in hooks for request lifecycle
|
|
145
|
-
- Plugin statistics and management
|
|
109
|
+
### Main Framework (`keypointJS.js`)
|
|
146
110
|
|
|
147
|
-
|
|
111
|
+
* Orchestrates all components
|
|
112
|
+
* Server creation & configuration
|
|
113
|
+
* Statistics & health checks
|
|
114
|
+
* Event emission & error handling
|
|
148
115
|
|
|
149
|
-
|
|
150
|
-
- File-based logging with rotation
|
|
151
|
-
- Console output with colors
|
|
152
|
-
- Queryable log storage
|
|
153
|
-
· Error tracking and reporting
|
|
116
|
+
---
|
|
154
117
|
|
|
155
|
-
##
|
|
118
|
+
## Quick Start
|
|
156
119
|
|
|
157
|
-
|
|
158
|
-
- Time window enforcement
|
|
159
|
-
- Request counting per window
|
|
160
|
-
|
|
161
|
-
## WebSocketGuard.js - WebSocket Security
|
|
162
|
-
|
|
163
|
-
- WebSocket server integration
|
|
164
|
-
- Keypoint validation for WebSocket connections
|
|
165
|
-
- Connection management and monitoring
|
|
166
|
-
- Message handling and broadcasting
|
|
167
|
-
- Ping/pong keepalive
|
|
168
|
-
|
|
169
|
-
# Router (router/)
|
|
170
|
-
|
|
171
|
-
## MinimalRouter.js - Simple HTTP Router
|
|
172
|
-
|
|
173
|
-
- Method-based route registration
|
|
174
|
-
- Direct path matching
|
|
175
|
-
- Request handling with context
|
|
176
|
-
- Route management
|
|
177
|
-
|
|
178
|
-
# Main Framework (keypointJS.js)
|
|
179
|
-
|
|
180
|
-
- Main class orchestrating all components
|
|
181
|
-
- Server creation and management
|
|
182
|
-
- Configuration system
|
|
183
|
-
- Statistics and health checks
|
|
184
|
-
- Event emission system
|
|
185
|
-
- Error handling
|
|
186
|
-
|
|
187
|
-
# Quick Start
|
|
188
|
-
|
|
189
|
-
Installation & Setup
|
|
190
|
-
|
|
191
|
-
``` bash
|
|
120
|
+
### Installation
|
|
192
121
|
|
|
122
|
+
```bash
|
|
193
123
|
npm install keypointjs
|
|
194
124
|
# or
|
|
195
125
|
yarn add keypointjs
|
|
196
126
|
# or
|
|
197
127
|
pnpm add keypointjs
|
|
198
|
-
|
|
199
128
|
```
|
|
200
129
|
|
|
130
|
+
### Initialization
|
|
131
|
+
|
|
201
132
|
```javascript
|
|
202
133
|
import { KeypointJS } from './src/keypointJS.js';
|
|
203
134
|
|
|
204
|
-
// Initialize the framework
|
|
205
135
|
const api = new KeypointJS({
|
|
206
136
|
requireKeypoint: true,
|
|
207
137
|
strictMode: false,
|
|
208
138
|
enableCORS: true,
|
|
209
139
|
maxRequestSize: '5mb'
|
|
210
140
|
});
|
|
141
|
+
```
|
|
211
142
|
|
|
212
|
-
|
|
143
|
+
### Create Keypoint
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
213
146
|
const keypoint = await api.createKeypoint({
|
|
214
147
|
keyId: 'test_key',
|
|
215
148
|
secret: 'test_secret',
|
|
216
149
|
scopes: ['api:public', 'users:read'],
|
|
217
150
|
protocols: ['https', 'wss'],
|
|
218
151
|
allowedOrigins: ['https://example.com'],
|
|
219
|
-
rateLimit: {
|
|
220
|
-
requests: 1000,
|
|
221
|
-
window: 3600 // 1 hour
|
|
222
|
-
}
|
|
152
|
+
rateLimit: { requests: 1000, window: 3600 }
|
|
223
153
|
});
|
|
154
|
+
```
|
|
224
155
|
|
|
225
|
-
|
|
156
|
+
### Define Routes
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
226
159
|
api.get('/api/data', (ctx) => {
|
|
227
160
|
return ctx.json({
|
|
228
161
|
data: 'protected data',
|
|
@@ -232,20 +165,23 @@ api.get('/api/data', (ctx) => {
|
|
|
232
165
|
});
|
|
233
166
|
|
|
234
167
|
api.post('/api/webhook', (ctx) => {
|
|
235
|
-
const body = ctx.body;
|
|
236
|
-
// Process webhook
|
|
237
168
|
return ctx.json({ received: true });
|
|
238
169
|
});
|
|
170
|
+
```
|
|
239
171
|
|
|
240
|
-
|
|
172
|
+
### Start Server
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
241
175
|
api.listen(3000, 'localhost', () => {
|
|
242
176
|
console.log('Server running on port 3000');
|
|
243
177
|
});
|
|
244
178
|
```
|
|
245
179
|
|
|
246
|
-
|
|
180
|
+
---
|
|
247
181
|
|
|
248
|
-
|
|
182
|
+
## Authentication Flow
|
|
183
|
+
|
|
184
|
+
1. **Request with Keypoint**
|
|
249
185
|
|
|
250
186
|
```http
|
|
251
187
|
GET /api/data HTTP/1.1
|
|
@@ -254,21 +190,19 @@ X-Keypoint-ID: test_key
|
|
|
254
190
|
X-Keypoint-Secret: test_secret
|
|
255
191
|
```
|
|
256
192
|
|
|
257
|
-
2. Validation Process
|
|
193
|
+
2. **Validation Process**
|
|
258
194
|
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
5. Response: Return formatted response
|
|
195
|
+
```text
|
|
196
|
+
Layer 1: ProtocolEngine (detect, parse)
|
|
197
|
+
Layer 2: KeypointValidator (validate keypoint)
|
|
198
|
+
Layer 3: PolicyEngine (evaluate rules)
|
|
199
|
+
Layer 4: Router (execute handler)
|
|
200
|
+
Layer 5: Response (format & return)
|
|
266
201
|
```
|
|
267
202
|
|
|
268
|
-
3. Scope-Based Authorization
|
|
203
|
+
3. **Scope-Based Authorization**
|
|
269
204
|
|
|
270
205
|
```javascript
|
|
271
|
-
// Route requiring specific scope
|
|
272
206
|
api.get('/api/users', (ctx) => {
|
|
273
207
|
if (!ctx.hasScope('users:read')) {
|
|
274
208
|
return ctx.status(403).json({ error: 'Insufficient scope' });
|
|
@@ -277,541 +211,39 @@ api.get('/api/users', (ctx) => {
|
|
|
277
211
|
});
|
|
278
212
|
```
|
|
279
213
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
KeypointJS Constructor Options
|
|
283
|
-
|
|
284
|
-
```javascript
|
|
285
|
-
const api = new KeypointJS({
|
|
286
|
-
// Core settings
|
|
287
|
-
requireKeypoint: true, // Require authentication
|
|
288
|
-
strictMode: true, // Strict validation mode
|
|
289
|
-
|
|
290
|
-
// Security
|
|
291
|
-
validateOrigin: true, // Validate request origin
|
|
292
|
-
validateProtocol: true, // Validate protocol
|
|
293
|
-
enableCORS: false, // Enable CORS
|
|
294
|
-
corsOrigins: ['*'], // Allowed origins
|
|
295
|
-
|
|
296
|
-
// Performance
|
|
297
|
-
maxRequestSize: '10mb', // Max request body size
|
|
298
|
-
|
|
299
|
-
// Headers
|
|
300
|
-
defaultResponseHeaders: {
|
|
301
|
-
'X-Powered-By': 'KeypointJS',
|
|
302
|
-
'X-Content-Type-Options': 'nosniff'
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
// Storage
|
|
306
|
-
keypointStorage: new MemoryKeypointStorage() // Custom storage
|
|
307
|
-
});
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
Keypoint Configuration
|
|
311
|
-
|
|
312
|
-
```javascript
|
|
313
|
-
const keypoint = {
|
|
314
|
-
keyId: 'unique_id', // Required
|
|
315
|
-
secret: 'secure_password', // Required
|
|
316
|
-
name: 'Production Key', // Optional
|
|
317
|
-
scopes: ['api:write', 'admin'],
|
|
318
|
-
protocols: ['https', 'wss'], // Allowed protocols
|
|
319
|
-
allowedOrigins: ['https://app.com'],
|
|
320
|
-
allowedIps: ['192.168.1.0/24'],
|
|
321
|
-
rateLimit: {
|
|
322
|
-
requests: 1000, // Requests per window
|
|
323
|
-
window: 3600 // Seconds (1 hour)
|
|
324
|
-
},
|
|
325
|
-
expiresAt: new Date('2024-12-31'),
|
|
326
|
-
metadata: {
|
|
327
|
-
userId: 'user_123',
|
|
328
|
-
environment: 'production'
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
# Plugin System
|
|
334
|
-
|
|
335
|
-
Built-in Plugins
|
|
336
|
-
|
|
337
|
-
1. Audit Logger
|
|
338
|
-
|
|
339
|
-
```javascript
|
|
340
|
-
import { AuditLogger } from './plugins/AuditLogger.js';
|
|
341
|
-
|
|
342
|
-
api.registerPlugin(new AuditLogger({
|
|
343
|
-
logLevel: 'info',
|
|
344
|
-
logToConsole: true,
|
|
345
|
-
logToFile: true,
|
|
346
|
-
filePath: './logs/audit.log',
|
|
347
|
-
maxFileSize: '50mb'
|
|
348
|
-
}));
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
2. Rate Limiter
|
|
352
|
-
|
|
353
|
-
```javascript
|
|
354
|
-
import { RateLimiter } from './plugins/RateLimiter.js';
|
|
355
|
-
|
|
356
|
-
api.registerPlugin(new RateLimiter({
|
|
357
|
-
window: 60000 // 1 minute in milliseconds
|
|
358
|
-
}));
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
3. WebSocket Guard
|
|
362
|
-
|
|
363
|
-
```javascript
|
|
364
|
-
import { WebSocketGuard } from './plugins/WebSocketGuard.js';
|
|
365
|
-
|
|
366
|
-
const wsGuard = api.enableWebSocket({
|
|
367
|
-
path: '/ws',
|
|
368
|
-
requireKeypoint: true,
|
|
369
|
-
pingInterval: 30000,
|
|
370
|
-
maxConnections: 1000
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
wsGuard.onConnection((connection) => {
|
|
374
|
-
console.log('New WebSocket connection:', connection.id);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
wsGuard.onMessage('chat', (message, connection) => {
|
|
378
|
-
// Handle chat messages
|
|
379
|
-
return { type: 'chat_response', data: 'Message received' };
|
|
380
|
-
});
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
Custom Plugin Creation
|
|
384
|
-
|
|
385
|
-
```javascript
|
|
386
|
-
export class CustomPlugin {
|
|
387
|
-
constructor(options = {}) {
|
|
388
|
-
this.name = 'CustomPlugin';
|
|
389
|
-
this.options = options;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
async process(ctx, next) {
|
|
393
|
-
const startTime = Date.now();
|
|
394
|
-
const result = await next(ctx);
|
|
395
|
-
const duration = Date.now() - startTime;
|
|
396
|
-
|
|
397
|
-
ctx.setPluginData(this.name, { duration });
|
|
398
|
-
return result;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
initialize() {
|
|
402
|
-
console.log(`${this.name} initialized`);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
cleanup() {
|
|
406
|
-
console.log(`${this.name} cleaned up`);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Register custom plugin
|
|
411
|
-
api.registerPlugin(new CustomPlugin({ debug: true }));
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
# Security Features
|
|
415
|
-
|
|
416
|
-
Rate Limiting
|
|
417
|
-
|
|
418
|
-
```javascript
|
|
419
|
-
// Built-in rate limiting rule
|
|
420
|
-
api.addPolicyRule(
|
|
421
|
-
BuiltInRules.rateLimitRule(100, 60) // 100 requests per minute
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
// Keypoint-specific rate limiting
|
|
425
|
-
const keypoint = new Keypoint({
|
|
426
|
-
keyId: 'limited_key',
|
|
427
|
-
secret: 'secret',
|
|
428
|
-
rateLimit: {
|
|
429
|
-
requests: 50, // 50 requests
|
|
430
|
-
window: 300 // per 5 minutes
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
IP Whitelisting/Blacklisting
|
|
436
|
-
|
|
437
|
-
```javascript
|
|
438
|
-
api.addPolicyRule(
|
|
439
|
-
BuiltInRules.ipRule(
|
|
440
|
-
['192.168.1.0/24'], // Allowed IPs
|
|
441
|
-
['10.0.0.5', '172.16.0.0/12'] // Blocked IPs
|
|
442
|
-
)
|
|
443
|
-
);
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
Time-Based Access Control
|
|
447
|
-
|
|
448
|
-
```javascript
|
|
449
|
-
// Only allow access between 9 AM and 5 PM
|
|
450
|
-
api.addPolicyRule(
|
|
451
|
-
BuiltInRules.timeWindowRule(9, 17)
|
|
452
|
-
);
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
Protocol Enforcement
|
|
456
|
-
|
|
457
|
-
```javascript
|
|
458
|
-
// Only allow HTTPS and WSS protocols
|
|
459
|
-
api.addPolicyRule(
|
|
460
|
-
BuiltInRules.protocolRule(['https', 'wss'])
|
|
461
|
-
);
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
# WebSocket Support
|
|
465
|
-
|
|
466
|
-
Setup WebSocket Server
|
|
467
|
-
|
|
468
|
-
```javascript
|
|
469
|
-
// Enable WebSocket support
|
|
470
|
-
const wsGuard = api.enableWebSocket({
|
|
471
|
-
path: '/realtime',
|
|
472
|
-
requireKeypoint: true,
|
|
473
|
-
keypointHeader: 'x-keypoint-id'
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// Handle WebSocket connections
|
|
477
|
-
wsGuard.onConnection((connection) => {
|
|
478
|
-
console.log('Connected:', {
|
|
479
|
-
id: connection.id,
|
|
480
|
-
keypointId: connection.keypointId,
|
|
481
|
-
ip: connection.ip
|
|
482
|
-
});
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// Broadcast messages
|
|
486
|
-
wsGuard.broadcast({
|
|
487
|
-
type: 'notification',
|
|
488
|
-
data: 'System update'
|
|
489
|
-
}, {
|
|
490
|
-
scope: 'admin' // Only send to admin keypoints
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Send to specific connection
|
|
494
|
-
wsGuard.sendToConnection('connection_id', {
|
|
495
|
-
type: 'private',
|
|
496
|
-
data: 'Secret message'
|
|
497
|
-
});
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
WebSocket Message Handling
|
|
501
|
-
|
|
502
|
-
```javascript
|
|
503
|
-
wsGuard.onMessage('subscribe', async (message, connection) => {
|
|
504
|
-
const { channel } = message;
|
|
505
|
-
|
|
506
|
-
// Validate subscription rights
|
|
507
|
-
if (channel === 'admin' && !connection.scopes?.includes('admin')) {
|
|
508
|
-
return { type: 'error', error: 'Access denied' };
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
connection.metadata.subscriptions =
|
|
512
|
-
connection.metadata.subscriptions || [];
|
|
513
|
-
connection.metadata.subscriptions.push(channel);
|
|
514
|
-
|
|
515
|
-
return {
|
|
516
|
-
type: 'subscribed',
|
|
517
|
-
channel,
|
|
518
|
-
timestamp: new Date().toISOString()
|
|
519
|
-
};
|
|
520
|
-
});
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
# Monitoring & Statistics
|
|
524
|
-
|
|
525
|
-
Framework Statistics
|
|
526
|
-
|
|
527
|
-
```javascript
|
|
528
|
-
const stats = api.getStats();
|
|
529
|
-
|
|
530
|
-
console.log('Framework Statistics:', {
|
|
531
|
-
uptime: stats.uptimeFormatted,
|
|
532
|
-
totalRequests: stats.requests,
|
|
533
|
-
successRate: stats.successRate,
|
|
534
|
-
activeKeypoints: stats.keypoints.active,
|
|
535
|
-
totalPlugins: stats.plugins.totalPlugins,
|
|
536
|
-
activeConnections: wsGuard?.getStats()?.totalConnections || 0
|
|
537
|
-
});
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
Health Check Endpoint
|
|
541
|
-
|
|
542
|
-
```javascript
|
|
543
|
-
api.get('/health', async (ctx) => {
|
|
544
|
-
const health = await api.healthCheck();
|
|
545
|
-
return ctx.json(health);
|
|
546
|
-
});
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
Audit Log Querying
|
|
550
|
-
|
|
551
|
-
```javascript
|
|
552
|
-
const auditLogger = api.pluginManager.getPlugin('AuditLogger');
|
|
553
|
-
|
|
554
|
-
const logs = await auditLogger.queryLogs({
|
|
555
|
-
startDate: '2024-01-01',
|
|
556
|
-
endDate: '2024-01-31',
|
|
557
|
-
keypointId: 'specific_key',
|
|
558
|
-
level: 'error',
|
|
559
|
-
limit: 100
|
|
560
|
-
});
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
# Storage Options
|
|
564
|
-
|
|
565
|
-
Memory Storage (Default)
|
|
566
|
-
|
|
567
|
-
```javascript
|
|
568
|
-
import { MemoryKeypointStorage } from './keypoint/KeypointStorage.js';
|
|
569
|
-
|
|
570
|
-
const api = new KeypointJS({
|
|
571
|
-
keypointStorage: new MemoryKeypointStorage()
|
|
572
|
-
});
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
# File Storage
|
|
214
|
+
---
|
|
576
215
|
|
|
577
|
-
|
|
578
|
-
import { FileKeypointStorage } from './keypoint/KeypointStorage.js';
|
|
216
|
+
## Contributing
|
|
579
217
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
218
|
+
1. Fork the repository
|
|
219
|
+
2. Create a feature branch (git checkout -b feature/amazing-feature)
|
|
220
|
+
3. Add tests for your changes
|
|
221
|
+
4. Ensure all tests pass (npm test)
|
|
222
|
+
5. Commit your changes (git commit -m 'Add amazing feature')
|
|
223
|
+
6. Push to the branch (git push origin feature/amazing-feature)
|
|
224
|
+
7. Open a Pull Request
|
|
584
225
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
```javascript
|
|
588
|
-
export class CustomKeypointStorage extends KeypointStorage {
|
|
589
|
-
constructor(databaseClient) {
|
|
590
|
-
super('custom');
|
|
591
|
-
this.db = databaseClient;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
async set(keypoint) {
|
|
595
|
-
// Save to database
|
|
596
|
-
await this.db.collection('keypoints').insertOne(keypoint);
|
|
597
|
-
return super.set(keypoint);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
async get(keyId) {
|
|
601
|
-
// Try memory cache first
|
|
602
|
-
const cached = super.get(keyId);
|
|
603
|
-
if (cached) return cached;
|
|
604
|
-
|
|
605
|
-
// Fallback to database
|
|
606
|
-
const doc = await this.db.collection('keypoints').findOne({ keyId });
|
|
607
|
-
if (doc) {
|
|
608
|
-
super.set(doc);
|
|
609
|
-
return doc;
|
|
610
|
-
}
|
|
611
|
-
return null;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
# Testing & Debugging
|
|
617
|
-
|
|
618
|
-
Error Handling
|
|
619
|
-
|
|
620
|
-
```javascript
|
|
621
|
-
// Global error handler
|
|
622
|
-
api.use(async (ctx, next) => {
|
|
623
|
-
try {
|
|
624
|
-
await next();
|
|
625
|
-
} catch (error) {
|
|
626
|
-
console.error('Request failed:', {
|
|
627
|
-
path: ctx.path,
|
|
628
|
-
method: ctx.method,
|
|
629
|
-
keypointId: ctx.getKeypointId(),
|
|
630
|
-
error: error.message,
|
|
631
|
-
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
return ctx.status(error.code || 500).json({
|
|
635
|
-
error: error.message,
|
|
636
|
-
code: error.code,
|
|
637
|
-
requestId: ctx.id
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
```
|
|
226
|
+
---
|
|
642
227
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
```javascript
|
|
646
|
-
api.use(async (ctx, next) => {
|
|
647
|
-
console.log('Incoming request:', {
|
|
648
|
-
id: ctx.id,
|
|
649
|
-
method: ctx.method,
|
|
650
|
-
path: ctx.path,
|
|
651
|
-
ip: ctx.ip,
|
|
652
|
-
keypointId: ctx.getKeypointId()
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
const start = Date.now();
|
|
656
|
-
await next();
|
|
657
|
-
const duration = Date.now() - start;
|
|
658
|
-
|
|
659
|
-
console.log('Request completed:', {
|
|
660
|
-
id: ctx.id,
|
|
661
|
-
duration: `${duration}ms`,
|
|
662
|
-
status: ctx.response.status
|
|
663
|
-
});
|
|
664
|
-
});
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
# Production Deployment
|
|
668
|
-
|
|
669
|
-
Docker Configuration
|
|
670
|
-
|
|
671
|
-
```dockerfile
|
|
672
|
-
FROM node:18-alpine
|
|
673
|
-
|
|
674
|
-
WORKDIR /app
|
|
675
|
-
|
|
676
|
-
COPY package*.json ./
|
|
677
|
-
RUN npm ci --only=production
|
|
678
|
-
|
|
679
|
-
COPY . .
|
|
680
|
-
|
|
681
|
-
# Create non-root user
|
|
682
|
-
RUN addgroup -g 1001 -S nodejs && \
|
|
683
|
-
adduser -S nodejs -u 1001
|
|
684
|
-
|
|
685
|
-
USER nodejs
|
|
686
|
-
|
|
687
|
-
EXPOSE 3000
|
|
688
|
-
|
|
689
|
-
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
690
|
-
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
|
691
|
-
|
|
692
|
-
CMD ["node", "server.js"]
|
|
693
|
-
```
|
|
694
|
-
|
|
695
|
-
# Environment Configuration
|
|
696
|
-
|
|
697
|
-
```javascript
|
|
698
|
-
const api = new KeypointJS({
|
|
699
|
-
requireKeypoint: process.env.REQUIRE_KEYPOINT !== 'false',
|
|
700
|
-
strictMode: process.env.NODE_ENV === 'production',
|
|
701
|
-
maxRequestSize: process.env.MAX_REQUEST_SIZE || '10mb',
|
|
702
|
-
enableCORS: process.env.ENABLE_CORS === 'true',
|
|
703
|
-
corsOrigins: process.env.CORS_ORIGINS?.split(',') || []
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
// Load keypoints from environment
|
|
707
|
-
if (process.env.KEYPOINTS) {
|
|
708
|
-
const keypoints = JSON.parse(process.env.KEYPOINTS);
|
|
709
|
-
for (const kp of keypoints) {
|
|
710
|
-
await api.createKeypoint(kp);
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
# Performance Optimization
|
|
716
|
-
|
|
717
|
-
Connection Pooling
|
|
718
|
-
|
|
719
|
-
```javascript
|
|
720
|
-
// Reuse HTTP agents for better performance
|
|
721
|
-
import http from 'http';
|
|
722
|
-
import https from 'https';
|
|
723
|
-
|
|
724
|
-
const httpAgent = new http.Agent({
|
|
725
|
-
keepAlive: true,
|
|
726
|
-
maxSockets: 100,
|
|
727
|
-
keepAliveMsecs: 1000
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
const httpsAgent = new https.Agent({
|
|
731
|
-
keepAlive: true,
|
|
732
|
-
maxSockets: 100,
|
|
733
|
-
keepAliveMsecs: 1000
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
// Use in outgoing requests
|
|
737
|
-
const response = await fetch(url, {
|
|
738
|
-
agent: url.startsWith('https') ? httpsAgent : httpAgent
|
|
739
|
-
});
|
|
740
|
-
```
|
|
741
|
-
|
|
742
|
-
Caching Strategy
|
|
743
|
-
|
|
744
|
-
```javascript
|
|
745
|
-
import NodeCache from 'node-cache';
|
|
746
|
-
|
|
747
|
-
const keypointCache = new NodeCache({
|
|
748
|
-
stdTTL: 300, // 5 minutes
|
|
749
|
-
checkperiod: 60
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
// Cache middleware
|
|
753
|
-
api.use(async (ctx, next) => {
|
|
754
|
-
if (ctx.method === 'GET') {
|
|
755
|
-
const cacheKey = `${ctx.getKeypointId()}:${ctx.path}`;
|
|
756
|
-
const cached = keypointCache.get(cacheKey);
|
|
757
|
-
|
|
758
|
-
if (cached) {
|
|
759
|
-
return ctx.json(cached);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
await next();
|
|
763
|
-
|
|
764
|
-
if (ctx.response.status === 200) {
|
|
765
|
-
keypointCache.set(cacheKey, ctx.response.body);
|
|
766
|
-
}
|
|
767
|
-
} else {
|
|
768
|
-
await next();
|
|
769
|
-
}
|
|
770
|
-
});
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
# Security Best Practices
|
|
774
|
-
|
|
775
|
-
- 1. Always use HTTPS in production
|
|
776
|
-
- 2. Rotate keypoint secrets regularly (every 90 days)
|
|
777
|
-
- 3. Implement IP whitelisting for sensitive endpoints
|
|
778
|
-
- 4. Use scope-based authorization instead of role-based
|
|
779
|
-
- 5. Enable audit logging for compliance
|
|
780
|
-
- 6. Set reasonable rate limits per keypoint
|
|
781
|
-
- 7. Validate origins and protocols for each keypoint
|
|
782
|
-
- 8. Monitor failed authentication attempts
|
|
783
|
-
- 9. Clean up expired keypoints regularly
|
|
784
|
-
- 10. Use secure secret storage (not plaintext in code)
|
|
785
|
-
|
|
786
|
-
# Contributing
|
|
787
|
-
|
|
788
|
-
### To contribute to KeypointJS:
|
|
789
|
-
|
|
790
|
-
- 1. Fork the repository
|
|
791
|
-
- 2. Create a feature branch (git checkout -b feature/amazing-feature)
|
|
792
|
-
- 3. Add tests for your changes
|
|
793
|
-
- 4. Ensure all tests pass (npm test)
|
|
794
|
-
- 5. Commit your changes (git commit -m 'Add amazing feature')
|
|
795
|
-
- 6. Push to the branch (git push origin feature/amazing-feature)
|
|
796
|
-
- 7. Open a Pull Request
|
|
797
|
-
|
|
798
|
-
# License
|
|
228
|
+
## License
|
|
799
229
|
|
|
800
230
|
Apache-2.0 license - see the LICENSE file for details.
|
|
801
231
|
|
|
802
|
-
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Support
|
|
803
235
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
236
|
+
* Documentation: Full API documentation in source code
|
|
237
|
+
* Issues: Report bugs via GitHub issues
|
|
238
|
+
* Contributions: PRs welcome
|
|
239
|
+
* Questions: Open a discussion for usage questions
|
|
808
240
|
|
|
809
241
|
## KeypointJS is Independent
|
|
810
242
|
|
|
811
|
-
KeypointJS does not depend on Express, Fastify, or any third-party HTTP framework.
|
|
812
|
-
It ships with its own HTTP server, routing system, middleware pipeline, and security layer.
|
|
243
|
+
KeypointJS does not depend on Express, Fastify, or any third-party HTTP framework. It ships with its own HTTP server, routing system, middleware pipeline, and security layer.
|
|
813
244
|
|
|
814
245
|
## Created Base ♥️ KeypointJS
|
|
815
|
-
### AnasBex - (づ ̄ ³ ̄)づ
|
|
816
246
|
|
|
817
|
-
|
|
247
|
+
### AnasBex - (づ ̄ ³ ̄)づ
|
|
248
|
+
|
|
249
|
+
KeypointJS provides a comprehensive, layered approach to API security with extensibility through plugins, real-time WebSocket capabilities, and detailed monitoring through audit logging. The framework is production-ready with built-in security features and can be extended to meet specific requirements.
|