mirror of
https://github.com/fosrl/pangolin.git
synced 2025-05-13 05:40:38 +01:00
commit
21f1326045
10 changed files with 40 additions and 54 deletions
|
@ -17,7 +17,7 @@ function detectIpVersion(ip: string): IPVersion {
|
||||||
*/
|
*/
|
||||||
function ipToBigInt(ip: string): bigint {
|
function ipToBigInt(ip: string): bigint {
|
||||||
const version = detectIpVersion(ip);
|
const version = detectIpVersion(ip);
|
||||||
|
|
||||||
if (version === 4) {
|
if (version === 4) {
|
||||||
return ip.split('.')
|
return ip.split('.')
|
||||||
.reduce((acc, octet) => {
|
.reduce((acc, octet) => {
|
||||||
|
@ -105,7 +105,7 @@ export function cidrToRange(cidr: string): IPRange {
|
||||||
const version = detectIpVersion(ip);
|
const version = detectIpVersion(ip);
|
||||||
const prefixBits = parseInt(prefix);
|
const prefixBits = parseInt(prefix);
|
||||||
const ipBigInt = ipToBigInt(ip);
|
const ipBigInt = ipToBigInt(ip);
|
||||||
|
|
||||||
// Validate prefix length
|
// Validate prefix length
|
||||||
const maxPrefix = version === 4 ? 32 : 128;
|
const maxPrefix = version === 4 ? 32 : 128;
|
||||||
if (prefixBits < 0 || prefixBits > maxPrefix) {
|
if (prefixBits < 0 || prefixBits > maxPrefix) {
|
||||||
|
@ -116,7 +116,7 @@ export function cidrToRange(cidr: string): IPRange {
|
||||||
const mask = BigInt.asUintN(version === 4 ? 64 : 128, (BigInt(1) << shiftBits) - BigInt(1));
|
const mask = BigInt.asUintN(version === 4 ? 64 : 128, (BigInt(1) << shiftBits) - BigInt(1));
|
||||||
const start = ipBigInt & ~mask;
|
const start = ipBigInt & ~mask;
|
||||||
const end = start | mask;
|
const end = start | mask;
|
||||||
|
|
||||||
return { start, end };
|
return { start, end };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,17 +136,17 @@ export function findNextAvailableCidr(
|
||||||
if (!startCidr && existingCidrs.length === 0) {
|
if (!startCidr && existingCidrs.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no existing CIDRs, use the IP version from startCidr
|
// If no existing CIDRs, use the IP version from startCidr
|
||||||
const version = startCidr
|
const version = startCidr
|
||||||
? detectIpVersion(startCidr.split('/')[0])
|
? detectIpVersion(startCidr.split('/')[0])
|
||||||
: 4; // Default to IPv4 if no startCidr provided
|
: 4; // Default to IPv4 if no startCidr provided
|
||||||
|
|
||||||
// Use appropriate default startCidr if none provided
|
// Use appropriate default startCidr if none provided
|
||||||
startCidr = startCidr || (version === 4 ? "0.0.0.0/0" : "::/0");
|
startCidr = startCidr || (version === 4 ? "0.0.0.0/0" : "::/0");
|
||||||
|
|
||||||
// If there are existing CIDRs, ensure all are same version
|
// If there are existing CIDRs, ensure all are same version
|
||||||
if (existingCidrs.length > 0 &&
|
if (existingCidrs.length > 0 &&
|
||||||
existingCidrs.some(cidr => detectIpVersion(cidr.split('/')[0]) !== version)) {
|
existingCidrs.some(cidr => detectIpVersion(cidr.split('/')[0]) !== version)) {
|
||||||
throw new Error('All CIDRs must be of the same IP version');
|
throw new Error('All CIDRs must be of the same IP version');
|
||||||
}
|
}
|
||||||
|
@ -196,12 +196,14 @@ export function findNextAvailableCidr(
|
||||||
export function isIpInCidr(ip: string, cidr: string): boolean {
|
export function isIpInCidr(ip: string, cidr: string): boolean {
|
||||||
const ipVersion = detectIpVersion(ip);
|
const ipVersion = detectIpVersion(ip);
|
||||||
const cidrVersion = detectIpVersion(cidr.split('/')[0]);
|
const cidrVersion = detectIpVersion(cidr.split('/')[0]);
|
||||||
|
|
||||||
|
// If IP versions don't match, the IP cannot be in the CIDR range
|
||||||
if (ipVersion !== cidrVersion) {
|
if (ipVersion !== cidrVersion) {
|
||||||
throw new Error('IP address and CIDR must be of the same version');
|
// throw new Erorr
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ipBigInt = ipToBigInt(ip);
|
const ipBigInt = ipToBigInt(ip);
|
||||||
const range = cidrToRange(cidr);
|
const range = cidrToRange(cidr);
|
||||||
return ipBigInt >= range.start && ipBigInt <= range.end;
|
return ipBigInt >= range.start && ipBigInt <= range.end;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ const bodySchema = z
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
const ensureTrailingSlash = (url: string): string => {
|
const ensureTrailingSlash = (url: string): string => {
|
||||||
return url.endsWith('/') ? url : `${url}/`;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GenerateOidcUrlResponse = {
|
export type GenerateOidcUrlResponse = {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { oidcAutoProvision } from "./oidcAutoProvision";
|
||||||
import license from "@server/license/license";
|
import license from "@server/license/license";
|
||||||
|
|
||||||
const ensureTrailingSlash = (url: string): string => {
|
const ensureTrailingSlash = (url: string): string => {
|
||||||
return url.endsWith("/") ? url : `${url}/`;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
|
@ -243,7 +243,7 @@ export async function validateOidcCallback(
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.UNAUTHORIZED,
|
HttpCode.UNAUTHORIZED,
|
||||||
"User not provisioned in the system"
|
`User with username ${userIdentifier} is unprovisioned. This user must be added to an organization before logging in.`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -363,12 +363,12 @@ export default function ReverseProxyTargets(props: {
|
||||||
setHttpsTlsLoading(true);
|
setHttpsTlsLoading(true);
|
||||||
await api.post(`/resource/${params.resourceId}`, {
|
await api.post(`/resource/${params.resourceId}`, {
|
||||||
ssl: data.ssl,
|
ssl: data.ssl,
|
||||||
tlsServerName: data.tlsServerName || undefined
|
tlsServerName: data.tlsServerName || null
|
||||||
});
|
});
|
||||||
updateResource({
|
updateResource({
|
||||||
...resource,
|
...resource,
|
||||||
ssl: data.ssl,
|
ssl: data.ssl,
|
||||||
tlsServerName: data.tlsServerName || undefined
|
tlsServerName: data.tlsServerName || null
|
||||||
});
|
});
|
||||||
toast({
|
toast({
|
||||||
title: "TLS settings updated",
|
title: "TLS settings updated",
|
||||||
|
@ -393,11 +393,11 @@ export default function ReverseProxyTargets(props: {
|
||||||
try {
|
try {
|
||||||
setProxySettingsLoading(true);
|
setProxySettingsLoading(true);
|
||||||
await api.post(`/resource/${params.resourceId}`, {
|
await api.post(`/resource/${params.resourceId}`, {
|
||||||
setHostHeader: data.setHostHeader || undefined
|
setHostHeader: data.setHostHeader || null
|
||||||
});
|
});
|
||||||
updateResource({
|
updateResource({
|
||||||
...resource,
|
...resource,
|
||||||
setHostHeader: data.setHostHeader || undefined
|
setHostHeader: data.setHostHeader || null
|
||||||
});
|
});
|
||||||
toast({
|
toast({
|
||||||
title: "Proxy settings updated",
|
title: "Proxy settings updated",
|
||||||
|
|
|
@ -173,13 +173,15 @@ export default function Page() {
|
||||||
if (httpData.isBaseDomain) {
|
if (httpData.isBaseDomain) {
|
||||||
Object.assign(payload, {
|
Object.assign(payload, {
|
||||||
domainId: httpData.domainId,
|
domainId: httpData.domainId,
|
||||||
isBaseDomain: true
|
isBaseDomain: true,
|
||||||
|
protocol: "tcp"
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Object.assign(payload, {
|
Object.assign(payload, {
|
||||||
subdomain: httpData.subdomain,
|
subdomain: httpData.subdomain,
|
||||||
domainId: httpData.domainId,
|
domainId: httpData.domainId,
|
||||||
isBaseDomain: false
|
isBaseDomain: false,
|
||||||
|
protocol: "tcp"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -137,8 +137,8 @@ export function SitePriceCalculator({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-muted-foreground text-sm mt-2 text-center">
|
<p className="text-muted-foreground text-sm mt-2 text-center">
|
||||||
For the most up-to-date pricing, please visit
|
For the most up-to-date pricing and discounts,
|
||||||
our{" "}
|
please visit the{" "}
|
||||||
<a
|
<a
|
||||||
href="https://docs.fossorial.io/pricing"
|
href="https://docs.fossorial.io/pricing"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
|
@ -452,6 +452,12 @@ export default function LicensePage() {
|
||||||
in system
|
in system
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{!licenseStatus?.isHostLicensed && (
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
There is no limit on the number of sites
|
||||||
|
using an unlicensed host.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
{licenseStatus?.maxSites && (
|
{licenseStatus?.maxSites && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
|
|
|
@ -16,33 +16,7 @@ export function Breadcrumbs() {
|
||||||
|
|
||||||
const breadcrumbs: BreadcrumbItem[] = segments.map((segment, index) => {
|
const breadcrumbs: BreadcrumbItem[] = segments.map((segment, index) => {
|
||||||
const href = `/${segments.slice(0, index + 1).join("/")}`;
|
const href = `/${segments.slice(0, index + 1).join("/")}`;
|
||||||
let label = segment;
|
let label = decodeURIComponent(segment);
|
||||||
|
|
||||||
// // Format labels
|
|
||||||
// if (segment === "settings") {
|
|
||||||
// label = "Settings";
|
|
||||||
// } else if (segment === "sites") {
|
|
||||||
// label = "Sites";
|
|
||||||
// } else if (segment === "resources") {
|
|
||||||
// label = "Resources";
|
|
||||||
// } else if (segment === "access") {
|
|
||||||
// label = "Access Control";
|
|
||||||
// } else if (segment === "general") {
|
|
||||||
// label = "General";
|
|
||||||
// } else if (segment === "share-links") {
|
|
||||||
// label = "Shareable Links";
|
|
||||||
// } else if (segment === "users") {
|
|
||||||
// label = "Users";
|
|
||||||
// } else if (segment === "roles") {
|
|
||||||
// label = "Roles";
|
|
||||||
// } else if (segment === "invitations") {
|
|
||||||
// label = "Invitations";
|
|
||||||
// } else if (segment === "proxy") {
|
|
||||||
// label = "proxy";
|
|
||||||
// } else if (segment === "authentication") {
|
|
||||||
// label = "Authentication";
|
|
||||||
// }
|
|
||||||
|
|
||||||
return { label, href };
|
return { label, href };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -189,10 +189,12 @@ export default function SupporterStatus() {
|
||||||
<CredenzaBody>
|
<CredenzaBody>
|
||||||
<p>
|
<p>
|
||||||
Purchase a supporter key to help us continue
|
Purchase a supporter key to help us continue
|
||||||
developing Pangolin. Your contribution allows us
|
developing Pangolin for the community. Your
|
||||||
commit more time to maintain and add new features to
|
contribution allows us to commit more time to
|
||||||
the application for everyone. We will never use this
|
maintain and add new features to the application for
|
||||||
to paywall features.
|
everyone. We will never use this to paywall
|
||||||
|
features. This is separate from the Professional
|
||||||
|
Edition.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -16,7 +16,7 @@ const ToastViewport = React.forwardRef<
|
||||||
<ToastPrimitives.Viewport
|
<ToastPrimitives.Viewport
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
"fixed top-0 right-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 md:max-w-[420px]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
Loading…
Reference in a new issue