fix: remove unused endpoints

This commit is contained in:
Arseniy Klempner 2025-09-05 14:06:11 -07:00
parent 5530796678
commit 40b3167cc5
No known key found for this signature in database
GPG Key ID: 51653F18863BD24B
6 changed files with 109 additions and 351 deletions

View File

@ -11,7 +11,6 @@ env:
EXAMPLE_TEMPLATE: "web-chat"
EXAMPLE_NAME: "example"
EXAMPLE_PORT: "8080"
PORT: "3000"
# Firefox in container fails due to $HOME not being owned by user running commands
# more details https://github.com/microsoft/playwright/issues/6500
HOME: "/root"

View File

@ -1,178 +1,149 @@
# Waku Browser Tests
This project provides a system for testing the Waku SDK in a browser environment.
This package provides a containerized Waku light node simulation server for testing and development. The server runs a headless browser using Playwright and exposes a REST API similar to the nwaku REST API. A Dockerfile is provided to allow programmatic simulation and "deployment" of js-waku nodes in any Waku orchestration environment that uses Docker (e.g. [10ksim](https://github.com/vacp2p/10ksim) ).
## Architecture
## Quick Start
The system consists of:
1. **Headless Web App**: A simple web application (in the `@waku/headless-tests` package) that loads the Waku SDK and exposes shared API functions.
2. **Express Server**: A server that communicates with the headless app using Playwright.
3. **Shared API**: TypeScript functions shared between the server and web app.
## Setup
1. Install dependencies:
```bash
# Install main dependencies
npm install
# Install headless app dependencies
cd ../headless-tests
npm install
cd ../browser-tests
```
2. Build the application:
### Build and Run
```bash
# Build the application
npm run build
# Start the server (port 8080)
npm run start:server
# Build and run Docker container
npm run docker:build
docker run -p 8080:8080 waku-browser-tests:local
```
This will:
- Build the headless web app using webpack
- Compile the TypeScript server code
## Configuration
## Running
Configure the Waku node using environment variables:
Start the server with:
### Network Configuration
- `WAKU_CLUSTER_ID`: Cluster ID (default: 1)
- `WAKU_SHARD`: Specific shard for static sharding (optional)
### Bootstrap Configuration
- `WAKU_ENR_BOOTSTRAP`: Enable ENR bootstrap mode with custom bootstrap peers (comma-separated)
### ENR Bootstrap Mode
When `WAKU_ENR_BOOTSTRAP` is set:
- Disables default bootstrap (`defaultBootstrap: false`)
- Enables DNS discovery using production ENR trees
- Enables peer exchange and peer cache
- Uses the specified ENR for additional bootstrap peers
```bash
npm run start:server
# Example: ENR bootstrap mode
WAKU_ENR_BOOTSTRAP="enr:-QEnuEBEAyErHEfhiQxAVQoWowGTCuEF9fKZtXSd7H_PymHFhGJA3rGAYDVSHKCyJDGRLBGsloNbS8AZF33IVuefjOO6BIJpZIJ2NIJpcIQS39tkim11bHRpYWRkcnO4lgAvNihub2RlLTAxLmRvLWFtczMud2FrdXYyLnRlc3Quc3RhdHVzaW0ubmV0BgG73gMAODcxbm9kZS0wMS5hYy1jbi1ob25na29uZy1jLndha3V2Mi50ZXN0LnN0YXR1c2ltLm5ldAYBu94DACm9A62t7AQL4Ef5ZYZosRpQTzFVAB8jGjf1TER2wH-0zBOe1-MDBNLeA4lzZWNwMjU2azGhAzfsxbxyCkgCqq8WwYsVWH7YkpMLnU2Bw5xJSimxKav-g3VkcIIjKA" npm run start:server
```
This will:
1. Serve the headless app on port 8080
2. Start a headless browser to load the app
3. Expose API endpoints to interact with Waku
## API Endpoints
- `GET /info`: Get information about the Waku node
- `GET /debug/v1/info`: Get debug information from the Waku node
- `POST /push`: Push a message to the Waku network (legacy)
- `POST /lightpush/v1/message`: Push a message to the Waku network (Waku REST API compatible)
- Waku nodes are now automatically created and started when the server launches
- `POST /admin/v1/peers`: Dial to specified peers (Waku REST API compatible)
- `GET /filter/v2/messages/:contentTopic`: Subscribe to messages on a specific content topic using Server-Sent Events (Waku REST API compatible)
- `GET /filter/v1/messages/:contentTopic`: Retrieve stored messages from a content topic (Waku REST API compatible)
The server exposes the following HTTP endpoints:
### Example: Pushing a message with the legacy endpoint
### Node Management
- `GET /`: Health check - returns server status
- `GET /waku/v1/peer-info`: Get node peer information
- `POST /waku/v1/wait-for-peers`: Wait for peers with specific protocols
### Messaging
- `POST /lightpush/v3/message`: Send message via lightpush (nwaku v3 API compatible)
### Static Files
- `GET /app/index.html`: Web application entry point
- `GET /app/*`: Static web application files
### Examples
#### Send a Message
```bash
curl -X POST http://localhost:3000/push \
-H "Content-Type: application/json" \
-d '{"contentTopic": "/toy-chat/2/huilong/proto", "payload": [1, 2, 3]}'
```
### Example: Pushing a message with the Waku REST API compatible endpoint
```bash
curl -X POST http://localhost:3000/lightpush/v1/message \
curl -X POST http://localhost:8080/lightpush/v3/message \
-H "Content-Type: application/json" \
-d '{
"pubsubTopic": "/waku/2/rs/0/0",
"pubsubTopic": "/waku/2/rs/1/4",
"message": {
"payload": "SGVsbG8sIFdha3Uh",
"contentTopic": "/toy-chat/2/huilong/proto",
"timestamp": 1712135330213797632
"contentTopic": "/test/1/example/proto",
"payload": "SGVsbG8gV2FrdQ==",
"version": 1
}
}'
```
### Example: Executing a function
#### Wait for Peers
```bash
curl -X POST http://localhost:3000/execute \
curl -X POST http://localhost:8080/waku/v1/wait-for-peers \
-H "Content-Type: application/json" \
-d '{"functionName": "getPeerInfo", "params": []}'
-d '{
"timeoutMs": 30000,
"protocols": ["lightpush", "filter"]
}'
```
### Node Configuration
#### Get Peer Info
```bash
curl -X GET http://localhost:8080/waku/v1/peer-info
```
Waku nodes are automatically created and started when the server launches. Configuration is controlled via environment variables:
## CLI Usage
- `WAKU_CLUSTER_ID`: Set the cluster ID (default: uses bootstrap configuration)
- `WAKU_SHARD`: Set a specific shard (optional)
- `WAKU_LIGHTPUSH_NODE`: Specify a preferred lightpush node address (optional)
- `WAKU_ENR_BOOTSTRAP`: Specify custom ENR bootstrap peers (comma-separated, optional)
### Example: Using ENR Bootstrap Peers
Run with CLI arguments:
```bash
# Via Docker CLI
# Custom cluster and shard
npm run start:cluster2-shard0
# Or manually
node dist/src/server.js --cluster-id=2 --shard=0
```
## Testing
Run the test suite:
```bash
# Local tests
npm test
# Docker tests (requires Docker)
npm run docker:test
# Build and test in Docker
npm run docker:build
npm run docker:test
```
## Docker Usage
The package includes Docker support for containerized testing:
```bash
# Build image
docker build -t waku-browser-tests:local .
# Run with ENR bootstrap
docker run -p 8080:8080 \
-e WAKU_ENR_BOOTSTRAP="enr:-MS4QGcHBZAnpu6qNYe_T6TGDCV6c9_3UsXlj5XlXY6QvLCUQKqajqDfs0aKOs7BISJzGxA7TuDzYXap4sP6JYUZ2Y9GAYh2F0dG5ldHOIAAAAAAAAAACEZXRoMpEJZZp0BAAAAf__________gmlkgnY0gmlwhC5QoeSJc2VjcDI1NmsxoQOZxJYJVoTfwo7zEom6U6L5Txrs3H9X0P_XBJbbOZBczYYN1ZHCCdl8" \
waku-browser-tests
-e WAKU_ENR_BOOTSTRAP="enr:-QEnuE..." \
-e WAKU_CLUSTER_ID="1" \
waku-browser-tests:local
# Via Docker entrypoint argument
docker run -p 8080:8080 waku-browser-tests \
--enr-bootstrap="enr:-MS4QGcHBZAnpu6qNYe_T6TGDCV6c9_3UsXlj5XlXY6QvLCUQKqajqDfs0aKOs7BISJzGxA7TuDzYXap4sP6JYUZ2Y9GAYh2F0dG5ldHOIAAAAAAAAAACEZXRoMpEJZZp0BAAAAf__________gmlkgnY0gmlwhC5QoeSJc2VjcDI1NmsxoQOZxJYJVoTfwo7zEom6U6L5Txrs3H9X0P_XBJbbOZBczYYN1ZHCCdl8"
# Run with specific configuration
docker run -p 8080:8080 \
-e WAKU_CLUSTER_ID="2" \
-e WAKU_SHARD="0" \
waku-browser-tests:local
```
### Example: Dialing to specific peers with the Waku REST API compatible endpoint
## Development
```bash
curl -X POST http://localhost:3000/admin/v1/peers \
-H "Content-Type: application/json" \
-d '{
"peerMultiaddrs": [
"/ip4/127.0.0.1/tcp/8000/p2p/16Uiu2HAm4v8KuHUH6Cwz3upPeQbkyxQJsFGPdt7kHtkN8F79QiE6"]
]
}'
```
The server automatically:
- Creates a Waku light node on startup
- Configures network settings from environment variables
- Enables appropriate protocols (lightpush, filter)
- Handles peer discovery and connection management
### Example: Dialing to specific peers with the execute endpoint
```bash
curl -X POST http://localhost:3000/execute \
-H "Content-Type: application/json" \
-d '{
"functionName": "dialPeers",
"params": [
["/ip4/127.0.0.1/tcp/8000/p2p/16Uiu2HAm4v8KuHUH6Cwz3upPeQbkyxQJsFGPdt7kHtkN8F79QiE6"]
]
}'
```
### Example: Subscribing to a content topic with the filter endpoint
```bash
# Open a persistent connection to receive messages as Server-Sent Events
curl -N http://localhost:3000/filter/v2/messages/%2Ftoy-chat%2F2%2Fhuilong%2Fproto
# You can also specify clustering options
curl -N "http://localhost:3000/filter/v2/messages/%2Ftoy-chat%2F2%2Fhuilong%2Fproto?clusterId=0&shard=0"
```
### Example: Retrieving stored messages from a content topic
```bash
# Get the most recent 20 messages
curl http://localhost:3000/filter/v1/messages/%2Ftoy-chat%2F2%2Fhuilong%2Fproto
# Get messages with pagination and time filtering
curl "http://localhost:3000/filter/v1/messages/%2Ftoy-chat%2F2%2Fhuilong%2Fproto?pageSize=10&startTime=1712000000000&endTime=1713000000000&ascending=true"
```
## Extending
To add new functionality:
1. Add your function to `src/api/shared.ts`
2. Add your function to the `API` object in `src/api/shared.ts`
3. Use it via the server endpoints
### Example: Dialing to specific peers
```bash
curl -X POST http://localhost:3000/execute \
-H "Content-Type: application/json" \
-d '{
"functionName": "dialPeers",
"params": [
["/ip4/127.0.0.1/tcp/8000/p2p/16Uiu2HAm4v8KuHUH6Cwz3upPeQbkyxQJsFGPdt7kHtkN8F79QiE6"]
]
}'
```
All endpoints are CORS-enabled for cross-origin requests.

View File

@ -1,17 +1,11 @@
import { Router } from "express";
import { createEndpointHandler, validators, errorHandlers } from "../utils/endpoint-handler.js";
import { getPage } from "../browser/index.js";
const router = Router();
const corsEndpoints = [
"/waku/v1/wait-for-peers",
"/waku/v1/dial-peers",
"/waku/v1/peer-info",
"/waku/v1/debug-info",
"/waku/v1/peer-protocols",
"/waku/v1/connection-status",
"/waku/v1/execute",
"/lightpush/v3/message"
];
@ -35,30 +29,14 @@ router.post("/waku/v1/wait-for-peers", createEndpointHandler({
})
}));
router.post("/waku/v1/dial-peers", createEndpointHandler({
methodName: "dialPeers",
validateInput: validators.requirePeerAddrs
}));
router.get("/waku/v1/peer-info", createEndpointHandler({
methodName: "getPeerInfo",
validateInput: validators.noInput
}));
router.get("/waku/v1/debug-info", createEndpointHandler({
methodName: "getDebugInfo",
validateInput: validators.noInput
}));
router.get("/waku/v1/peer-protocols", createEndpointHandler({
methodName: "getAvailablePeerProtocols",
validateInput: validators.noInput
}));
router.get("/waku/v1/connection-status", createEndpointHandler({
methodName: "getPeerConnectionStatus",
validateInput: validators.noInput
}));
@ -90,45 +68,5 @@ router.post("/lightpush/v3/message", createEndpointHandler({
}));
router.post("/waku/v1/execute", async (req, res) => {
try {
const { functionName, params = [] } = req.body;
if (!functionName || typeof functionName !== "string") {
return res.status(400).json({
code: 400,
message: "functionName is required and must be a string"
});
}
const page = getPage();
if (!page) {
return res.status(503).json({
code: 503,
message: "Browser not initialized"
});
}
const result = await page.evaluate(
({ fnName, fnParams }) => {
const api: any = (window as any).wakuApi;
if (!api || typeof api[fnName] !== "function") {
return { error: `Function ${fnName} not found` };
}
return api[fnName](...fnParams);
},
{ fnName: functionName, fnParams: params }
);
console.log(`[execute:${functionName}] Result:`, JSON.stringify(result, null, 2));
res.status(200).json(result);
} catch (error: any) {
console.error("Error executing function", error);
res.status(500).json({
code: 500,
message: `Could not execute function: ${error.message}`
});
}
});
export default router;

View File

@ -181,7 +181,6 @@ function parseCliArgs() {
const args = process.argv.slice(2);
let clusterId: number | undefined;
let shard: number | undefined;
let lightpushNode: string | undefined;
for (const arg of args) {
if (arg.startsWith('--cluster-id=')) {
@ -196,16 +195,10 @@ function parseCliArgs() {
console.error('Invalid shard value. Must be a number.');
process.exit(1);
}
} else if (arg.startsWith('--lightpushnode=')) {
lightpushNode = arg.split('=')[1];
if (!lightpushNode || lightpushNode.trim() === '') {
console.error('Invalid lightpushnode value. Must be a valid multiaddr.');
process.exit(1);
}
}
}
return { clusterId, shard, lightpushNode };
return { clusterId, shard };
}
const isMainModule = process.argv[1] === fileURLToPath(import.meta.url);
@ -222,10 +215,6 @@ if (isMainModule) {
process.env.WAKU_SHARD = cliArgs.shard.toString();
console.log(`Using CLI shard: ${cliArgs.shard}`);
}
if (cliArgs.lightpushNode !== undefined) {
process.env.WAKU_LIGHTPUSH_NODE = cliArgs.lightpushNode;
console.log(`Using CLI lightpushnode: ${cliArgs.lightpushNode}`);
}
void startServer(port);
}

View File

@ -144,16 +144,7 @@ export const validators = {
};
},
requirePeerAddrs: (body: any) => {
if (!Array.isArray(body.peerAddrs)) {
throw new Error("peerAddrs must be an array");
}
return body.peerAddrs;
},
noInput: () => null,
passThrough: (body: any) => body
};
export const errorHandlers = {

View File

@ -369,21 +369,6 @@ export class WakuHeadless {
return { success: true };
}
async dialPeers(peerAddrs: string[]) {
if (!this.waku) {
throw new Error("Waku node not started");
}
const errors: string[] = [];
await Promise.allSettled(
(peerAddrs || []).map((addr) =>
this.waku!.dial(addr).catch((err: any) =>
errors.push(String(err?.message || err)),
),
),
);
return { total: (peerAddrs || []).length, errors };
}
getPeerInfo() {
if (!this.waku) {
@ -398,124 +383,9 @@ export class WakuHeadless {
};
}
getDebugInfo() {
if (!this.waku) {
throw new Error("Waku node not started");
}
return {
listenAddresses: this.waku.libp2p
.getMultiaddrs()
.map((a: any) => a.toString()),
peerId: this.waku.libp2p.peerId.toString(),
protocols: Array.from(this.waku.libp2p.getProtocols()),
};
}
getAvailablePeerProtocols() {
if (!this.waku) {
throw new Error("Waku node not started");
}
try {
const libp2p = this.waku.libp2p;
const availableProtocols = new Set<string>();
const ownProtocols = Array.from(libp2p.getProtocols());
ownProtocols.forEach(p => availableProtocols.add(p));
if (libp2p.components && libp2p.components.connectionManager) {
const connections = libp2p.components.connectionManager.getConnections();
connections.forEach((conn: any) => {
console.log(`Peer ${conn.remotePeer.toString()} connected via ${conn.remoteAddr.toString()}`);
});
}
return {
ownProtocols: ownProtocols,
availableProtocols: Array.from(availableProtocols),
totalConnections: libp2p.components?.connectionManager?.getConnections().length || 0
};
} catch (error) {
return {
error: `Failed to get peer protocols: ${error instanceof Error ? error.message : String(error)}`,
ownProtocols: this.waku.libp2p.getProtocols(),
availableProtocols: [],
totalConnections: 0
};
}
}
getPeerConnectionStatus() {
if (!this.waku) {
throw new Error("Waku node not started");
}
try {
const libp2p = this.waku.libp2p;
const basicInfo: any = {
peerId: libp2p.peerId.toString(),
listenAddresses: libp2p.getMultiaddrs().map((a: any) => a.toString()),
protocols: Array.from(libp2p.getProtocols()),
networkConfig: this.networkConfig,
libp2pKeys: Object.keys(libp2p),
libp2pType: typeof libp2p,
};
try {
if (libp2p.components && libp2p.components.connectionManager) {
const connectionManager = libp2p.components.connectionManager;
const connections = connectionManager.getConnections().map((conn: any) => ({
remotePeer: conn.remotePeer.toString(),
remoteAddr: conn.remoteAddr.toString(),
status: conn.status,
}));
basicInfo.connections = connections;
} else {
basicInfo.connections = [];
basicInfo.connectionError = `No connection manager found in components`;
}
} catch (connError) {
basicInfo.connections = [];
basicInfo.connectionError = `Connection manager error: ${connError instanceof Error ? connError.message : String(connError)}`;
}
try {
if (typeof libp2p.getPeers === 'function') {
const peers = libp2p.getPeers().map((peerId: any) => peerId.toString());
basicInfo.peers = peers;
} else {
basicInfo.peers = [];
basicInfo.peerError = `libp2p.getPeers is not a function`;
}
} catch (peerError) {
basicInfo.peers = [];
basicInfo.peerError = `Peer error: ${peerError instanceof Error ? peerError.message : String(peerError)}`;
}
try {
if (libp2p.status) {
basicInfo.isStarted = libp2p.status;
} else {
basicInfo.isStarted = 'unknown';
basicInfo.startError = `No status property found`;
}
} catch (startError) {
basicInfo.isStarted = 'error';
basicInfo.startError = `Start check error: ${startError instanceof Error ? startError.message : String(startError)}`;
}
return basicInfo;
} catch (error) {
return {
error: `Failed to get peer status: ${error instanceof Error ? error.message : String(error)}`,
peerId: this.waku.libp2p.peerId.toString(),
isStarted: 'unknown',
};
}
}
}