mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-10 20:02:26 +00:00
Copy in config to db, remove 2nd column, + prefer
This commit is contained in:
@@ -1895,5 +1895,6 @@
|
|||||||
"certResolver": "Certificate Resolver",
|
"certResolver": "Certificate Resolver",
|
||||||
"certResolverDescription": "Select the certificate resolver to use for this resource.",
|
"certResolverDescription": "Select the certificate resolver to use for this resource.",
|
||||||
"selectCertResolver": "Select Certificate Resolver",
|
"selectCertResolver": "Select Certificate Resolver",
|
||||||
"enterCustomResolver": "Enter Custom Resolver"
|
"enterCustomResolver": "Enter Custom Resolver",
|
||||||
|
"preferWildcardCert": "Prefer Wildcard Certificate"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ export const domains = pgTable("domains", {
|
|||||||
failed: boolean("failed").notNull().default(false),
|
failed: boolean("failed").notNull().default(false),
|
||||||
tries: integer("tries").notNull().default(0),
|
tries: integer("tries").notNull().default(0),
|
||||||
certResolver: varchar("certResolver"),
|
certResolver: varchar("certResolver"),
|
||||||
customCertResolver: varchar("customCertResolver")
|
customCertResolver: varchar("customCertResolver"),
|
||||||
|
preferWildcardCert: boolean("preferWildcardCert")
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgs = pgTable("orgs", {
|
export const orgs = pgTable("orgs", {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const domains = sqliteTable("domains", {
|
|||||||
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
|
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
|
||||||
tries: integer("tries").notNull().default(0),
|
tries: integer("tries").notNull().default(0),
|
||||||
certResolver: text("certResolver"),
|
certResolver: text("certResolver"),
|
||||||
customCertResolver: text("customCertResolver")
|
preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgs = sqliteTable("orgs", {
|
export const orgs = sqliteTable("orgs", {
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ export async function getTraefikConfig(
|
|||||||
subnet: sites.subnet,
|
subnet: sites.subnet,
|
||||||
exitNodeId: sites.exitNodeId,
|
exitNodeId: sites.exitNodeId,
|
||||||
// Domain cert resolver fields
|
// Domain cert resolver fields
|
||||||
domainCertResolver: domains.certResolver,
|
domainCertResolver: domains.certResolver
|
||||||
domainCustomCertResolver: domains.customCertResolver
|
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||||
@@ -167,8 +166,7 @@ export async function getTraefikConfig(
|
|||||||
rewritePathType: row.rewritePathType,
|
rewritePathType: row.rewritePathType,
|
||||||
priority: priority,
|
priority: priority,
|
||||||
// Store domain cert resolver fields
|
// Store domain cert resolver fields
|
||||||
domainCertResolver: row.domainCertResolver,
|
domainCertResolver: row.domainCertResolver
|
||||||
domainCustomCertResolver: row.domainCustomCertResolver
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,42 +245,47 @@ export async function getTraefikConfig(
|
|||||||
wildCard = resource.fullDomain;
|
wildCard = resource.fullDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configDomain = config.getDomain(resource.domainId);
|
const globalDefaultResolver =
|
||||||
const rawTraefikCfg = config.getRawConfig().traefik || {};
|
config.getRawConfig().traefik.cert_resolver;
|
||||||
const globalDefaultResolver = rawTraefikCfg.cert_resolver;
|
const globalDefaultPreferWildcard =
|
||||||
|
config.getRawConfig().traefik.prefer_wildcard_cert;
|
||||||
|
|
||||||
|
const domainCertResolver = resource.domainCertResolver;
|
||||||
const domainCertResolver =
|
const preferWildcardCert = resource.preferWildcardCert;
|
||||||
resource.domainCertResolver ?? configDomain?.cert_resolver;
|
|
||||||
const domainCustomResolver =
|
|
||||||
resource.domainCustomCertResolver;
|
|
||||||
const preferWildcardCert =
|
|
||||||
resource.preferWildcardCert ?? configDomain?.prefer_wildcard_cert ?? false;
|
|
||||||
|
|
||||||
let resolverName: string | undefined;
|
let resolverName: string | undefined;
|
||||||
|
let preferWildcard: boolean | undefined;
|
||||||
// Handle both letsencrypt & custom cases
|
// Handle both letsencrypt & custom cases
|
||||||
if (domainCertResolver === "custom") {
|
if (domainCertResolver) {
|
||||||
resolverName = domainCustomResolver?.trim();
|
resolverName = domainCertResolver.trim();
|
||||||
} else if (domainCertResolver) {
|
|
||||||
resolverName = domainCertResolver;
|
|
||||||
} else {
|
} else {
|
||||||
resolverName = globalDefaultResolver;
|
resolverName = globalDefaultResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tls = {
|
if (
|
||||||
certResolver: resolverName,
|
preferWildcardCert !== undefined &&
|
||||||
...(preferWildcardCert
|
preferWildcardCert !== null
|
||||||
? {
|
) {
|
||||||
domains: [
|
preferWildcard = preferWildcardCert;
|
||||||
{
|
} else {
|
||||||
main: wildCard
|
preferWildcard = globalDefaultPreferWildcard;
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
: {})
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let tls = {};
|
||||||
|
if (build == "oss") {
|
||||||
|
tls = {
|
||||||
|
certResolver: resolverName,
|
||||||
|
...(preferWildcard
|
||||||
|
? {
|
||||||
|
domains: [
|
||||||
|
{
|
||||||
|
main: wildCard
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const additionalMiddlewares =
|
const additionalMiddlewares =
|
||||||
config.getRawConfig().traefik.additional_middlewares || [];
|
config.getRawConfig().traefik.additional_middlewares || [];
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ export async function getTraefikConfig(
|
|||||||
// Certificate
|
// Certificate
|
||||||
certificateStatus: certificates.status,
|
certificateStatus: certificates.status,
|
||||||
domainCertResolver: domains.certResolver,
|
domainCertResolver: domains.certResolver,
|
||||||
domainCustomCertResolver: domains.customCertResolver
|
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||||
@@ -206,7 +205,6 @@ export async function getTraefikConfig(
|
|||||||
rewritePathType: row.rewritePathType,
|
rewritePathType: row.rewritePathType,
|
||||||
priority: priority, // may be null, we fallback later
|
priority: priority, // may be null, we fallback later
|
||||||
domainCertResolver: row.domainCertResolver,
|
domainCertResolver: row.domainCertResolver,
|
||||||
domainCustomCertResolver: row.domainCustomCertResolver
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,29 +304,6 @@ export async function getTraefikConfig(
|
|||||||
wildCard = resource.fullDomain;
|
wildCard = resource.fullDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configDomain = config.getDomain(resource.domainId);
|
|
||||||
const rawTraefikCfg = config.getRawConfig().traefik || {};
|
|
||||||
const globalDefaultResolver = rawTraefikCfg.cert_resolver;
|
|
||||||
|
|
||||||
|
|
||||||
const domainCertResolver =
|
|
||||||
resource.domainCertResolver ?? configDomain?.cert_resolver;
|
|
||||||
const domainCustomResolver =
|
|
||||||
resource.domainCustomCertResolver;
|
|
||||||
const preferWildcardCert =
|
|
||||||
resource.preferWildcardCert ?? configDomain?.prefer_wildcard_cert ?? false;
|
|
||||||
|
|
||||||
let resolverName: string | undefined;
|
|
||||||
|
|
||||||
// Handle both letsencrypt & custom cases
|
|
||||||
if (domainCertResolver === "custom") {
|
|
||||||
resolverName = domainCustomResolver?.trim();
|
|
||||||
} else if (domainCertResolver) {
|
|
||||||
resolverName = domainCertResolver;
|
|
||||||
} else {
|
|
||||||
resolverName = globalDefaultResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tls = {};
|
let tls = {};
|
||||||
if (!privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) {
|
if (!privateConfig.getRawPrivateConfig().flags.use_pangolin_dns) {
|
||||||
const domainParts = fullDomain.split(".");
|
const domainParts = fullDomain.split(".");
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const bodySchema = z
|
|||||||
.object({
|
.object({
|
||||||
type: z.enum(["ns", "cname", "wildcard"]),
|
type: z.enum(["ns", "cname", "wildcard"]),
|
||||||
baseDomain: subdomainSchema,
|
baseDomain: subdomainSchema,
|
||||||
certResolver: z.enum(["letsencrypt", "custom"]).optional(), // optional, only for wildcard
|
certResolver: z.string().optional().nullable(),
|
||||||
customCertResolver: z.string().optional() // required if certResolver === "custom"
|
preferWildcardCert: z.boolean().optional().nullable() // optional, only for wildcard
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ export type CreateDomainResponse = {
|
|||||||
aRecords?: { baseDomain: string; value: string }[];
|
aRecords?: { baseDomain: string; value: string }[];
|
||||||
txtRecords?: { baseDomain: string; value: string }[];
|
txtRecords?: { baseDomain: string; value: string }[];
|
||||||
certResolver?: string | null;
|
certResolver?: string | null;
|
||||||
customCertResolver?: string | null;
|
preferWildcardCert?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to check if a domain is a subdomain or equal to another domain
|
// Helper to check if a domain is a subdomain or equal to another domain
|
||||||
@@ -76,7 +76,7 @@ export async function createOrgDomain(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
const { type, baseDomain, certResolver, customCertResolver } = parsedBody.data;
|
const { type, baseDomain, certResolver, preferWildcardCert } = parsedBody.data;
|
||||||
|
|
||||||
if (build == "oss") {
|
if (build == "oss") {
|
||||||
if (type !== "wildcard") {
|
if (type !== "wildcard") {
|
||||||
@@ -261,7 +261,7 @@ export async function createOrgDomain(
|
|||||||
type,
|
type,
|
||||||
verified: type === "wildcard" ? true : false,
|
verified: type === "wildcard" ? true : false,
|
||||||
certResolver: certResolver || null,
|
certResolver: certResolver || null,
|
||||||
customCertResolver: customCertResolver || null
|
preferWildcardCert: preferWildcardCert || false
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ export async function createOrgDomain(
|
|||||||
nsRecords,
|
nsRecords,
|
||||||
aRecords,
|
aRecords,
|
||||||
certResolver: returned.certResolver,
|
certResolver: returned.certResolver,
|
||||||
customCertResolver: returned.customCertResolver
|
preferWildcardCert: returned.preferWildcardCert
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ async function queryDomains(orgId: string, limit: number, offset: number) {
|
|||||||
tries: domains.tries,
|
tries: domains.tries,
|
||||||
configManaged: domains.configManaged,
|
configManaged: domains.configManaged,
|
||||||
certResolver: domains.certResolver,
|
certResolver: domains.certResolver,
|
||||||
customCertResolver: domains.customCertResolver,
|
preferWildcardCert: domains.preferWildcardCert
|
||||||
})
|
})
|
||||||
.from(orgDomains)
|
.from(orgDomains)
|
||||||
.where(eq(orgDomains.orgId, orgId))
|
.where(eq(orgDomains.orgId, orgId))
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ async function copyInDomains() {
|
|||||||
const configDomains = Object.entries(rawDomains).map(
|
const configDomains = Object.entries(rawDomains).map(
|
||||||
([key, value]) => ({
|
([key, value]) => ({
|
||||||
domainId: key,
|
domainId: key,
|
||||||
baseDomain: value.base_domain.toLowerCase()
|
baseDomain: value.base_domain.toLowerCase(),
|
||||||
|
certResolver: value.cert_resolver || null,
|
||||||
|
preferWildcardCert: value.prefer_wildcard_cert || null
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -59,11 +61,11 @@ async function copyInDomains() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { domainId, baseDomain } of configDomains) {
|
for (const { domainId, baseDomain, certResolver, preferWildcardCert } of configDomains) {
|
||||||
if (existingDomainKeys.has(domainId)) {
|
if (existingDomainKeys.has(domainId)) {
|
||||||
await trx
|
await trx
|
||||||
.update(domains)
|
.update(domains)
|
||||||
.set({ baseDomain, verified: true, type: "wildcard" })
|
.set({ baseDomain, verified: true, type: "wildcard", certResolver, preferWildcardCert })
|
||||||
.where(eq(domains.domainId, domainId))
|
.where(eq(domains.domainId, domainId))
|
||||||
.execute();
|
.execute();
|
||||||
} else {
|
} else {
|
||||||
@@ -74,7 +76,9 @@ async function copyInDomains() {
|
|||||||
baseDomain,
|
baseDomain,
|
||||||
configManaged: true,
|
configManaged: true,
|
||||||
type: "wildcard",
|
type: "wildcard",
|
||||||
verified: true
|
verified: true,
|
||||||
|
certResolver,
|
||||||
|
preferWildcardCert
|
||||||
})
|
})
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
FormDescription
|
FormDescription
|
||||||
} from "@app/components/ui/form";
|
} from "@app/components/ui/form";
|
||||||
import { Input } from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
|
import { Checkbox, CheckboxWithLabel } from "@app/components/ui/checkbox";
|
||||||
import { useToast } from "@app/hooks/useToast";
|
import { useToast } from "@app/hooks/useToast";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
@@ -98,8 +99,8 @@ const formSchema = z.object({
|
|||||||
.refine((val) => isValidDomainFormat(val), "Invalid domain format")
|
.refine((val) => isValidDomainFormat(val), "Invalid domain format")
|
||||||
.transform((val) => toPunycode(val)),
|
.transform((val) => toPunycode(val)),
|
||||||
type: z.enum(["ns", "cname", "wildcard"]),
|
type: z.enum(["ns", "cname", "wildcard"]),
|
||||||
certResolver: z.string().optional(),
|
certResolver: z.string().nullable().optional(),
|
||||||
customCertResolver: z.string().optional()
|
preferWildcardCert: z.boolean().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
@@ -112,7 +113,7 @@ type CreateDomainFormProps = {
|
|||||||
|
|
||||||
// Example cert resolver options (replace with real API/fetch if needed)
|
// Example cert resolver options (replace with real API/fetch if needed)
|
||||||
const certResolverOptions = [
|
const certResolverOptions = [
|
||||||
{ id: "letsencrypt", title: "Let's Encrypt" },
|
{ id: "default", title: "Default" },
|
||||||
{ id: "custom", title: "Custom Resolver" }
|
{ id: "custom", title: "Custom Resolver" }
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -135,8 +136,8 @@ export default function CreateDomainForm({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
baseDomain: "",
|
baseDomain: "",
|
||||||
type: build == "oss" || !env.flags.usePangolinDns ? "wildcard" : "ns",
|
type: build == "oss" || !env.flags.usePangolinDns ? "wildcard" : "ns",
|
||||||
certResolver: "",
|
certResolver: null,
|
||||||
customCertResolver: ""
|
preferWildcardCert: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -281,8 +282,20 @@ export default function CreateDomainForm({
|
|||||||
<FormLabel>{t("certResolver")}</FormLabel>
|
<FormLabel>{t("certResolver")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
value={field.value}
|
value={
|
||||||
onValueChange={(val) => field.onChange(val)}
|
field.value === null ? "default" :
|
||||||
|
(field.value === "" || (field.value && field.value !== "default")) ? "custom" :
|
||||||
|
"default"
|
||||||
|
}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
if (val === "default") {
|
||||||
|
field.onChange(null);
|
||||||
|
} else if (val === "custom") {
|
||||||
|
field.onChange("");
|
||||||
|
} else {
|
||||||
|
field.onChange(val);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder={t("selectCertResolver")} />
|
<SelectValue placeholder={t("selectCertResolver")} />
|
||||||
@@ -297,21 +310,40 @@ export default function CreateDomainForm({
|
|||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
{field.value === "custom" && (
|
{field.value !== null && field.value !== "default" && (
|
||||||
<FormControl className="mt-2">
|
<div className="space-y-2 mt-2">
|
||||||
<Input
|
<FormControl>
|
||||||
placeholder={t("enterCustomResolver")}
|
<Input
|
||||||
value={form.watch("customCertResolver") || ""}
|
placeholder={t("enterCustomResolver")}
|
||||||
onChange={(e) =>
|
value={field.value || ""}
|
||||||
form.setValue("customCertResolver", e.target.value)
|
onChange={(e) => field.onChange(e.target.value)}
|
||||||
}
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="preferWildcardCert"
|
||||||
|
render={({ field: checkboxField }) => (
|
||||||
|
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<CheckboxWithLabel
|
||||||
|
label={t("preferWildcardCert")}
|
||||||
|
checked={checkboxField.value}
|
||||||
|
onCheckedChange={checkboxField.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
{/* <div className="space-y-1 leading-none">
|
||||||
|
<FormLabel>
|
||||||
|
{t("preferWildcardCert")}
|
||||||
|
</FormLabel>
|
||||||
|
</div> */}
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</div>
|
||||||
)}
|
)}
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
Reference in New Issue
Block a user