Hide admin settings on electron and fix server manager URl verification
This commit is contained in:
@@ -63,25 +63,6 @@ http {
|
|||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Electron OIDC success/error handlers
|
|
||||||
location /electron-oidc-success {
|
|
||||||
proxy_pass http://127.0.0.1:8081;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /electron-oidc-error {
|
|
||||||
proxy_pass http://127.0.0.1:8081;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /ssh/ {
|
location /ssh/ {
|
||||||
proxy_pass http://127.0.0.1:8081;
|
proxy_pass http://127.0.0.1:8081;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|||||||
@@ -127,18 +127,6 @@ ipcMain.handle('save-server-config', (event, config) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// OIDC success/error handlers
|
|
||||||
ipcMain.handle('oidc-success', (event, data) => {
|
|
||||||
console.log('OIDC authentication successful:', data);
|
|
||||||
// You can add additional logic here if needed
|
|
||||||
return { success: true };
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle('oidc-error', (event, data) => {
|
|
||||||
console.log('OIDC authentication error:', data);
|
|
||||||
// You can add additional logic here if needed
|
|
||||||
return { success: false, error: data.error };
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle('test-server-connection', async (event, serverUrl) => {
|
ipcMain.handle('test-server-connection', async (event, serverUrl) => {
|
||||||
try {
|
try {
|
||||||
@@ -204,18 +192,28 @@ ipcMain.handle('test-server-connection', async (event, serverUrl) => {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.text();
|
const data = await response.text();
|
||||||
|
|
||||||
|
// Reject if response looks like HTML (YouTube, etc.)
|
||||||
|
if (data.includes('<html') || data.includes('<!DOCTYPE') || data.includes('<head>') || data.includes('<body>')) {
|
||||||
|
console.log('Health endpoint returned HTML instead of JSON - not a Termix server');
|
||||||
|
return { success: false, error: 'Server returned HTML instead of JSON. This does not appear to be a Termix server.' };
|
||||||
|
}
|
||||||
|
|
||||||
// A valid Termix health check should return JSON with specific structure
|
// A valid Termix health check should return JSON with specific structure
|
||||||
try {
|
try {
|
||||||
const healthData = JSON.parse(data);
|
const healthData = JSON.parse(data);
|
||||||
// Check if it has the expected health check structure
|
// Check if it has the expected Termix health check structure
|
||||||
if (healthData && (healthData.status === 'healthy' || healthData.healthy === true || healthData.database === 'connected')) {
|
if (healthData && (
|
||||||
|
healthData.status === 'healthy' ||
|
||||||
|
healthData.healthy === true ||
|
||||||
|
healthData.database === 'connected' ||
|
||||||
|
(healthData.app && healthData.app.toLowerCase().includes('termix'))
|
||||||
|
)) {
|
||||||
return { success: true, status: response.status, testedUrl: healthUrl };
|
return { success: true, status: response.status, testedUrl: healthUrl };
|
||||||
}
|
}
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
// If not JSON, check for text indicators
|
// If not JSON, reject - Termix health endpoint should return JSON
|
||||||
if (data && (data.includes('healthy') || data.includes('ok') || data.includes('connected'))) {
|
console.log('Health endpoint did not return valid JSON');
|
||||||
return { success: true, status: response.status, testedUrl: healthUrl };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (urlError) {
|
} catch (urlError) {
|
||||||
@@ -232,17 +230,26 @@ ipcMain.handle('test-server-connection', async (event, serverUrl) => {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.text();
|
const data = await response.text();
|
||||||
|
|
||||||
|
// Reject if response looks like HTML (YouTube, etc.)
|
||||||
|
if (data.includes('<html') || data.includes('<!DOCTYPE') || data.includes('<head>') || data.includes('<body>')) {
|
||||||
|
console.log('Version endpoint returned HTML instead of JSON - not a Termix server');
|
||||||
|
return { success: false, error: 'Server returned HTML instead of JSON. This does not appear to be a Termix server.' };
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const versionData = JSON.parse(data);
|
const versionData = JSON.parse(data);
|
||||||
// Check if it looks like a Termix version response
|
// Check if it looks like a Termix version response - must be JSON and contain Termix-specific fields
|
||||||
if (versionData && (versionData.version || versionData.app === 'termix' || versionData.name === 'termix')) {
|
if (versionData && (
|
||||||
|
(versionData.app && versionData.app.toLowerCase().includes('termix')) ||
|
||||||
|
(versionData.name && versionData.name.toLowerCase().includes('termix')) ||
|
||||||
|
(versionData.version && versionData.description && versionData.description.toLowerCase().includes('termix'))
|
||||||
|
)) {
|
||||||
return { success: true, status: response.status, testedUrl: versionUrl, warning: 'Health endpoint not available, but server appears to be running' };
|
return { success: true, status: response.status, testedUrl: versionUrl, warning: 'Health endpoint not available, but server appears to be running' };
|
||||||
}
|
}
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
// If not JSON, check for text indicators
|
// If not JSON, reject - Termix version endpoint should return JSON
|
||||||
if (data && (data.includes('termix') || data.includes('1.6.0') || data.includes('version'))) {
|
console.log('Version endpoint did not return valid JSON');
|
||||||
return { success: true, status: response.status, testedUrl: versionUrl, warning: 'Health endpoint not available, but server appears to be running' };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (versionError) {
|
} catch (versionError) {
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
// Generic invoke method
|
// Generic invoke method
|
||||||
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
|
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
|
||||||
|
|
||||||
// OIDC handlers
|
|
||||||
oidcSuccess: (data) => ipcRenderer.invoke('oidc-success', data),
|
|
||||||
oidcError: (data) => ipcRenderer.invoke('oidc-error', data)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also set the legacy IS_ELECTRON flag for backward compatibility
|
// Also set the legacy IS_ELECTRON flag for backward compatibility
|
||||||
|
|||||||
@@ -315,15 +315,7 @@ router.get('/oidc/authorize', async (req, res) => {
|
|||||||
|
|
||||||
let origin = req.get('Origin') || req.get('Referer')?.replace(/\/[^\/]*$/, '') || 'http://localhost:5173';
|
let origin = req.get('Origin') || req.get('Referer')?.replace(/\/[^\/]*$/, '') || 'http://localhost:5173';
|
||||||
|
|
||||||
// Handle Electron app - check for custom headers or user agent
|
if (origin.includes('localhost')) {
|
||||||
const userAgent = req.get('User-Agent') || '';
|
|
||||||
const isElectron = userAgent.includes('Electron') || req.get('X-Electron-App') === 'true';
|
|
||||||
|
|
||||||
if (isElectron) {
|
|
||||||
// For Electron, use the configured server URL or fallback to localhost
|
|
||||||
const serverUrl = process.env.SERVER_URL || 'http://localhost:8081';
|
|
||||||
origin = serverUrl;
|
|
||||||
} else if (origin.includes('localhost')) {
|
|
||||||
origin = 'http://localhost:8081';
|
origin = 'http://localhost:8081';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,15 +557,7 @@ router.get('/oidc/callback', async (req, res) => {
|
|||||||
|
|
||||||
let frontendUrl = redirectUri.replace('/users/oidc/callback', '');
|
let frontendUrl = redirectUri.replace('/users/oidc/callback', '');
|
||||||
|
|
||||||
// Handle Electron app redirects
|
if (frontendUrl.includes('localhost')) {
|
||||||
const userAgent = req.get('User-Agent') || '';
|
|
||||||
const isElectron = userAgent.includes('Electron') || req.get('X-Electron-App') === 'true';
|
|
||||||
|
|
||||||
if (isElectron) {
|
|
||||||
// For Electron, we need to redirect to a special endpoint that will handle the token
|
|
||||||
// and then redirect to the Electron app using a custom protocol or file URL
|
|
||||||
frontendUrl = redirectUri.replace('/users/oidc/callback', '/electron-oidc-success');
|
|
||||||
} else if (frontendUrl.includes('localhost')) {
|
|
||||||
frontendUrl = 'http://localhost:5173';
|
frontendUrl = 'http://localhost:5173';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,14 +572,7 @@ router.get('/oidc/callback', async (req, res) => {
|
|||||||
|
|
||||||
let frontendUrl = redirectUri.replace('/users/oidc/callback', '');
|
let frontendUrl = redirectUri.replace('/users/oidc/callback', '');
|
||||||
|
|
||||||
// Handle Electron app redirects
|
if (frontendUrl.includes('localhost')) {
|
||||||
const userAgent = req.get('User-Agent') || '';
|
|
||||||
const isElectron = userAgent.includes('Electron') || req.get('X-Electron-App') === 'true';
|
|
||||||
|
|
||||||
if (isElectron) {
|
|
||||||
// For Electron, we need to redirect to a special endpoint that will handle the error
|
|
||||||
frontendUrl = redirectUri.replace('/users/oidc/callback', '/electron-oidc-error');
|
|
||||||
} else if (frontendUrl.includes('localhost')) {
|
|
||||||
frontendUrl = 'http://localhost:5173';
|
frontendUrl = 'http://localhost:5173';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,140 +583,6 @@ router.get('/oidc/callback', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Route: Electron OIDC success handler
|
|
||||||
// GET /electron-oidc-success
|
|
||||||
router.get('/electron-oidc-success', (req, res) => {
|
|
||||||
const { success, token, error } = req.query;
|
|
||||||
|
|
||||||
if (success === 'true' && token) {
|
|
||||||
// Return an HTML page that will communicate with the Electron app
|
|
||||||
res.send(`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OIDC Authentication Success</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
padding: 50px;
|
|
||||||
background: #1a1a1a;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.success { color: #4ade80; }
|
|
||||||
.error { color: #f87171; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="success">
|
|
||||||
<h2>Authentication Successful!</h2>
|
|
||||||
<p>You can close this window and return to the Termix application.</p>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
// Try to communicate with the Electron app
|
|
||||||
if (window.electronAPI) {
|
|
||||||
window.electronAPI.invoke('oidc-success', { token: '${token}' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: try to close the window after a delay
|
|
||||||
setTimeout(() => {
|
|
||||||
if (window.close) {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
} else if (error) {
|
|
||||||
res.send(`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OIDC Authentication Error</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
padding: 50px;
|
|
||||||
background: #1a1a1a;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.error { color: #f87171; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="error">
|
|
||||||
<h2>Authentication Failed</h2>
|
|
||||||
<p>Error: ${error}</p>
|
|
||||||
<p>You can close this window and try again.</p>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
// Try to communicate with the Electron app
|
|
||||||
if (window.electronAPI) {
|
|
||||||
window.electronAPI.invoke('oidc-error', { error: '${error}' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: try to close the window after a delay
|
|
||||||
setTimeout(() => {
|
|
||||||
if (window.close) {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
} else {
|
|
||||||
res.status(400).send('Invalid request');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Route: Electron OIDC error handler
|
|
||||||
// GET /electron-oidc-error
|
|
||||||
router.get('/electron-oidc-error', (req, res) => {
|
|
||||||
const { error } = req.query;
|
|
||||||
|
|
||||||
res.send(`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OIDC Authentication Error</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
padding: 50px;
|
|
||||||
background: #1a1a1a;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.error { color: #f87171; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="error">
|
|
||||||
<h2>Authentication Failed</h2>
|
|
||||||
<p>Error: ${error || 'Unknown error'}</p>
|
|
||||||
<p>You can close this window and try again.</p>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
// Try to communicate with the Electron app
|
|
||||||
if (window.electronAPI) {
|
|
||||||
window.electronAPI.invoke('oidc-error', { error: '${error || 'Unknown error'}' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: try to close the window after a delay
|
|
||||||
setTimeout(() => {
|
|
||||||
if (window.close) {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Route: Get user JWT by username and password (traditional login)
|
// Route: Get user JWT by username and password (traditional login)
|
||||||
// POST /users/login
|
// POST /users/login
|
||||||
router.post('/login', async (req, res) => {
|
router.post('/login', async (req, res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user