@olane/o-server 0.8.2 → 0.8.4
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 +136 -37
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ HTTP server entrypoint for Olane OS nodes. Exposes a node's `use` functionality
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
pnpm install @olane/o-server
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
@@ -219,11 +219,19 @@ const server = oServer({
|
|
|
219
219
|
credentials: true
|
|
220
220
|
},
|
|
221
221
|
|
|
222
|
-
// Optional:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
// Optional: JWT authentication (recommended)
|
|
223
|
+
jwtAuth: {
|
|
224
|
+
method: 'secret',
|
|
225
|
+
secret: 'your-jwt-secret',
|
|
226
|
+
issuer: 'https://auth.example.com',
|
|
227
|
+
audience: 'https://api.example.com'
|
|
226
228
|
},
|
|
229
|
+
|
|
230
|
+
// DEPRECATED: Use jwtAuth instead. Will be removed in a future version.
|
|
231
|
+
// authenticate: async (req) => {
|
|
232
|
+
// const token = req.headers.authorization;
|
|
233
|
+
// return validateToken(token);
|
|
234
|
+
// },
|
|
227
235
|
|
|
228
236
|
// Optional: Enable debug logging (default: false)
|
|
229
237
|
debug: true
|
|
@@ -232,28 +240,71 @@ const server = oServer({
|
|
|
232
240
|
await server.start();
|
|
233
241
|
```
|
|
234
242
|
|
|
235
|
-
### Authentication
|
|
243
|
+
### JWT Authentication (Recommended)
|
|
244
|
+
|
|
245
|
+
Protect your endpoints with built-in JWT verification. All routes except `/health` require a valid token when `jwtAuth` is configured.
|
|
246
|
+
|
|
247
|
+
**RS256 with public key:**
|
|
248
|
+
```typescript
|
|
249
|
+
const server = oServer({
|
|
250
|
+
node: myNode,
|
|
251
|
+
port: 3000,
|
|
252
|
+
jwtAuth: {
|
|
253
|
+
method: 'publicKey',
|
|
254
|
+
publicKeyPath: '/path/to/public-key.pem',
|
|
255
|
+
issuer: 'https://auth.example.com',
|
|
256
|
+
audience: 'https://api.example.com',
|
|
257
|
+
algorithms: ['RS256']
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**HS256 with shared secret:**
|
|
263
|
+
```typescript
|
|
264
|
+
const server = oServer({
|
|
265
|
+
node: myNode,
|
|
266
|
+
port: 3000,
|
|
267
|
+
jwtAuth: {
|
|
268
|
+
method: 'secret',
|
|
269
|
+
secret: process.env.JWT_SECRET!,
|
|
270
|
+
clockTolerance: 5 // seconds of tolerance for exp/nbf
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**`jwtAuth` configuration options:**
|
|
276
|
+
|
|
277
|
+
| Option | Type | Required | Description |
|
|
278
|
+
|--------|------|----------|-------------|
|
|
279
|
+
| `method` | `'publicKey' \| 'secret'` | Yes | Verification method |
|
|
280
|
+
| `publicKeyPath` | `string` | If `method='publicKey'` | Path to PEM public key file |
|
|
281
|
+
| `secret` | `string` | If `method='secret'` | Shared secret for HS256 |
|
|
282
|
+
| `issuer` | `string` | No | Expected `iss` claim |
|
|
283
|
+
| `audience` | `string` | No | Expected `aud` claim |
|
|
284
|
+
| `algorithms` | `Algorithm[]` | No | Allowed algorithms (defaults based on method) |
|
|
285
|
+
| `clockTolerance` | `number` | No | Seconds of clock tolerance (default: 0) |
|
|
236
286
|
|
|
237
|
-
|
|
287
|
+
### Legacy Authentication (Deprecated)
|
|
288
|
+
|
|
289
|
+
> **Deprecated**: The `authenticate` option is deprecated and will be removed in a future version. Migrate to `jwtAuth` instead.
|
|
238
290
|
|
|
239
291
|
```typescript
|
|
292
|
+
// @deprecated - use jwtAuth instead
|
|
240
293
|
const server = oServer({
|
|
241
294
|
node: myNode,
|
|
242
295
|
port: 3000,
|
|
296
|
+
// @deprecated - use jwtAuth instead
|
|
243
297
|
authenticate: async (req) => {
|
|
244
|
-
// Validate JWT token
|
|
245
298
|
const token = req.headers.authorization?.split(' ')[1];
|
|
246
|
-
|
|
299
|
+
|
|
247
300
|
if (!token) {
|
|
248
301
|
throw new Error('No token provided');
|
|
249
302
|
}
|
|
250
|
-
|
|
303
|
+
|
|
251
304
|
const user = await verifyJWT(token);
|
|
252
305
|
return { userId: user.id, roles: user.roles };
|
|
253
306
|
}
|
|
254
307
|
});
|
|
255
|
-
|
|
256
|
-
// Now all requests require valid authentication
|
|
257
308
|
```
|
|
258
309
|
|
|
259
310
|
### CORS Configuration
|
|
@@ -323,6 +374,8 @@ await server.start();
|
|
|
323
374
|
|
|
324
375
|
```typescript
|
|
325
376
|
// frontend/src/api.ts
|
|
377
|
+
// Note: HTTP responses from o-server use the flat { success, data, error } format.
|
|
378
|
+
// This is different from internal node.use() calls which return response.result.success.
|
|
326
379
|
export async function analyzeRevenue(startDate: string, endDate: string) {
|
|
327
380
|
const response = await fetch('http://localhost:3000/api/v1/use', {
|
|
328
381
|
method: 'POST',
|
|
@@ -333,8 +386,13 @@ export async function analyzeRevenue(startDate: string, endDate: string) {
|
|
|
333
386
|
params: { startDate, endDate }
|
|
334
387
|
})
|
|
335
388
|
});
|
|
336
|
-
|
|
389
|
+
|
|
337
390
|
const result = await response.json();
|
|
391
|
+
|
|
392
|
+
if (!result.success) {
|
|
393
|
+
throw new Error(result.error?.message || 'Request failed');
|
|
394
|
+
}
|
|
395
|
+
|
|
338
396
|
return result.data;
|
|
339
397
|
}
|
|
340
398
|
```
|
|
@@ -349,11 +407,10 @@ import { oServer } from '@olane/o-server';
|
|
|
349
407
|
const server = oServer({
|
|
350
408
|
node: myNode,
|
|
351
409
|
port: 8080,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
return { userId: decodedToken.uid };
|
|
410
|
+
jwtAuth: {
|
|
411
|
+
method: 'secret',
|
|
412
|
+
secret: process.env.JWT_SECRET!,
|
|
413
|
+
issuer: 'https://your-firebase-project.firebaseapp.com'
|
|
357
414
|
}
|
|
358
415
|
});
|
|
359
416
|
|
|
@@ -373,17 +430,22 @@ const server = oServer({ node: myNode, port: 3000 });
|
|
|
373
430
|
// Stripe webhook
|
|
374
431
|
server.app.post('/webhooks/stripe', async (req, res) => {
|
|
375
432
|
const event = req.body;
|
|
376
|
-
|
|
377
|
-
// Process via your node
|
|
378
|
-
await myNode.use(
|
|
433
|
+
|
|
434
|
+
// Process via your node (internal call uses response.result pattern)
|
|
435
|
+
const response = await myNode.use(
|
|
379
436
|
new oAddress('o://payments/processor'),
|
|
380
437
|
{
|
|
381
438
|
method: 'handle_payment',
|
|
382
439
|
params: { event }
|
|
383
440
|
}
|
|
384
441
|
);
|
|
385
|
-
|
|
386
|
-
|
|
442
|
+
|
|
443
|
+
if (!response.result.success) {
|
|
444
|
+
res.status(500).json({ error: response.result.error });
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
res.json({ received: true, data: response.result.data });
|
|
387
449
|
});
|
|
388
450
|
|
|
389
451
|
await server.start();
|
|
@@ -420,18 +482,20 @@ await server.start();
|
|
|
420
482
|
// -d '{"address": "o://any/node/in/network", "method": "...", "params": {...}}'
|
|
421
483
|
```
|
|
422
484
|
|
|
423
|
-
##
|
|
485
|
+
## Response Structure
|
|
424
486
|
|
|
425
|
-
|
|
487
|
+
### HTTP Response (from o-server)
|
|
426
488
|
|
|
427
|
-
|
|
428
|
-
|
|
489
|
+
`o-server` flattens the internal node response into a simple HTTP-friendly format:
|
|
490
|
+
|
|
491
|
+
```json
|
|
492
|
+
// Success response (HTTP)
|
|
429
493
|
{
|
|
430
494
|
"success": true,
|
|
431
495
|
"data": { ... }
|
|
432
496
|
}
|
|
433
497
|
|
|
434
|
-
// Error response
|
|
498
|
+
// Error response (HTTP)
|
|
435
499
|
{
|
|
436
500
|
"success": false,
|
|
437
501
|
"error": {
|
|
@@ -442,6 +506,38 @@ await server.start();
|
|
|
442
506
|
}
|
|
443
507
|
```
|
|
444
508
|
|
|
509
|
+
### Internal Node Response (from node.use())
|
|
510
|
+
|
|
511
|
+
Within the Olane node ecosystem, `node.use()` returns a JSON-RPC wrapped response. When calling nodes programmatically (not via HTTP), always access data through the `result` property:
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
const response = await node.use(address, { method: 'my_method', params: {} });
|
|
515
|
+
|
|
516
|
+
// Access the response correctly:
|
|
517
|
+
if (response.result.success) {
|
|
518
|
+
const data = response.result.data; // Your method's return value
|
|
519
|
+
} else {
|
|
520
|
+
const error = response.result.error; // Error message string
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Full internal response shape:
|
|
524
|
+
// {
|
|
525
|
+
// jsonrpc: "2.0",
|
|
526
|
+
// id: "request-id",
|
|
527
|
+
// result: {
|
|
528
|
+
// success: boolean,
|
|
529
|
+
// data: any, // Present on success
|
|
530
|
+
// error?: string // Present on failure
|
|
531
|
+
// }
|
|
532
|
+
// }
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
> **Important**: The `o-server` translates the internal `response.result.success` / `response.result.data` / `response.result.error` structure into the flat HTTP `{ success, data }` / `{ success, error }` format. When writing code that runs inside the node ecosystem (not behind o-server), always use `response.result.success`, `response.result.data`, and `response.result.error`.
|
|
536
|
+
|
|
537
|
+
## Error Handling
|
|
538
|
+
|
|
539
|
+
`o-server` provides consistent error responses:
|
|
540
|
+
|
|
445
541
|
**Common error codes:**
|
|
446
542
|
- `INVALID_PARAMS` - Missing or invalid request parameters
|
|
447
543
|
- `NODE_NOT_FOUND` - Target node address not found
|
|
@@ -540,8 +636,9 @@ const config: ServerConfig = {
|
|
|
540
636
|
cors: {
|
|
541
637
|
origin: 'https://example.com'
|
|
542
638
|
},
|
|
543
|
-
|
|
544
|
-
|
|
639
|
+
jwtAuth: {
|
|
640
|
+
method: 'secret',
|
|
641
|
+
secret: process.env.JWT_SECRET!,
|
|
545
642
|
},
|
|
546
643
|
debug: true
|
|
547
644
|
};
|
|
@@ -568,13 +665,15 @@ LOG_LEVEL=info
|
|
|
568
665
|
# Dockerfile
|
|
569
666
|
FROM node:20-alpine
|
|
570
667
|
|
|
668
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
669
|
+
|
|
571
670
|
WORKDIR /app
|
|
572
671
|
|
|
573
|
-
COPY package
|
|
574
|
-
RUN
|
|
672
|
+
COPY package.json pnpm-lock.yaml ./
|
|
673
|
+
RUN pnpm install --frozen-lockfile --prod
|
|
575
674
|
|
|
576
675
|
COPY . .
|
|
577
|
-
RUN
|
|
676
|
+
RUN pnpm run build
|
|
578
677
|
|
|
579
678
|
EXPOSE 8080
|
|
580
679
|
|
|
@@ -686,13 +785,13 @@ Complete examples:
|
|
|
686
785
|
const server = oServer({ node: myNode, port: 3000 });
|
|
687
786
|
await server.start();
|
|
688
787
|
|
|
689
|
-
// With authentication
|
|
788
|
+
// With JWT authentication
|
|
690
789
|
const server = oServer({
|
|
691
790
|
node: myNode,
|
|
692
791
|
port: 3000,
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
792
|
+
jwtAuth: {
|
|
793
|
+
method: 'secret',
|
|
794
|
+
secret: process.env.JWT_SECRET!,
|
|
696
795
|
}
|
|
697
796
|
});
|
|
698
797
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olane/o-server",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"typescript": "5.4.5"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@olane/o-core": "0.8.
|
|
64
|
+
"@olane/o-core": "0.8.4",
|
|
65
65
|
"cors": "^2.8.5",
|
|
66
66
|
"debug": "^4.4.1",
|
|
67
67
|
"dotenv": "^16.5.0",
|
|
@@ -69,5 +69,5 @@
|
|
|
69
69
|
"jsonwebtoken": "^9.0.3",
|
|
70
70
|
"zod": "^3.25.76"
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "b53623b1ad4365133911722f80d5597a72b65bf2"
|
|
73
73
|
}
|