Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "MQTT Explorer Development",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",

"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-vscode.vscode-typescript-next",
"ms-azuretools.vscode-docker",
"eamodio.gitlens"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.tsdk": "node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
}
},

"forwardPorts": [3000, 8080, 1883],
"portsAttributes": {
"3000": {
"label": "MQTT Explorer Server",
"onAutoForward": "notify"
},
"8080": {
"label": "Webpack Dev Server",
"onAutoForward": "notify"
},
"1883": {
"label": "MQTT Broker",
"onAutoForward": "ignore"
}
},

"postCreateCommand": "yarn install",

"remoteUser": "node"
}
21 changes: 21 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: '3.8'

services:
app:
image: mcr.microsoft.com/devcontainers/javascript-node:20
volumes:
- ../..:/workspace:cached
command: sleep infinity
network_mode: service:mosquitto
environment:
- MQTT_EXPLORER_USERNAME=dev
- MQTT_EXPLORER_PASSWORD=dev123

mosquitto:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
- "3000:3000"
- "8080:8080"
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
4 changes: 4 additions & 0 deletions .devcontainer/mosquitto.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Mosquitto configuration for development
listener 1883
allow_anonymous true
persistence false
6 changes: 5 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ yarn install
# Build the project
yarn build

# Set password for browser testing
export MQTT_EXPLORER_USERNAME=admin
export MQTT_EXPLORER_PASSWORD=secretpassword

# Start the application
yarn start

Expand Down Expand Up @@ -322,5 +326,5 @@ yarn package-with-docker
- The app uses Electron (see `package.json` for version)
- MQTT communication is handled via [mqttjs](https://github.com/mqttjs/MQTT.js)
- All code changes should pass linting (`yarn lint`)
- Node.js version requirement: >= 18
- Node.js version requirement: >= 20
- The project uses workspace-like structure with separate package.json files for app and backend
2 changes: 1 addition & 1 deletion .github/workflows/copilot-setup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '20'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,53 @@ jobs:
run: echo '${{ steps.upload.outputs.file-url }}'
id: artifact-upload-step
- run: echo '<picture><img src="${{ steps.upload.outputs.file-url }}"></picture>' >> $GITHUB_STEP_SUMMARY

test-browser:
runs-on: ubuntu-latest
services:
mosquitto:
image: eclipse-mosquitto:2
ports:
- 1883:1883
options: >-
--health-cmd "mosquitto_sub -t '$SYS/#' -C 1"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Build Browser Mode
run: yarn build:server
- name: Test App
run: yarn test:app
- name: Test Backend
run: yarn test:backend
- name: Start Server in Background
run: |
yarn start:server &
echo $! > server.pid
env:
MQTT_EXPLORER_USERNAME: test
MQTT_EXPLORER_PASSWORD: test123
PORT: 3000
- name: Wait for Server
run: |
timeout 30 bash -c 'until curl -f http://localhost:3000; do sleep 1; done'
- name: Browser Smoke Test
run: |
# Test server is running
curl -f http://localhost:3000 || exit 1
echo "Browser mode server is running successfully"
- name: Stop Server
if: always()
run: |
if [ -f server.pid ]; then
kill $(cat server.pid) || true
rm server.pid
fi
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ mqtt-explorer-mcp-screenshot.png
screenshot-mcp-*.png
test-mcp-introspection.js

# UI test artifacts
/data
test-screenshot-*.png
193 changes: 193 additions & 0 deletions BROWSER_MODE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Browser Mode Documentation

MQTT Explorer now supports running as a web application served by a Node.js server, in addition to the existing Electron desktop app.

## Running in Browser Mode

### Quick Start

1. Build the application for browser mode:
```bash
yarn build:server
```

2. Start the server:
```bash
yarn start:server
```

3. Open your browser and navigate to `http://localhost:3000`

4. You'll be prompted to log in with credentials that were generated on server startup.

### Development Mode

To run in development mode with hot reload:

```bash
yarn dev:server
```

This starts both the webpack dev server and the backend server.

## Authentication

### Environment Variables

You can set custom authentication credentials using environment variables:

```bash
export MQTT_EXPLORER_USERNAME=admin
export MQTT_EXPLORER_PASSWORD=secretpassword
yarn start:server
```

### Generated Credentials

If no environment variables are set, the server will generate credentials on first startup and save them to `data/credentials.json`. The generated credentials will be printed to the console:

```
============================================================
Generated new credentials:
Username: user-abc123
Password: 123e4567-e89b-12d3-a456-426614174000
============================================================
Please save these credentials. They will be persisted to:
/path/to/data/credentials.json
============================================================
```

## Features

### Certificate Upload

In browser mode, certificate files are uploaded directly through the browser using the HTML5 File API. The certificates are:
- Read client-side as base64
- Stored in the connection configuration
- Used when establishing MQTT connections

### Data Storage

In browser mode, all data is stored on the server:
- Credentials: `data/credentials.json`
- Uploaded certificates: `data/certificates/`
- File uploads: `data/uploads/`

### Port Configuration

The default port is 3000. You can change it using the `PORT` environment variable:

```bash
PORT=8080 yarn start:server
```

## Architecture

### Client-Server Communication

- **Electron Mode**: Uses Electron IPC for communication between renderer and main process
- **Browser Mode**: Uses Socket.io WebSockets for real-time communication between browser and server

The application automatically detects the environment and uses the appropriate transport layer.

### Event Bus Abstraction

Both Electron IPC and Socket.io implement the same `EventBusInterface`, allowing the application code to work seamlessly in both modes without modification.

## Differences from Electron Mode

### Browser Mode Limitations

1. **File System Access**: Limited to server-side operations
2. **Native Dialogs**: File selection uses browser file input instead of native dialogs
3. **Auto-Updates**: Not available in browser mode
4. **Tray Icon**: Not available in browser mode

### Browser Mode Advantages

1. **No Installation**: Access from any browser
2. **Cross-Platform**: Works on any device with a modern browser
3. **Remote Access**: Can be deployed on a server for remote access
4. **Multi-User**: Can support authentication for multiple users

## Security Considerations

1. **HTTPS**: For production, always use HTTPS to encrypt credentials and MQTT data
2. **Authentication**: Keep credentials secure and rotate them regularly
3. **Network**: Ensure the server is on a trusted network or behind a firewall
4. **Environment Variables**: Use environment variables for production credentials, not the generated ones

## Deployment

For production deployment:

1. Build the application:
```bash
yarn build:server
```

2. Set environment variables:
```bash
export MQTT_EXPLORER_USERNAME=your_username
export MQTT_EXPLORER_PASSWORD=your_secure_password
export PORT=3000
```

3. Start the server:
```bash
yarn start:server
```

4. Use a reverse proxy (nginx, Apache) to add HTTPS and additional security features

## Troubleshooting

### Debugging

Enable detailed Socket.IO connection and lifecycle debugging:

```bash
DEBUG=mqtt-explorer:socketio* yarn start:server
```

Available debug namespaces:
- `mqtt-explorer:socketio` - General Socket.IO events and metrics
- `mqtt-explorer:socketio:connect` - Client connection events
- `mqtt-explorer:socketio:disconnect` - Client disconnection and cleanup
- `mqtt-explorer:socketio:subscriptions` - Subscription lifecycle tracking
- `mqtt-explorer:socketio:connections` - MQTT connection ownership

This will log:
- Client connect/disconnect events
- Subscription counts per socket
- MQTT connection ownership tracking
- Memory leak detection metrics (subscriptions, handlers, connections)

Example output:
```
mqtt-explorer:socketio:connect Client connected: abc123de
mqtt-explorer:socketio [connect] clients=1 subscriptions=8 mqttConns=0 | socket[abc123de]: subs=8 conns=0
mqtt-explorer:socketio:connections Connection my-mqtt owned by socket abc123de (total: 1)
mqtt-explorer:socketio:disconnect Client disconnected: abc123de
mqtt-explorer:socketio:subscriptions Removed 8 subscriptions for socket abc123de
mqtt-explorer:socketio [disconnect] clients=0 subscriptions=0 mqttConns=0 | socket[abc123de]: subs=0 conns=0
```

### Authentication Fails

1. Check the console output for the generated credentials
2. Clear browser session storage: `sessionStorage.clear()` in browser console
3. Restart the server to regenerate credentials

### Connection Issues

1. Check that the server is running: `http://localhost:3000`
2. Check browser console for Socket.io connection errors
3. Verify firewall rules allow the port

### Certificate Upload Issues

In browser mode, certificates are handled differently:
- Use the file upload button to select certificate files
- Files are read and encoded client-side
- Large certificate files (>16KB) will be rejected
Loading
Loading