Refactor: Use @simpleworkjs/conf for configuration
- Replace ENV vars with proper config system - Add conf/ directory with base, development, production, secrets - Add secrets.example.js template - Update .gitignore for secrets.js - Show environment in startup banner
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ data/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.log
|
*.log
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
conf/secrets.js
|
||||||
36
conf/base.js
Normal file
36
conf/base.js
Normal file
@@ -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'
|
||||||
|
}
|
||||||
|
};
|
||||||
16
conf/development.js
Normal file
16
conf/development.js
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
};
|
||||||
22
conf/production.js
Normal file
22
conf/production.js
Normal file
@@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
26
conf/secrets.example.js
Normal file
26
conf/secrets.example.js
Normal file
@@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "openclaw-webui",
|
"name": "openclaw-webui",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@simpleworkjs/conf": "^1.0.0",
|
||||||
"eventsource": "^2.0.2",
|
"eventsource": "^2.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-session": "^1.17.3",
|
"express-session": "^1.17.3",
|
||||||
@@ -870,6 +871,18 @@
|
|||||||
"win32"
|
"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": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
@@ -1435,6 +1448,12 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"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": {
|
"node_modules/extsprintf": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -14,19 +14,20 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@simpleworkjs/conf": "^1.0.0",
|
||||||
|
"eventsource": "^2.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-session": "^1.17.3",
|
"express-session": "^1.17.3",
|
||||||
"http-proxy-middleware": "^2.0.6",
|
"http-proxy-middleware": "^2.0.6",
|
||||||
"ldapjs": "^3.0.7",
|
"ldapjs": "^3.0.7",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"ws": "^8.16.0",
|
"ws": "^8.16.0"
|
||||||
"eventsource": "^2.0.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^5.0.12",
|
"concurrently": "^8.2.2",
|
||||||
"concurrently": "^8.2.2"
|
"vite": "^5.0.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,31 +18,27 @@ import { join, dirname } from 'path';
|
|||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import ldap from 'ldapjs';
|
import ldap from 'ldapjs';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import conf from '@simpleworkjs/conf';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
// Configuration
|
// Configuration via @simpleworkjs/conf
|
||||||
const CONFIG = {
|
const CONFIG = {
|
||||||
port: process.env.PORT || 3000,
|
port: conf.server?.port || 3000,
|
||||||
gatewayUrl: process.env.OPENCLAW_GATEWAY || 'http://127.0.0.1:18789',
|
gatewayUrl: conf.gateway?.url || 'http://127.0.0.1:18789',
|
||||||
gatewayToken: process.env.OPENCLAW_TOKEN || 'a41984619a5f4b9bf9148ab6eb4abca53eb796d046cbbec5',
|
gatewayToken: conf.gateway?.token || '',
|
||||||
sessionSecret: process.env.SESSION_SECRET || 'openclaw-webui-secret-change-in-production',
|
sessionSecret: conf.session?.secret || 'dev-secret',
|
||||||
|
sessionMaxAge: conf.session?.maxAge || 24 * 60 * 60 * 1000,
|
||||||
// LDAP Configuration
|
authDisabled: conf.auth?.disabled || false,
|
||||||
ldap: {
|
ldap: {
|
||||||
url: process.env.LDAP_URL || 'ldap://localhost:389',
|
enabled: conf.auth?.ldap?.enabled || false,
|
||||||
baseDN: process.env.LDAP_BASE_DN || 'ou=users,dc=example,dc=com',
|
url: conf.auth?.ldap?.url || 'ldap://localhost:389',
|
||||||
bindDN: process.env.LDAP_BIND_DN || '',
|
baseDN: conf.auth?.ldap?.baseDN || 'ou=users,dc=example,dc=com',
|
||||||
bindPassword: process.env.LDAP_BIND_PASSWORD || '',
|
bindDN: conf.auth?.ldap?.bindDN || '',
|
||||||
searchFilter: process.env.LDAP_SEARCH_FILTER || '(uid={{username}})',
|
bindPassword: conf.auth?.ldap?.bindPassword || '',
|
||||||
enabled: process.env.LDAP_ENABLED === 'true'
|
searchFilter: conf.auth?.ldap?.searchFilter || '(uid={{username}})'
|
||||||
},
|
},
|
||||||
|
dataDir: conf.data?.dir || join(__dirname, '../data')
|
||||||
// Data paths
|
|
||||||
dataDir: process.env.DATA_DIR || join(__dirname, '../data'),
|
|
||||||
|
|
||||||
// Disable auth for development
|
|
||||||
disableAuth: process.env.DISABLE_AUTH === 'true'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure data directory exists
|
// Ensure data directory exists
|
||||||
@@ -65,7 +61,7 @@ app.use(session({
|
|||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
cookie: {
|
cookie: {
|
||||||
secure: process.env.NODE_ENV === 'production',
|
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
|
// Check auth status
|
||||||
app.get('/api/auth/status', (req, res) => {
|
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' } });
|
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
|
// Development bypass
|
||||||
if (CONFIG.disableAuth) {
|
if (CONFIG.authDisabled) {
|
||||||
req.session.user = { username, displayName: username };
|
req.session.user = { username, displayName: username };
|
||||||
return res.json({ success: true, user: req.session.user });
|
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
|
// Auth middleware for protected routes
|
||||||
function requireAuth(req, res, next) {
|
function requireAuth(req, res, next) {
|
||||||
if (CONFIG.disableAuth) return next();
|
if (CONFIG.authDisabled) return next();
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.status(401).json({ error: 'Authentication required' });
|
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 gatewayUrl = CONFIG.gatewayUrl.replace('http', 'ws');
|
||||||
const gatewayWs = new WebSocket(`${gatewayUrl}/ws`);
|
const gatewayWs = new WebSocket(`${gatewayUrl}/ws`);
|
||||||
|
|
||||||
let helloReceived = false;
|
|
||||||
|
|
||||||
gatewayWs.on('open', () => {
|
gatewayWs.on('open', () => {
|
||||||
// Wait for challenge and send connect
|
// Forward client messages to gateway
|
||||||
ws.on('message', (data) => {
|
ws.on('message', (data) => {
|
||||||
// Forward client messages to gateway
|
|
||||||
gatewayWs.send(data);
|
gatewayWs.send(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -478,10 +471,12 @@ server.listen(CONFIG.port, () => {
|
|||||||
╔═══════════════════════════════════════════════════════════╗
|
╔═══════════════════════════════════════════════════════════╗
|
||||||
║ OpenClaw WebUI Server ║
|
║ OpenClaw WebUI Server ║
|
||||||
╠═══════════════════════════════════════════════════════════╣
|
╠═══════════════════════════════════════════════════════════╣
|
||||||
║ Port: ${CONFIG.port.toString().padEnd(44)}║
|
║ Environment: ${(conf.environment || 'development').padEnd(43)}║
|
||||||
║ Gateway: ${CONFIG.gatewayUrl.padEnd(44)}║
|
║ Port: ${CONFIG.port.toString().padEnd(43)}║
|
||||||
║ LDAP: ${(CONFIG.ldap.enabled ? 'Enabled' : 'Disabled').padEnd(44)}║
|
║ Gateway: ${CONFIG.gatewayUrl.padEnd(43)}║
|
||||||
║ Auth: ${(CONFIG.disableAuth ? 'Disabled (dev mode)' : 'Enabled').padEnd(44)}║
|
║ LDAP: ${(CONFIG.ldap.enabled ? 'Enabled' : 'Disabled').padEnd(43)}║
|
||||||
|
║ Auth: ${(CONFIG.authDisabled ? 'Disabled (dev mode)' : 'Enabled').padEnd(43)}║
|
||||||
|
║ Data: ${CONFIG.dataDir.padEnd(43)}║
|
||||||
╚═══════════════════════════════════════════════════════════╝
|
╚═══════════════════════════════════════════════════════════╝
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user