feat(oracle): support service name and SID connections

This commit is contained in:
t8y2
2026-05-18 21:04:37 +08:00
parent df87ec994e
commit 008656806e
14 changed files with 187 additions and 0 deletions
@@ -77,6 +77,7 @@ const defaultForm = (): Omit<ConnectionConfig, "id"> => ({
proxy_username: "",
proxy_password: "",
ssl: false,
oracle_connection_type: "service_name",
connection_string: undefined,
jdbc_driver_class: undefined,
jdbc_driver_paths: [],
@@ -297,6 +298,7 @@ watch(
proxy_username: config.proxy_username || "",
proxy_password: config.proxy_password || "",
ssl: config.ssl || false,
oracle_connection_type: config.oracle_connection_type || "service_name",
connection_string: config.connection_string,
jdbc_driver_class: config.jdbc_driver_class,
jdbc_driver_paths: config.jdbc_driver_paths || [],
@@ -547,6 +549,11 @@ function connectionConfigForSubmit(id: string): ConnectionConfig {
if (config.db_type === "mongodb" && !mongoUseUrl.value) {
config.connection_string = undefined;
}
if (config.db_type !== "oracle") {
config.oracle_connection_type = undefined;
} else {
config.oracle_connection_type = config.oracle_connection_type || "service_name";
}
if (config.db_type === "jdbc") {
config.host = "";
config.port = 0;
@@ -1189,6 +1196,40 @@ function openExternalUrl(url: string) {
<Input v-model="form.database" class="col-span-3" :placeholder="databasePlaceholder" />
</div>
<div v-if="form.db_type === 'oracle'" class="grid grid-cols-4 items-center gap-4">
<Label class="text-right text-xs">连接方式</Label>
<div
class="col-span-3 grid h-8 grid-cols-2 overflow-hidden rounded-md border border-input bg-muted/30 p-0.5"
>
<button
type="button"
class="h-7 rounded-sm px-3 text-sm transition-colors"
:class="
form.oracle_connection_type !== 'sid'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
"
:aria-pressed="form.oracle_connection_type !== 'sid'"
@click="form.oracle_connection_type = 'service_name'"
>
服务名
</button>
<button
type="button"
class="h-7 rounded-sm px-3 text-sm transition-colors"
:class="
form.oracle_connection_type === 'sid'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
"
:aria-pressed="form.oracle_connection_type === 'sid'"
@click="form.oracle_connection_type = 'sid'"
>
SID
</button>
</div>
</div>
<div v-if="shouldShowAgentDriverInstallHint" class="grid grid-cols-4 items-center gap-4">
<span />
<p class="col-span-3 text-xs text-muted-foreground">
+44
View File
@@ -12,6 +12,7 @@ export interface ParsedConnectionUrl {
urlParams: string;
ssl: boolean;
connectionString?: string;
oracleConnectionType?: "service_name" | "sid";
useMongoUrl?: boolean;
}
@@ -108,6 +109,46 @@ function parseJdbcSqlServerUrl(source: string): ParsedConnectionUrl | null {
};
}
function parseJdbcOracleUrl(source: string): ParsedConnectionUrl | null {
const serviceMatch = source.match(/^jdbc:oracle:thin:@\/\/([^:/?#]+)(?::(\d+))?\/([^?]+)(?:\?(.*))?$/i);
if (serviceMatch) {
const profile = SCHEME_PROFILES.oracle;
return {
dbType: profile.type,
driverProfile: profile.profile,
driverLabel: profile.label,
host: serviceMatch[1],
port: serviceMatch[2] ? Number(serviceMatch[2]) : profile.defaultPort,
username: "",
password: "",
database: decodeUrlPart(serviceMatch[3]),
urlParams: serviceMatch[4] || "",
ssl: false,
oracleConnectionType: "service_name",
};
}
const sidMatch = source.match(/^jdbc:oracle:thin:@([^:/?#]+)(?::(\d+))?:([^?]+)(?:\?(.*))?$/i);
if (sidMatch) {
const profile = SCHEME_PROFILES.oracle;
return {
dbType: profile.type,
driverProfile: profile.profile,
driverLabel: profile.label,
host: sidMatch[1],
port: sidMatch[2] ? Number(sidMatch[2]) : profile.defaultPort,
username: "",
password: "",
database: decodeUrlPart(sidMatch[3]),
urlParams: sidMatch[4] || "",
ssl: false,
oracleConnectionType: "sid",
};
}
return null;
}
function parseJdbcUCanAccessUrl(source: string): ParsedConnectionUrl | null {
const match = source.match(/^jdbc:ucanaccess:\/\/(.+?)(?:;.*)?$/i);
if (!match) return null;
@@ -138,6 +179,8 @@ export function parseConnectionUrl(value: string, preferredProfile?: string): Pa
}
const jdbcUCanAccess = parseJdbcUCanAccessUrl(input);
if (jdbcUCanAccess) return jdbcUCanAccess;
const jdbcOracle = parseJdbcOracleUrl(input);
if (jdbcOracle) return jdbcOracle;
const jdbcSqlServer = parseJdbcSqlServerUrl(input);
if (jdbcSqlServer) return jdbcSqlServer;
const source = input.replace(/^jdbc:/i, "");
@@ -204,5 +247,6 @@ export function applyParsedConnectionUrl(
url_params: parsed.urlParams,
ssl: parsed.ssl,
connection_string: parsed.connectionString,
oracle_connection_type: parsed.oracleConnectionType,
};
}
+4
View File
@@ -119,6 +119,7 @@ function parseJdbcUrl(url: string, profile: ConnectionProfile) {
params?: string;
username?: string;
password?: string;
oracleConnectionType?: "service_name" | "sid";
} = {};
const source = url.trim();
if (!source) return result;
@@ -143,6 +144,7 @@ function parseJdbcUrl(url: string, profile: ConnectionProfile) {
result.host = oracleServiceMatch[1];
result.port = getNumber(oracleServiceMatch[2]);
result.database = oracleServiceMatch[3];
result.oracleConnectionType = "service_name";
return result;
}
@@ -151,6 +153,7 @@ function parseJdbcUrl(url: string, profile: ConnectionProfile) {
result.host = oracleSidMatch[1];
result.port = getNumber(oracleSidMatch[2]);
result.database = oracleSidMatch[3];
result.oracleConnectionType = "sid";
return result;
}
@@ -228,6 +231,7 @@ function buildConnection(
ssh_expose_lan: false,
ssh_connect_timeout_secs: 5,
ssl: false,
oracle_connection_type: profile.dbType === "oracle" ? parsedUrl.oracleConnectionType || "service_name" : undefined,
connection_string: profile.dbType === "jdbc" || profile.dbType === "mongodb" ? url || undefined : undefined,
jdbc_driver_class:
profile.dbType === "jdbc" ? getString(config["driver-class"] || entry.driver) || undefined : undefined,
+7
View File
@@ -134,6 +134,12 @@ async function parseConnection(node: ParsedNode): Promise<ConnectionConfig | nul
(profile.dbType === "sqlite" ? "" : "127.0.0.1");
const portValue = Number(getAny(node.values, ["port", "serverPort"]));
const database = getAny(node.values, ["database", "databaseName", "initialDatabase", "serviceName", "sid", "schema"]);
const oracleConnectionType =
profile.dbType === "oracle" && getAny(node.values, ["sid"])
? "sid"
: profile.dbType === "oracle"
? "service_name"
: undefined;
const username = getAny(node.values, ["user", "username", "userName", "uid"]) || profile.user;
const password = await decryptNavicatPassword(getAny(node.values, ["password"]));
@@ -159,6 +165,7 @@ async function parseConnection(node: ParsedNode): Promise<ConnectionConfig | nul
ssh_expose_lan: false,
ssh_connect_timeout_secs: 5,
ssl: false,
oracle_connection_type: oracleConnectionType,
connection_string: undefined,
jdbc_driver_class: undefined,
jdbc_driver_paths: [],
+1
View File
@@ -66,6 +66,7 @@ export interface ConnectionConfig {
proxy_password?: string;
ssl?: boolean;
sysdba?: boolean;
oracle_connection_type?: "service_name" | "sid";
connection_string?: string;
jdbc_driver_class?: string;
jdbc_driver_paths?: string[];