diff --git a/.gitignore b/.gitignore index 330a338..3a944c6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ data/ .DS_Store *.log .env -.env.local \ No newline at end of file +.env.local +conf/secrets.js \ No newline at end of file diff --git a/conf/base.js b/conf/base.js new file mode 100644 index 0000000..209e8f0 --- /dev/null +++ b/conf/base.js @@ -0,0 +1,36 @@ +/** + * Base configuration - shared across all environments + */ + +module.exports = { + server: { + port: 3000, + host: '0.0.0.0' + }, + + gateway: { + url: 'http://127.0.0.1:18789', + token: 'a41984619a5f4b9bf9148ab6eb4abca53eb796d046cbbec5' + }, + + session: { + secret: 'change-me-in-production', + maxAge: 24 * 60 * 60 * 1000 // 24 hours + }, + + auth: { + disabled: false, + ldap: { + enabled: false, + url: 'ldap://localhost:389', + baseDN: 'ou=users,dc=example,dc=com', + bindDN: '', + bindPassword: '', + searchFilter: '(uid={{username}})' + } + }, + + data: { + dir: './data' + } +}; \ No newline at end of file diff --git a/conf/development.js b/conf/development.js new file mode 100644 index 0000000..02892d7 --- /dev/null +++ b/conf/development.js @@ -0,0 +1,16 @@ +/** + * Development environment configuration + */ + +module.exports = { + auth: { + disabled: true, // Skip auth in dev + ldap: { + enabled: false + } + }, + + server: { + // Vite runs on 5173, proxy from there + } +}; \ No newline at end of file diff --git a/conf/production.js b/conf/production.js new file mode 100644 index 0000000..3d7da1a --- /dev/null +++ b/conf/production.js @@ -0,0 +1,22 @@ +/** + * Production environment configuration + */ + +module.exports = { + server: { + // Use PORT env var if set, otherwise default + port: parseInt(process.env.PORT) || 3000 + }, + + session: { + // Should be set via secrets.js in production + secret: process.env.SESSION_SECRET || 'CHANGE-ME-NOW' + }, + + auth: { + disabled: false, + ldap: { + enabled: process.env.LDAP_ENABLED === 'true' + } + } +}; \ No newline at end of file diff --git a/conf/secrets.example.js b/conf/secrets.example.js new file mode 100644 index 0000000..4725417 --- /dev/null +++ b/conf/secrets.example.js @@ -0,0 +1,26 @@ +/** + * Example secrets file + * + * Copy this to secrets.js and fill in your actual values + * secrets.js is ignored by git + */ + +module.exports = { + gateway: { + // Your OpenClaw gateway token + token: 'your-gateway-token-here' + }, + + session: { + // Random string for session signing + secret: 'generate-a-random-string-here' + }, + + auth: { + ldap: { + // LDAP bind credentials (if needed for search) + bindDN: 'cn=admin,dc=example,dc=com', + bindPassword: 'ldap-admin-password' + } + } +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 01d4cf2..d87ceb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "openclaw-webui", "version": "1.0.0", "dependencies": { + "@simpleworkjs/conf": "^1.0.0", "eventsource": "^2.0.2", "express": "^4.18.2", "express-session": "^1.17.3", @@ -870,6 +871,18 @@ "win32" ] }, + "node_modules/@simpleworkjs/conf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@simpleworkjs/conf/-/conf-1.0.0.tgz", + "integrity": "sha512-p1dQAELW0oUBRpDoz260TYw18IMI/Y11xYAb17P1MEPjsTAUB0LWE/6ZeA2VQmpU/LXoRnDysg0G/oASGILyUA==", + "license": "MIT", + "dependencies": { + "extend": "^3.0.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1435,6 +1448,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extsprintf": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", diff --git a/package.json b/package.json index ab845fa..891a4c4 100644 --- a/package.json +++ b/package.json @@ -14,19 +14,20 @@ "preview": "vite preview" }, "dependencies": { + "@simpleworkjs/conf": "^1.0.0", + "eventsource": "^2.0.2", "express": "^4.18.2", "express-session": "^1.17.3", "http-proxy-middleware": "^2.0.6", "ldapjs": "^3.0.7", "uuid": "^9.0.0", - "ws": "^8.16.0", - "eventsource": "^2.0.2" + "ws": "^8.16.0" }, "devDependencies": { - "vite": "^5.0.12", - "concurrently": "^8.2.2" + "concurrently": "^8.2.2", + "vite": "^5.0.12" }, "engines": { "node": ">=18.0.0" } -} \ No newline at end of file +} diff --git a/server/index.js b/server/index.js index 1c5b00c..b065233 100644 --- a/server/index.js +++ b/server/index.js @@ -18,31 +18,27 @@ import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import ldap from 'ldapjs'; import { v4 as uuidv4 } from 'uuid'; +import conf from '@simpleworkjs/conf'; const __dirname = dirname(fileURLToPath(import.meta.url)); -// Configuration +// Configuration via @simpleworkjs/conf const CONFIG = { - port: process.env.PORT || 3000, - gatewayUrl: process.env.OPENCLAW_GATEWAY || 'http://127.0.0.1:18789', - gatewayToken: process.env.OPENCLAW_TOKEN || 'a41984619a5f4b9bf9148ab6eb4abca53eb796d046cbbec5', - sessionSecret: process.env.SESSION_SECRET || 'openclaw-webui-secret-change-in-production', - - // LDAP Configuration + port: conf.server?.port || 3000, + gatewayUrl: conf.gateway?.url || 'http://127.0.0.1:18789', + gatewayToken: conf.gateway?.token || '', + sessionSecret: conf.session?.secret || 'dev-secret', + sessionMaxAge: conf.session?.maxAge || 24 * 60 * 60 * 1000, + authDisabled: conf.auth?.disabled || false, ldap: { - url: process.env.LDAP_URL || 'ldap://localhost:389', - baseDN: process.env.LDAP_BASE_DN || 'ou=users,dc=example,dc=com', - bindDN: process.env.LDAP_BIND_DN || '', - bindPassword: process.env.LDAP_BIND_PASSWORD || '', - searchFilter: process.env.LDAP_SEARCH_FILTER || '(uid={{username}})', - enabled: process.env.LDAP_ENABLED === 'true' + enabled: conf.auth?.ldap?.enabled || false, + url: conf.auth?.ldap?.url || 'ldap://localhost:389', + baseDN: conf.auth?.ldap?.baseDN || 'ou=users,dc=example,dc=com', + bindDN: conf.auth?.ldap?.bindDN || '', + bindPassword: conf.auth?.ldap?.bindPassword || '', + searchFilter: conf.auth?.ldap?.searchFilter || '(uid={{username}})' }, - - // Data paths - dataDir: process.env.DATA_DIR || join(__dirname, '../data'), - - // Disable auth for development - disableAuth: process.env.DISABLE_AUTH === 'true' + dataDir: conf.data?.dir || join(__dirname, '../data') }; // Ensure data directory exists @@ -65,7 +61,7 @@ app.use(session({ saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', - maxAge: 24 * 60 * 60 * 1000 // 24 hours + maxAge: CONFIG.sessionMaxAge } })); @@ -141,7 +137,7 @@ async function authenticateLDAP(username, password) { // Check auth status app.get('/api/auth/status', (req, res) => { - if (CONFIG.disableAuth) { + if (CONFIG.authDisabled) { return res.json({ authenticated: true, user: { username: 'dev-user', displayName: 'Dev User' } }); } @@ -161,7 +157,7 @@ app.post('/api/auth/login', async (req, res) => { } // Development bypass - if (CONFIG.disableAuth) { + if (CONFIG.authDisabled) { req.session.user = { username, displayName: username }; return res.json({ success: true, user: req.session.user }); } @@ -190,7 +186,7 @@ app.post('/api/auth/logout', (req, res) => { // Auth middleware for protected routes function requireAuth(req, res, next) { - if (CONFIG.disableAuth) return next(); + if (CONFIG.authDisabled) return next(); if (!req.session.user) { return res.status(401).json({ error: 'Authentication required' }); } @@ -434,12 +430,9 @@ wss.on('connection', (ws, req) => { const gatewayUrl = CONFIG.gatewayUrl.replace('http', 'ws'); const gatewayWs = new WebSocket(`${gatewayUrl}/ws`); - let helloReceived = false; - gatewayWs.on('open', () => { - // Wait for challenge and send connect + // Forward client messages to gateway ws.on('message', (data) => { - // Forward client messages to gateway gatewayWs.send(data); }); }); @@ -478,10 +471,12 @@ server.listen(CONFIG.port, () => { ╔═══════════════════════════════════════════════════════════╗ ║ OpenClaw WebUI Server ║ ╠═══════════════════════════════════════════════════════════╣ -║ Port: ${CONFIG.port.toString().padEnd(44)}║ -║ Gateway: ${CONFIG.gatewayUrl.padEnd(44)}║ -║ LDAP: ${(CONFIG.ldap.enabled ? 'Enabled' : 'Disabled').padEnd(44)}║ -║ Auth: ${(CONFIG.disableAuth ? 'Disabled (dev mode)' : 'Enabled').padEnd(44)}║ +║ Environment: ${(conf.environment || 'development').padEnd(43)}║ +║ Port: ${CONFIG.port.toString().padEnd(43)}║ +║ Gateway: ${CONFIG.gatewayUrl.padEnd(43)}║ +║ LDAP: ${(CONFIG.ldap.enabled ? 'Enabled' : 'Disabled').padEnd(43)}║ +║ Auth: ${(CONFIG.authDisabled ? 'Disabled (dev mode)' : 'Enabled').padEnd(43)}║ +║ Data: ${CONFIG.dataDir.padEnd(43)}║ ╚═══════════════════════════════════════════════════════════╝ `); }); \ No newline at end of file