feat(Redis Node): Add option to disable TLS verification in Redis node (#19143)

Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
Jason Schell
2025-09-10 19:51:57 +08:00
committed by GitHub
parent 6cd1dbd109
commit 52d44c26db
5 changed files with 356 additions and 142 deletions

View File

@@ -10,16 +10,31 @@ import { createClient } from 'redis';
import type { RedisCredential, RedisClient } from './types';
export function setupRedisClient(credentials: RedisCredential): RedisClient {
export function setupRedisClient(credentials: RedisCredential, isTest = false): RedisClient {
const socketConfig: any = {
host: credentials.host,
port: credentials.port,
tls: credentials.ssl === true,
connectTimeout: 10000,
// Disable reconnection for tests to prevent hanging
reconnectStrategy: isTest ? false : undefined,
};
// If SSL is enabled and TLS verification should be disabled
if (credentials.ssl === true && credentials.disableTlsVerification === true) {
socketConfig.rejectUnauthorized = false;
}
return createClient({
socket: {
host: credentials.host,
port: credentials.port,
tls: credentials.ssl === true,
},
socket: socketConfig,
database: credentials.database,
username: credentials.user || undefined,
password: credentials.password || undefined,
username: credentials.user ?? undefined,
password: credentials.password ?? undefined,
// Disable automatic error retry for tests
...(isTest && {
disableOfflineQueue: true,
enableOfflineQueue: false,
}),
});
}
@@ -28,26 +43,68 @@ export async function redisConnectionTest(
credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> {
const credentials = credential.data as RedisCredential;
let client: RedisClient | undefined;
try {
const client = setupRedisClient(credentials);
await client.connect();
client = setupRedisClient(credentials, true);
// Add error event handler to catch connection errors
const errorPromise = new Promise<never>((_, reject) => {
client!.on('error', (err) => {
reject(err);
});
});
// Create a timeout promise
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('Connection timeout: Unable to connect to Redis server'));
}, 10000); // 10 seconds timeout
});
// Race between connecting and error/timeout
await Promise.race([client.connect(), errorPromise, timeoutPromise]);
await client.ping();
return {
status: 'OK',
message: 'Connection successful!',
};
} catch (error) {
// Handle specific error types for better user feedback
let errorMessage = error.message;
if (error.code === 'ECONNRESET') {
errorMessage =
'Connection reset: The Redis server rejected the connection. This often happens when trying to connect without SSL to an SSL-only server.';
} else if (error.code === 'ECONNREFUSED') {
errorMessage =
'Connection refused: Unable to connect to the Redis server. Please check the host and port.';
}
return {
status: 'Error',
message: error.message,
message: errorMessage,
};
} finally {
// Ensure the Redis client is always closed to prevent leaked connections
if (client) {
try {
await client.quit();
} catch {
// If quit fails, forcefully disconnect
try {
await client.disconnect();
} catch {
// Ignore disconnect errors in cleanup
}
}
}
}
}
/** Parses the given value in a number if it is one else returns a string */
function getParsedValue(value: string): string | number {
if (value.match(/^[\d\.]+$/) === null) {
if (value.match(/^[\d.]+$/) === null) {
// Is a string
return value;
} else {