added ws authentication
This commit is contained in:
@@ -29,7 +29,10 @@ class RunCommand extends Component
|
||||
}
|
||||
|
||||
return $server->definedResources()
|
||||
->filter(fn ($resource) => str_starts_with($resource->status, 'running:'))
|
||||
->filter(function ($resource) {
|
||||
$status = method_exists($resource, 'realStatus') ? $resource->realStatus() : (method_exists($resource, 'status') ? $resource->status() : 'exited');
|
||||
return str_starts_with($status, 'running:');
|
||||
})
|
||||
->map(function ($resource) use ($server) {
|
||||
$container_name = $resource->uuid;
|
||||
|
||||
|
||||
32
package-lock.json
generated
32
package-lock.json
generated
@@ -10,6 +10,8 @@
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"alpinejs": "3.14.0",
|
||||
"cookie": "^0.6.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"ioredis": "5.4.1",
|
||||
"node-pty": "^1.0.0",
|
||||
"tailwindcss-scrollbar": "0.1.0",
|
||||
@@ -18,7 +20,7 @@
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "4.5.1",
|
||||
"autoprefixer": "10.4.19",
|
||||
"axios": "1.7.2",
|
||||
"axios": "^1.7.4",
|
||||
"laravel-echo": "1.16.1",
|
||||
"laravel-vite-plugin": "0.8.1",
|
||||
"postcss": "8.4.38",
|
||||
@@ -783,10 +785,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
|
||||
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -956,6 +959,15 @@
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
@@ -1016,6 +1028,18 @@
|
||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.4.5",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
||||
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.692",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "4.5.1",
|
||||
"autoprefixer": "10.4.19",
|
||||
"axios": "1.7.2",
|
||||
"axios": "^1.7.4",
|
||||
"laravel-echo": "1.16.1",
|
||||
"laravel-vite-plugin": "0.8.1",
|
||||
"postcss": "8.4.38",
|
||||
@@ -23,6 +23,8 @@
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"alpinejs": "3.14.0",
|
||||
"cookie": "^0.6.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"ioredis": "5.4.1",
|
||||
"node-pty": "^1.0.0",
|
||||
"tailwindcss-scrollbar": "0.1.0",
|
||||
|
||||
@@ -34,8 +34,9 @@
|
||||
|
||||
function initializeWebSocket() {
|
||||
if (!socket || socket.readyState === WebSocket.CLOSED) {
|
||||
const url = "{{ str_replace(['http://', 'https://'], '', config('app.url')) }}" || window.location.hostname;
|
||||
socket = new WebSocket((window.location.protocol === 'https:' ? 'wss://' : 'ws://') +
|
||||
"{{ str_replace(['http://', 'https://'], '', config('app.url')) }}" +
|
||||
url +
|
||||
':6002/terminal');
|
||||
socket.onmessage = handleSocketMessage;
|
||||
socket.onerror = (e) => {
|
||||
|
||||
@@ -154,6 +154,12 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
||||
});
|
||||
|
||||
Route::get('/command-center', CommandCenterIndex::class)->name('command-center');
|
||||
Route::post('/terminal/auth', function () {
|
||||
if (auth()->check()) {
|
||||
return response()->json(['authenticated' => true], 200);
|
||||
}
|
||||
return response()->json(['authenticated' => false], 401);
|
||||
})->name('terminal.auth');
|
||||
|
||||
Route::prefix('invitations')->group(function () {
|
||||
Route::get('/{uuid}', [Controller::class, 'accept_invitation'])->name('team.invitation.accept');
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { WebSocketServer } from 'ws';
|
||||
import http from 'http';
|
||||
import pty from 'node-pty';
|
||||
import axios from 'axios';
|
||||
import cookie from 'cookie';
|
||||
import 'dotenv/config'
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.url === '/ready') {
|
||||
@@ -12,7 +15,45 @@ const server = http.createServer((req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
const wss = new WebSocketServer({ server, path: '/terminal' });
|
||||
const verifyClient = async (info, callback) => {
|
||||
const cookies = cookie.parse(info.req.headers.cookie || '');
|
||||
const origin = new URL(info.origin);
|
||||
const protocol = origin.protocol;
|
||||
const xsrfToken = cookies['XSRF-TOKEN'];
|
||||
|
||||
// Generate session cookie name based on APP_NAME
|
||||
const appName = process.env.APP_NAME || 'laravel';
|
||||
const sessionCookieName = `${appName.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}_session`;
|
||||
const laravelSession = cookies[sessionCookieName];
|
||||
|
||||
// Verify presence of required tokens
|
||||
if (!laravelSession || !xsrfToken) {
|
||||
return callback(false, 401, 'Unauthorized: Missing required tokens');
|
||||
}
|
||||
|
||||
try {
|
||||
// Authenticate with Laravel backend
|
||||
const response = await axios.post(`${protocol}//coolify/terminal/auth`, null, {
|
||||
headers: {
|
||||
'Cookie': `${sessionCookieName}=${laravelSession}`,
|
||||
'X-XSRF-TOKEN': xsrfToken
|
||||
},
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
// Authentication successful
|
||||
callback(true);
|
||||
} else {
|
||||
callback(false, 401, 'Unauthorized: Invalid credentials');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Authentication error:', error.message);
|
||||
callback(false, 500, 'Internal Server Error');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const wss = new WebSocketServer({ server, path: '/terminal', verifyClient: verifyClient });
|
||||
const userSessions = new Map();
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
|
||||
Reference in New Issue
Block a user