mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-08-01 23:13:34 +00:00
Add certificate model for http and dns
change is_ecc to boolean, its still stored as int in sqlite
This commit is contained in:
@@ -52,12 +52,16 @@ export interface Certificate {
|
||||
id: number;
|
||||
createdOn: number;
|
||||
modifiedOn: number;
|
||||
expiresOn: number | null;
|
||||
type: string;
|
||||
userId: number;
|
||||
certificateAuthorityId: number;
|
||||
dnsProviderId: number;
|
||||
name: string;
|
||||
acmeshServer: string;
|
||||
caBundle: string;
|
||||
maxDomains: number;
|
||||
isWildcardSupported: boolean;
|
||||
isSetup: boolean;
|
||||
domainNames: string[];
|
||||
status: string;
|
||||
errorMessage: string;
|
||||
isEcc: boolean;
|
||||
}
|
||||
|
||||
export interface CertificateAuthority {
|
||||
|
@@ -47,7 +47,7 @@ const useSetCertificate = () => {
|
||||
onError: (error, values, rollback: any) => rollback(),
|
||||
onSuccess: async ({ id }: Certificate) => {
|
||||
queryClient.invalidateQueries(["certificate", id]);
|
||||
queryClient.invalidateQueries("certificate");
|
||||
queryClient.invalidateQueries("certificates");
|
||||
},
|
||||
},
|
||||
);
|
||||
|
@@ -28,7 +28,7 @@ const useCertificates = (
|
||||
options = {},
|
||||
) => {
|
||||
return useQuery<CertificatesResponse, Error>(
|
||||
["hosts", { offset, limit, sortBy, filters }],
|
||||
["certificates", { offset, limit, sortBy, filters }],
|
||||
() => fetchCertificates(offset, limit, sortBy, filters),
|
||||
{
|
||||
keepPreviousData: true,
|
||||
|
@@ -1,3 +1,17 @@
|
||||
# Certificates Help
|
||||
|
||||
## HTTP Certificate
|
||||
|
||||
todo
|
||||
|
||||
## DNS Certificate
|
||||
|
||||
todo
|
||||
|
||||
## Custom Certificate
|
||||
|
||||
todo
|
||||
|
||||
## MKCert Certificate
|
||||
|
||||
todo
|
||||
|
@@ -314,6 +314,9 @@
|
||||
"certificate-authorities.title": {
|
||||
"defaultMessage": "Certificate Authorities"
|
||||
},
|
||||
"certificate-authority": {
|
||||
"defaultMessage": "Certificate Authority"
|
||||
},
|
||||
"certificate-authority.acmesh-server": {
|
||||
"defaultMessage": "ACME Server"
|
||||
},
|
||||
@@ -404,12 +407,24 @@
|
||||
"disabled": {
|
||||
"defaultMessage": "Disabled"
|
||||
},
|
||||
"dns-provider": {
|
||||
"defaultMessage": "DNS Provider"
|
||||
},
|
||||
"dns-provider.acmesh-name": {
|
||||
"defaultMessage": "Acme.sh Provider"
|
||||
},
|
||||
"dns-provider.create": {
|
||||
"defaultMessage": "Create DNS Provider"
|
||||
},
|
||||
"dns-providers-empty": {
|
||||
"defaultMessage": "No DNS Providers - Create one first"
|
||||
},
|
||||
"domain_names": {
|
||||
"defaultMessage": "Domain Names"
|
||||
},
|
||||
"domain_names.max": {
|
||||
"defaultMessage": "{count} domain names maximum"
|
||||
},
|
||||
"name": {
|
||||
"defaultMessage": "Name"
|
||||
},
|
||||
@@ -524,6 +539,9 @@
|
||||
"https-only": {
|
||||
"defaultMessage": "HTTPS Only"
|
||||
},
|
||||
"is-ecc": {
|
||||
"defaultMessage": "ECC Certificate"
|
||||
},
|
||||
"lets-go": {
|
||||
"defaultMessage": "Let's go"
|
||||
},
|
||||
@@ -638,6 +656,9 @@
|
||||
"type.http": {
|
||||
"defaultMessage": "HTTP"
|
||||
},
|
||||
"type.mkcert": {
|
||||
"defaultMessage": "MKCert"
|
||||
},
|
||||
"type.proxy": {
|
||||
"defaultMessage": "Proxy Host"
|
||||
},
|
||||
|
@@ -1,9 +1,5 @@
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
@@ -16,14 +12,14 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import { Certificate } from "api/npm";
|
||||
import { PrettyButton } from "components";
|
||||
import { Formik, Form, Field } from "formik";
|
||||
import { Formik, Form } from "formik";
|
||||
import { useSetCertificate } from "hooks";
|
||||
import { intl } from "locale";
|
||||
import { validateString } from "modules/Validations";
|
||||
|
||||
import CustomForm from "./CustomForm";
|
||||
import DNSForm from "./DNSForm";
|
||||
import HTTPForm from "./HTTPForm";
|
||||
import MKCertForm from "./MKCertForm";
|
||||
|
||||
interface CertificateCreateModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -44,8 +40,9 @@ function CertificateCreateModal({
|
||||
|
||||
const onSubmit = async (
|
||||
payload: Certificate,
|
||||
{ setErrors, setSubmitting }: any,
|
||||
{ /*setErrors,*/ setSubmitting }: any,
|
||||
) => {
|
||||
payload.type = certType;
|
||||
const showErr = (msg: string) => {
|
||||
toast({
|
||||
description: intl.formatMessage({
|
||||
@@ -60,6 +57,8 @@ function CertificateCreateModal({
|
||||
|
||||
setCertificate(payload, {
|
||||
onError: (err: any) => {
|
||||
showErr(err.message);
|
||||
/*
|
||||
if (err.message === "ca-bundle-does-not-exist") {
|
||||
setErrors({
|
||||
caBundle: intl.formatMessage({
|
||||
@@ -68,28 +67,52 @@ function CertificateCreateModal({
|
||||
});
|
||||
} else {
|
||||
showErr(err.message);
|
||||
}
|
||||
}*/
|
||||
},
|
||||
onSuccess: () => onModalClose(),
|
||||
onSettled: () => setSubmitting(false),
|
||||
});
|
||||
};
|
||||
|
||||
const getInitialValues = (type: string): any => {
|
||||
switch (type) {
|
||||
case "http":
|
||||
return {
|
||||
certificateAuthorityId: 0,
|
||||
name: "",
|
||||
domainNames: [],
|
||||
isEcc: false,
|
||||
} as any;
|
||||
case "dns":
|
||||
return {
|
||||
certificateAuthorityId: 0,
|
||||
dnsProviderId: 0,
|
||||
name: "",
|
||||
domainNames: [],
|
||||
isEcc: false,
|
||||
} as any;
|
||||
case "custom":
|
||||
return {
|
||||
name: "",
|
||||
domainNames: [],
|
||||
// isEcc: false, // todo, required?
|
||||
// todo: add meta?
|
||||
} as any;
|
||||
case "mkcert":
|
||||
return {
|
||||
name: "",
|
||||
domainNames: [],
|
||||
// isEcc: false, // todo, supported?
|
||||
// todo: add meta?
|
||||
} as any;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onModalClose} closeOnOverlayClick={false}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<Formik
|
||||
initialValues={
|
||||
{
|
||||
name: "",
|
||||
acmeshServer: "",
|
||||
caBundle: "",
|
||||
maxDomains: 5,
|
||||
isWildcardSupported: false,
|
||||
} as Certificate
|
||||
}
|
||||
onSubmit={onSubmit}>
|
||||
<Formik initialValues={getInitialValues(certType)} onSubmit={onSubmit}>
|
||||
{({ isSubmitting }) => (
|
||||
<Form>
|
||||
<ModalHeader>
|
||||
@@ -98,39 +121,16 @@ function CertificateCreateModal({
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Stack spacing={4}>
|
||||
<Field name="name" validate={validateString(1, 100)}>
|
||||
{({ field, form }: any) => (
|
||||
<FormControl
|
||||
isRequired
|
||||
isInvalid={form.errors.name && form.touched.name}>
|
||||
<FormLabel htmlFor="name">
|
||||
{intl.formatMessage({
|
||||
id: "name",
|
||||
})}
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
id="name"
|
||||
placeholder={intl.formatMessage({
|
||||
id: "name",
|
||||
})}
|
||||
/>
|
||||
<FormErrorMessage>{form.errors.name}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
{certType === "http" ? <HTTPForm /> : null}
|
||||
{certType === "dns" ? <DNSForm /> : null}
|
||||
{certType === "custom" ? <CustomForm /> : null}
|
||||
{certType === "mkcert" ? <MKCertForm /> : null}
|
||||
</Stack>
|
||||
|
||||
{certType === "http" ? <HTTPForm /> : null}
|
||||
{certType === "dns" ? <DNSForm /> : null}
|
||||
{certType === "custom" ? <CustomForm /> : null}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{certType !== "" ? (
|
||||
<PrettyButton mr={3} isLoading={isSubmitting}>
|
||||
{intl.formatMessage({ id: "form.save" })}
|
||||
</PrettyButton>
|
||||
) : null}
|
||||
<PrettyButton mr={3} isLoading={isSubmitting}>
|
||||
{intl.formatMessage({ id: "form.save" })}
|
||||
</PrettyButton>
|
||||
<Button onClick={onModalClose} isLoading={isSubmitting}>
|
||||
{intl.formatMessage({ id: "form.cancel" })}
|
||||
</Button>
|
||||
|
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Select,
|
||||
} from "@chakra-ui/react";
|
||||
import { CertificateAuthority } from "api/npm";
|
||||
import { Field, useFormikContext } from "formik";
|
||||
import { useCertificateAuthorities } from "hooks";
|
||||
import { intl } from "locale";
|
||||
|
||||
const fieldName = "certificateAuthorityId";
|
||||
|
||||
interface CertificateAuthorityFieldProps {
|
||||
onChange?: (maxDomains: number, isWildcardSupported: boolean) => any;
|
||||
}
|
||||
function CertificateAuthorityField({
|
||||
onChange,
|
||||
}: CertificateAuthorityFieldProps) {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { data, isLoading } = useCertificateAuthorities(0, 999, [{ id: "id" }]);
|
||||
|
||||
const handleOnChange = (e: any) => {
|
||||
if (e.currentTarget.value) {
|
||||
const id = parseInt(e.currentTarget.value, 10);
|
||||
// This step enforces that the formik payload has a
|
||||
// string number instead of a string as the value
|
||||
// for this field
|
||||
setFieldValue(fieldName, id);
|
||||
if (onChange) {
|
||||
// find items in list of data
|
||||
const ca = data?.items.find((item) => item.id === id);
|
||||
if (ca) {
|
||||
onChange(ca.maxDomains, ca.isWildcardSupported);
|
||||
} else {
|
||||
onChange(0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Field name={fieldName}>
|
||||
{({ field, form }: any) => (
|
||||
<FormControl
|
||||
isRequired
|
||||
isInvalid={form.errors[fieldName] && form.touched[fieldName]}>
|
||||
<FormLabel htmlFor={fieldName}>
|
||||
{intl.formatMessage({
|
||||
id: "certificate-authority",
|
||||
})}
|
||||
</FormLabel>
|
||||
<Select
|
||||
{...field}
|
||||
id={fieldName}
|
||||
disabled={isLoading}
|
||||
onChange={(e: any) => {
|
||||
field.onChange(e);
|
||||
handleOnChange(e);
|
||||
}}>
|
||||
<option value="" />
|
||||
{data?.items?.map((item: CertificateAuthority) => {
|
||||
return (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<FormErrorMessage>{form.errors[fieldName]}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
export { CertificateAuthorityField };
|
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Select,
|
||||
} from "@chakra-ui/react";
|
||||
import { DNSProvider } from "api/npm";
|
||||
import { Field, useFormikContext } from "formik";
|
||||
import { useDNSProviders } from "hooks";
|
||||
import { intl } from "locale";
|
||||
|
||||
const fieldName = "dnsProviderId";
|
||||
|
||||
function DNSProviderField() {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { data, isLoading } = useDNSProviders(0, 999);
|
||||
|
||||
const handleOnChange = (e: any) => {
|
||||
if (e.currentTarget.value) {
|
||||
const id = parseInt(e.currentTarget.value, 10);
|
||||
// This step enforces that the formik payload has a
|
||||
// string number instead of a string as the value
|
||||
// for this field
|
||||
setFieldValue(fieldName, id);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Field name={fieldName}>
|
||||
{({ field, form }: any) => (
|
||||
<FormControl
|
||||
isRequired
|
||||
isInvalid={
|
||||
!isLoading &&
|
||||
(!data?.total ||
|
||||
(form.errors[fieldName] && form.touched[fieldName]))
|
||||
}>
|
||||
<FormLabel htmlFor={fieldName}>
|
||||
{intl.formatMessage({
|
||||
id: "dns-provider",
|
||||
})}
|
||||
</FormLabel>
|
||||
<Select
|
||||
{...field}
|
||||
id={fieldName}
|
||||
disabled={isLoading}
|
||||
onChange={(e: any) => {
|
||||
field.onChange(e);
|
||||
handleOnChange(e);
|
||||
}}>
|
||||
<option value="" />
|
||||
{data?.items?.map((item: DNSProvider) => {
|
||||
return (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<FormErrorMessage>
|
||||
{!isLoading && !data?.total
|
||||
? intl.formatMessage({
|
||||
id: "dns-providers-empty",
|
||||
})
|
||||
: form.errors[fieldName]}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
export { DNSProviderField };
|
@@ -0,0 +1,109 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
FormHelperText,
|
||||
} from "@chakra-ui/react";
|
||||
import { CreatableSelect, OptionBase } from "chakra-react-select";
|
||||
import { Field, useFormikContext } from "formik";
|
||||
import { intl } from "locale";
|
||||
|
||||
interface SelectOption extends OptionBase {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
interface DomainNamesFieldProps {
|
||||
maxDomains?: number;
|
||||
isWildcardSupported?: boolean;
|
||||
onChange?: (i: string[]) => any;
|
||||
}
|
||||
function DomainNamesField({
|
||||
maxDomains,
|
||||
isWildcardSupported,
|
||||
onChange,
|
||||
}: DomainNamesFieldProps) {
|
||||
const { values, setFieldValue } = useFormikContext();
|
||||
|
||||
const getDomainCount = (v: string[] | undefined) => {
|
||||
if (typeof v !== "undefined" && v?.length) {
|
||||
return v.length;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const isDomainValid = (d: string): boolean => {
|
||||
const dom = d.trim().toLowerCase();
|
||||
const v: any = values;
|
||||
|
||||
// Deny if the list of domains is hit
|
||||
if (maxDomains && getDomainCount(v?.domainNames) >= maxDomains) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dom.length < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent wildcards
|
||||
if (!isWildcardSupported && dom.indexOf("*") !== -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent duplicate * in domain
|
||||
if ((dom.match(/\*/g) || []).length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent some invalid characters
|
||||
// @ ,
|
||||
if ((dom.match(/(@|,)/g) || []).length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This will match *.com type domains,
|
||||
return dom.match(/\*\.[^.]+$/m) === null;
|
||||
};
|
||||
|
||||
const handleChange = (values: any) => {
|
||||
const doms = values?.map((i: SelectOption) => {
|
||||
return i.value;
|
||||
});
|
||||
setFieldValue("domainNames", doms);
|
||||
};
|
||||
|
||||
return (
|
||||
<Field name="domainNames">
|
||||
{({ field, form }: any) => (
|
||||
<FormControl
|
||||
isInvalid={form.errors.domainNames && form.touched.domainNames}>
|
||||
<FormLabel htmlFor="domainNames">
|
||||
{intl.formatMessage({
|
||||
id: "domain_names",
|
||||
})}
|
||||
</FormLabel>
|
||||
<CreatableSelect
|
||||
name={field.domainNames}
|
||||
id="domainNames"
|
||||
closeMenuOnSelect={true}
|
||||
isClearable={false}
|
||||
onChange={handleChange}
|
||||
isValidNewOption={isDomainValid}
|
||||
isMulti
|
||||
placeholder="example.com"
|
||||
/>
|
||||
{maxDomains ? (
|
||||
<FormHelperText>
|
||||
{intl.formatMessage(
|
||||
{ id: "domain_names.max" },
|
||||
{ count: maxDomains },
|
||||
)}
|
||||
</FormHelperText>
|
||||
) : null}
|
||||
<FormErrorMessage>{form.errors.domainNames}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
export { DomainNamesField };
|
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Switch,
|
||||
} from "@chakra-ui/react";
|
||||
import { Field } from "formik";
|
||||
import { intl } from "locale";
|
||||
|
||||
const fieldName = "isEcc";
|
||||
|
||||
function EccField() {
|
||||
return (
|
||||
<Field name={fieldName}>
|
||||
{({ field, form }: any) => (
|
||||
<FormControl
|
||||
isInvalid={form.errors[fieldName] && form.touched[fieldName]}>
|
||||
<FormLabel htmlFor={fieldName}>
|
||||
{intl.formatMessage({
|
||||
id: "is-ecc",
|
||||
})}
|
||||
</FormLabel>
|
||||
<Switch {...field} id={fieldName} />
|
||||
<FormErrorMessage>{form.errors[fieldName]}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
export { EccField };
|
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Input,
|
||||
} from "@chakra-ui/react";
|
||||
import { Field } from "formik";
|
||||
import { intl } from "locale";
|
||||
import { validateString } from "modules/Validations";
|
||||
|
||||
function NameField() {
|
||||
return (
|
||||
<Field name="name" validate={validateString(1, 100)}>
|
||||
{({ field, form }: any) => (
|
||||
<FormControl
|
||||
isRequired
|
||||
isInvalid={form.errors.name && form.touched.name}>
|
||||
<FormLabel htmlFor="name">
|
||||
{intl.formatMessage({
|
||||
id: "name",
|
||||
})}
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
id="name"
|
||||
placeholder={intl.formatMessage({
|
||||
id: "name",
|
||||
})}
|
||||
/>
|
||||
<FormErrorMessage>{form.errors.name}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
export { NameField };
|
@@ -0,0 +1,5 @@
|
||||
export * from "./CertificateAuthorityField";
|
||||
export * from "./DNSProviderField";
|
||||
export * from "./DomainNamesField";
|
||||
export * from "./EccField";
|
||||
export * from "./NameField";
|
@@ -1,5 +1,12 @@
|
||||
import { DomainNamesField, NameField } from "./Common";
|
||||
|
||||
function CustomForm() {
|
||||
return <p>Custom form</p>;
|
||||
return (
|
||||
<>
|
||||
<NameField />
|
||||
<DomainNamesField />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomForm;
|
||||
|
@@ -1,5 +1,34 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import {
|
||||
CertificateAuthorityField,
|
||||
DNSProviderField,
|
||||
DomainNamesField,
|
||||
EccField,
|
||||
NameField,
|
||||
} from "./Common";
|
||||
|
||||
function DNSForm() {
|
||||
return <p>DNS form</p>;
|
||||
const [maxDomains, setMaxDomains] = useState(0);
|
||||
const [isWildcardSupported, setIsWildcardSupported] = useState(false);
|
||||
|
||||
const handleCAChange = (maxD: number, wildcards: boolean) => {
|
||||
setMaxDomains(maxD);
|
||||
setIsWildcardSupported(wildcards);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<NameField />
|
||||
<CertificateAuthorityField onChange={handleCAChange} />
|
||||
<DomainNamesField
|
||||
maxDomains={maxDomains}
|
||||
isWildcardSupported={isWildcardSupported}
|
||||
/>
|
||||
<DNSProviderField />
|
||||
<EccField />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default DNSForm;
|
||||
|
@@ -1,5 +1,32 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import {
|
||||
CertificateAuthorityField,
|
||||
DomainNamesField,
|
||||
EccField,
|
||||
NameField,
|
||||
} from "./Common";
|
||||
|
||||
function HTTPForm() {
|
||||
return <p>Http form</p>;
|
||||
const [maxDomains, setMaxDomains] = useState(0);
|
||||
const [isWildcardSupported, setIsWildcardSupported] = useState(false);
|
||||
|
||||
const handleCAChange = (maxD: number, wildcards: boolean) => {
|
||||
setMaxDomains(maxD);
|
||||
setIsWildcardSupported(wildcards);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<NameField />
|
||||
<CertificateAuthorityField onChange={handleCAChange} />
|
||||
<DomainNamesField
|
||||
maxDomains={maxDomains}
|
||||
isWildcardSupported={isWildcardSupported}
|
||||
/>
|
||||
<EccField />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default HTTPForm;
|
||||
|
12
frontend/src/modals/CertificateCreateModal/MKCertForm.tsx
Normal file
12
frontend/src/modals/CertificateCreateModal/MKCertForm.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { DomainNamesField, NameField } from "./Common";
|
||||
|
||||
function MKCertForm() {
|
||||
return (
|
||||
<>
|
||||
<NameField />
|
||||
<DomainNamesField />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default MKCertForm;
|
@@ -9,14 +9,17 @@ import {
|
||||
MenuDivider,
|
||||
} from "@chakra-ui/react";
|
||||
import { HelpDrawer, PrettyMenuButton } from "components";
|
||||
import { useDNSProviders } from "hooks";
|
||||
import { intl } from "locale";
|
||||
import { CertificateCreateModal } from "modals";
|
||||
import { FiGlobe, FiServer, FiUpload } from "react-icons/fi";
|
||||
import { FiGlobe, FiServer, FiShieldOff, FiUpload } from "react-icons/fi";
|
||||
|
||||
import TableWrapper from "./TableWrapper";
|
||||
|
||||
function Certificates() {
|
||||
const [createShown, setCreateShown] = useState("");
|
||||
const { data: dnsProviders, isLoading: dnsProvidersIsLoading } =
|
||||
useDNSProviders(0, 999);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -37,6 +40,7 @@ function Certificates() {
|
||||
{intl.formatMessage({ id: "type.http" })}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
isDisabled={dnsProvidersIsLoading || !dnsProviders?.total}
|
||||
icon={<FiServer />}
|
||||
onClick={() => setCreateShown("dns")}>
|
||||
{intl.formatMessage({ id: "type.dns" })}
|
||||
@@ -47,6 +51,11 @@ function Certificates() {
|
||||
onClick={() => setCreateShown("custom")}>
|
||||
{intl.formatMessage({ id: "type.custom" })}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<FiShieldOff />}
|
||||
onClick={() => setCreateShown("mkcert")}>
|
||||
{intl.formatMessage({ id: "type.mkcert" })}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</HStack>
|
||||
|
Reference in New Issue
Block a user