+
+ {t("provider.badge.unsupported")}
+
{t("access.props.provider.builtin")}
diff --git a/ui/src/components/provider/AccessProviderSelect.tsx b/ui/src/components/provider/AccessProviderSelect.tsx
index 934015ab9..b84bb02a1 100644
--- a/ui/src/components/provider/AccessProviderSelect.tsx
+++ b/ui/src/components/provider/AccessProviderSelect.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
import { Avatar, Select, Tag, Typography, theme } from "antd";
import Show from "@/components/Show";
-import { ACCESS_USAGES, type AccessProvider, type AccessUsageType, accessProvidersMap } from "@/domain/provider";
+import { ACCESS_PROVIDERS, ACCESS_USAGES, type AccessProvider, type AccessUsageType, accessProvidersMap } from "@/domain/provider";
import { type SharedSelectProps } from "./_shared";
@@ -45,22 +45,30 @@ const AccessProviderSelect = ({ showOptionTags, onFilter, ...props }: AccessProv
key: provider.type,
value: provider.type,
label: t(provider.name),
- disabled: provider.builtin,
+ disabled: provider.builtin || provider.disabled,
data: provider,
}));
}, [onFilter]);
const renderOption = (key: string) => {
const provider = accessProvidersMap.get(key) ?? ({ type: "", name: "", icon: "", usages: [] } as unknown as AccessProvider);
+ const showUnsupportedBadge = provider.type === ACCESS_PROVIDERS.DOCKERHOST && provider.disabled;
return (
+
+ {t("provider.badge.unsupported")}
+
{t("access.props.provider.builtin")}
diff --git a/ui/src/components/provider/_shared.ts b/ui/src/components/provider/_shared.ts
index ec72f09c4..876aceb76 100644
--- a/ui/src/components/provider/_shared.ts
+++ b/ui/src/components/provider/_shared.ts
@@ -46,6 +46,7 @@ export const useSelectDataSource =
({
return accesses.some((access) => {
if ("builtin" in provider && provider.builtin) return true;
if ("provider" in provider) return access.provider === provider.provider;
+ if ("disabled" in provider && provider.disabled) return false;
return access.provider === provider.type;
});
});
@@ -133,6 +134,7 @@ export const usePickerDataSource = ({
return accesses.some((access) => {
if ("builtin" in provider && provider.builtin) return true;
if ("provider" in provider) return access.provider === provider.provider;
+ if ("disabled" in provider && provider.disabled) return false;
return access.provider === provider.type;
});
});
diff --git a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx
index 9c99cf64c..3cff225ae 100644
--- a/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx
+++ b/ui/src/components/workflow/designer/forms/BizApplyNodeConfigForm.tsx
@@ -128,6 +128,9 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
case CHALLENGE_TYPE_HTTP01:
switch (fieldProvider) {
+ case ACME_HTTP01_PROVIDERS.DOCKERHOST: {
+ return BizApplyNodeConfigFieldsProviderSSH;
+ }
case ACME_HTTP01_PROVIDERS.LOCAL: {
return BizApplyNodeConfigFieldsProviderLocal;
}
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index 50f716634..bbd1a389e 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -43,6 +43,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
DIGITALOCEAN: "digitalocean",
DINGTALKBOT: "dingtalkbot",
DISCORDBOT: "discordbot",
+ DOCKERHOST: "dockerhost",
DNSLA: "dnsla",
DOGECLOUD: "dogecloud",
DUCKDNS: "duckdns",
@@ -118,6 +119,7 @@ export type AccessUsageType = (typeof ACCESS_USAGES)[keyof typeof ACCESS_USAGES]
export interface AccessProvider extends BaseProvider {
usages: AccessUsageType[];
+ disabled?: boolean;
}
export const accessProvidersMap: Map = new Map(
@@ -129,6 +131,7 @@ export const accessProvidersMap: Map
diff --git a/ui/src/domain/system.ts b/ui/src/domain/system.ts
new file mode 100644
index 000000000..4486b47fc
--- /dev/null
+++ b/ui/src/domain/system.ts
@@ -0,0 +1,7 @@
+export interface SystemEnvironment {
+ dockerHost: {
+ reachable: boolean;
+ address?: string;
+ };
+}
+
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json
index 7b3f7d0d9..ea1aa4ac8 100644
--- a/ui/src/i18n/locales/en/nls.provider.json
+++ b/ui/src/i18n/locales/en/nls.provider.json
@@ -144,6 +144,7 @@
"provider.slackbot": "Slack Bot",
"provider.spaceship": "Spaceship",
"provider.ssh": "Remote host (SSH)",
+ "provider.dockerhost": "Docker host",
"provider.sslcom": "SSL.com",
"provider.technitiumdns": "Technitium DNS",
"provider.telegrambot": "Telegram Bot",
@@ -212,5 +213,6 @@
"provider.text.default_group": "Default",
"provider.text.available_group": "Available (with added credentials)",
"provider.text.unavailable_group": "Unavailable (without added credentials)",
- "provider.text.unavailable_divider": "The following providers are not available (without added credentials)"
+ "provider.text.unavailable_divider": "The following providers are not available (without added credentials)",
+ "provider.badge.unsupported": "Unsupported"
}
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index bf23ceca9..9a2a23287 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -144,6 +144,7 @@
"provider.slackbot": "Slack 机器人",
"provider.spaceship": "Spaceship",
"provider.ssh": "远程主机(SSH)",
+ "provider.dockerhost": "Docker 宿主机",
"provider.sslcom": "SSL.com",
"provider.technitiumdns": "Technitium DNS",
"provider.telegrambot": "Telegram 机器人",
@@ -211,5 +212,6 @@
"provider.text.default_group": "默认",
"provider.text.available_group": "可用(已添加授权凭据)",
"provider.text.unavailable_group": "不可用(未添加授权凭据)",
- "provider.text.unavailable_divider": "以下提供商不可用(即未添加过授权凭据)"
+ "provider.text.unavailable_divider": "以下提供商不可用(即未添加过授权凭据)",
+ "provider.badge.unsupported": "环境不支持"
}
diff --git a/ui/src/stores/system/index.ts b/ui/src/stores/system/index.ts
new file mode 100644
index 000000000..d3cda5427
--- /dev/null
+++ b/ui/src/stores/system/index.ts
@@ -0,0 +1,40 @@
+import { create } from "zustand";
+
+import { getEnvironment as requestEnvironment } from "@/api/system";
+import { ACCESS_PROVIDERS, accessProvidersMap } from "@/domain/provider";
+
+import { type SystemEnvironmentStore } from "./types";
+
+export const useSystemEnvironmentStore = create((set, get) => ({
+ environment: null,
+ loadingEnvironment: false,
+ loadedEnvironment: false,
+
+ fetchEnvironment: async (refresh = true) => {
+ if (!refresh && get().loadedEnvironment) {
+ return get().environment;
+ }
+ if (get().loadingEnvironment) {
+ return get().environment;
+ }
+
+ set({ loadingEnvironment: true });
+
+ try {
+ const resp = await requestEnvironment();
+ const environment = resp.data ?? null;
+
+ accessProvidersMap.get(ACCESS_PROVIDERS.DOCKERHOST)!.disabled = !(environment?.dockerHost.reachable ?? false);
+
+ set({ environment, loadedEnvironment: true });
+ return environment;
+ } catch (err) {
+ accessProvidersMap.get(ACCESS_PROVIDERS.DOCKERHOST)!.disabled = true;
+ set({ environment: null, loadedEnvironment: false });
+ return null;
+ } finally {
+ set({ loadingEnvironment: false });
+ }
+ },
+}));
+
diff --git a/ui/src/stores/system/types.ts b/ui/src/stores/system/types.ts
new file mode 100644
index 000000000..0583f4917
--- /dev/null
+++ b/ui/src/stores/system/types.ts
@@ -0,0 +1,14 @@
+import { type SystemEnvironment } from "@/domain/system";
+
+export interface SystemEnvironmentState {
+ environment: SystemEnvironment | null;
+ loadingEnvironment: boolean;
+ loadedEnvironment: boolean;
+}
+
+export interface SystemEnvironmentActions {
+ fetchEnvironment: (refresh?: boolean) => Promise;
+}
+
+export interface SystemEnvironmentStore extends SystemEnvironmentState, SystemEnvironmentActions {}
+