Files
dbx/apps/desktop/src/lib/connectionDeepLink.ts
T
yavon007 13424588b9 Support DBX connection deep links (#359)
* fix(mysql): avoid metadata collation conflicts

Split MySQL column and primary key metadata lookups so MariaDB and OceanBase do not join information_schema tables with incompatible collations.

* feat(grid): add invert column visibility action

* feat(grid): improve cell detail editing

* feat(grid): add keyboard cell navigation

* feat(grid): add column name copy in header tooltip

* feat(grid): improve transpose navigation

* feat(connection): support dbx connection deep links

* docs: document connection deep links

* fix(connection): harden deep link boolean handling
2026-05-20 16:10:14 +08:00

110 lines
3.6 KiB
TypeScript

import type { DatabaseType } from "@/types/database";
import { connectionProfileForScheme, parseConnectionUrl } from "@/lib/connectionUrl";
export interface ConnectionDeepLinkDraft {
name?: string;
dbType: DatabaseType;
driverProfile: string;
driverLabel: string;
host?: string;
port?: number;
username?: string;
password?: string;
database?: string;
urlParams?: string;
ssl?: boolean;
connectionString?: string;
oracleConnectionType?: "service_name" | "sid";
useMongoUrl?: boolean;
oneTime?: boolean;
}
const CONNECTION_DEEP_LINK_TARGET = "connection/new";
function normalizePath(url: URL): string {
return [url.hostname, url.pathname.replace(/^\/+/, "")].filter(Boolean).join("/").replace(/\/+$/, "");
}
function optionalParam(params: URLSearchParams, ...keys: string[]): string | undefined {
for (const key of keys) {
const value = params.get(key)?.trim();
if (value) return value;
}
return undefined;
}
function optionalNumberParam(params: URLSearchParams, ...keys: string[]): number | undefined {
const value = optionalParam(params, ...keys);
if (!value) return undefined;
const numberValue = Number(value);
return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : undefined;
}
function optionalBooleanParam(params: URLSearchParams, ...keys: string[]): boolean {
const value = optionalParam(params, ...keys)?.toLowerCase();
return value === "true" || value === "1" || value === "yes" || value === "on";
}
function draftFromConnectionUrl(value: string, preferredProfile?: string): ConnectionDeepLinkDraft {
const parsed = parseConnectionUrl(value, preferredProfile);
return {
dbType: parsed.dbType,
driverProfile: parsed.driverProfile,
driverLabel: parsed.driverLabel,
host: parsed.host,
port: parsed.port,
username: parsed.username,
password: parsed.password,
database: parsed.database,
urlParams: parsed.urlParams,
ssl: parsed.ssl,
connectionString: parsed.connectionString,
oracleConnectionType: parsed.oracleConnectionType,
useMongoUrl: parsed.useMongoUrl,
};
}
export function parseConnectionDeepLink(value: string): ConnectionDeepLinkDraft | null {
let url: URL;
try {
url = new URL(value);
} catch {
return null;
}
if (url.protocol !== "dbx:") return null;
if (normalizePath(url) !== CONNECTION_DEEP_LINK_TARGET) return null;
const params = url.searchParams;
const preferredProfile = optionalParam(params, "type");
const rawConnectionUrl = optionalParam(params, "url");
const draft: ConnectionDeepLinkDraft = rawConnectionUrl
? draftFromConnectionUrl(rawConnectionUrl, preferredProfile)
: (() => {
const profile = connectionProfileForScheme(preferredProfile || "mysql");
if (!profile) throw new Error(`Unsupported connection type: ${preferredProfile}`);
return {
dbType: profile.type,
driverProfile: profile.profile,
driverLabel: profile.label,
port: profile.defaultPort,
ssl: false,
};
})();
const oneTime = optionalBooleanParam(params, "one_time");
return {
...draft,
name: optionalParam(params, "name") ?? draft.name,
host: optionalParam(params, "host") ?? draft.host,
port: optionalNumberParam(params, "port") ?? draft.port,
username: optionalParam(params, "user") ?? draft.username,
password: optionalParam(params, "password") ?? draft.password,
database: optionalParam(params, "database") ?? draft.database,
urlParams: optionalParam(params, "url_params") ?? draft.urlParams,
ssl: optionalBooleanParam(params, "ssl") ? true : draft.ssl,
...(oneTime ? { oneTime: true } : {}),
};
}