* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main: - Rebrand Cal.com to Cal.diy across the entire codebase - Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions - Switch license from AGPL-3.0 to MIT - Remove docs/ directory (migrated to Nextra at cal.diy) - Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc. - Clean up .env.example for self-hosted Cal.diy - Update Docker image references to calcom/cal.diy - Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork - Add PR welcome bot for Cal.diy contributors - Fix API v2 breaking changes oasdiff ignore entries - Replace Blacksmith CI runners with default GitHub Actions 3893 files changed, 20789 insertions(+), 411020 deletions(-) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * rip out org related comments in api v2 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
102 lines
4.8 KiB
TypeScript
102 lines
4.8 KiB
TypeScript
import { sync as globSync } from "glob";
|
|
|
|
import { nextJsOrgRewriteConfig } from "./getNextjsOrgRewriteConfig";
|
|
|
|
// Top-level route names that are explicitly allowed for org rewrite (whitelist)
|
|
export const topLevelRouteNamesWhitelistedForRewrite: string[] = [
|
|
// We don't allow all dashboard route names to be used as slug because people are probably accustomed to access links like acme.cal.com/event-types etc.
|
|
// So, we carefully allow, what is absolutely needed.
|
|
// Allowed to be a team/user slug in organization because onboarding is a common team name
|
|
"onboarding",
|
|
];
|
|
|
|
/**
|
|
* Extracts top-level route names from all pages/app files and excludes them from org rewrite.
|
|
* For example: /abc/def/ghi -> 'abc'
|
|
*
|
|
* These top-level route names are excluded from rewrites in beforeFiles in next.config.js
|
|
* to prevent conflicts with organization slug rewrites.
|
|
*/
|
|
export const topLevelRoutesExcludedFromOrgRewrite: string[] = globSync(
|
|
"{pages,app,app/(booking-page-wrapper),app/(use-page-wrapper),app/(use-page-wrapper)/(main-nav)}/**/[^_]*.{tsx,js,ts}",
|
|
{
|
|
cwd: __dirname,
|
|
}
|
|
)
|
|
.map((filename) =>
|
|
filename
|
|
// Remove the directory prefix (pages/, app/ and route group folders.)
|
|
.replace(
|
|
/^(app\/\(use-page-wrapper\)\/\(main-nav\)|app\/\(use-page-wrapper\)|app\/\(booking-page-wrapper\)|pages|app)\//,
|
|
""
|
|
)
|
|
// Remove file extensions
|
|
.replace(/(\.tsx|\.js|\.ts)/, "")
|
|
// Extract only the top-level route name (e.g., /abc/def -> abc)
|
|
.replace(/\/.*/, "")
|
|
)
|
|
.filter(
|
|
(v, i, self) =>
|
|
self.indexOf(v) === i &&
|
|
![
|
|
"[user]",
|
|
"_trpc",
|
|
"layout",
|
|
"layoutHOC",
|
|
"WithAppDirSsg",
|
|
"global-error",
|
|
"WithAppDirSsr",
|
|
"WithEmbedSSR",
|
|
"WithEmbedSSR.test",
|
|
"ShellMainAppDir",
|
|
"ShellMainAppDirBackButton",
|
|
].some((prefix) => v.startsWith(prefix))
|
|
)
|
|
.filter((page) => {
|
|
return !topLevelRouteNamesWhitelistedForRewrite.includes(page);
|
|
});
|
|
|
|
// .* matches / as well(Note: *(i.e wildcard) doesn't match / but .*(i.e. RegExp) does)
|
|
// It would match /free/30min but not /bookings/upcoming because 'bookings' is an item in pages
|
|
// It would also not match /free/30min/embed because we are ensuring just two slashes
|
|
// ?!book ensures it doesn't match /free/book page which doesn't have a corresponding new-booker page.
|
|
// [^/]+ makes the RegExp match the full path, it seems like a partial match doesn't work.
|
|
// book$ ensures that only /book is excluded from rewrite(which is at the end always) and not /booked
|
|
export { nextJsOrgRewriteConfig };
|
|
|
|
/**
|
|
* Returns a regex that matches all existing routes, virtual routes (like /forms, /router, /success etc) and nextjs special paths (_next, public)
|
|
* @param suffix - The suffix to append to each route in the regex
|
|
*/
|
|
function getRegExpMatchingAllReservedRoutes(suffix: string): string {
|
|
// Following routes don't exist but they work by doing rewrite. Thus they need to be excluded from matching the orgRewrite patterns
|
|
// Make sure to keep it upto date as more nonExistingRouteRewrites are added.
|
|
// "app" is reserved for the Cal.diy Companion landing page served by Framer at cal.com/app.
|
|
// The browser extension redirects users to cal.com/app when clicked on restricted pages (like chrome://newtab).
|
|
// Without this reservation, /app would be treated as a username lookup and show "username available" error.
|
|
const otherNonExistingRoutePrefixes = ["forms", "router", "success", "cancel", "app"];
|
|
|
|
// Most files/dirs in public dir must not be rewritten to org pages. Ideally it should be all the content of public dir, but that can be done later
|
|
// It is important to exclude the embed pages separately here because with SINGLE_ORG_SLUG enabled, the entire domain is eligible for rewrite vs just the org subdomain otherwise
|
|
const staticAssets = ["embed"];
|
|
|
|
// FIXME: I am not sure why public is needed here, an asset 'test' in public isn't accessible through "/public/test" but only through "/test"
|
|
// We should infact scan through all files in public and exclude them instead.
|
|
const nextJsSpecialPaths = ["_next", "public"];
|
|
|
|
const allTopLevelRoutesExcludedFromOrgRewrite = topLevelRoutesExcludedFromOrgRewrite
|
|
.concat(otherNonExistingRoutePrefixes)
|
|
.concat(nextJsSpecialPaths)
|
|
.concat(staticAssets);
|
|
return allTopLevelRoutesExcludedFromOrgRewrite.join(`${suffix}|`) + suffix;
|
|
}
|
|
|
|
// To handle /something
|
|
export const orgUserRoutePath = `/:user((?!${getRegExpMatchingAllReservedRoutes("/?$")})[a-zA-Z0-9\\-_]+)`;
|
|
|
|
// To handle /something/somethingelse
|
|
export const orgUserTypeRoutePath = `/:user((?!${getRegExpMatchingAllReservedRoutes("/")})[^/]+)/:type((?!avatar\\.png)[^/]+)`;
|
|
|
|
// To handle /something/somethingelse/embed
|
|
export const orgUserTypeEmbedRoutePath = `/:user((?!${getRegExpMatchingAllReservedRoutes("/")})[^/]+)/:type/embed`;
|