@cap-js-community/websocket
Advanced tools
Comparing version 1.4.1 to 1.5.0
@@ -8,2 +8,10 @@ # Changelog | ||
## Version 1.5.0 - 2025-01-08 | ||
### Fixed | ||
- Redis lookup for websocket via CDS env `cds.requires.redis-websocket` | ||
- Reworked redis client connection logic | ||
- Document options | ||
## Version 1.4.1 - 2024-12-02 | ||
@@ -10,0 +18,0 @@ |
{ | ||
"name": "@cap-js-community/websocket", | ||
"version": "1.4.1", | ||
"version": "1.5.0", | ||
"description": "WebSocket adapter for CDS", | ||
@@ -44,5 +44,4 @@ "homepage": "https://cap.cloud.sap/", | ||
"dependencies": { | ||
"@sap/xsenv": "^5.4.0", | ||
"cookie": "^1.0.2", | ||
"express": "^4.21.1", | ||
"express": "^4.21.2", | ||
"redis": "^4.7.0", | ||
@@ -54,16 +53,16 @@ "socket.io": "^4.8.1", | ||
"@cap-js-community/websocket": "./", | ||
"@cap-js/sqlite": "^1.7.7", | ||
"@eslint/js": "^9.16.0", | ||
"@sap/cds": "^8.5.0", | ||
"@sap/cds-dk": "^8.5.0", | ||
"@cap-js/sqlite": "^1.7.8", | ||
"@eslint/js": "^9.17.0", | ||
"@sap/cds": "^8.6.0", | ||
"@sap/cds-dk": "^8.6.0", | ||
"@socket.io/redis-adapter": "^8.3.0", | ||
"@socket.io/redis-streams-adapter": "^0.2.2", | ||
"eslint": "^9.16.0", | ||
"eslint": "^9.17.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-jest": "^28.9.0", | ||
"eslint-plugin-n": "^17.14.0", | ||
"globals": "^15.13.0", | ||
"eslint-plugin-jest": "^28.10.0", | ||
"eslint-plugin-n": "^17.15.1", | ||
"globals": "^15.14.0", | ||
"jest": "^29.7.0", | ||
"passport": "^0.7.0", | ||
"prettier": "^3.4.1", | ||
"prettier": "^3.4.2", | ||
"socket.io-client": "^4.8.1" | ||
@@ -85,2 +84,7 @@ }, | ||
} | ||
}, | ||
"redis-websocket": { | ||
"vcap": { | ||
"label": "redis-cache" | ||
} | ||
} | ||
@@ -152,17 +156,2 @@ }, | ||
"default": false | ||
}, | ||
"vcap": { | ||
"type": "object", | ||
"description": "VCAP service environment", | ||
"properties": { | ||
"label": { | ||
"type": "string", | ||
"description": "VCAP service label" | ||
}, | ||
"tag": { | ||
"type": "string", | ||
"description": "VCAP service tag" | ||
} | ||
}, | ||
"additionalProperties": true | ||
} | ||
@@ -169,0 +158,0 @@ } |
@@ -20,2 +20,3 @@ # @cap-js-community/websocket | ||
- [Client](#client) | ||
- [Options](#options) | ||
- [Documentation](#documentation) | ||
@@ -162,2 +163,20 @@ - [Architecture Overview](#architecture-overview) | ||
## Options | ||
The CDS websocket modules can be configured with the following options: | ||
- **kind: String**: Websocket implementation kind (`ws`, `socket.io`). Default is `'ws'`. | ||
- **impl: String**: Websocket implementation path. Default is provided by module for kind. | ||
- **options: Object**: Websocket implementation configuration options. Default is `{}`. | ||
- **adapter: Object**: Websocket adapter configuration options. Default is `{}`. | ||
- **adapter.impl: String**: Websocket adapter implementation (`redis`, `@socket.io/redis-adapter`, `@socket.io/redis-streams-adapter`). Default is `''`. | ||
- **adapter.options: Object**: Websocket adapter implementation options. Default is `{}`. | ||
- **adapter.options.key: String**: Websocket adapter channel prefix. Default is `websocket`. | ||
- **adapter.config: Object**: Websocket adapter implementation configurations (i.e. Redis client options). Default is `{}`. | ||
- **adapter.active: Boolean**: Enable websocket adapter. Default is `true`. | ||
- **adapter.local: Boolean**: Enable websocket adapter in local environment. Default is `false`. | ||
> All CDS Websocket options can also be specified as part of CDS project-specific configuration | ||
> under section `cds.websocket` and accessed during runtime via `cds.env.websocket`. | ||
## Documentation | ||
@@ -327,7 +346,24 @@ | ||
Authentication only works via AppRouter (e.g. using a UAA configuration), as the auth token is forwarded | ||
via authorization header bearer token by AppRouter to backend instance. CDS middlewares process the auth token and | ||
Authentication works best via [AppRouter](https://www.npmjs.com/package/@sap/approuter) (e.g. using a UAA configuration), | ||
as the auth token is forwarded via authorization header bearer token by AppRouter to backend websocket call. | ||
CDS auth strategy (e.g. `cds.auth.kind: 'xsuaa'`) is applied and CDS auth middleware processes the auth token and | ||
set the auth info accordingly. Authorization scopes are checked as defined in the CDS services `@requires` annotations | ||
and authorization restrictions are checked as defined in the CDS services `@restrict` annotations. | ||
Authentication can also be performed without AppRouter as long as the WebSocket Upgrade request contains | ||
a valid authorization header in accordance to the CDS auth strategy defined in CDS env: | ||
Example for xsuaa based CDS auth strategy: | ||
```json | ||
{ | ||
"cds": { | ||
"auth": { | ||
"kind": "xsuaa" | ||
} | ||
} | ||
} | ||
``` | ||
### Invocation Context | ||
@@ -1227,3 +1263,3 @@ | ||
Authorization in provided in production by Approuter component (e.g. via XSUAA auth). | ||
Authorization is best provided in production by [Approuter](https://www.npmjs.com/package/@sap/approuter) component (e.g. via XSUAA auth). | ||
Valid UAA bindings for Approuter and backend are necessary, so that the authorization flow is working. | ||
@@ -1492,8 +1528,50 @@ Locally, the following default environment files need to exist: | ||
- Local: Redis is NOT automatically active | ||
- Use option `cds.websocket.adapter.local: true` to enable Redis adapter | ||
- File `default-env.json` need to exist with Redis configuration | ||
- Use option `cds.websocket.adapter.local: true` to enable Redis adapter locally | ||
- Local Redis configuration needs to be in environment (e.g. CAP hybrid testing or `default-env.json`) | ||
- Redis can be configured for local testing as described in the following documentation: https://cap-js-community.github.io/event-queue/setup/#configure-redis | ||
- Redis Adapter options can be specified via `cds.websocket.adapter.options` | ||
- Redis channel key can be specified via `cds.websocket.adapter.options.key`. Default value is `websocket` | ||
- Redis channel key can be specified via `cds.websocket.adapter.options.key`. Default value is `websocket`. | ||
- Redis client connection configuration can be passed via `cds.websocket.adapter.config` | ||
- Redis lookup is performed via `cds.env`. | ||
- Default lookup is done via VCAP `label: "redis-cache"` | ||
- Custom Redis lookup can be specified via `cds.requires.redis-websocket`. | ||
**Example:** | ||
```json | ||
{ | ||
"cds": { | ||
"requires": { | ||
"redis-websocket": { | ||
"vcap": { | ||
"tag": "ws-redis" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
- A shared Redis service across multiple consumers can be configured via `cds.requires.redis`: | ||
**Example:** | ||
```json | ||
{ | ||
"cds": { | ||
"requires": { | ||
"redis-websocket": null, | ||
"redis": { | ||
"vcap": { | ||
"tag": "ws-redis" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
- Redis service options can be specified via `cds.requires.redis-websocket.options` resp. `cds.requires.redis.options`. | ||
##### Custom Adapter | ||
@@ -1500,0 +1578,0 @@ |
"use strict"; | ||
const path = require("path"); | ||
const cds = require("@sap/cds"); | ||
const redis = require("redis"); | ||
const xsenv = require("@sap/xsenv"); | ||
const cds = require("@sap/cds"); | ||
const LOG = cds.log("/websocket/redis"); | ||
xsenv.loadEnv(path.join(process.cwd(), "default-env.json")); | ||
const IS_ON_CF = process.env.USER === "vcap"; | ||
@@ -57,8 +53,4 @@ const LOG_AFTER_SEC = 5; | ||
} | ||
let credentials; | ||
try { | ||
credentials = xsenv.serviceCredentials({ label: "redis-cache", ...options?.vcap }); | ||
} catch (err) { | ||
LOG?.info(err.message); | ||
} | ||
const requiresRedis = cds.env.requires?.["redis-websocket"] ?? cds.env.requires?.redis; | ||
const credentials = requiresRedis?.credentials; | ||
if (!credentials) { | ||
@@ -68,17 +60,23 @@ LOG?.info("No Redis credentials found"); | ||
} | ||
const socket = { | ||
host: credentials.hostname, | ||
tls: !!credentials.tls, | ||
port: credentials.port, | ||
...requiresRedis?.options?.socket, | ||
...options?.config?.socket, | ||
}; | ||
const redisOptions = { | ||
...requiresRedis?.options, | ||
...options?.config, | ||
password: options?.config?.password ?? requiresRedis?.options?.password ?? credentials.password, | ||
socket, | ||
}; | ||
try { | ||
const redisIsCluster = credentials.cluster_mode; | ||
const url = credentials.uri.replace(/(?<=rediss:\/\/)[\w-]+?(?=:)/, ""); | ||
if (redisIsCluster) { | ||
if (credentials.cluster_mode) { | ||
return redis.createCluster({ | ||
rootNodes: [{ url }], | ||
// https://github.com/redis/node-redis/issues/1782 | ||
defaults: { | ||
password: credentials.password, | ||
socket: { tls: credentials.tls }, | ||
...options?.config, | ||
}, | ||
rootNodes: [redisOptions], | ||
defaults: redisOptions, | ||
}); | ||
} | ||
return redis.createClient({ url, ...options?.config }); | ||
return redis.createClient(redisOptions); | ||
} catch (err) { | ||
@@ -85,0 +83,0 @@ throw new Error("Error during create client with redis-cache service:" + err); |
@@ -15,3 +15,3 @@ "use strict"; | ||
super(server, path, config); | ||
this.wss = new WebSocket.Server({ server }); | ||
this.wss = new WebSocket.Server({ ...config?.options, server }); | ||
this.services = {}; | ||
@@ -18,0 +18,0 @@ cds.ws = this; |
170875
5
1668
2598
- Removed@sap/xsenv@^5.4.0
- Removed@sap/xsenv@5.4.0(transitive)
- Removedassert-plus@1.0.0(transitive)
- Removedclone@2.1.2(transitive)
- Removedcore-util-is@1.0.2(transitive)
- Removedextsprintf@1.4.1(transitive)
- Removednode-cache@5.1.2(transitive)
- Removedverror@1.10.1(transitive)
Updatedexpress@^4.21.2