diff --git a/packages/web/src/settings/ConnectionDriverFields.svelte b/packages/web/src/settings/ConnectionDriverFields.svelte
index 8830861d3..32076ef62 100644
--- a/packages/web/src/settings/ConnectionDriverFields.svelte
+++ b/packages/web/src/settings/ConnectionDriverFields.svelte
@@ -19,6 +19,7 @@
import { getConnectionLabel } from 'dbgate-tools';
import { _t } from '../translations';
import FormFileInputField from '../forms/FormFileInputField.svelte';
+ import FormClusterNodesField from '../forms/FormClusterNodesField.svelte';
export let getDatabaseList;
export let currentConnection;
@@ -122,6 +123,24 @@
{/key}
{/if}
+{#if driver?.showConnectionField('clusterNodes', $values, showConnectionFieldArgs)}
+
+{/if}
+
+{#if driver?.showConnectionField('autoDetectNatMap', $values, showConnectionFieldArgs)}
+
+{/if}
+
{#if driver?.showConnectionField('databaseFile', $values, showConnectionFieldArgs)}
{#if electron && !driver?.dialect?.useServerDatabaseFile}
{
+ const single = new Redis({
+ host: typeof seed === 'string' ? new URL(seed).hostname : seed.host,
+ port: typeof seed === 'string' ? Number(new URL(seed).port || 6379) : seed.port,
+ ...redisOptions,
+ // Make these connections quick and disposable
+ lazyConnect: false,
+ enableReadyCheck: false,
+ maxRetriesPerRequest: 0,
+ });
+
+ try {
+ const nodes = await single.cluster('nodes'); // text blob
+ const line = nodes.split(/\r?\n/).find((l) => /\bmyself\b/.test(l));
+ if (!line) return;
+
+ // Example addr token: "172.18.0.3:6379@16379" or "redis-node-0:6379@16379"
+ const addrToken = line.split(' ')[1];
+ const hostPort = addrToken.split('@')[0]; // strip bus port
+ const [advHost, advPortStr] = hostPort.split(':');
+ const advKey = `${advHost}:${Number(advPortStr)}`;
+
+ const extHost = typeof seed === 'string' ? new URL(seed).hostname : seed.host;
+ const extPort = typeof seed === 'string' ? Number(new URL(seed).port || 6379) : seed.port;
+
+ natMap[advKey] = { host: extHost, port: extPort };
+ } catch {
+ // ignore this seed if it fails; others may still succeed
+ } finally {
+ single.disconnect();
+ }
+ })
+ );
+
+ return natMap;
+}
+
/** @type {import('dbgate-types').EngineDriver} */
const driver = {
...driverBase,
@@ -92,6 +135,9 @@ const driver = {
treeKeySeparator,
ssl,
skipSetName,
+ authType,
+ clusterNodes,
+ autoDetectNatMap,
}) {
let db = 0;
let client;
@@ -100,6 +146,26 @@ const driver = {
if (!skipSetName) {
await client.client('SETNAME', 'dbgate');
}
+ } else if (authType === 'cluster' && isProApp && isProApp()) {
+ const redisOptions = {
+ user,
+ password,
+ };
+
+ let seeds = [];
+ try {
+ seeds = JSON.parse(clusterNodes);
+ } catch {}
+ if (!Array.isArray(seeds) || seeds.length === 0) {
+ throw new Error('Cluster nodes must be a non-empty array of host:port or objects with host and port');
+ }
+
+ const natMap = autoDetectNatMap ? await buildNatMapFromSeeds(seeds, redisOptions) : undefined;
+
+ client = new Redis.Cluster(seeds, {
+ redisOptions,
+ natMap,
+ });
} else {
if (_.isString(database) && database.startsWith('db')) db = parseInt(database.substring(2));
if (_.isNumber(database)) db = database;
@@ -487,6 +553,20 @@ const driver = {
async close(dbhan) {
return dbhan.client.quit();
},
+
+ getAuthTypes() {
+ if (isProApp && isProApp()) {
+ return [
+ { name: 'node', title: 'Single Redis node' },
+ { name: 'cluster', title: 'Redis Cluster' },
+ ];
+ }
+ return null;
+ },
+};
+
+driver.initialize = (dbgateEnv) => {
+ isProApp = dbgateEnv.isProApp;
};
module.exports = driver;
diff --git a/plugins/dbgate-plugin-redis/src/backend/index.js b/plugins/dbgate-plugin-redis/src/backend/index.js
index cdad41316..100b1962a 100644
--- a/plugins/dbgate-plugin-redis/src/backend/index.js
+++ b/plugins/dbgate-plugin-redis/src/backend/index.js
@@ -3,4 +3,7 @@ const driver = require('./driver');
module.exports = {
packageName: 'dbgate-plugin-redis',
drivers: [driver],
+ initialize(dbgateEnv) {
+ driver.initialize(dbgateEnv);
+ },
};
diff --git a/plugins/dbgate-plugin-redis/src/frontend/driver.js b/plugins/dbgate-plugin-redis/src/frontend/driver.js
index d440ad2e7..3182cbc25 100644
--- a/plugins/dbgate-plugin-redis/src/frontend/driver.js
+++ b/plugins/dbgate-plugin-redis/src/frontend/driver.js
@@ -23,10 +23,13 @@ const driver = {
title: 'Redis',
defaultPort: 6379,
editorMode: 'text',
+ authTypeFirst: true,
databaseEngineTypes: ['keyvalue'],
supportedCreateDatabase: false,
getQuerySplitterOptions: () => redisSplitterOptions,
databaseUrlPlaceholder: 'e.g. redis://:authpassword@127.0.0.1:6380/4',
+ authTypeLabel: 'Connection mode',
+ defaultAuthTypeName: 'node',
supportedKeyTypes: [
{
name: 'string',
@@ -76,10 +79,14 @@ const driver = {
],
showConnectionField: (field, values) => {
- if (field == 'useDatabaseUrl') return true;
+ if (field == 'useDatabaseUrl') return values.authType != 'cluster';
+ if (field == 'authType') return !values.useDatabaseUrl;
if (values.useDatabaseUrl) {
return ['databaseUrl', 'isReadOnly', 'treeKeySeparator'].includes(field);
}
+ if (values.authType == 'cluster') {
+ return ['user', 'password', 'isReadOnly', 'treeKeySeparator', 'clusterNodes', 'autoDetectNatMap'].includes(field);
+ }
return ['server', 'port', 'user', 'password', 'isReadOnly', 'treeKeySeparator'].includes(field);
},