feat: certificate key algorithm

This commit is contained in:
Fu Diwei 2024-10-16 20:20:27 +08:00
parent 71f43c5bd4
commit 1ce2a52d70
15 changed files with 595 additions and 409 deletions

View File

@ -60,11 +60,12 @@ type Certificate struct {
} }
type ApplyOption struct { type ApplyOption struct {
Email string `json:"email"` Email string `json:"email"`
Domain string `json:"domain"` Domain string `json:"domain"`
Access string `json:"access"` Access string `json:"access"`
Nameservers string `json:"nameservers"` KeyAlgorithm string `json:"keyAlgorithm"`
Timeout int64 `json:"timeout"` Nameservers string `json:"nameservers"`
Timeout int64 `json:"timeout"`
} }
type ApplyUser struct { type ApplyUser struct {
@ -91,11 +92,10 @@ type Applicant interface {
func Get(record *models.Record) (Applicant, error) { func Get(record *models.Record) (Applicant, error) {
if record.GetString("applyConfig") == "" { if record.GetString("applyConfig") == "" {
return nil, errors.New("apply config is empty") return nil, errors.New("applyConfig is empty")
} }
applyConfig := &domain.ApplyConfig{} applyConfig := &domain.ApplyConfig{}
record.UnmarshalJSONField("applyConfig", applyConfig) record.UnmarshalJSONField("applyConfig", applyConfig)
access, err := app.GetApp().Dao().FindRecordById("access", applyConfig.Access) access, err := app.GetApp().Dao().FindRecordById("access", applyConfig.Access)
@ -103,17 +103,23 @@ func Get(record *models.Record) (Applicant, error) {
return nil, fmt.Errorf("access record not found: %w", err) return nil, fmt.Errorf("access record not found: %w", err)
} }
email := applyConfig.Email if applyConfig.Email == "" {
if email == "" { applyConfig.Email = defaultEmail
email = defaultEmail
} }
if applyConfig.Timeout == 0 {
applyConfig.Timeout = defaultTimeout
}
option := &ApplyOption{ option := &ApplyOption{
Email: email, Email: applyConfig.Email,
Domain: record.GetString("domain"), Domain: record.GetString("domain"),
Access: access.GetString("config"), Access: access.GetString("config"),
Nameservers: applyConfig.Nameservers, KeyAlgorithm: applyConfig.KeyAlgorithm,
Timeout: applyConfig.Timeout, Nameservers: applyConfig.Nameservers,
Timeout: applyConfig.Timeout,
} }
switch access.GetString("configType") { switch access.GetString("configType") {
case configTypeAliyun: case configTypeAliyun:
return NewAliyun(option), nil return NewAliyun(option), nil
@ -171,7 +177,7 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
config.CADirURL = sslProviderUrls[sslProvider.Provider] config.CADirURL = sslProviderUrls[sslProvider.Provider]
config.Certificate.KeyType = certcrypto.RSA2048 config.Certificate.KeyType = parseKeyAlgorithm(option.KeyAlgorithm)
// A client facilitates communication with the CA server. // A client facilitates communication with the CA server.
client, err := lego.NewClient(config) client, err := lego.NewClient(config)
@ -180,7 +186,7 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
} }
challengeOptions := make([]dns01.ChallengeOption, 0) challengeOptions := make([]dns01.ChallengeOption, 0)
nameservers := ParseNameservers(option.Nameservers) nameservers := parseNameservers(option.Nameservers)
if len(nameservers) > 0 { if len(nameservers) > 0 {
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(nameservers)) challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(nameservers))
} }
@ -195,7 +201,6 @@ func apply(option *ApplyOption, provider challenge.Provider) (*Certificate, erro
myUser.Registration = reg myUser.Registration = reg
domains := strings.Split(option.Domain, ";") domains := strings.Split(option.Domain, ";")
request := certificate.ObtainRequest{ request := certificate.ObtainRequest{
Domains: domains, Domains: domains,
Bundle: true, Bundle: true,
@ -231,7 +236,6 @@ func getReg(client *lego.Client, sslProvider *SSLProviderConfig) (*registration.
default: default:
err = errors.New("unknown ssl provider") err = errors.New("unknown ssl provider")
} }
if err != nil { if err != nil {
@ -241,15 +245,13 @@ func getReg(client *lego.Client, sslProvider *SSLProviderConfig) (*registration.
return reg, nil return reg, nil
} }
func ParseNameservers(ns string) []string { func parseNameservers(ns string) []string {
nameservers := make([]string, 0) nameservers := make([]string, 0)
lines := strings.Split(ns, ";") lines := strings.Split(ns, ";")
for _, line := range lines { for _, line := range lines {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if line == "" { if line == "" {
continue continue
} }
@ -259,3 +261,22 @@ func ParseNameservers(ns string) []string {
return nameservers return nameservers
} }
func parseKeyAlgorithm(algo string) certcrypto.KeyType {
switch algo {
case "RSA2048":
return certcrypto.RSA2048
case "RSA3072":
return certcrypto.RSA3072
case "RSA4096":
return certcrypto.RSA4096
case "RSA8192":
return certcrypto.RSA8192
case "EC256":
return certcrypto.EC256
case "EC384":
return certcrypto.EC384
default:
return certcrypto.RSA2048
}
}

View File

@ -1,10 +1,11 @@
package domain package domain
type ApplyConfig struct { type ApplyConfig struct {
Email string `json:"email"` Email string `json:"email"`
Access string `json:"access"` Access string `json:"access"`
Timeout int64 `json:"timeout"` KeyAlgorithm string `json:"keyAlgorithm"`
Nameservers string `json:"nameservers"` Nameservers string `json:"nameservers"`
Timeout int64 `json:"timeout"`
} }
type DeployConfig struct { type DeployConfig struct {

329
ui/dist/assets/index-C20g8xcX.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
ui/dist/assets/index-YqBWA4KK.css vendored Normal file

File diff suppressed because one or more lines are too long

4
ui/dist/index.html vendored
View File

@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title> <title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-DIhd7QG6.js"></script> <script type="module" crossorigin src="/assets/index-C20g8xcX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CV_7sKTK.css"> <link rel="stylesheet" crossorigin href="/assets/index-YqBWA4KK.css">
</head> </head>
<body class="bg-background"> <body class="bg-background">
<div id="root"></div> <div id="root"></div>

77
ui/package-lock.json generated
View File

@ -11,6 +11,7 @@
"@hookform/resolvers": "^3.9.0", "@hookform/resolvers": "^3.9.0",
"@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",
@ -1184,6 +1185,35 @@
} }
} }
}, },
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collapsible": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz",
"integrity": "sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-alert-dialog": { "node_modules/@radix-ui/react-alert-dialog": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.1.tgz", "resolved": "https://registry.npmmirror.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.1.tgz",
@ -1234,15 +1264,15 @@
} }
}, },
"node_modules/@radix-ui/react-collapsible": { "node_modules/@radix-ui/react-collapsible": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz",
"integrity": "sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==", "integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==",
"dependencies": { "dependencies": {
"@radix-ui/primitive": "1.1.0", "@radix-ui/primitive": "1.1.0",
"@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-context": "1.1.0", "@radix-ui/react-context": "1.1.1",
"@radix-ui/react-id": "1.1.0", "@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.0", "@radix-ui/react-presence": "1.1.1",
"@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0" "@radix-ui/react-use-layout-effect": "1.1.0"
@ -1262,6 +1292,43 @@
} }
} }
}, },
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-context": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.1.1.tgz",
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": { "node_modules/@radix-ui/react-collection": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz",

View File

@ -13,6 +13,7 @@
"@hookform/resolvers": "^3.9.0", "@hookform/resolvers": "^3.9.0",
"@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0", "@radix-ui/react-label": "^2.1.0",

View File

@ -0,0 +1,22 @@
import * as React from "react";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
import { cn } from "@/lib/utils";
const Collapsible = CollapsiblePrimitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleContent = React.forwardRef<
React.ElementRef<typeof CollapsiblePrimitive.CollapsibleContent>,
React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleContent>
>(({ className, ...props }, ref) => (
<CollapsiblePrimitive.CollapsibleContent
ref={ref}
className={cn("overflow-y-hidden transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down", className)}
{...props}
/>
));
CollapsibleContent.displayName = CollapsiblePrimitive.CollapsibleContent.displayName;
export { Collapsible, CollapsibleTrigger, CollapsibleContent };

View File

@ -50,8 +50,9 @@ export type DeployConfig = {
export type ApplyConfig = { export type ApplyConfig = {
access: string; access: string;
email: string; email: string;
timeout?: number; keyAlgorithm?: string;
nameservers?: string; nameservers?: string;
timeout?: number;
}; };
export type Statistic = { export type Statistic = {

View File

@ -30,15 +30,17 @@
"domain.application.form.domain.changed.message": "Domain updated successfully", "domain.application.form.domain.changed.message": "Domain updated successfully",
"domain.application.form.email.label": "Email", "domain.application.form.email.label": "Email",
"domain.application.form.email.tips": "(A email is required to apply for a certificate)", "domain.application.form.email.tips": "(A email is required to apply for a certificate)",
"domain.application.form.email.placeholder": "Please select email",
"domain.application.form.email.add": "Add Email", "domain.application.form.email.add": "Add Email",
"domain.application.form.email.list": "Email List", "domain.application.form.email.list": "Email List",
"domain.application.form.email.errmsg.empty": "Please select email",
"domain.application.form.access.label": "DNS Provider Authorization Configuration", "domain.application.form.access.label": "DNS Provider Authorization Configuration",
"domain.application.form.access.placeholder": "Please select DNS provider authorization configuration", "domain.application.form.access.placeholder": "Please select DNS provider authorization configuration",
"domain.application.form.access.errmsg.empty": "Please select DNS provider authorization configuration",
"domain.application.form.access.list": "Provider Authorization Configurations", "domain.application.form.access.list": "Provider Authorization Configurations",
"domain.application.form.timeout.label": "Timeout", "domain.application.form.advanced_settings.label": "Advanced Settings",
"domain.application.form.timeoue.placeholder": "Timeout (seconds)", "domain.application.form.key_algorithm.label": "Certificate Key Algorithm",
"domain.application.form.key_algorithm.placeholder": "Please select certificate key algorithm",
"domain.application.form.timeout.label": "DNS Propagation Timeout (seconds)",
"domain.application.form.timeoue.placeholder": "Please enter maximum waiting time for DNS propagation",
"domain.application.unsaved.message": "Please save applyment configuration first", "domain.application.unsaved.message": "Please save applyment configuration first",
"domain.deployment.tab": "Deploy Settings", "domain.deployment.tab": "Deploy Settings",

View File

@ -30,15 +30,17 @@
"domain.application.form.domain.changed.message": "域名编辑成功", "domain.application.form.domain.changed.message": "域名编辑成功",
"domain.application.form.email.label": "邮箱", "domain.application.form.email.label": "邮箱",
"domain.application.form.email.tips": "(申请证书需要提供邮箱)", "domain.application.form.email.tips": "(申请证书需要提供邮箱)",
"domain.application.form.email.placeholder": "请选择邮箱",
"domain.application.form.email.add": "添加邮箱", "domain.application.form.email.add": "添加邮箱",
"domain.application.form.email.list": "邮箱列表", "domain.application.form.email.list": "邮箱列表",
"domain.application.form.email.errmsg.empty": "请选择邮箱",
"domain.application.form.access.label": "DNS 服务商授权配置", "domain.application.form.access.label": "DNS 服务商授权配置",
"domain.application.form.access.placeholder": "请选择 DNS 服务商授权配置", "domain.application.form.access.placeholder": "请选择 DNS 服务商授权配置",
"domain.application.form.access.errmsg.empty": "请选择 DNS 服务商授权配置",
"domain.application.form.access.list": "已有的 DNS 服务商授权配置", "domain.application.form.access.list": "已有的 DNS 服务商授权配置",
"domain.application.form.timeout.label": "超时时间", "domain.application.form.advanced_settings.label": "高级设置",
"domain.application.form.timeoue.placeholder": "超时时间(单位:秒)", "domain.application.form.key_algorithm.label": "数字证书算法",
"domain.application.form.key_algorithm.placeholder": "请选择数字证书算法",
"domain.application.form.timeout.label": "DNS 传播检查超时时间(单位:秒)",
"domain.application.form.timeoue.placeholder": "请输入 DNS 传播检查超时时间",
"domain.application.unsaved.message": "请先保存申请配置", "domain.application.unsaved.message": "请先保存申请配置",
"domain.deployment.tab": "部署配置", "domain.deployment.tab": "部署配置",

View File

@ -4,11 +4,12 @@ import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import z from "zod"; import z from "zod";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Plus } from "lucide-react"; import { ChevronsUpDown, Plus } from "lucide-react";
import { ClientResponseError } from "pocketbase"; import { ClientResponseError } from "pocketbase";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb"; import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
@ -58,8 +59,9 @@ const Edit = () => {
}), }),
email: z.string().email("common.errmsg.email_invalid").optional(), email: z.string().email("common.errmsg.email_invalid").optional(),
access: z.string().regex(/^[a-zA-Z0-9]+$/, { access: z.string().regex(/^[a-zA-Z0-9]+$/, {
message: "domain.application.form.access.errmsg.empty", message: "domain.application.form.access.placeholder",
}), }),
keyAlgorithm: z.string().optional(),
nameservers: z.string().optional(), nameservers: z.string().optional(),
timeout: z.number().optional(), timeout: z.number().optional(),
}); });
@ -71,6 +73,7 @@ const Edit = () => {
domain: "", domain: "",
email: "", email: "",
access: "", access: "",
keyAlgorithm: "RSA2048",
nameservers: "", nameservers: "",
timeout: 60, timeout: 60,
}, },
@ -83,6 +86,7 @@ const Edit = () => {
domain: domain.domain, domain: domain.domain,
email: domain.applyConfig?.email, email: domain.applyConfig?.email,
access: domain.applyConfig?.access, access: domain.applyConfig?.access,
keyAlgorithm: domain.applyConfig?.keyAlgorithm,
nameservers: domain.applyConfig?.nameservers, nameservers: domain.applyConfig?.nameservers,
timeout: domain.applyConfig?.timeout, timeout: domain.applyConfig?.timeout,
}); });
@ -101,6 +105,7 @@ const Edit = () => {
applyConfig: { applyConfig: {
email: data.email ?? "", email: data.email ?? "",
access: data.access, access: data.access,
keyAlgorithm: data.keyAlgorithm,
nameservers: data.nameservers, nameservers: data.nameservers,
timeout: data.timeout, timeout: data.timeout,
}, },
@ -235,6 +240,7 @@ const Edit = () => {
</FormItem> </FormItem>
)} )}
/> />
{/* 邮箱 */} {/* 邮箱 */}
<FormField <FormField
control={form.control} control={form.control}
@ -261,7 +267,7 @@ const Edit = () => {
}} }}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder={t("domain.application.form.email.errmsg.empty")} /> <SelectValue placeholder={t("domain.application.form.email.placeholder")} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
@ -280,7 +286,8 @@ const Edit = () => {
</FormItem> </FormItem>
)} )}
/> />
{/* 授权 */}
{/* DNS 服务商授权 */}
<FormField <FormField
control={form.control} control={form.control}
name="access" name="access"
@ -332,48 +339,95 @@ const Edit = () => {
)} )}
/> />
{/* 超时时间 */} <div>
<FormField <hr />
control={form.control} <Collapsible>
name="timeout" <CollapsibleTrigger className="w-full my-4">
render={({ field }) => ( <div className="flex items-center justify-between space-x-4">
<FormItem> <span className="flex-1 text-sm text-gray-600 text-left">{t("domain.application.form.advanced_settings.label")}</span>
<FormLabel>{t("domain.application.form.timeout.label")}</FormLabel> <ChevronsUpDown className="h-4 w-4" />
<FormControl> </div>
<Input </CollapsibleTrigger>
type="number" <CollapsibleContent>
placeholder={t("ddomain.application.form.timeout.placeholder")} <div className="flex flex-col space-y-8">
{...field} {/* 证书算法 */}
value={field.value} <FormField
onChange={(e) => { control={form.control}
form.setValue("timeout", parseInt(e.target.value)); name="keyAlgorithm"
}} render={({ field }) => (
<FormItem>
<FormLabel>{t("domain.application.form.key_algorithm.label")}</FormLabel>
<Select
{...field}
value={field.value}
onValueChange={(value) => {
form.setValue("keyAlgorithm", value);
}}
>
<SelectTrigger>
<SelectValue placeholder={t("domain.application.form.key_algorithm.placeholder")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="RSA2048">RSA2048</SelectItem>
<SelectItem value="RSA3072">RSA3072</SelectItem>
<SelectItem value="RSA4096">RSA4096</SelectItem>
<SelectItem value="RSA8192">RSA8192</SelectItem>
<SelectItem value="EC256">EC256</SelectItem>
<SelectItem value="EC384">EC384</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</FormItem>
)}
/> />
</FormControl>
<FormMessage /> {/* DNS */}
</FormItem> <FormField
)} control={form.control}
/> name="nameservers"
render={({ field }) => (
<FormItem>
<StringList
value={field.value ?? ""}
onValueChange={(val: string) => {
form.setValue("nameservers", val);
}}
valueType="dns"
></StringList>
{/* nameservers */} <FormMessage />
<FormField </FormItem>
control={form.control} )}
name="nameservers" />
render={({ field }) => (
<FormItem>
<StringList
value={field.value ?? ""}
onValueChange={(val: string) => {
form.setValue("nameservers", val);
}}
valueType="dns"
></StringList>
<FormMessage /> {/* DNS 超时时间 */}
</FormItem> <FormField
)} control={form.control}
/> name="timeout"
render={({ field }) => (
<FormItem>
<FormLabel>{t("domain.application.form.timeout.label")}</FormLabel>
<FormControl>
<Input
type="number"
placeholder={t("domain.application.form.timeout.placeholder")}
{...field}
value={field.value}
onChange={(e) => {
form.setValue("timeout", parseInt(e.target.value));
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</CollapsibleContent>
</Collapsible>
</div>
<div className="flex justify-end"> <div className="flex justify-end">
<Button type="submit">{domain?.id ? t("common.save") : t("common.next")}</Button> <Button type="submit">{domain?.id ? t("common.save") : t("common.next")}</Button>

View File

@ -70,10 +70,20 @@ module.exports = {
height: "0", height: "0",
}, },
}, },
"collapsible-down": {
from: { height: 0 },
to: { height: "var(--radix-collapsible-content-height)" },
},
"collapsible-up": {
from: { height: "var(--radix-collapsible-content-height)" },
to: { height: 0 },
},
}, },
animation: { animation: {
"accordion-down": "accordion-down 0.2s ease-out", "accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out",
"collapsible-down": "collapsible-down 0.2s ease-out",
"collapsible-up": "collapsible-up 0.2s ease-out",
}, },
}, },
}, },