"use client";
import { useEffect, useMemo, useState } from "react";
import { useParams } from "next/navigation";
import { LandingNav } from "@/components/landing/LandingNav";
import { downloadLinksFor, formatSize, type AgentDownloadCatalog, type DownloadSource, type JreDisplayEntry, type NativeAgentDisplayEntry, type OfflineBundleEntry } from "@/lib/agentRegistry";
import { Archive, Cpu, Database, Download, Plug, Search, Terminal, X } from "lucide-react";
const i18n = {
en: {
title: "Offline Driver Downloads",
subtitle: "Download database drivers and JRE packages for offline use. Search for the exact resource your air-gapped environment needs.",
jdbcPlugin: "JDBC Plugin",
jdbcPluginDesc: "Install this optional DBX sidecar before using custom JDBC connections. Database vendor JDBC driver JARs still need to be imported separately.",
jdbcPluginFile: "Plugin package",
jdbcPluginInstallHint: "Import this ZIP in DBX from Settings > Driver Manager > JDBC Drivers > Local Install.",
bundles: "Offline Bundles",
bundlesDesc: "Platform-specific ZIP packages that include the agent registry, database drivers, native agents, and the matching JRE.",
drivers: "Database Drivers",
driversDesc: "Single-driver .tar.zst packages for Java agents. Install the matching JRE separately when it is not already available.",
nativeAgents: "Native Agents",
nativeAgentsDesc: "Platform-specific .tar.zst packages for DuckDB, Oracle, KingbaseES, XuguDB, and RabbitMQ. Import the package directly in Driver Manager.",
jre: "Java Runtime (JRE)",
jreDesc: "JRE packages used by Java agent-based database drivers such as SQL Server and Dameng.",
download: "Download",
installMethod: "Install",
version: "Version",
size: "Size",
requiresJre: "Requires JRE",
platform: "Platform",
filename: "File",
search: "Search drivers, platforms, versions...",
noResults: "No matching downloads.",
showing: "Showing",
of: "of",
clearSearch: "Clear search",
mirrorHint: "The CNB mirror is available for GitHub release assets on mainland China networks.",
downloadSources: "Download source",
sources: {
github: "GitHub",
cnb: "CNB",
official: "Official",
},
downloadHint: "For air-gapped environments: download the bundle for your platform on an internet-connected machine, then transfer it to the offline machine and import it in DBX from Settings > Driver Manager. Use the driver and JRE tabs only when you need individual artifacts.",
},
cn: {
title: "离线驱动下载",
subtitle: "下载数据库驱动和 JRE 离线包。搜索内网环境需要的资源,在有网机器下载后传输。",
jdbcPlugin: "JDBC 插件",
jdbcPluginDesc: "使用自定义 JDBC 连接前先安装这个 DBX 可选插件。数据库厂商的 JDBC Driver JAR 仍需单独导入。",
jdbcPluginFile: "插件包",
jdbcPluginInstallHint: "在 DBX 的“设置 > 驱动管理 > JDBC 驱动 > 本地安装”中导入这个 ZIP。",
bundles: "整包下载",
bundlesDesc: "按平台提供的 ZIP 离线包,包含 Agent registry、数据库驱动、原生 Agent 和匹配的 JRE。",
drivers: "数据库驱动",
driversDesc: "Java Agent 的单驱动 .tar.zst 包;目标机器尚未安装 JRE 时需要另外安装一次对应 JRE。",
nativeAgents: "原生 Agent",
nativeAgentsDesc: "DuckDB、Oracle、金仓KingbaseES、虚谷和 RabbitMQ 的按平台 .tar.zst 单驱动包,可直接在驱动管理中导入。",
jre: "Java 运行时 (JRE)",
jreDesc: "Java Agent 驱动所需的 JRE 环境,例如 SQL Server、达梦等连接会使用。",
download: "下载",
installMethod: "安装方式",
version: "版本",
size: "大小",
requiresJre: "依赖 JRE",
platform: "平台",
filename: "文件",
search: "搜索驱动、平台、版本...",
noResults: "没有匹配的下载项。",
showing: "显示",
of: "/",
clearSearch: "清空搜索",
mirrorHint: "中国大陆网络可选择 CNB 镜像下载,GitHub Release 资源保持同步。",
downloadSources: "下载来源",
sources: {
github: "GitHub",
cnb: "CNB",
official: "官方下载",
},
downloadHint: "内网环境使用说明:在有网的电脑上下载对应平台的整包,然后传输到内网机器,在 DBX 的“设置 > 驱动管理”中导入。只有需要单个产物时再使用驱动和 JRE 标签页。",
},
};
type ActiveTab = "bundles" | "drivers" | "native" | "jre" | "jdbcPlugin";
function platformKey(j: JreDisplayEntry): string {
return `${j.jreKey}-${j.platformKey}`;
}
function bundleKey(bundle: OfflineBundleEntry): string {
return `${bundle.platformKey}-${bundle.filename}`;
}
function nativeKey(agent: NativeAgentDisplayEntry): string {
return `${agent.key}-${agent.platformKey}`;
}
type NativeAgentGroup = {
key: string;
label: string;
version: string;
options: NativeAgentDisplayEntry[];
};
type DriverTranslations = (typeof i18n)["en"];
function DownloadLinks({ url, t }: { url: string; t: DriverTranslations }) {
return (
);
}
function matchesSearch(values: Array, query: string): boolean {
if (!query) return true;
return values.filter(Boolean).join(" ").toLowerCase().includes(query);
}
export function DriversClient({ initialCatalog }: { initialCatalog: AgentDownloadCatalog }) {
const params = useParams();
const rawLang = params?.lang as string | undefined;
const lang: "en" | "cn" = rawLang === "cn" ? "cn" : "en";
const t = i18n[lang];
const catalog = initialCatalog;
const [activeTab, setActiveTab] = useState("bundles");
const [searchQuery, setSearchQuery] = useState("");
const [selectedNativePlatforms, setSelectedNativePlatforms] = useState>({});
const bundles = catalog?.bundles ?? [];
const drivers = catalog?.drivers ?? [];
const nativeAgents = catalog?.nativeAgents ?? [];
const jres = catalog?.jres ?? [];
const jdbcPlugin = catalog?.jdbcPlugin;
const normalizedSearch = searchQuery.trim().toLowerCase();
const filteredBundles = useMemo(() => bundles.filter((bundle) => matchesSearch([bundle.platformLabel, bundle.platformKey, bundle.filename, formatSize(bundle.size)], normalizedSearch)), [bundles, normalizedSearch]);
const filteredDrivers = useMemo(() => drivers.filter((d) => matchesSearch([d.label, d.key, d.version, d.jre, formatSize(d.jar.size)], normalizedSearch)), [drivers, normalizedSearch]);
const nativeGroups = useMemo(() => {
const groups = new Map();
for (const agent of nativeAgents) {
const group = groups.get(agent.key);
if (group) {
group.options.push(agent);
} else {
groups.set(agent.key, { key: agent.key, label: agent.label, version: agent.version, options: [agent] });
}
}
return Array.from(groups.values());
}, [nativeAgents]);
useEffect(() => {
if (nativeGroups.length === 0) return;
setSelectedNativePlatforms((current) => {
const next = { ...current };
let changed = false;
for (const group of nativeGroups) {
if (group.options.length === 0) continue;
if (!next[group.key] || !group.options.some((option) => option.platformKey === next[group.key])) {
next[group.key] = group.options[0].platformKey;
changed = true;
}
}
return changed ? next : current;
});
}, [nativeGroups]);
const filteredNativeGroups = useMemo(
() =>
nativeGroups.filter((group) =>
matchesSearch(
[
group.label,
group.key,
group.version,
...group.options.flatMap((option) => [option.platformLabel, option.platformKey, option.filename, formatSize(option.info.size)]),
],
normalizedSearch,
),
),
[nativeGroups, normalizedSearch],
);
const filteredJres = useMemo(() => jres.filter((j) => matchesSearch([j.platformLabel, j.platformKey, j.jreVersion, j.jreKey, formatSize(j.info.size)], normalizedSearch)), [jres, normalizedSearch]);
const filteredJdbcPlugin = useMemo(() => (jdbcPlugin && matchesSearch([jdbcPlugin.label, jdbcPlugin.filename, jdbcPlugin.url, t.jdbcPlugin, t.jdbcPluginDesc], normalizedSearch) ? [jdbcPlugin] : []), [jdbcPlugin, normalizedSearch, t.jdbcPlugin, t.jdbcPluginDesc]);
const activeCount = activeTab === "bundles" ? filteredBundles.length : activeTab === "drivers" ? filteredDrivers.length : activeTab === "native" ? filteredNativeGroups.length : activeTab === "jre" ? filteredJres.length : filteredJdbcPlugin.length;
const activeTotal = activeTab === "bundles" ? bundles.length : activeTab === "drivers" ? drivers.length : activeTab === "native" ? nativeGroups.length : activeTab === "jre" ? jres.length : jdbcPlugin ? 1 : 0;
return (
{catalog && (
<>
{t.mirrorHint}
{activeTab === "jdbcPlugin" && (
<>
{t.jdbcPluginDesc}
| {t.jdbcPluginFile} |
{t.filename} |
{t.installMethod} |
{t.downloadSources} |
{filteredJdbcPlugin.map((plugin) => (
|
{plugin.label}
ZIP
{t.jdbcPluginInstallHint}
|
{plugin.filename} |
{t.jdbcPluginInstallHint} |
|
))}
{filteredJdbcPlugin.length === 0 &&
{t.noResults}
}
>
)}
{activeTab === "bundles" && (
<>
{t.bundlesDesc}
| {t.platform} |
{t.filename} |
{t.size} |
{t.downloadSources} |
{filteredBundles.map((bundle) => (
|
{bundle.platformLabel}
ZIP
|
{bundle.filename} |
{formatSize(bundle.size)} |
|
))}
{filteredBundles.length === 0 &&
{t.noResults}
}
>
)}
{activeTab === "drivers" && (
<>
{t.driversDesc}
| Driver |
Key |
{t.version} |
{t.requiresJre} |
{t.size} |
{t.downloadSources} |
{filteredDrivers.map((d) => (
|
{d.label}
{d.key}
|
{d.key}
|
{d.version} |
{d.jre} |
{formatSize(d.jar.size)} |
|
))}
{filteredDrivers.length === 0 &&
{t.noResults}
}
>
)}
{activeTab === "native" && (
<>
{t.nativeAgentsDesc}
| Agent |
{t.platform} |
{t.version} |
{t.size} |
{t.downloadSources} |
{filteredNativeGroups.map((group) => {
const selectedPlatform = selectedNativePlatforms[group.key] ?? group.options[0]?.platformKey;
const selectedAgent = group.options.find((option) => option.platformKey === selectedPlatform) ?? group.options[0];
if (!selectedAgent) return null;
return (
|
{group.label}
{selectedAgent.platformKey}
|
|
{group.version} |
{formatSize(selectedAgent.info.size)} |
|
);
})}
{filteredNativeGroups.length === 0 &&
{t.noResults}
}
>
)}
{activeTab === "jre" && (
<>
{t.jreDesc}
| {t.platform} |
JRE |
{t.version} |
{t.size} |
{t.downloadSources} |
{filteredJres.map((j) => {
const key = platformKey(j);
return (
|
{j.platformLabel}
JRE {j.jreKey}
|
JRE {j.jreKey}
|
{j.jreVersion} |
{formatSize(j.info.size)} |
|
);
})}
{filteredJres.length === 0 &&
{t.noResults}
}
>
)}
{lang === "cn" ? "离线使用说明" : "Offline Usage"}
{t.downloadHint}
>
)}
);
}