test: Hotfix/Orgs rewrite causing 404 with 3 or more dots domains (#9803)
* Fix RegExp * Used ORGANIZATIONS_ENABLED to opt-out of orgs behaviour * Add comments * Add a new testcase * Add response headers for easier debugging and Checkly tests in productionpull/9728/head^2
parent
66d47974cb
commit
8384c3f9ff
|
@ -0,0 +1,15 @@
|
||||||
|
const getDefaultSubdomain = (url) => {
|
||||||
|
if (!url.startsWith("http:") && !url.startsWith("https:")) {
|
||||||
|
// Make it a valid URL. Mabe we can simply return null and opt-out from orgs support till the use a URL scheme.
|
||||||
|
url = `https://${url}`;
|
||||||
|
}
|
||||||
|
const _url = new URL(url);
|
||||||
|
const regex = new RegExp(/^([a-z]+\:\/{2})?((?<subdomain>[\w-.]+)\.[\w-]+\.\w+)$/);
|
||||||
|
//console.log(_url.hostname, _url.hostname.match(regex));
|
||||||
|
return _url.hostname.match(regex)?.groups?.subdomain || null;
|
||||||
|
};
|
||||||
|
exports.getSubdomainRegExp = (url) => {
|
||||||
|
const defaultSubdomain = getDefaultSubdomain(url);
|
||||||
|
const subdomain = defaultSubdomain ? `(?!${defaultSubdomain})[^.]+` : "[^.]+";
|
||||||
|
return subdomain;
|
||||||
|
};
|
|
@ -5,6 +5,7 @@ const englishTranslation = require("./public/static/locales/en/common.json");
|
||||||
const { withAxiom } = require("next-axiom");
|
const { withAxiom } = require("next-axiom");
|
||||||
const { i18n } = require("./next-i18next.config");
|
const { i18n } = require("./next-i18next.config");
|
||||||
const { pages } = require("./pages");
|
const { pages } = require("./pages");
|
||||||
|
const { getSubdomainRegExp } = require("./getSubdomainRegExp");
|
||||||
|
|
||||||
if (!process.env.NEXTAUTH_SECRET) throw new Error("Please set NEXTAUTH_SECRET");
|
if (!process.env.NEXTAUTH_SECRET) throw new Error("Please set NEXTAUTH_SECRET");
|
||||||
if (!process.env.CALENDSO_ENCRYPTION_KEY) throw new Error("Please set CALENDSO_ENCRYPTION_KEY");
|
if (!process.env.CALENDSO_ENCRYPTION_KEY) throw new Error("Please set CALENDSO_ENCRYPTION_KEY");
|
||||||
|
@ -70,14 +71,6 @@ const informAboutDuplicateTranslations = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
informAboutDuplicateTranslations();
|
informAboutDuplicateTranslations();
|
||||||
|
|
||||||
const getSubdomain = () => {
|
|
||||||
const _url = new URL(process.env.NEXT_PUBLIC_WEBAPP_URL);
|
|
||||||
const regex = new RegExp(/^([a-z]+\:\/{2})?((?<subdomain>[\w-]+)\.[\w-]+\.\w+)$/);
|
|
||||||
//console.log(_url.hostname, _url.hostname.match(regex));
|
|
||||||
return _url.hostname.match(regex)?.groups?.subdomain || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const plugins = [];
|
const plugins = [];
|
||||||
if (process.env.ANALYZE === "true") {
|
if (process.env.ANALYZE === "true") {
|
||||||
// only load dependency if env `ANALYZE` was set
|
// only load dependency if env `ANALYZE` was set
|
||||||
|
@ -102,6 +95,39 @@ const teamTypeRouteRegExp = "/team/:slug/:type((?!book$)[^/]+)";
|
||||||
const privateLinkRouteRegExp = "/d/:link/:slug((?!book$)[^/]+)";
|
const privateLinkRouteRegExp = "/d/:link/:slug((?!book$)[^/]+)";
|
||||||
const embedUserTypeRouteRegExp = `/:user((?!${pages.join("/|")})[^/]*)/:type/embed`;
|
const embedUserTypeRouteRegExp = `/:user((?!${pages.join("/|")})[^/]*)/:type/embed`;
|
||||||
const embedTeamTypeRouteRegExp = "/team/:slug/:type/embed";
|
const embedTeamTypeRouteRegExp = "/team/:slug/:type/embed";
|
||||||
|
const subdomainRegExp = getSubdomainRegExp(process.env.NEXT_PUBLIC_WEBAPP_URL);
|
||||||
|
// Important Note: Do update the RegExp in apps/web/test/lib/next-config.test.ts when changing it.
|
||||||
|
const orgHostRegExp = `^(?<orgSlug>${subdomainRegExp})\\..*`;
|
||||||
|
|
||||||
|
const matcherConfigRootPath = {
|
||||||
|
has: [
|
||||||
|
{
|
||||||
|
type: "host",
|
||||||
|
value: orgHostRegExp,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
source: "/",
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcherConfigOrgMemberPath = {
|
||||||
|
has: [
|
||||||
|
{
|
||||||
|
type: "host",
|
||||||
|
value: orgHostRegExp,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
source: `/:user((?!${pages.join("|")}|_next|public)[a-zA-Z0-9\-_]+)`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const matcherConfigUserPath = {
|
||||||
|
has: [
|
||||||
|
{
|
||||||
|
type: "host",
|
||||||
|
value: `^(?<orgSlug>${subdomainRegExp}[^.]+)\\..*`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
source: `/:user((?!${pages.join("|")}|_next|public))/:path*`,
|
||||||
|
};
|
||||||
|
|
||||||
/** @type {import("next").NextConfig} */
|
/** @type {import("next").NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
|
@ -193,40 +219,23 @@ const nextConfig = {
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
const defaultSubdomain = getSubdomain();
|
|
||||||
const subdomain = defaultSubdomain ? `(?!${defaultSubdomain})[^.]+` : "[^.]+";
|
|
||||||
|
|
||||||
const beforeFiles = [
|
const beforeFiles = [
|
||||||
{
|
...(process.env.ORGANIZATIONS_ENABLED
|
||||||
has: [
|
? [
|
||||||
{
|
{
|
||||||
type: "host",
|
...matcherConfigRootPath,
|
||||||
value: `^(?<orgSlug>${subdomain})\\..*`,
|
destination: "/team/:orgSlug",
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
source: "/",
|
...matcherConfigOrgMemberPath,
|
||||||
destination: "/team/:orgSlug",
|
destination: "/org/:orgSlug/:user",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
has: [
|
...matcherConfigUserPath,
|
||||||
{
|
destination: "/:user/:path*",
|
||||||
type: "host",
|
},
|
||||||
value: `^(?<orgSlug>${subdomain})\\..*`,
|
]
|
||||||
},
|
: []),
|
||||||
],
|
|
||||||
source: `/:user((?!${pages.join("|")}|_next|public)[a-zA-Z0-9\-_]+)`,
|
|
||||||
destination: "/org/:orgSlug/:user",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
has: [
|
|
||||||
{
|
|
||||||
type: "host",
|
|
||||||
value: `^(?<orgSlug>${subdomain}[^.]+)\\..*`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
source: `/:user((?!${pages.join("|")}|_next|public))/:path*`,
|
|
||||||
destination: "/:user/:path*",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let afterFiles = [
|
let afterFiles = [
|
||||||
|
@ -381,6 +390,35 @@ const nextConfig = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
...matcherConfigRootPath,
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
key: "X-Cal-Org-path",
|
||||||
|
value: "/team/:orgSlug",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...matcherConfigOrgMemberPath,
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
key: "X-Cal-Org-path",
|
||||||
|
value: "/org/:orgSlug/:user",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...matcherConfigUserPath,
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
key: "X-Cal-Org-path",
|
||||||
|
value: "/:user/:path",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
async redirects() {
|
async redirects() {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
|
||||||
import { it, expect, describe, beforeAll, afterAll } from "vitest";
|
import { it, expect, describe, beforeAll, afterAll } from "vitest";
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const { getSubdomainRegExp } = require("../../getSubdomainRegExp");
|
||||||
let userTypeRouteRegExp: RegExp;
|
let userTypeRouteRegExp: RegExp;
|
||||||
let teamTypeRouteRegExp:RegExp;
|
let teamTypeRouteRegExp:RegExp;
|
||||||
let privateLinkRouteRegExp:RegExp
|
let privateLinkRouteRegExp:RegExp
|
||||||
|
@ -161,3 +163,36 @@ describe('next.config.js - RegExp', ()=>{
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
describe('next.config.js - Org Rewrite', ()=> {
|
||||||
|
// RegExp copied from next.config.js
|
||||||
|
const orgHostRegExp = (subdomainRegExp:string)=> new RegExp(`^(?<orgSlug>${subdomainRegExp})\\..*`)
|
||||||
|
describe('SubDomain Retrieval from NEXT_PUBLIC_WEBAPP_URL', ()=>{
|
||||||
|
it('https://app.cal.com', ()=>{
|
||||||
|
const subdomainRegExp = getSubdomainRegExp('https://app.cal.com');
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('app.cal.com')).toEqual(null)
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('company.app.cal.com')?.groups?.orgSlug).toEqual('company')
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('org.cal.com')?.groups?.orgSlug).toEqual('org')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('app.cal.com', ()=>{
|
||||||
|
const subdomainRegExp = getSubdomainRegExp('app.cal.com');
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('app.cal.com')).toEqual(null)
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('company.app.cal.com')?.groups?.orgSlug).toEqual('company')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('https://calcom.app.company.com', ()=>{
|
||||||
|
const subdomainRegExp = getSubdomainRegExp('https://calcom.app.company.com');
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('calcom.app.company.com')).toEqual(null)
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('acme.calcom.app.company.com')?.groups?.orgSlug).toEqual('acme')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('https://calcom.example.com', ()=>{
|
||||||
|
const subdomainRegExp = getSubdomainRegExp('https://calcom.example.com');
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('calcom.example.com')).toEqual(null)
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('acme.calcom.example.com')?.groups?.orgSlug).toEqual('acme')
|
||||||
|
// The following also matches which causes anything other than the domain in NEXT_PUBLIC_WEBAPP_URL to give 404
|
||||||
|
expect(orgHostRegExp(subdomainRegExp).exec('some-other.company.com')?.groups?.orgSlug).toEqual('some-other')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -22,7 +22,8 @@ export function defaultCookies(useSecureCookies: boolean): CookiesOptions {
|
||||||
const defaultOptions: CookieOption["options"] = {
|
const defaultOptions: CookieOption["options"] = {
|
||||||
domain: isENVDev
|
domain: isENVDev
|
||||||
? process.env.ORGANIZATIONS_ENABLED
|
? process.env.ORGANIZATIONS_ENABLED
|
||||||
? ".cal.local"
|
? //FIXME: This is causing login to not work if someone uses anything other .cal.local for testing
|
||||||
|
".cal.local"
|
||||||
: undefined
|
: undefined
|
||||||
: NEXTAUTH_COOKIE_DOMAIN,
|
: NEXTAUTH_COOKIE_DOMAIN,
|
||||||
// To enable cookies on widgets,
|
// To enable cookies on widgets,
|
||||||
|
|
Loading…
Reference in New Issue