This commit is contained in:
星野梦月 2026-09-11 13:11:52 -04:00 committed by GitHub
commit 6ad718c670
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 97 additions and 5 deletions

View File

@ -196,6 +196,16 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
},
);
const { data: certificateResolvers } =
api.domain.certificateResolvers.useQuery(
{
serverId: application?.serverId || undefined,
},
{
enabled: isOpen,
},
);
const {
data: services,
isFetching: isLoadingServices,
@ -234,12 +244,23 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
});
const certificateType = form.watch("certificateType");
const customCertResolver = form.watch("customCertResolver");
const useCustomEntrypoint = form.watch("useCustomEntrypoint");
const https = form.watch("https");
const domainType = form.watch("domainType");
const host = form.watch("host");
const isTraefikMeDomain = host?.includes("sslip.io") || false;
// Synthetic value for the certificate provider Select: detected resolvers
// from traefik.yml are stored as certificateType="custom" +
// customCertResolver=<name>, but displayed as their own option.
const certSelectValue =
certificateType === "custom" &&
customCertResolver &&
certificateResolvers?.includes(customCertResolver)
? `resolver:${customCertResolver}`
: (certificateType ?? "");
useEffect(() => {
if (data) {
form.reset({
@ -750,15 +771,29 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
<FormLabel>Certificate Provider</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
if (value !== "custom") {
if (value.startsWith("resolver:")) {
field.onChange("custom");
form.setValue(
"customCertResolver",
undefined,
value.slice("resolver:".length),
);
} else {
field.onChange(value);
if (
value !== "custom" ||
(customCertResolver &&
certificateResolvers?.includes(
customCertResolver,
))
) {
form.setValue(
"customCertResolver",
undefined,
);
}
}
}}
value={field.value}
value={certSelectValue}
>
<FormControl>
<SelectTrigger>
@ -771,6 +806,18 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
Let's Encrypt
</SelectItem>
<SelectItem value={"custom"}>Custom</SelectItem>
{certificateResolvers
?.filter(
(resolver) => resolver !== "letsencrypt",
)
.map((resolver) => (
<SelectItem
key={resolver}
value={`resolver:${resolver}`}
>
{resolver}
</SelectItem>
))}
</SelectContent>
</Select>
<FormDescription>
@ -810,7 +857,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
}}
/>
{certificateType === "custom" && (
{certSelectValue === "custom" && (
<FormField
control={form.control}
name="customCertResolver"

View File

@ -7,8 +7,10 @@ import {
findPreviewDeploymentById,
findServerById,
generateTraefikMeDomain,
getCertificateResolvers,
getServerIpCandidates,
getWebServerSettings,
IS_CLOUD,
manageDomain,
removeDomain,
removeDomainById,
@ -101,6 +103,25 @@ export const domainRouter = createTRPCRouter({
return settings?.serverIp || "";
}),
certificateResolvers: withPermission("domain", "read")
.input(z.object({ serverId: z.string().optional() }))
.query(async ({ input, ctx }) => {
if (input.serverId) {
const server = await findServerById(input.serverId);
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You don't have access to this server",
});
}
return getCertificateResolvers(input.serverId);
}
if (IS_CLOUD) {
return [];
}
return getCertificateResolvers();
}),
update: protectedProcedure
.input(apiUpdateDomain)
.mutation(async ({ input, ctx }) => {

View File

@ -3,6 +3,7 @@ import { join } from "node:path";
import { paths } from "@dokploy/server/constants";
import type { webServerSettings } from "@dokploy/server/db/schema/web-server-settings";
import { parse, stringify } from "yaml";
import { execAsyncRemote } from "../process/execAsync";
import {
loadOrCreateConfig,
removeTraefikConfig,
@ -109,6 +110,29 @@ export const readMainConfig = () => {
return null;
};
export const getCertificateResolvers = async (
serverId?: string | null,
): Promise<string[]> => {
let yamlStr: string | null = null;
if (serverId) {
const { MAIN_TRAEFIK_PATH } = paths(true);
const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml");
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
yamlStr = stdout || null;
} else {
yamlStr = readMainConfig();
}
if (!yamlStr) return [];
const config = parse(yamlStr) as MainTraefikConfig;
if (
!config?.certificatesResolvers ||
typeof config.certificatesResolvers !== "object"
) {
return [];
}
return Object.keys(config.certificatesResolvers);
};
export const writeMainConfig = (traefikConfig: string) => {
try {
const { MAIN_TRAEFIK_PATH } = paths();