mirror of
https://github.com/andwati/dokploy.git
synced 2026-10-21 12:34:29 +00:00
feat: add DNS provider integration (Cloudflare, AWS Route53)
Lets you connect a DNS provider and manage its records (create, update, delete) from Settings -> DNS Providers, instead of doing it by hand in Cloudflare/AWS. - dns_provider table, org-scoped, jsonb config as a discriminated union per provider type - Cloudflare adapter (REST, bearer token) - Route53 adapter (AWS SDK, SigV4); records are identified by type:name since Route53 has no native record id, so update handles renames as delete-old + upsert-new - listZones/listRecords/createRecord/updateRecord/deleteRecord/ testConnection wired through a shared DnsClient interface - Settings UI: provider management, zone browser, record CRUD, IP-fill dropdown for A records (panel IP + remote servers) - Access control: dnsProvider resource wired into custom roles - Unit tests for both adapters and the config mask/merge logic
This commit is contained in:
@@ -93,4 +93,5 @@ export type AuditResourceType =
|
||||
| "application"
|
||||
| "compose"
|
||||
| "network"
|
||||
| "vaultProvider";
|
||||
| "vaultProvider"
|
||||
| "dnsProvider";
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import { jsonb, pgEnum, pgTable, text, uniqueIndex } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { organization } from "./account";
|
||||
|
||||
export const dnsProviderType = pgEnum("DnsProviderType", [
|
||||
"cloudflare",
|
||||
"route53",
|
||||
]);
|
||||
|
||||
export const cloudflareDnsConfigSchema = z.object({
|
||||
providerType: z.literal("cloudflare"),
|
||||
apiToken: z.string().trim().min(1),
|
||||
});
|
||||
|
||||
export const route53DnsConfigSchema = z.object({
|
||||
providerType: z.literal("route53"),
|
||||
accessKeyId: z.string().trim().min(1),
|
||||
secretAccessKey: z.string().trim().min(1),
|
||||
endpoint: z.string().trim().url().optional(),
|
||||
});
|
||||
|
||||
export const dnsProviderConfigSchema = z.discriminatedUnion("providerType", [
|
||||
cloudflareDnsConfigSchema,
|
||||
route53DnsConfigSchema,
|
||||
]);
|
||||
|
||||
export type DnsProviderConfig = z.infer<typeof dnsProviderConfigSchema>;
|
||||
|
||||
export const dnsProvider = pgTable(
|
||||
"dns_provider",
|
||||
{
|
||||
dnsProviderId: text("dnsProviderId")
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
name: text("name").notNull(),
|
||||
providerType: dnsProviderType("providerType").notNull(),
|
||||
config: jsonb("config").$type<DnsProviderConfig>().notNull(),
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
},
|
||||
(table) => [
|
||||
uniqueIndex("dns_provider_org_name_idx").on(
|
||||
table.organizationId,
|
||||
table.name,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
const dnsProviderNameSchema = z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(64)
|
||||
.regex(
|
||||
/^[a-zA-Z0-9_-]+$/,
|
||||
"Name can only contain letters, numbers, dashes and underscores",
|
||||
);
|
||||
|
||||
const createSchema = createInsertSchema(dnsProvider);
|
||||
|
||||
export const apiCreateDnsProvider = createSchema.pick({}).extend({
|
||||
name: dnsProviderNameSchema,
|
||||
config: dnsProviderConfigSchema,
|
||||
});
|
||||
|
||||
export const apiUpdateDnsProvider = createSchema.pick({}).extend({
|
||||
dnsProviderId: z.string().min(1),
|
||||
name: dnsProviderNameSchema,
|
||||
config: dnsProviderConfigSchema,
|
||||
});
|
||||
|
||||
export const apiFindOneDnsProvider = z.object({
|
||||
dnsProviderId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiRemoveDnsProvider = z.object({
|
||||
dnsProviderId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiTestDnsProvider = z.object({
|
||||
dnsProviderId: z.string().min(1).optional(),
|
||||
config: dnsProviderConfigSchema.optional(),
|
||||
});
|
||||
|
||||
export const apiListDnsZones = z.object({
|
||||
dnsProviderId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiListDnsRecords = z.object({
|
||||
dnsProviderId: z.string().min(1),
|
||||
zoneId: z.string().min(1),
|
||||
});
|
||||
|
||||
const dnsRecordFieldsSchema = z.object({
|
||||
type: z.enum(["A", "CNAME"]),
|
||||
name: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
ttl: z.number().int().positive().optional(),
|
||||
});
|
||||
|
||||
export const apiCreateDnsRecord = dnsRecordFieldsSchema.extend({
|
||||
dnsProviderId: z.string().min(1),
|
||||
zoneId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiUpdateDnsRecord = dnsRecordFieldsSchema.extend({
|
||||
dnsProviderId: z.string().min(1),
|
||||
zoneId: z.string().min(1),
|
||||
recordId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiDeleteDnsRecord = z.object({
|
||||
dnsProviderId: z.string().min(1),
|
||||
zoneId: z.string().min(1),
|
||||
recordId: z.string().min(1),
|
||||
});
|
||||
@@ -8,6 +8,7 @@ export * from "./certificate";
|
||||
export * from "./compose";
|
||||
export * from "./deployment";
|
||||
export * from "./destination";
|
||||
export * from "./dns-provider";
|
||||
export * from "./domain";
|
||||
export * from "./environment";
|
||||
export * from "./forward-auth";
|
||||
|
||||
Reference in New Issue
Block a user