added ws authentication
This commit is contained in:
@@ -29,7 +29,10 @@ class RunCommand extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $server->definedResources()
|
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) {
|
->map(function ($resource) use ($server) {
|
||||||
$container_name = $resource->uuid;
|
$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/addon-fit": "^0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"alpinejs": "3.14.0",
|
"alpinejs": "3.14.0",
|
||||||
|
"cookie": "^0.6.0",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
"ioredis": "5.4.1",
|
"ioredis": "5.4.1",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.0.0",
|
||||||
"tailwindcss-scrollbar": "0.1.0",
|
"tailwindcss-scrollbar": "0.1.0",
|
||||||
@@ -18,7 +20,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.5.1",
|
"@vitejs/plugin-vue": "4.5.1",
|
||||||
"autoprefixer": "10.4.19",
|
"autoprefixer": "10.4.19",
|
||||||
"axios": "1.7.2",
|
"axios": "^1.7.4",
|
||||||
"laravel-echo": "1.16.1",
|
"laravel-echo": "1.16.1",
|
||||||
"laravel-vite-plugin": "0.8.1",
|
"laravel-vite-plugin": "0.8.1",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.38",
|
||||||
@@ -783,10 +785,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||||
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
|
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
@@ -956,6 +959,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
"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": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
"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": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.692",
|
"version": "1.4.692",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.5.1",
|
"@vitejs/plugin-vue": "4.5.1",
|
||||||
"autoprefixer": "10.4.19",
|
"autoprefixer": "10.4.19",
|
||||||
"axios": "1.7.2",
|
"axios": "^1.7.4",
|
||||||
"laravel-echo": "1.16.1",
|
"laravel-echo": "1.16.1",
|
||||||
"laravel-vite-plugin": "0.8.1",
|
"laravel-vite-plugin": "0.8.1",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.38",
|
||||||
@@ -23,6 +23,8 @@
|
|||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"alpinejs": "3.14.0",
|
"alpinejs": "3.14.0",
|
||||||
|
"cookie": "^0.6.0",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
"ioredis": "5.4.1",
|
"ioredis": "5.4.1",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.0.0",
|
||||||
"tailwindcss-scrollbar": "0.1.0",
|
"tailwindcss-scrollbar": "0.1.0",
|
||||||
|
|||||||
@@ -24,168 +24,169 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@script
|
@script
|
||||||
<script>
|
<script>
|
||||||
const MAX_PENDING_WRITES = 5;
|
const MAX_PENDING_WRITES = 5;
|
||||||
let pendingWrites = 0;
|
let pendingWrites = 0;
|
||||||
let paused = false;
|
let paused = false;
|
||||||
|
|
||||||
let socket;
|
let socket;
|
||||||
let commandBuffer = '';
|
let commandBuffer = '';
|
||||||
|
|
||||||
function initializeWebSocket() {
|
function initializeWebSocket() {
|
||||||
if (!socket || socket.readyState === WebSocket.CLOSED) {
|
if (!socket || socket.readyState === WebSocket.CLOSED) {
|
||||||
socket = new WebSocket((window.location.protocol === 'https:' ? 'wss://' : 'ws://') +
|
const url = "{{ str_replace(['http://', 'https://'], '', config('app.url')) }}" || window.location.hostname;
|
||||||
"{{ str_replace(['http://', 'https://'], '', config('app.url')) }}" +
|
socket = new WebSocket((window.location.protocol === 'https:' ? 'wss://' : 'ws://') +
|
||||||
':6002/terminal');
|
url +
|
||||||
socket.onmessage = handleSocketMessage;
|
':6002/terminal');
|
||||||
socket.onerror = (e) => {
|
socket.onmessage = handleSocketMessage;
|
||||||
console.error('WebSocket error:', e);
|
socket.onerror = (e) => {
|
||||||
};
|
console.error('WebSocket error:', e);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSocketMessage(event) {
|
|
||||||
// Initialize Terminal
|
|
||||||
if (event.data === 'pty-ready') {
|
|
||||||
term.open(document.getElementById('terminal'));
|
|
||||||
$data.terminalActive = true;
|
|
||||||
term.reset();
|
|
||||||
term.focus();
|
|
||||||
document.querySelector('.xterm-viewport').classList.add('scrollbar', 'rounded')
|
|
||||||
$data.resizeTerminal()
|
|
||||||
} else {
|
|
||||||
pendingWrites++;
|
|
||||||
term.write(event.data, flowControlCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function flowControlCallback() {
|
|
||||||
pendingWrites--;
|
|
||||||
if (pendingWrites > MAX_PENDING_WRITES && !paused) {
|
|
||||||
paused = true;
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
pause: true
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pendingWrites <= MAX_PENDING_WRITES && paused) {
|
|
||||||
paused = false;
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
resume: true
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
term.onData((data) => {
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
message: data
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Type CTRL + D or exit in the terminal
|
|
||||||
if (data === '\x04' || (data === '\r' && stripAnsiCommands(commandBuffer).trim() === 'exit')) {
|
|
||||||
checkIfProcessIsRunningAndKillIt();
|
|
||||||
setTimeout(() => {
|
|
||||||
term.reset();
|
|
||||||
term.write('(connection closed)');
|
|
||||||
$data.terminalActive = false;
|
|
||||||
}, 500);
|
|
||||||
commandBuffer = '';
|
|
||||||
} else if (data === '\r') {
|
|
||||||
commandBuffer = '';
|
|
||||||
} else {
|
|
||||||
commandBuffer += data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function stripAnsiCommands(input) {
|
|
||||||
return input.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy and paste
|
|
||||||
// Enables ctrl + c and ctrl + v
|
|
||||||
// defaults otherwise to ctrl + insert, shift + insert
|
|
||||||
term.attachCustomKeyEventHandler((arg) => {
|
|
||||||
if (arg.ctrlKey && arg.code === "KeyV" && arg.type === "keydown") {
|
|
||||||
navigator.clipboard.readText()
|
|
||||||
.then(text => {
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
message: text
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (arg.ctrlKey && arg.code === "KeyC" && arg.type === "keydown") {
|
|
||||||
const selection = term.getSelection();
|
|
||||||
if (selection) {
|
|
||||||
navigator.clipboard.writeText(selection);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
$wire.on('send-back-command', function(command) {
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
command: command
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('beforeunload', function(e) {
|
|
||||||
checkIfProcessIsRunningAndKillIt();
|
|
||||||
});
|
|
||||||
|
|
||||||
function checkIfProcessIsRunningAndKillIt() {
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
checkActive: 'force'
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.onresize = function() {
|
function handleSocketMessage(event) {
|
||||||
|
// Initialize Terminal
|
||||||
|
if (event.data === 'pty-ready') {
|
||||||
|
term.open(document.getElementById('terminal'));
|
||||||
|
$data.terminalActive = true;
|
||||||
|
term.reset();
|
||||||
|
term.focus();
|
||||||
|
document.querySelector('.xterm-viewport').classList.add('scrollbar', 'rounded')
|
||||||
$data.resizeTerminal()
|
$data.resizeTerminal()
|
||||||
};
|
} else {
|
||||||
|
pendingWrites++;
|
||||||
|
term.write(event.data, flowControlCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Alpine.data('data', () => ({
|
function flowControlCallback() {
|
||||||
fullscreen: false,
|
pendingWrites--;
|
||||||
terminalActive: false,
|
if (pendingWrites > MAX_PENDING_WRITES && !paused) {
|
||||||
init() {
|
paused = true;
|
||||||
this.$watch('terminalActive', (value) => {
|
socket.send(JSON.stringify({
|
||||||
this.$nextTick(() => {
|
pause: true
|
||||||
if (value) {
|
}));
|
||||||
$refs.terminalWrapper.style.display = 'block';
|
return;
|
||||||
this.resizeTerminal();
|
}
|
||||||
} else {
|
if (pendingWrites <= MAX_PENDING_WRITES && paused) {
|
||||||
$refs.terminalWrapper.style.display = 'none';
|
paused = false;
|
||||||
}
|
socket.send(JSON.stringify({
|
||||||
});
|
resume: true
|
||||||
});
|
}));
|
||||||
},
|
return;
|
||||||
makeFullscreen() {
|
}
|
||||||
this.fullscreen = !this.fullscreen;
|
}
|
||||||
$nextTick(() => {
|
|
||||||
this.resizeTerminal()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
resizeTerminal() {
|
term.onData((data) => {
|
||||||
if (!this.terminalActive) return;
|
socket.send(JSON.stringify({
|
||||||
|
message: data
|
||||||
fitAddon.fit();
|
|
||||||
const height = $refs.terminalWrapper.clientHeight;
|
|
||||||
const rows = height / term._core._renderService._charSizeService.height - 1;
|
|
||||||
var termWidth = term.cols;
|
|
||||||
var termHeight = parseInt(rows.toString(), 10);
|
|
||||||
term.resize(termWidth, termHeight);
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
resize: {
|
|
||||||
cols: termWidth,
|
|
||||||
rows: termHeight
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
initializeWebSocket();
|
// Type CTRL + D or exit in the terminal
|
||||||
</script>
|
if (data === '\x04' || (data === '\r' && stripAnsiCommands(commandBuffer).trim() === 'exit')) {
|
||||||
|
checkIfProcessIsRunningAndKillIt();
|
||||||
|
setTimeout(() => {
|
||||||
|
term.reset();
|
||||||
|
term.write('(connection closed)');
|
||||||
|
$data.terminalActive = false;
|
||||||
|
}, 500);
|
||||||
|
commandBuffer = '';
|
||||||
|
} else if (data === '\r') {
|
||||||
|
commandBuffer = '';
|
||||||
|
} else {
|
||||||
|
commandBuffer += data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function stripAnsiCommands(input) {
|
||||||
|
return input.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy and paste
|
||||||
|
// Enables ctrl + c and ctrl + v
|
||||||
|
// defaults otherwise to ctrl + insert, shift + insert
|
||||||
|
term.attachCustomKeyEventHandler((arg) => {
|
||||||
|
if (arg.ctrlKey && arg.code === "KeyV" && arg.type === "keydown") {
|
||||||
|
navigator.clipboard.readText()
|
||||||
|
.then(text => {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
message: text
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
if (arg.ctrlKey && arg.code === "KeyC" && arg.type === "keydown") {
|
||||||
|
const selection = term.getSelection();
|
||||||
|
if (selection) {
|
||||||
|
navigator.clipboard.writeText(selection);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
$wire.on('send-back-command', function(command) {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
command: command
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', function(e) {
|
||||||
|
checkIfProcessIsRunningAndKillIt();
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkIfProcessIsRunningAndKillIt() {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
checkActive: 'force'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onresize = function() {
|
||||||
|
$data.resizeTerminal()
|
||||||
|
};
|
||||||
|
|
||||||
|
Alpine.data('data', () => ({
|
||||||
|
fullscreen: false,
|
||||||
|
terminalActive: false,
|
||||||
|
init() {
|
||||||
|
this.$watch('terminalActive', (value) => {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (value) {
|
||||||
|
$refs.terminalWrapper.style.display = 'block';
|
||||||
|
this.resizeTerminal();
|
||||||
|
} else {
|
||||||
|
$refs.terminalWrapper.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
makeFullscreen() {
|
||||||
|
this.fullscreen = !this.fullscreen;
|
||||||
|
$nextTick(() => {
|
||||||
|
this.resizeTerminal()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
resizeTerminal() {
|
||||||
|
if (!this.terminalActive) return;
|
||||||
|
|
||||||
|
fitAddon.fit();
|
||||||
|
const height = $refs.terminalWrapper.clientHeight;
|
||||||
|
const rows = height / term._core._renderService._charSizeService.height - 1;
|
||||||
|
var termWidth = term.cols;
|
||||||
|
var termHeight = parseInt(rows.toString(), 10);
|
||||||
|
term.resize(termWidth, termHeight);
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
resize: {
|
||||||
|
cols: termWidth,
|
||||||
|
rows: termHeight
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
initializeWebSocket();
|
||||||
|
</script>
|
||||||
@endscript
|
@endscript
|
||||||
</div>
|
</div>
|
||||||
@@ -154,6 +154,12 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Route::get('/command-center', CommandCenterIndex::class)->name('command-center');
|
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::prefix('invitations')->group(function () {
|
||||||
Route::get('/{uuid}', [Controller::class, 'accept_invitation'])->name('team.invitation.accept');
|
Route::get('/{uuid}', [Controller::class, 'accept_invitation'])->name('team.invitation.accept');
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { WebSocketServer } from 'ws';
|
import { WebSocketServer } from 'ws';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import pty from 'node-pty';
|
import pty from 'node-pty';
|
||||||
|
import axios from 'axios';
|
||||||
|
import cookie from 'cookie';
|
||||||
|
import 'dotenv/config'
|
||||||
|
|
||||||
const server = http.createServer((req, res) => {
|
const server = http.createServer((req, res) => {
|
||||||
if (req.url === '/ready') {
|
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();
|
const userSessions = new Map();
|
||||||
|
|
||||||
wss.on('connection', (ws) => {
|
wss.on('connection', (ws) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user