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