From 05b3ae0071729aa271062804b5883e99fccad701 Mon Sep 17 00:00:00 2001 From: Nova Date: Wed, 25 Feb 2026 03:54:51 +0000 Subject: [PATCH] Fix file uploads: include content in messages, update README, remove secrets - File uploads now read content and include in AI context - Truncate files over 50KB to avoid token limits - Updated README with JSON config documentation - Removed secrets.json from git tracking --- README.md | 203 ++++++++++++++++++++++++++----------------------- client/main.js | 32 ++++++-- 2 files changed, 132 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 1b376dd..5661586 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,10 @@ A modern, OpenWebUI-compatible chat interface for OpenClaw with LDAP SSO support. -![OpenClaw WebUI](https://via.placeholder.com/800x400?text=OpenClaw+WebUI) - ## Features - **Modern Chat Interface** - Clean, responsive UI inspired by OpenWebUI -- **Multi-file Upload** - Attach files to messages +- **Multi-file Upload** - Attach files with content included in context - **Code Canvas** - Side panel for code editing and viewing - **Chat History** - Persistent conversation storage - **Streaming Responses** - Real-time token streaming @@ -18,43 +16,95 @@ A modern, OpenWebUI-compatible chat interface for OpenClaw with LDAP SSO support ## Quick Start ```bash -# Install dependencies +# Clone +git clone https://git.theta42.com/nova/openclaw-webui.git +cd openclaw-webui + +# Install npm install -# Development mode (with hot reload) +# Development (hot reload) npm run dev # Production build npm run build -# Start production server -npm start +# Production server +NODE_ENV=production npm start ``` -The server runs on port 3000 by default. - ## Configuration -Configure via environment variables: +Uses JSON config files in `conf/` directory: -```bash -# Server -PORT=3000 -NODE_ENV=production +``` +conf/ +├── base.json # Base config (all environments) +├── development.json # Dev overrides (auth disabled) +├── production.json # Production overrides +├── secrets.json # Secrets (gitignored!) +└── secrets.example.json # Template +``` -# OpenClaw Gateway -OPENCLAW_GATEWAY=http://127.0.0.1:18789 +### Load Order -# LDAP Authentication -LDAP_ENABLED=true -LDAP_URL=ldap://localhost:389 -LDAP_BASE_DN=ou=users,dc=example,dc=com +Files merge in order: `base.json` → `[environment].json` → `secrets.json` -# Session -SESSION_SECRET=your-secret-key +### Example Configs -# Development (disable auth) -DISABLE_AUTH=true +**conf/base.json:** +```json +{ + "server": { "port": 8089 }, + "gateway": { "url": "http://127.0.0.1:18789" }, + "auth": { + "disabled": false, + "ldap": { + "enabled": true, + "url": "ldap://10.1.0.55:389", + "baseDN": "dc=example,dc=com", + "searchFilter": "(uid={{username}})" + } + } +} +``` + +**conf/secrets.json:** +```json +{ + "gateway": { "token": "your-openclaw-token" }, + "session": { "secret": "random-session-secret" }, + "auth": { + "ldap": { + "bindDN": "cn=service,ou=people,dc=example,dc=com", + "bindPassword": "ldap-password" + } + } +} +``` + +### Environment Variables + +Can override config at runtime: + +- `PORT` - Server port +- `OPENCLAW_GATEWAY` - Gateway URL +- `OPENCLAW_TOKEN` - Gateway auth token +- `SESSION_SECRET` - Session signing secret +- `LDAP_ENABLED` - Enable LDAP auth + +## LDAP Authentication + +Supports standard LDAP servers (OpenLDAP, Active Directory): + +1. Service binds with `bindDN` + `bindPassword` +2. Searches for user with `searchFilter` +3. Binds as user to verify password + +**Search Filter:** +Use `{{username}}` as placeholder: +``` +(&(memberof=cn=app_access,ou=groups,dc=example,dc=com)(uid={{username}})) ``` ## Architecture @@ -65,7 +115,7 @@ DISABLE_AUTH=true ├─────────────────────────────────────────────────────────┤ │ Frontend (Vanilla JS + Vite) │ │ ├── Chat Interface │ -│ ├── File Upload │ +│ ├── File Upload (content included) │ │ ├── Code Canvas │ │ └── History Sidebar │ ├─────────────────────────────────────────────────────────┤ @@ -73,108 +123,67 @@ DISABLE_AUTH=true │ ├── LDAP SSO Authentication │ │ ├── Session Management │ │ ├── Chat History Persistence │ -│ ├── File Upload Handling │ │ └── /v1/chat/completions Proxy │ ├─────────────────────────────────────────────────────────┤ │ OpenClaw Gateway (port 18789) │ -│ ├── WebSocket Protocol │ -│ ├── OpenAI-Compatible API │ -│ └── Agent Execution │ └─────────────────────────────────────────────────────────┘ ``` ## API Endpoints ### Authentication -- `GET /api/auth/status` - Check authentication status -- `POST /api/auth/login` - Login with username/password +- `GET /api/auth/status` - Check auth status +- `POST /api/auth/login` - Login - `POST /api/auth/logout` - Logout ### Conversations -- `GET /api/conversations` - List all conversations -- `POST /api/conversations` - Create new conversation +- `GET /api/conversations` - List conversations +- `POST /api/conversations` - Create conversation - `PUT /api/conversations/:id` - Update conversation - `DELETE /api/conversations/:id` - Delete conversation - `GET /api/conversations/:id/messages` - Get messages -- `POST /api/conversations/:id/messages` - Save message - -### Files -- `POST /api/upload` - Upload file -- `GET /api/upload/:id` - Download file -- `GET /api/upload/:id/meta` - Get file metadata ### OpenAI-Compatible -- `POST /v1/chat/completions` - Chat completions (proxied to OpenClaw) -- `GET /v1/models` - List available models +- `POST /v1/chat/completions` - Chat (proxied to OpenClaw) +- `GET /v1/models` - List models -## LDAP Configuration - -The LDAP integration supports standard Active Directory and OpenLDAP setups: +## Production Deployment +**Systemd Service:** ```bash -# LDAP Server URL -LDAP_URL=ldap://ldap.example.com:389 +# Create service file +mkdir -p ~/.config/systemd/user +cp openclaw-webui.service ~/.config/systemd/user/ -# Base DN for user search -LDAP_BASE_DN=ou=users,dc=example,dc=com +# Enable and start +systemctl --user enable openclaw-webui +systemctl --user start openclaw-webui -# Bind DN for searching (if needed) -LDAP_BIND_DN=cn=admin,dc=example,dc=com -LDAP_BIND_PASSWORD=admin_password - -# Custom search filter (optional) -LDAP_SEARCH_FILTER=(uid={{username}}) +# View logs +journalctl --user -u openclaw-webui -f ``` -Users authenticate with their LDAP username and password. +**Requirements:** +- OpenClaw Gateway running on port 18789 +- Enable HTTP chat completions in gateway config: +```json +{ + "gateway": { + "http": { + "endpoints": { "chatCompletions": { "enabled": true } } + } + } +} +``` ## Development ```bash -# Run in development mode npm run dev - -# This starts: -# - Backend server on port 3000 (with hot reload) -# - Vite dev server on port 5173 (with HMR) -# - Proxy from Vite to backend for /api and /v1 -``` - -## Production Deployment - -```bash -# Build frontend -npm run build - -# Start production server -NODE_ENV=production npm start -``` - -The production server serves static files from `dist/` and handles all API routes. - -## File Structure - -``` -openclaw-webui/ -├── client/ -│ ├── index.html -│ ├── main.js # Main application -│ └── public/ -│ ├── styles.css -│ └── favicon.svg -├── server/ -│ └── index.js # Express server -├── data/ # Chat history (gitignored) -├── dist/ # Production build (gitignored) -├── package.json -├── vite.config.js -└── README.md +# Frontend: http://localhost:5173 (Vite HMR) +# Backend: http://localhost:3000 (auto-restart) ``` ## License -MIT License - See LICENSE file for details. - -## Credits - -Built for [OpenClaw](https://github.com/openclaw/openclaw) - AI assistant framework. \ No newline at end of file +MIT \ No newline at end of file diff --git a/client/main.js b/client/main.js index f36b5cc..be126e3 100644 --- a/client/main.js +++ b/client/main.js @@ -550,15 +550,18 @@ async function handleFileSelect(e) { for (const file of files) { try { - const uploaded = await api.uploadFile(file); + // Read file content + const content = await readFileContent(file); + state.files.push({ - id: uploaded.id, + id: file.name + '-' + Date.now(), name: file.name, type: file.type, - size: file.size + size: file.size, + content: content }); } catch (err) { - console.error('Failed to upload file:', file.name, err); + console.error('Failed to read file:', file.name, err); } } @@ -566,6 +569,15 @@ async function handleFileSelect(e) { e.target.value = ''; } +function readFileContent(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsText(file); + }); +} + function renderFilesPreview() { const container = document.getElementById('files-preview'); if (!container) return; @@ -663,8 +675,16 @@ async function handleSendMessage() { function buildMessageContent(text, files) { if (!files.length) return text; - const fileDescriptions = files.map(f => `[Attached: ${f.name}]`).join('\n'); - return `${text}\n\n${fileDescriptions}`; + const fileContents = files.map(f => { + const maxLen = 50000; // Limit file content length + const content = f.content || '[File content not available]'; + const truncated = content.length > maxLen + ? content.substring(0, maxLen) + '\n... [truncated]' + : content; + return `--- ${f.name} ---\n${truncated}\n--- end of ${f.name} ---`; + }).join('\n\n'); + + return `${text}\n\n${fileContents}`; } function updateLastMessage(content) {