keypointjs 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.
- package/LICENSE +201 -0
- package/README.md +808 -0
- package/package.json +72 -0
- package/src/core/Context.js +104 -0
- package/src/core/ProtocolEngine.js +144 -0
- package/src/keypoint/Keypoint.js +36 -0
- package/src/keypoint/KeypointContext.js +88 -0
- package/src/keypoint/KeypointStorage.js +236 -0
- package/src/keypoint/KeypointValidator.js +51 -0
- package/src/keypoint/ScopeManager.js +206 -0
- package/src/keypointJS.js +779 -0
- package/src/plugins/AuditLogger.js +294 -0
- package/src/plugins/PluginManager.js +303 -0
- package/src/plugins/RateLimiter.js +24 -0
- package/src/plugins/WebSocketGuard.js +351 -0
- package/src/policy/AccessDecision.js +104 -0
- package/src/policy/PolicyEngine.js +82 -0
- package/src/policy/PolicyRule.js +246 -0
- package/src/router/MinimalRouter.js +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
# KeypointJS - Complete Documentation
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+

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