mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-03 08:39:09 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e986def78 | ||
|
|
d16a05959d | ||
|
|
7e58e0b490 | ||
|
|
9b01aecf3c | ||
|
|
86043fd5f8 | ||
|
|
372a1758e9 | ||
|
|
0a2b1d9e53 | ||
|
|
e562946308 | ||
|
|
398e15b3c6 |
@@ -16,7 +16,7 @@ _Pangolin tunnels your services to the internet so you can access anything from
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="https://fossorial.io">
|
<a href="https://digpangolin.com">
|
||||||
Website
|
Website
|
||||||
</a>
|
</a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
@@ -111,7 +111,7 @@ Host the full application on your own server or on the cloud with a VPS. Take a
|
|||||||
|
|
||||||
### Pangolin Cloud
|
### Pangolin Cloud
|
||||||
|
|
||||||
Easy to use with simple [pay as you go pricing](https://fossorial.io/pricing). [Check it out here](https://pangolin.fossorial.io/auth/signup).
|
Easy to use with simple [pay as you go pricing](https://digpangolin.io/pricing). [Check it out here](https://pangolin.fossorial.io/auth/signup).
|
||||||
|
|
||||||
- Everything you get with self hosted Pangolin, but fully managed for you.
|
- Everything you get with self hosted Pangolin, but fully managed for you.
|
||||||
|
|
||||||
|
|||||||
@@ -1266,6 +1266,7 @@
|
|||||||
"createDomainName": "Name:",
|
"createDomainName": "Name:",
|
||||||
"createDomainValue": "Value:",
|
"createDomainValue": "Value:",
|
||||||
"createDomainCnameRecords": "CNAME Records",
|
"createDomainCnameRecords": "CNAME Records",
|
||||||
|
"createDomainARecords": "A Records",
|
||||||
"createDomainRecordNumber": "Record {number}",
|
"createDomainRecordNumber": "Record {number}",
|
||||||
"createDomainTxtRecords": "TXT Records",
|
"createDomainTxtRecords": "TXT Records",
|
||||||
"createDomainSaveTheseRecords": "Save These Records",
|
"createDomainSaveTheseRecords": "Save These Records",
|
||||||
|
|||||||
@@ -128,7 +128,9 @@ export const configSchema = z
|
|||||||
.object({
|
.object({
|
||||||
http_entrypoint: z.string().optional().default("web"),
|
http_entrypoint: z.string().optional().default("web"),
|
||||||
https_entrypoint: z.string().optional().default("websecure"),
|
https_entrypoint: z.string().optional().default("websecure"),
|
||||||
additional_middlewares: z.array(z.string()).optional()
|
additional_middlewares: z.array(z.string()).optional(),
|
||||||
|
cert_resolver: z.string().optional().default("letsencrypt"),
|
||||||
|
prefer_wildcard_cert: z.boolean().optional().default(false)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.default({}),
|
||||||
@@ -157,13 +159,16 @@ export const configSchema = z
|
|||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.default({}),
|
||||||
orgs: z.object({
|
orgs: z
|
||||||
block_size: z.number().positive().gt(0).optional().default(24),
|
.object({
|
||||||
subnet_group: z.string().optional().default("100.90.128.0/24")
|
block_size: z.number().positive().gt(0).optional().default(24),
|
||||||
}).optional().default({
|
subnet_group: z.string().optional().default("100.90.128.0/24")
|
||||||
block_size: 24,
|
})
|
||||||
subnet_group: "100.90.128.0/24"
|
.optional()
|
||||||
}),
|
.default({
|
||||||
|
block_size: 24,
|
||||||
|
subnet_group: "100.90.128.0/24"
|
||||||
|
}),
|
||||||
rate_limits: z
|
rate_limits: z
|
||||||
.object({
|
.object({
|
||||||
global: z
|
global: z
|
||||||
@@ -242,7 +247,7 @@ export const configSchema = z
|
|||||||
{
|
{
|
||||||
message: "At least one domain must be defined"
|
message: "At least one domain must be defined"
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
|
|
||||||
export function readConfigFile() {
|
export function readConfigFile() {
|
||||||
const loadConfig = (configPath: string) => {
|
const loadConfig = (configPath: string) => {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export type CreateDomainResponse = {
|
|||||||
domainId: string;
|
domainId: string;
|
||||||
nsRecords?: string[];
|
nsRecords?: string[];
|
||||||
cnameRecords?: { baseDomain: string; value: string }[];
|
cnameRecords?: { baseDomain: string; value: string }[];
|
||||||
|
aRecords?: { baseDomain: string; value: string }[];
|
||||||
txtRecords?: { baseDomain: string; value: string }[];
|
txtRecords?: { baseDomain: string; value: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,6 +98,7 @@ export async function createOrgDomain(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let numOrgDomains: OrgDomains[] | undefined;
|
let numOrgDomains: OrgDomains[] | undefined;
|
||||||
|
let aRecords: CreateDomainResponse["aRecords"];
|
||||||
let cnameRecords: CreateDomainResponse["cnameRecords"];
|
let cnameRecords: CreateDomainResponse["cnameRecords"];
|
||||||
let txtRecords: CreateDomainResponse["txtRecords"];
|
let txtRecords: CreateDomainResponse["txtRecords"];
|
||||||
let nsRecords: CreateDomainResponse["nsRecords"];
|
let nsRecords: CreateDomainResponse["nsRecords"];
|
||||||
@@ -239,7 +241,7 @@ export async function createOrgDomain(
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
} else if (type === "wildcard") {
|
} else if (type === "wildcard") {
|
||||||
cnameRecords = [
|
aRecords = [
|
||||||
{
|
{
|
||||||
value: `Server IP Address`,
|
value: `Server IP Address`,
|
||||||
baseDomain: `*.${baseDomain}`
|
baseDomain: `*.${baseDomain}`
|
||||||
@@ -271,7 +273,8 @@ export async function createOrgDomain(
|
|||||||
domainId: returned.domainId,
|
domainId: returned.domainId,
|
||||||
cnameRecords,
|
cnameRecords,
|
||||||
txtRecords,
|
txtRecords,
|
||||||
nsRecords
|
nsRecords,
|
||||||
|
aRecords
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
|||||||
@@ -214,22 +214,29 @@ export async function traefikConfigProvider(
|
|||||||
|
|
||||||
const configDomain = config.getDomain(resource.domainId);
|
const configDomain = config.getDomain(resource.domainId);
|
||||||
|
|
||||||
let tls = {};
|
let certResolver: string, preferWildcardCert: boolean;
|
||||||
if (configDomain) {
|
if (!configDomain) {
|
||||||
tls = {
|
certResolver = config.getRawConfig().traefik.cert_resolver;
|
||||||
certResolver: configDomain.cert_resolver,
|
preferWildcardCert =
|
||||||
...(configDomain.prefer_wildcard_cert
|
config.getRawConfig().traefik.prefer_wildcard_cert;
|
||||||
? {
|
} else {
|
||||||
domains: [
|
certResolver = configDomain.cert_resolver;
|
||||||
{
|
preferWildcardCert = configDomain.prefer_wildcard_cert;
|
||||||
main: wildCard
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {})
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tls = {
|
||||||
|
certResolver: certResolver,
|
||||||
|
...(preferWildcardCert
|
||||||
|
? {
|
||||||
|
domains: [
|
||||||
|
{
|
||||||
|
main: wildCard
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
};
|
||||||
|
|
||||||
const additionalMiddlewares =
|
const additionalMiddlewares =
|
||||||
config.getRawConfig().traefik.additional_middlewares || [];
|
config.getRawConfig().traefik.additional_middlewares || [];
|
||||||
|
|
||||||
|
|||||||
@@ -205,188 +205,267 @@ export default function CreateDomainForm({
|
|||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{domainType === "ns" &&
|
{createdDomain.nsRecords &&
|
||||||
createdDomain.nsRecords && (
|
createdDomain.nsRecords.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium mb-3">
|
||||||
|
{t("createDomainNsRecords")}
|
||||||
|
</h3>
|
||||||
|
<InfoSections cols={1}>
|
||||||
|
<InfoSection>
|
||||||
|
<InfoSectionTitle>
|
||||||
|
{t("createDomainRecord")}
|
||||||
|
</InfoSectionTitle>
|
||||||
|
<InfoSectionContent>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{t(
|
||||||
|
"createDomainType"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-mono">
|
||||||
|
NS
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{t(
|
||||||
|
"createDomainName"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-mono">
|
||||||
|
{baseDomain}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{t(
|
||||||
|
"createDomainValue"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
{createdDomain.nsRecords.map(
|
||||||
|
(
|
||||||
|
nsRecord,
|
||||||
|
index
|
||||||
|
) => (
|
||||||
|
<div
|
||||||
|
className="flex justify-between items-center"
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<CopyToClipboard
|
||||||
|
text={
|
||||||
|
nsRecord
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</InfoSectionContent>
|
||||||
|
</InfoSection>
|
||||||
|
</InfoSections>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{createdDomain.cnameRecords &&
|
||||||
|
createdDomain.cnameRecords.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium mb-3">
|
<h3 className="font-medium mb-3">
|
||||||
{t("createDomainNsRecords")}
|
{t("createDomainCnameRecords")}
|
||||||
</h3>
|
</h3>
|
||||||
<InfoSections cols={1}>
|
<InfoSections cols={1}>
|
||||||
<InfoSection>
|
{createdDomain.cnameRecords.map(
|
||||||
<InfoSectionTitle>
|
(cnameRecord, index) => (
|
||||||
{t("createDomainRecord")}
|
<InfoSection
|
||||||
</InfoSectionTitle>
|
key={index}
|
||||||
<InfoSectionContent>
|
>
|
||||||
<div className="space-y-2">
|
<InfoSectionTitle>
|
||||||
<div className="flex justify-between items-center">
|
{t(
|
||||||
<span className="text-sm font-medium">
|
"createDomainRecordNumber",
|
||||||
{t("createDomainType")}
|
{
|
||||||
</span>
|
number:
|
||||||
<span className="text-sm font-mono">
|
index +
|
||||||
NS
|
1
|
||||||
</span>
|
}
|
||||||
</div>
|
)}
|
||||||
<div className="flex justify-between items-center">
|
</InfoSectionTitle>
|
||||||
<span className="text-sm font-medium">
|
<InfoSectionContent>
|
||||||
{t("createDomainName")}
|
<div className="space-y-2">
|
||||||
</span>
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-sm font-mono">
|
<span className="text-sm font-medium">
|
||||||
{baseDomain}
|
{t(
|
||||||
</span>
|
"createDomainType"
|
||||||
</div>
|
)}
|
||||||
<span className="text-sm font-medium">
|
</span>
|
||||||
{t("createDomainValue")}
|
<span className="text-sm font-mono">
|
||||||
</span>
|
CNAME
|
||||||
{createdDomain.nsRecords.map(
|
</span>
|
||||||
(
|
</div>
|
||||||
nsRecord,
|
<div className="flex justify-between items-center">
|
||||||
index
|
<span className="text-sm font-medium">
|
||||||
) => (
|
{t(
|
||||||
<div
|
"createDomainName"
|
||||||
className="flex justify-between items-center"
|
)}
|
||||||
key={
|
</span>
|
||||||
index
|
<span className="text-sm font-mono">
|
||||||
}
|
{
|
||||||
>
|
cnameRecord.baseDomain
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{t(
|
||||||
|
"createDomainValue"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
<CopyToClipboard
|
<CopyToClipboard
|
||||||
text={
|
text={
|
||||||
nsRecord
|
cnameRecord.value
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
)}
|
</InfoSectionContent>
|
||||||
</div>
|
</InfoSection>
|
||||||
</InfoSectionContent>
|
)
|
||||||
</InfoSection>
|
)}
|
||||||
</InfoSections>
|
</InfoSections>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{domainType === "cname" ||
|
{createdDomain.aRecords &&
|
||||||
(domainType == "wildcard" && (
|
createdDomain.aRecords.length > 0 && (
|
||||||
<>
|
<div>
|
||||||
{createdDomain.cnameRecords &&
|
<h3 className="font-medium mb-3">
|
||||||
createdDomain.cnameRecords
|
{t("createDomainARecords")}
|
||||||
.length > 0 && (
|
</h3>
|
||||||
<div>
|
<InfoSections cols={1}>
|
||||||
<h3 className="font-medium mb-3">
|
{createdDomain.aRecords.map(
|
||||||
{t("createDomainCnameRecords")}
|
(aRecord, index) => (
|
||||||
</h3>
|
<InfoSection
|
||||||
<InfoSections cols={1}>
|
key={index}
|
||||||
{createdDomain.cnameRecords.map(
|
>
|
||||||
(
|
<InfoSectionTitle>
|
||||||
cnameRecord,
|
{t(
|
||||||
index
|
"createDomainRecordNumber",
|
||||||
) => (
|
{
|
||||||
<InfoSection
|
number:
|
||||||
key={
|
index +
|
||||||
index
|
1
|
||||||
}
|
}
|
||||||
>
|
)}
|
||||||
<InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
{t("createDomainRecordNumber", { number: index + 1 })}
|
<InfoSectionContent>
|
||||||
</InfoSectionTitle>
|
<div className="space-y-2">
|
||||||
<InfoSectionContent>
|
<div className="flex justify-between items-center">
|
||||||
<div className="space-y-2">
|
<span className="text-sm font-medium">
|
||||||
<div className="flex justify-between items-center">
|
{t(
|
||||||
<span className="text-sm font-medium">
|
"createDomainType"
|
||||||
{t("createDomainType")}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm font-mono">
|
<span className="text-sm font-mono">
|
||||||
CNAME
|
A
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{t("createDomainName")}
|
{t(
|
||||||
</span>
|
"createDomainName"
|
||||||
<span className="text-sm font-mono">
|
)}
|
||||||
{
|
</span>
|
||||||
cnameRecord.baseDomain
|
<span className="text-sm font-mono">
|
||||||
}
|
{
|
||||||
</span>
|
aRecord.baseDomain
|
||||||
</div>
|
}
|
||||||
<div className="flex justify-between items-center">
|
</span>
|
||||||
<span className="text-sm font-medium">
|
</div>
|
||||||
{t("createDomainValue")}
|
<div className="flex justify-between items-center">
|
||||||
</span>
|
<span className="text-sm font-medium">
|
||||||
<CopyToClipboard
|
{t(
|
||||||
text={
|
"createDomainValue"
|
||||||
cnameRecord.value
|
)}
|
||||||
}
|
</span>
|
||||||
/>
|
<span className="text-sm font-mono">
|
||||||
</div>
|
{
|
||||||
</div>
|
aRecord.value
|
||||||
</InfoSectionContent>
|
}
|
||||||
</InfoSection>
|
</span>
|
||||||
)
|
</div>
|
||||||
)}
|
</div>
|
||||||
</InfoSections>
|
</InfoSectionContent>
|
||||||
</div>
|
</InfoSection>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
|
</InfoSections>
|
||||||
{createdDomain.txtRecords &&
|
</div>
|
||||||
createdDomain.txtRecords
|
)}
|
||||||
.length > 0 && (
|
{createdDomain.txtRecords &&
|
||||||
<div>
|
createdDomain.txtRecords.length > 0 && (
|
||||||
<h3 className="font-medium mb-3">
|
<div>
|
||||||
{t("createDomainTxtRecords")}
|
<h3 className="font-medium mb-3">
|
||||||
</h3>
|
{t("createDomainTxtRecords")}
|
||||||
<InfoSections cols={1}>
|
</h3>
|
||||||
{createdDomain.txtRecords.map(
|
<InfoSections cols={1}>
|
||||||
(
|
{createdDomain.txtRecords.map(
|
||||||
txtRecord,
|
(txtRecord, index) => (
|
||||||
index
|
<InfoSection
|
||||||
) => (
|
key={index}
|
||||||
<InfoSection
|
>
|
||||||
key={
|
<InfoSectionTitle>
|
||||||
index
|
{t(
|
||||||
}
|
"createDomainRecordNumber",
|
||||||
>
|
{
|
||||||
<InfoSectionTitle>
|
number:
|
||||||
{t("createDomainRecordNumber", { number: index + 1 })}
|
index +
|
||||||
</InfoSectionTitle>
|
1
|
||||||
<InfoSectionContent>
|
}
|
||||||
<div className="space-y-2">
|
)}
|
||||||
<div className="flex justify-between items-center">
|
</InfoSectionTitle>
|
||||||
<span className="text-sm font-medium">
|
<InfoSectionContent>
|
||||||
{t("createDomainType")}
|
<div className="space-y-2">
|
||||||
</span>
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-sm font-mono">
|
<span className="text-sm font-medium">
|
||||||
TXT
|
{t(
|
||||||
</span>
|
"createDomainType"
|
||||||
</div>
|
)}
|
||||||
<div className="flex justify-between items-center">
|
</span>
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-mono">
|
||||||
{t("createDomainName")}
|
TXT
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm font-mono">
|
</div>
|
||||||
{
|
<div className="flex justify-between items-center">
|
||||||
txtRecord.baseDomain
|
<span className="text-sm font-medium">
|
||||||
}
|
{t(
|
||||||
</span>
|
"createDomainName"
|
||||||
</div>
|
)}
|
||||||
<div className="flex justify-between items-center">
|
</span>
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-mono">
|
||||||
{t("createDomainValue")}
|
{
|
||||||
</span>
|
txtRecord.baseDomain
|
||||||
<CopyToClipboard
|
}
|
||||||
text={
|
</span>
|
||||||
txtRecord.value
|
</div>
|
||||||
}
|
<div className="flex justify-between items-center">
|
||||||
/>
|
<span className="text-sm font-medium">
|
||||||
</div>
|
{t(
|
||||||
</div>
|
"createDomainValue"
|
||||||
</InfoSectionContent>
|
)}
|
||||||
</InfoSection>
|
</span>
|
||||||
)
|
<CopyToClipboard
|
||||||
)}
|
text={
|
||||||
</InfoSections>
|
txtRecord.value
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</InfoSectionContent>
|
||||||
|
</InfoSection>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</>
|
</InfoSections>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{build == "saas" ||
|
{build == "saas" ||
|
||||||
@@ -397,7 +476,9 @@ export default function CreateDomainForm({
|
|||||||
{t("createDomainSaveTheseRecords")}
|
{t("createDomainSaveTheseRecords")}
|
||||||
</AlertTitle>
|
</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{t("createDomainSaveTheseRecordsDescription")}
|
{t(
|
||||||
|
"createDomainSaveTheseRecordsDescription"
|
||||||
|
)}
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user