mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-19 01:41:55 +08:00
feat: deploy provider category
This commit is contained in:
parent
664bb692b6
commit
0b7b544d4e
@ -35,7 +35,7 @@ const CertificateDetailDrawer = ({ data, loading, trigger, ...props }: Certifica
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
placement="right"
|
placement="right"
|
||||||
title={`Certificate #${data?.id}`}
|
title={`Certificate #${data?.id}`}
|
||||||
width={640}
|
width={720}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
<Show when={!!data}>
|
<Show when={!!data}>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { memo, useEffect, useRef, useState } from "react";
|
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Avatar, Card, Col, Empty, Flex, Input, type InputRef, Row, Typography } from "antd";
|
import { Avatar, Card, Col, Empty, Flex, Input, type InputRef, Row, Tabs, Tooltip, Typography } from "antd";
|
||||||
|
|
||||||
import Show from "@/components/Show";
|
import Show from "@/components/Show";
|
||||||
import { deployProvidersMap } from "@/domain/provider";
|
import { DEPLOY_CATEGORIES, type DeployProvider, deployProvidersMap } from "@/domain/provider";
|
||||||
|
|
||||||
export type DeployProviderPickerProps = {
|
export type DeployProviderPickerProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -24,15 +24,26 @@ const DeployProviderPicker = ({ className, style, autoFocus, placeholder, onSele
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const providers = Array.from(deployProvidersMap.values());
|
const [category, setCategory] = useState<string>(DEPLOY_CATEGORIES.ALL);
|
||||||
const filteredProviders = providers.filter((provider) => {
|
|
||||||
if (keyword) {
|
|
||||||
const value = keyword.toLowerCase();
|
|
||||||
return provider.type.toLowerCase().includes(value) || t(provider.name).toLowerCase().includes(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
const providers = useMemo(() => {
|
||||||
});
|
return Array.from(deployProvidersMap.values())
|
||||||
|
.filter((provider) => {
|
||||||
|
if (keyword) {
|
||||||
|
const value = keyword.toLowerCase();
|
||||||
|
return provider.type.toLowerCase().includes(value) || t(provider.name).toLowerCase().includes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.filter((provider) => {
|
||||||
|
if (category && category !== DEPLOY_CATEGORIES.ALL) {
|
||||||
|
return provider.category === category;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}, [keyword, category]);
|
||||||
|
|
||||||
const handleProviderTypeSelect = (value: string) => {
|
const handleProviderTypeSelect = (value: string) => {
|
||||||
onSelect?.(value);
|
onSelect?.(value);
|
||||||
@ -43,29 +54,55 @@ const DeployProviderPicker = ({ className, style, autoFocus, placeholder, onSele
|
|||||||
<Input.Search ref={keywordInputRef} placeholder={placeholder} onChange={(e) => setKeyword(e.target.value.trim())} />
|
<Input.Search ref={keywordInputRef} placeholder={placeholder} onChange={(e) => setKeyword(e.target.value.trim())} />
|
||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Show when={filteredProviders.length > 0} fallback={<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}>
|
<Flex>
|
||||||
<Row gutter={[16, 16]}>
|
<Tabs
|
||||||
{filteredProviders.map((provider, index) => {
|
defaultActiveKey={DEPLOY_CATEGORIES.ALL}
|
||||||
return (
|
items={[
|
||||||
<Col key={index} xs={24} md={12} span={12}>
|
DEPLOY_CATEGORIES.ALL,
|
||||||
<Card
|
DEPLOY_CATEGORIES.CDN,
|
||||||
className="h-16 w-full overflow-hidden shadow-sm"
|
DEPLOY_CATEGORIES.STORAGE,
|
||||||
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
|
DEPLOY_CATEGORIES.LOADBALANCE,
|
||||||
hoverable
|
DEPLOY_CATEGORIES.FIREWALL,
|
||||||
onClick={() => {
|
DEPLOY_CATEGORIES.LIVE,
|
||||||
handleProviderTypeSelect(provider.type);
|
DEPLOY_CATEGORIES.OTHER,
|
||||||
}}
|
].map((key) => ({
|
||||||
>
|
key: key,
|
||||||
<Flex className="size-full overflow-hidden" align="center" gap={8}>
|
label: t(`provider.category.${key}`),
|
||||||
<Avatar src={provider.icon} size="small" />
|
}))}
|
||||||
<Typography.Text className="line-clamp-2 flex-1">{t(provider.name)}</Typography.Text>
|
size="small"
|
||||||
</Flex>
|
tabBarStyle={{ marginLeft: "-1rem" }}
|
||||||
</Card>
|
tabPosition="left"
|
||||||
</Col>
|
onChange={(key) => setCategory(key)}
|
||||||
);
|
/>
|
||||||
})}
|
|
||||||
</Row>
|
<div className="flex-1">
|
||||||
</Show>
|
<Show when={providers.length > 0} fallback={<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
{providers.map((provider, index) => {
|
||||||
|
return (
|
||||||
|
<Col key={index} xs={24} md={12} span={12}>
|
||||||
|
<Card
|
||||||
|
className="h-16 w-full overflow-hidden shadow-sm"
|
||||||
|
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
|
||||||
|
hoverable
|
||||||
|
onClick={() => {
|
||||||
|
handleProviderTypeSelect(provider.type);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tooltip title={t(provider.name)} mouseEnterDelay={1}>
|
||||||
|
<Flex className="size-full overflow-hidden" align="center" gap={8}>
|
||||||
|
<Avatar src={provider.icon} size="small" />
|
||||||
|
<Typography.Text className="line-clamp-2 flex-1">{t(provider.name)}</Typography.Text>
|
||||||
|
</Flex>
|
||||||
|
</Tooltip>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -36,7 +36,7 @@ const WorkflowRunDetailDrawer = ({ data, loading, trigger, ...props }: WorkflowR
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
placement="right"
|
placement="right"
|
||||||
title={`WorkflowRun #${data?.id}`}
|
title={`WorkflowRun #${data?.id}`}
|
||||||
width={640}
|
width={720}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
<Show when={!!data}>
|
<Show when={!!data}>
|
||||||
|
@ -307,7 +307,7 @@ const SharedNodeConfigDrawer = ({
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
open={open}
|
open={open}
|
||||||
title={<div className="max-w-[480px] truncate">{node.name}</div>}
|
title={<div className="max-w-[480px] truncate">{node.name}</div>}
|
||||||
width={640}
|
width={720}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -219,11 +219,24 @@ export const DEPLOY_PROVIDERS = Object.freeze({
|
|||||||
|
|
||||||
export type DeployProviderType = (typeof DEPLOY_PROVIDERS)[keyof typeof DEPLOY_PROVIDERS];
|
export type DeployProviderType = (typeof DEPLOY_PROVIDERS)[keyof typeof DEPLOY_PROVIDERS];
|
||||||
|
|
||||||
|
export const DEPLOY_CATEGORIES = Object.freeze({
|
||||||
|
ALL: "all",
|
||||||
|
CDN: "cdn",
|
||||||
|
STORAGE: "storage",
|
||||||
|
LOADBALANCE: "loadbalance",
|
||||||
|
FIREWALL: "firewall",
|
||||||
|
LIVE: "live",
|
||||||
|
OTHER: "other",
|
||||||
|
} as const);
|
||||||
|
|
||||||
|
export type DeployCategoryType = (typeof DEPLOY_CATEGORIES)[keyof typeof DEPLOY_CATEGORIES];
|
||||||
|
|
||||||
export type DeployProvider = {
|
export type DeployProvider = {
|
||||||
type: DeployProviderType;
|
type: DeployProviderType;
|
||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
provider: AccessProviderType;
|
provider: AccessProviderType;
|
||||||
|
category: DeployCategoryType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProvider> = new Map(
|
export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProvider> = new Map(
|
||||||
@ -232,51 +245,52 @@ export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProv
|
|||||||
NOTICE: The following order determines the order displayed at the frontend.
|
NOTICE: The following order determines the order displayed at the frontend.
|
||||||
*/
|
*/
|
||||||
[
|
[
|
||||||
[DEPLOY_PROVIDERS.LOCAL, "provider.local"],
|
[DEPLOY_PROVIDERS.LOCAL, "provider.local", DEPLOY_CATEGORIES.OTHER],
|
||||||
[DEPLOY_PROVIDERS.SSH, "provider.ssh"],
|
[DEPLOY_PROVIDERS.SSH, "provider.ssh", DEPLOY_CATEGORIES.OTHER],
|
||||||
[DEPLOY_PROVIDERS.WEBHOOK, "provider.webhook"],
|
[DEPLOY_PROVIDERS.WEBHOOK, "provider.webhook", DEPLOY_CATEGORIES.OTHER],
|
||||||
[DEPLOY_PROVIDERS.KUBERNETES_SECRET, "provider.kubernetes.secret"],
|
[DEPLOY_PROVIDERS.KUBERNETES_SECRET, "provider.kubernetes.secret", DEPLOY_CATEGORIES.OTHER],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_OSS, "provider.aliyun.oss"],
|
[DEPLOY_PROVIDERS.ALIYUN_OSS, "provider.aliyun.oss", DEPLOY_CATEGORIES.STORAGE],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_CDN, "provider.aliyun.cdn"],
|
[DEPLOY_PROVIDERS.ALIYUN_CDN, "provider.aliyun.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_DCDN, "provider.aliyun.dcdn"],
|
[DEPLOY_PROVIDERS.ALIYUN_DCDN, "provider.aliyun.dcdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_ESA, "provider.aliyun.esa"],
|
[DEPLOY_PROVIDERS.ALIYUN_ESA, "provider.aliyun.esa", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_CLB, "provider.aliyun.clb"],
|
[DEPLOY_PROVIDERS.ALIYUN_CLB, "provider.aliyun.clb", DEPLOY_CATEGORIES.LOADBALANCE],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_ALB, "provider.aliyun.alb"],
|
[DEPLOY_PROVIDERS.ALIYUN_ALB, "provider.aliyun.alb", DEPLOY_CATEGORIES.LOADBALANCE],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_NLB, "provider.aliyun.nlb"],
|
[DEPLOY_PROVIDERS.ALIYUN_NLB, "provider.aliyun.nlb", DEPLOY_CATEGORIES.LOADBALANCE],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_WAF, "provider.aliyun.waf"],
|
[DEPLOY_PROVIDERS.ALIYUN_WAF, "provider.aliyun.waf", DEPLOY_CATEGORIES.FIREWALL],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_LIVE, "provider.aliyun.live"],
|
[DEPLOY_PROVIDERS.ALIYUN_LIVE, "provider.aliyun.live", DEPLOY_CATEGORIES.LIVE],
|
||||||
[DEPLOY_PROVIDERS.ALIYUN_CAS_DEPLOY, "provider.aliyun.cas_deploy"],
|
[DEPLOY_PROVIDERS.ALIYUN_CAS_DEPLOY, "provider.aliyun.cas_deploy", DEPLOY_CATEGORIES.OTHER],
|
||||||
[DEPLOY_PROVIDERS.TENCENTCLOUD_COS, "provider.tencentcloud.cos"],
|
[DEPLOY_PROVIDERS.TENCENTCLOUD_COS, "provider.tencentcloud.cos", DEPLOY_CATEGORIES.STORAGE],
|
||||||
[DEPLOY_PROVIDERS.TENCENTCLOUD_CDN, "provider.tencentcloud.cdn"],
|
[DEPLOY_PROVIDERS.TENCENTCLOUD_CDN, "provider.tencentcloud.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.TENCENTCLOUD_ECDN, "provider.tencentcloud.ecdn"],
|
[DEPLOY_PROVIDERS.TENCENTCLOUD_ECDN, "provider.tencentcloud.ecdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.TENCENTCLOUD_EO, "provider.tencentcloud.eo"],
|
[DEPLOY_PROVIDERS.TENCENTCLOUD_EO, "provider.tencentcloud.eo", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.TENCENTCLOUD_CLB, "provider.tencentcloud.clb"],
|
[DEPLOY_PROVIDERS.TENCENTCLOUD_CLB, "provider.tencentcloud.clb", DEPLOY_CATEGORIES.LOADBALANCE],
|
||||||
[DEPLOY_PROVIDERS.TENCENTCLOUD_CSS, "provider.tencentcloud.css"],
|
[DEPLOY_PROVIDERS.TENCENTCLOUD_CSS, "provider.tencentcloud.css", DEPLOY_CATEGORIES.LIVE],
|
||||||
[DEPLOY_PROVIDERS.TENCENTCLOUD_SSL_DEPLOY, "provider.tencentcloud.ssl_deploy"],
|
[DEPLOY_PROVIDERS.TENCENTCLOUD_SSL_DEPLOY, "provider.tencentcloud.ssl_deploy", DEPLOY_CATEGORIES.OTHER],
|
||||||
[DEPLOY_PROVIDERS.HUAWEICLOUD_CDN, "provider.huaweicloud.cdn"],
|
[DEPLOY_PROVIDERS.HUAWEICLOUD_CDN, "provider.huaweicloud.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud.elb"],
|
[DEPLOY_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud.elb", DEPLOY_CATEGORIES.LOADBALANCE],
|
||||||
[DEPLOY_PROVIDERS.BAIDUCLOUD_CDN, "provider.baiducloud.cdn"],
|
[DEPLOY_PROVIDERS.BAIDUCLOUD_CDN, "provider.baiducloud.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.VOLCENGINE_TOS, "provider.volcengine.tos"],
|
[DEPLOY_PROVIDERS.VOLCENGINE_TOS, "provider.volcengine.tos", DEPLOY_CATEGORIES.STORAGE],
|
||||||
[DEPLOY_PROVIDERS.VOLCENGINE_CDN, "provider.volcengine.cdn"],
|
[DEPLOY_PROVIDERS.VOLCENGINE_CDN, "provider.volcengine.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.VOLCENGINE_DCDN, "provider.volcengine.dcdn"],
|
[DEPLOY_PROVIDERS.VOLCENGINE_DCDN, "provider.volcengine.dcdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.VOLCENGINE_CLB, "provider.volcengine.clb"],
|
[DEPLOY_PROVIDERS.VOLCENGINE_CLB, "provider.volcengine.clb", DEPLOY_CATEGORIES.LOADBALANCE],
|
||||||
[DEPLOY_PROVIDERS.VOLCENGINE_LIVE, "provider.volcengine.live"],
|
[DEPLOY_PROVIDERS.VOLCENGINE_LIVE, "provider.volcengine.live", DEPLOY_CATEGORIES.LIVE],
|
||||||
[DEPLOY_PROVIDERS.QINIU_CDN, "provider.qiniu.cdn"],
|
[DEPLOY_PROVIDERS.QINIU_CDN, "provider.qiniu.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.QINIU_PILI, "provider.qiniu.pili"],
|
[DEPLOY_PROVIDERS.QINIU_PILI, "provider.qiniu.pili", DEPLOY_CATEGORIES.LIVE],
|
||||||
[DEPLOY_PROVIDERS.DOGECLOUD_CDN, "provider.dogecloud.cdn"],
|
[DEPLOY_PROVIDERS.DOGECLOUD_CDN, "provider.dogecloud.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.BYTEPLUS_CDN, "provider.byteplus.cdn"],
|
[DEPLOY_PROVIDERS.BYTEPLUS_CDN, "provider.byteplus.cdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.UCLOUD_US3, "provider.ucloud.us3"],
|
[DEPLOY_PROVIDERS.UCLOUD_US3, "provider.ucloud.us3", DEPLOY_CATEGORIES.STORAGE],
|
||||||
[DEPLOY_PROVIDERS.UCLOUD_UCDN, "provider.ucloud.ucdn"],
|
[DEPLOY_PROVIDERS.UCLOUD_UCDN, "provider.ucloud.ucdn", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.AWS_CLOUDFRONT, "provider.aws.cloudfront"],
|
[DEPLOY_PROVIDERS.AWS_CLOUDFRONT, "provider.aws.cloudfront", DEPLOY_CATEGORIES.CDN],
|
||||||
[DEPLOY_PROVIDERS.BAOTAPANEL_SITE, "provider.baotapanel.site"],
|
[DEPLOY_PROVIDERS.BAOTAPANEL_SITE, "provider.baotapanel.site", DEPLOY_CATEGORIES.OTHER],
|
||||||
[DEPLOY_PROVIDERS.EDGIO_APPLICATIONS, "provider.edgio.applications"],
|
[DEPLOY_PROVIDERS.EDGIO_APPLICATIONS, "provider.edgio.applications", DEPLOY_CATEGORIES.OTHER],
|
||||||
].map(([type, name]) => [
|
].map(([type, name, category]) => [
|
||||||
type,
|
type,
|
||||||
{
|
{
|
||||||
type: type as DeployProviderType,
|
type: type as DeployProviderType,
|
||||||
name: name,
|
name: name,
|
||||||
icon: accessProvidersMap.get(type.split("-")[0])!.icon,
|
icon: accessProvidersMap.get(type.split("-")[0])!.icon,
|
||||||
provider: type.split("-")[0] as AccessProviderType,
|
provider: type.split("-")[0] as AccessProviderType,
|
||||||
|
category: category as DeployCategoryType,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
@ -67,5 +67,13 @@
|
|||||||
"provider.volcengine.live": "Volcengine - Live",
|
"provider.volcengine.live": "Volcengine - Live",
|
||||||
"provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)",
|
"provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)",
|
||||||
"provider.webhook": "Webhook",
|
"provider.webhook": "Webhook",
|
||||||
"provider.westcn": "West.cn"
|
"provider.westcn": "West.cn",
|
||||||
|
|
||||||
|
"provider.category.all": "All",
|
||||||
|
"provider.category.cdn": "CDN",
|
||||||
|
"provider.category.storage": "Storage",
|
||||||
|
"provider.category.loadbalance": "Load Balance",
|
||||||
|
"provider.category.firewall": "Firewall",
|
||||||
|
"provider.category.live": "Live",
|
||||||
|
"provider.category.other": "Other"
|
||||||
}
|
}
|
||||||
|
@ -67,5 +67,13 @@
|
|||||||
"provider.volcengine.live": "火山引擎 - 视频直播 Live",
|
"provider.volcengine.live": "火山引擎 - 视频直播 Live",
|
||||||
"provider.volcengine.tos": "火山引擎 - 对象存储 TOS",
|
"provider.volcengine.tos": "火山引擎 - 对象存储 TOS",
|
||||||
"provider.webhook": "Webhook",
|
"provider.webhook": "Webhook",
|
||||||
"provider.westcn": "西部数码"
|
"provider.westcn": "西部数码",
|
||||||
|
|
||||||
|
"provider.category.all": "全部",
|
||||||
|
"provider.category.cdn": "CDN",
|
||||||
|
"provider.category.storage": "存储",
|
||||||
|
"provider.category.loadbalance": "负载均衡",
|
||||||
|
"provider.category.firewall": "防火墙",
|
||||||
|
"provider.category.live": "直播",
|
||||||
|
"provider.category.other": "其他"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user