diff --git a/server/lib/ip.ts b/server/lib/ip.ts index 86fe116..fd6f07a 100644 --- a/server/lib/ip.ts +++ b/server/lib/ip.ts @@ -17,7 +17,7 @@ function detectIpVersion(ip: string): IPVersion { */ function ipToBigInt(ip: string): bigint { const version = detectIpVersion(ip); - + if (version === 4) { return ip.split('.') .reduce((acc, octet) => { @@ -105,7 +105,7 @@ export function cidrToRange(cidr: string): IPRange { const version = detectIpVersion(ip); const prefixBits = parseInt(prefix); const ipBigInt = ipToBigInt(ip); - + // Validate prefix length const maxPrefix = version === 4 ? 32 : 128; 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 start = ipBigInt & ~mask; const end = start | mask; - + return { start, end }; } @@ -136,17 +136,17 @@ export function findNextAvailableCidr( if (!startCidr && existingCidrs.length === 0) { return null; } - + // If no existing CIDRs, use the IP version from startCidr - const version = startCidr + const version = startCidr ? detectIpVersion(startCidr.split('/')[0]) : 4; // Default to IPv4 if no startCidr provided - + // Use appropriate default startCidr if none provided startCidr = startCidr || (version === 4 ? "0.0.0.0/0" : "::/0"); - + // 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)) { 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 { const ipVersion = detectIpVersion(ip); const cidrVersion = detectIpVersion(cidr.split('/')[0]); - + + // If IP versions don't match, the IP cannot be in the CIDR range 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 range = cidrToRange(cidr); return ipBigInt >= range.start && ipBigInt <= range.end; -} \ No newline at end of file +} diff --git a/server/routers/idp/generateOidcUrl.ts b/server/routers/idp/generateOidcUrl.ts index 4a62cac..371a2c2 100644 --- a/server/routers/idp/generateOidcUrl.ts +++ b/server/routers/idp/generateOidcUrl.ts @@ -28,7 +28,7 @@ const bodySchema = z .strict(); const ensureTrailingSlash = (url: string): string => { - return url.endsWith('/') ? url : `${url}/`; + return url; }; export type GenerateOidcUrlResponse = { diff --git a/server/routers/idp/validateOidcCallback.ts b/server/routers/idp/validateOidcCallback.ts index 1624616..7d588fe 100644 --- a/server/routers/idp/validateOidcCallback.ts +++ b/server/routers/idp/validateOidcCallback.ts @@ -23,7 +23,7 @@ import { oidcAutoProvision } from "./oidcAutoProvision"; import license from "@server/license/license"; const ensureTrailingSlash = (url: string): string => { - return url.endsWith("/") ? url : `${url}/`; + return url; }; const paramsSchema = z @@ -243,7 +243,7 @@ export async function validateOidcCallback( return next( createHttpError( 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.` ) ); } diff --git a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx index 85c2541..90e05ff 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/proxy/page.tsx @@ -363,12 +363,12 @@ export default function ReverseProxyTargets(props: { setHttpsTlsLoading(true); await api.post(`/resource/${params.resourceId}`, { ssl: data.ssl, - tlsServerName: data.tlsServerName || undefined + tlsServerName: data.tlsServerName || null }); updateResource({ ...resource, ssl: data.ssl, - tlsServerName: data.tlsServerName || undefined + tlsServerName: data.tlsServerName || null }); toast({ title: "TLS settings updated", @@ -393,11 +393,11 @@ export default function ReverseProxyTargets(props: { try { setProxySettingsLoading(true); await api.post(`/resource/${params.resourceId}`, { - setHostHeader: data.setHostHeader || undefined + setHostHeader: data.setHostHeader || null }); updateResource({ ...resource, - setHostHeader: data.setHostHeader || undefined + setHostHeader: data.setHostHeader || null }); toast({ title: "Proxy settings updated", diff --git a/src/app/[orgId]/settings/resources/create/page.tsx b/src/app/[orgId]/settings/resources/create/page.tsx index 704a194..c1be635 100644 --- a/src/app/[orgId]/settings/resources/create/page.tsx +++ b/src/app/[orgId]/settings/resources/create/page.tsx @@ -173,13 +173,15 @@ export default function Page() { if (httpData.isBaseDomain) { Object.assign(payload, { domainId: httpData.domainId, - isBaseDomain: true + isBaseDomain: true, + protocol: "tcp" }); } else { Object.assign(payload, { subdomain: httpData.subdomain, domainId: httpData.domainId, - isBaseDomain: false + isBaseDomain: false, + protocol: "tcp" }); } } else { diff --git a/src/app/admin/license/components/SitePriceCalculator.tsx b/src/app/admin/license/components/SitePriceCalculator.tsx index 5622876..cf771b5 100644 --- a/src/app/admin/license/components/SitePriceCalculator.tsx +++ b/src/app/admin/license/components/SitePriceCalculator.tsx @@ -137,8 +137,8 @@ export function SitePriceCalculator({

- For the most up-to-date pricing, please visit - our{" "} + For the most up-to-date pricing and discounts, + please visit the{" "} + {!licenseStatus?.isHostLicensed && ( +

+ There is no limit on the number of sites + using an unlicensed host. +

+ )} {licenseStatus?.maxSites && (
diff --git a/src/components/Breadcrumbs.tsx b/src/components/Breadcrumbs.tsx index 9740759..25366ff 100644 --- a/src/components/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs.tsx @@ -16,33 +16,7 @@ export function Breadcrumbs() { const breadcrumbs: BreadcrumbItem[] = segments.map((segment, index) => { const href = `/${segments.slice(0, index + 1).join("/")}`; - let label = 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"; - // } - + let label = decodeURIComponent(segment); return { label, href }; }); diff --git a/src/components/SupporterStatus.tsx b/src/components/SupporterStatus.tsx index 25da9b5..48abf7b 100644 --- a/src/components/SupporterStatus.tsx +++ b/src/components/SupporterStatus.tsx @@ -189,10 +189,12 @@ export default function SupporterStatus() {

Purchase a supporter key to help us continue - developing Pangolin. Your contribution allows us - commit more time to maintain and add new features to - the application for everyone. We will never use this - to paywall features. + developing Pangolin for the community. Your + contribution allows us to commit more time to + maintain and add new features to the application for + everyone. We will never use this to paywall + features. This is separate from the Professional + Edition.

diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx index f5cbe00..af5d5ed 100644 --- a/src/components/ui/toast.tsx +++ b/src/components/ui/toast.tsx @@ -16,7 +16,7 @@ const ToastViewport = React.forwardRef<