mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
Traffic analytics: - Reject enabling unless the server runs a Coolify-managed Traefik or Caddy proxy, and show the reason in the settings UI - Save the proxy configuration before the setting so a failure leaves both unchanged; allow disabling after the proxy was removed - Skip the proxy restart when the proxy is stopped and report that the config applies on next start - Preserve the user's own Traefik --accesslog* flags and restore them when analytics is disabled - Rotate the Traefik access log in the sidecar with BusyBox tools (copytruncate, 5 gzip rotations) - Create the access log before starting Sentinel, which opens it once Terminal: - Detect and surface WebSocket connection rejections in the browser terminal, with shared helpers in terminal-connection.js and terminal-utils.js Database import: - Restore PostgreSQL backups in a single transaction; SQL replace restores go into a temporary database and swap in only on success, leaving the current database untouched on failure
90 lines
3.7 KiB
JavaScript
90 lines
3.7 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
TERMINAL_CLOSE_CODES,
|
|
TERMINAL_CONNECT_TIMEOUT_MS,
|
|
TERMINAL_CONNECTION_ERRORS,
|
|
TERMINAL_SESSION_START_TIMEOUT_MS,
|
|
classifyTerminalServerMessage,
|
|
isTerminalAuthRejectionCode,
|
|
resolveTerminalCloseOutcome,
|
|
} from './terminal-connection.js';
|
|
|
|
const closeOutcome = (overrides = {}) => resolveTerminalCloseOutcome({
|
|
code: 1006,
|
|
sessionPending: false,
|
|
reconnectAttempts: 0,
|
|
maxReconnectAttempts: 10,
|
|
...overrides,
|
|
});
|
|
|
|
test('terminal close codes match the codes sent by the terminal server', () => {
|
|
assert.equal(TERMINAL_CLOSE_CODES.AUTH_REJECTED, 4401);
|
|
assert.equal(TERMINAL_CLOSE_CODES.TOKEN_REJECTED, 4403);
|
|
assert.equal(isTerminalAuthRejectionCode(4401), true);
|
|
assert.equal(isTerminalAuthRejectionCode(4403), true);
|
|
assert.equal(isTerminalAuthRejectionCode(1006), false);
|
|
assert.equal(isTerminalAuthRejectionCode(4000), false);
|
|
});
|
|
|
|
test('auth rejection closes stop reconnecting and show the reload message', () => {
|
|
for (const code of [4401, 4403]) {
|
|
for (const sessionPending of [true, false]) {
|
|
assert.deepEqual(closeOutcome({ code, sessionPending }), {
|
|
authRejected: true,
|
|
reconnect: false,
|
|
error: 'Terminal access was rejected. Reload the page and try again.',
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
test('a clean close neither reconnects nor reports an error', () => {
|
|
assert.deepEqual(closeOutcome({ code: 1000, sessionPending: true }), {
|
|
authRejected: false,
|
|
reconnect: false,
|
|
error: null,
|
|
});
|
|
});
|
|
|
|
test('a close before the session is ready leaves the connecting state with a generic error', () => {
|
|
for (const code of [1006, 1011, 1001, 4000]) {
|
|
assert.deepEqual(closeOutcome({ code, sessionPending: true }), {
|
|
authRejected: false,
|
|
reconnect: true,
|
|
error: TERMINAL_CONNECTION_ERRORS.connectionFailed,
|
|
});
|
|
}
|
|
});
|
|
|
|
test('an idle close reconnects silently until the retry limit, then reports an error', () => {
|
|
assert.deepEqual(closeOutcome({ reconnectAttempts: 9 }), {
|
|
authRejected: false,
|
|
reconnect: true,
|
|
error: null,
|
|
});
|
|
assert.deepEqual(closeOutcome({ reconnectAttempts: 10 }), {
|
|
authRejected: false,
|
|
reconnect: false,
|
|
error: TERMINAL_CONNECTION_ERRORS.connectionFailed,
|
|
});
|
|
});
|
|
|
|
test('server text messages are classified for backward compatible rejection handling', () => {
|
|
assert.equal(classifyTerminalServerMessage('Unauthorized: Invalid terminal token'), 'auth-rejected');
|
|
assert.equal(classifyTerminalServerMessage('Unauthorized: Terminal token was rejected'), 'auth-rejected');
|
|
assert.equal(classifyTerminalServerMessage('Unauthorized: Missing required tokens'), 'auth-rejected');
|
|
assert.equal(classifyTerminalServerMessage('Unauthorized: Target host 10.0.0.5 not in authorized list'), 'startup-rejected');
|
|
assert.equal(classifyTerminalServerMessage('Invalid SSH command: No target host found'), 'startup-rejected');
|
|
assert.equal(classifyTerminalServerMessage('pty-ready'), null);
|
|
assert.equal(classifyTerminalServerMessage('root@host:~# '), null);
|
|
assert.equal(classifyTerminalServerMessage(new Blob([])), null);
|
|
});
|
|
|
|
test('connection timeouts stay within a bounded window', () => {
|
|
assert.equal(TERMINAL_CONNECT_TIMEOUT_MS, 15000);
|
|
assert.ok(TERMINAL_SESSION_START_TIMEOUT_MS >= TERMINAL_CONNECT_TIMEOUT_MS);
|
|
assert.ok(TERMINAL_SESSION_START_TIMEOUT_MS <= 30000);
|
|
assert.equal(TERMINAL_CONNECTION_ERRORS.timeout, 'Timed out while connecting to the terminal. Reload the page and try again.');
|
|
});
|