mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-15 19:24:23 +08:00
- 支持 Nacos 连接测试及多命名空间资源树 - 提供配置搜索、发布、删除、历史回滚和变更监听 - 增加 Beta 发布、配置导入导出与批量操作 - 支持服务发现、实例管理、只读保护和多语言界面 - 补充 Wails 接口绑定及前后端回归测试 Refs #756
440 lines
9.1 KiB
TypeScript
440 lines
9.1 KiB
TypeScript
import { t } from '../i18n';
|
|
|
|
type StoredSecretPlaceholderOptions = {
|
|
hasStoredSecret?: boolean;
|
|
emptyPlaceholder: string;
|
|
retainedLabel: string;
|
|
};
|
|
|
|
type ConnectionTestFailureKind =
|
|
| 'validation'
|
|
| 'runtime'
|
|
| 'driver_unavailable'
|
|
| 'secret_blocked';
|
|
|
|
type ConnectionTestFailureFeedback = {
|
|
message: string;
|
|
shouldToast: boolean;
|
|
};
|
|
|
|
export type ConnectionConfigSectionKey =
|
|
| 'identity'
|
|
| 'uri'
|
|
| 'target'
|
|
| 'fileTarget'
|
|
| 'readOnly'
|
|
| 'connectionMode'
|
|
| 'oceanBaseProtocol'
|
|
| 'mongoDiscovery'
|
|
| 'replica'
|
|
| 'service'
|
|
| 'mongoPolicy'
|
|
| 'credentials'
|
|
| 'databaseScope'
|
|
| 'customDriver'
|
|
| 'customDsn'
|
|
| 'jvmRuntime';
|
|
|
|
export type ConnectionConfigLayoutKind =
|
|
| 'mysql-compatible'
|
|
| 'mongodb'
|
|
| 'redis'
|
|
| 'postgres-compatible'
|
|
| 'oracle'
|
|
| 'file'
|
|
| 'search'
|
|
| 'vector'
|
|
| 'timeseries'
|
|
| 'custom'
|
|
| 'jvm'
|
|
| 'nacos'
|
|
| 'generic-sql';
|
|
|
|
export type ConnectionConfigLayout = {
|
|
kind: ConnectionConfigLayoutKind;
|
|
sections: ConnectionConfigSectionKey[];
|
|
};
|
|
|
|
type ConnectionConfigSectionCopy = {
|
|
title: string;
|
|
description: string;
|
|
};
|
|
|
|
const mysqlCompatibleTypes = new Set([
|
|
'mysql',
|
|
'goldendb',
|
|
'mariadb',
|
|
'oceanbase',
|
|
'doris',
|
|
'diros',
|
|
'starrocks',
|
|
'sphinx',
|
|
]);
|
|
const postgresCompatibleTypes = new Set([
|
|
'postgres',
|
|
'kingbase',
|
|
'highgo',
|
|
'vastbase',
|
|
'opengauss',
|
|
'gaussdb',
|
|
]);
|
|
const fileDatabaseTypes = new Set(['sqlite', 'duckdb']);
|
|
|
|
const connectionConfigSectionKeyMap: Record<
|
|
ConnectionConfigSectionKey,
|
|
string
|
|
> = {
|
|
identity: 'identity',
|
|
uri: 'uri',
|
|
target: 'target',
|
|
fileTarget: 'fileTarget',
|
|
readOnly: 'readOnly',
|
|
connectionMode: 'connectionMode',
|
|
oceanBaseProtocol: 'oceanBaseProtocol',
|
|
mongoDiscovery: 'mongoDiscovery',
|
|
replica: 'replica',
|
|
service: 'service',
|
|
mongoPolicy: 'mongoPolicy',
|
|
credentials: 'credentials',
|
|
databaseScope: 'databaseScope',
|
|
customDriver: 'customDriver',
|
|
customDsn: 'customDsn',
|
|
jvmRuntime: 'jvmRuntime',
|
|
};
|
|
|
|
export const getConnectionConfigSectionCopy = (
|
|
key: ConnectionConfigSectionKey,
|
|
): ConnectionConfigSectionCopy => ({
|
|
title: t(`connection.modal.section.${connectionConfigSectionKeyMap[key]}.title`),
|
|
description: t(
|
|
`connection.modal.section.${connectionConfigSectionKeyMap[key]}.description`,
|
|
),
|
|
});
|
|
|
|
export const getConnectionConfigLayoutKindLabel = (
|
|
kind: ConnectionConfigLayoutKind,
|
|
): string => {
|
|
switch (kind) {
|
|
case 'mysql-compatible':
|
|
return t('connection.modal.layoutKind.mysqlCompatible');
|
|
case 'mongodb':
|
|
return t('connection.modal.layoutKind.mongodb');
|
|
case 'redis':
|
|
return t('connection.modal.layoutKind.redis');
|
|
case 'postgres-compatible':
|
|
return t('connection.modal.layoutKind.postgresCompatible');
|
|
case 'oracle':
|
|
return t('connection.modal.layoutKind.oracle');
|
|
case 'file':
|
|
return t('connection.modal.layoutKind.file');
|
|
case 'search':
|
|
return t('connection.modal.layoutKind.search');
|
|
case 'vector':
|
|
return t('connection.modal.layoutKind.vector');
|
|
case 'timeseries':
|
|
return t('connection.modal.layoutKind.timeseries');
|
|
case 'custom':
|
|
return t('connection.modal.layoutKind.custom');
|
|
case 'jvm':
|
|
return t('connection.modal.layoutKind.jvm');
|
|
case 'nacos':
|
|
return t('connection.modal.layoutKind.nacos');
|
|
case 'generic-sql':
|
|
default:
|
|
return t('connection.modal.layoutKind.genericSql');
|
|
}
|
|
};
|
|
|
|
export const resolveConnectionConfigLayout = (
|
|
rawType: string,
|
|
): ConnectionConfigLayout => {
|
|
const type = String(rawType || '').trim().toLowerCase();
|
|
|
|
if (type === 'jvm') {
|
|
return {
|
|
kind: 'jvm',
|
|
sections: ['identity', 'jvmRuntime'],
|
|
};
|
|
}
|
|
if (type === 'custom') {
|
|
return {
|
|
kind: 'custom',
|
|
sections: ['identity', 'customDriver', 'customDsn'],
|
|
};
|
|
}
|
|
if (fileDatabaseTypes.has(type)) {
|
|
return {
|
|
kind: 'file',
|
|
sections: ['identity', 'uri', 'fileTarget'],
|
|
};
|
|
}
|
|
if (mysqlCompatibleTypes.has(type)) {
|
|
return {
|
|
kind: 'mysql-compatible',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'connectionMode',
|
|
'replica',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'mongodb') {
|
|
return {
|
|
kind: 'mongodb',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'connectionMode',
|
|
'mongoDiscovery',
|
|
'replica',
|
|
'mongoPolicy',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'redis') {
|
|
return {
|
|
kind: 'redis',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'connectionMode',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'nacos') {
|
|
return {
|
|
kind: 'nacos',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'credentials',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'elasticsearch') {
|
|
return {
|
|
kind: 'search',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'service',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'chroma' || type === 'qdrant' || type === 'milvus') {
|
|
return {
|
|
kind: 'vector',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'service',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'iotdb') {
|
|
return {
|
|
kind: 'timeseries',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'service',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'mqtt') {
|
|
return {
|
|
kind: 'generic-sql',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'connectionMode',
|
|
'replica',
|
|
'service',
|
|
'credentials',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'rocketmq') {
|
|
return {
|
|
kind: 'generic-sql',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'connectionMode',
|
|
'replica',
|
|
'service',
|
|
'credentials',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'kafka') {
|
|
return {
|
|
kind: 'generic-sql',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'connectionMode',
|
|
'replica',
|
|
'service',
|
|
'credentials',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'rabbitmq') {
|
|
return {
|
|
kind: 'generic-sql',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'service',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'trino') {
|
|
return {
|
|
kind: 'generic-sql',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'service',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (postgresCompatibleTypes.has(type)) {
|
|
return {
|
|
kind: 'postgres-compatible',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'service',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
if (type === 'oracle') {
|
|
return {
|
|
kind: 'oracle',
|
|
sections: [
|
|
'identity',
|
|
'uri',
|
|
'target',
|
|
'service',
|
|
'credentials',
|
|
'databaseScope',
|
|
],
|
|
};
|
|
}
|
|
|
|
return {
|
|
kind: 'generic-sql',
|
|
sections: ['identity', 'uri', 'target', 'credentials', 'databaseScope'],
|
|
};
|
|
};
|
|
|
|
const normalizeText = (value: unknown, fallback = ''): string => {
|
|
const text = String(value ?? '').trim();
|
|
if (!text || text === 'undefined' || text === 'null') {
|
|
return fallback;
|
|
}
|
|
return text;
|
|
};
|
|
|
|
export const getStoredSecretPlaceholder = ({
|
|
hasStoredSecret,
|
|
emptyPlaceholder,
|
|
retainedLabel,
|
|
}: StoredSecretPlaceholderOptions): string => (
|
|
hasStoredSecret
|
|
? t('connection.modal.secret.placeholder.retained', { retainedLabel })
|
|
: emptyPlaceholder
|
|
);
|
|
|
|
export const normalizeConnectionSecretErrorMessage = (
|
|
value: unknown,
|
|
fallback = '',
|
|
): string => {
|
|
const text = normalizeText(value, fallback);
|
|
const lower = text.toLowerCase();
|
|
|
|
if (lower.includes('saved connection not found:')) {
|
|
return t('connection.modal.error.savedConnectionNotFound');
|
|
}
|
|
if (lower.includes('secret store unavailable')) {
|
|
return t('connection.modal.error.secretStoreUnavailable');
|
|
}
|
|
|
|
return text;
|
|
};
|
|
|
|
export const summarizeConnectionTestFailureMessage = (
|
|
value: unknown,
|
|
fallback = '',
|
|
): string => {
|
|
const text = normalizeConnectionSecretErrorMessage(value, fallback);
|
|
const [firstLine] = text
|
|
.split(/\r?\n/)
|
|
.map((item) => item.trim())
|
|
.filter((item) => item !== '');
|
|
return firstLine || text;
|
|
};
|
|
|
|
export const resolveConnectionTestFailureFeedback = ({
|
|
kind,
|
|
reason,
|
|
fallback,
|
|
}: {
|
|
kind: ConnectionTestFailureKind;
|
|
reason: unknown;
|
|
fallback: string;
|
|
}): ConnectionTestFailureFeedback => {
|
|
if (kind === 'validation') {
|
|
return {
|
|
message: t('connection.modal.test.validation'),
|
|
shouldToast: false,
|
|
};
|
|
}
|
|
|
|
return {
|
|
message: t('connection.modal.test.failure', {
|
|
reason: normalizeConnectionSecretErrorMessage(reason, fallback),
|
|
}),
|
|
shouldToast: false,
|
|
};
|
|
};
|
|
|
|
export type {
|
|
ConnectionTestFailureFeedback,
|
|
ConnectionTestFailureKind,
|
|
};
|