From da55306a5aa7c621c5786df192a6956773a6c956 Mon Sep 17 00:00:00 2001 From: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:32:16 +0400 Subject: [PATCH] chore: add dev docs on mintlify (#16935) * --init * progress... * add more docs * bunch of changes and added back platform * development guides added * more guides in develop with cal * self hosted WIP * deployments * fix deployment steps * self-hosted guides * .. * Removed the development only sections * Removed 'with Cal.com' * use steps component for steps --------- Co-authored-by: Keith Williams --- docs/api-reference/v2/introduction.mdx | 8 +- .../how-to-setup-api-in-a-local-instance.mdx | 30 ++ .../adding-your-first-app.mdx | 25 ++ .../build-a-greeter-app.mdx | 95 ++++++ .../appstore-and-integration/build-an-app.mdx | 86 +++++ ...how-to-show-assigned-people-from-a-crm.mdx | 33 ++ .../integration-with-hubspot.mdx | 33 ++ .../guides/appstore-and-integration/oauth.mdx | 70 ++++ .../how-to-setup-oidc-with-okta.mdx | 50 +++ .../how-to-setup-scim-with-okta.mdx | 82 +++++ .../guides/auth-and-provision/sso-setup.mdx | 75 +++++ .../setup-mailtrap-for-email-testing.mdx | 39 +++ .../developing/guides/embeds/embed-events.mdx | 38 +++ docs/developing/introduction.mdx | 26 ++ docs/developing/local-development.mdx | 179 ++++++++++ .../open-source-contribution/code-styling.mdx | 20 ++ .../contributors-guide.mdx | 164 +++++++++ .../open-source-contribution/index.mdx | 137 ++++++++ .../pull-requests.mdx | 37 ++ docs/essentials/code.mdx | 37 -- docs/essentials/images.mdx | 59 ---- docs/essentials/markdown.mdx | 88 ----- docs/essentials/navigation.mdx | 66 ---- docs/essentials/reusable-snippets.mdx | 110 ------ docs/essentials/settings.mdx | 318 ------------------ docs/images/high-priority.svg | 1 + .../images/i958x1232-mgsIib6PZJLt_zobva8.webp | Bin 0 -> 43622 bytes docs/images/low-priority.svg | 1 + docs/images/medium-priority.svg | 1 + docs/images/urgent-priority.svg | 1 + docs/mint.json | 148 +++++++- docs/platform/atoms-reference.mdx | 231 +++++++++++++ docs/self-hosting/apps/install-apps/daily.mdx | 22 ++ .../self-hosting/apps/install-apps/google.mdx | 54 +++ .../apps/install-apps/hubspot.mdx | 45 +++ .../apps/install-apps/introduction.mdx | 35 ++ .../apps/install-apps/microsoft.mdx | 29 ++ .../apps/install-apps/sendgrid.mdx | 30 ++ .../self-hosting/apps/install-apps/stripe.mdx | 38 +++ .../self-hosting/apps/install-apps/twilio.mdx | 70 ++++ docs/self-hosting/apps/install-apps/zoho.mdx | 125 +++++++ docs/self-hosting/apps/install-apps/zoom.mdx | 54 +++ docs/self-hosting/database-migrations.mdx | 69 ++++ docs/self-hosting/deployments/aws.mdx | 46 +++ docs/self-hosting/deployments/azure.mdx | 48 +++ docs/self-hosting/deployments/gcp.mdx | 109 ++++++ docs/self-hosting/deployments/vercel.mdx | 76 +++++ docs/self-hosting/docker.mdx | 86 +++++ .../syncing-third-party-apps.mdx | 53 +++ ...-calcom-sponsor-my-open-source-project.mdx | 40 +++ .../guides/white-labeling/custom-css.mdx | 36 ++ .../guides/white-labeling/introduction.mdx | 29 ++ docs/self-hosting/installation.mdx | 142 ++++++++ docs/self-hosting/sso-setup.mdx | 68 ++++ docs/self-hosting/upgrading.mdx | 53 +++ 55 files changed, 2964 insertions(+), 681 deletions(-) create mode 100644 docs/developing/guides/api/how-to-setup-api-in-a-local-instance.mdx create mode 100644 docs/developing/guides/appstore-and-integration/adding-your-first-app.mdx create mode 100644 docs/developing/guides/appstore-and-integration/build-a-greeter-app.mdx create mode 100644 docs/developing/guides/appstore-and-integration/build-an-app.mdx create mode 100644 docs/developing/guides/appstore-and-integration/how-to-show-assigned-people-from-a-crm.mdx create mode 100644 docs/developing/guides/appstore-and-integration/integration-with-hubspot.mdx create mode 100644 docs/developing/guides/appstore-and-integration/oauth.mdx create mode 100644 docs/developing/guides/auth-and-provision/how-to-setup-oidc-with-okta.mdx create mode 100644 docs/developing/guides/auth-and-provision/how-to-setup-scim-with-okta.mdx create mode 100644 docs/developing/guides/auth-and-provision/sso-setup.mdx create mode 100644 docs/developing/guides/email/setup-mailtrap-for-email-testing.mdx create mode 100644 docs/developing/guides/embeds/embed-events.mdx create mode 100644 docs/developing/introduction.mdx create mode 100644 docs/developing/local-development.mdx create mode 100644 docs/developing/open-source-contribution/code-styling.mdx create mode 100644 docs/developing/open-source-contribution/contributors-guide.mdx create mode 100644 docs/developing/open-source-contribution/index.mdx create mode 100644 docs/developing/open-source-contribution/pull-requests.mdx delete mode 100644 docs/essentials/code.mdx delete mode 100644 docs/essentials/images.mdx delete mode 100644 docs/essentials/markdown.mdx delete mode 100644 docs/essentials/navigation.mdx delete mode 100644 docs/essentials/reusable-snippets.mdx delete mode 100644 docs/essentials/settings.mdx create mode 100644 docs/images/high-priority.svg create mode 100644 docs/images/i958x1232-mgsIib6PZJLt_zobva8.webp create mode 100644 docs/images/low-priority.svg create mode 100644 docs/images/medium-priority.svg create mode 100644 docs/images/urgent-priority.svg create mode 100644 docs/platform/atoms-reference.mdx create mode 100644 docs/self-hosting/apps/install-apps/daily.mdx create mode 100644 docs/self-hosting/apps/install-apps/google.mdx create mode 100644 docs/self-hosting/apps/install-apps/hubspot.mdx create mode 100644 docs/self-hosting/apps/install-apps/introduction.mdx create mode 100644 docs/self-hosting/apps/install-apps/microsoft.mdx create mode 100644 docs/self-hosting/apps/install-apps/sendgrid.mdx create mode 100644 docs/self-hosting/apps/install-apps/stripe.mdx create mode 100644 docs/self-hosting/apps/install-apps/twilio.mdx create mode 100644 docs/self-hosting/apps/install-apps/zoho.mdx create mode 100644 docs/self-hosting/apps/install-apps/zoom.mdx create mode 100644 docs/self-hosting/database-migrations.mdx create mode 100644 docs/self-hosting/deployments/aws.mdx create mode 100644 docs/self-hosting/deployments/azure.mdx create mode 100644 docs/self-hosting/deployments/gcp.mdx create mode 100644 docs/self-hosting/deployments/vercel.mdx create mode 100644 docs/self-hosting/docker.mdx create mode 100644 docs/self-hosting/guides/appstore-and-integration/syncing-third-party-apps.mdx create mode 100644 docs/self-hosting/guides/sponsorship/can-calcom-sponsor-my-open-source-project.mdx create mode 100644 docs/self-hosting/guides/white-labeling/custom-css.mdx create mode 100644 docs/self-hosting/guides/white-labeling/introduction.mdx create mode 100644 docs/self-hosting/installation.mdx create mode 100644 docs/self-hosting/sso-setup.mdx create mode 100644 docs/self-hosting/upgrading.mdx diff --git a/docs/api-reference/v2/introduction.mdx b/docs/api-reference/v2/introduction.mdx index 81db0e1749..de7402cedf 100644 --- a/docs/api-reference/v2/introduction.mdx +++ b/docs/api-reference/v2/introduction.mdx @@ -15,6 +15,12 @@ Test mode secret keys have the prefix `cal_` and live mode secret keys have the Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth. -Authentication to the API is performed via the Authentication header like: `Bearer: cal_live_xxxxxx` +Authentication to the API is performed via the Authorization header. For example, the request would go something like: + +``` +'Authorization': 'Bearer YOUR_API_KEY' +``` + +in your request header. All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail. diff --git a/docs/developing/guides/api/how-to-setup-api-in-a-local-instance.mdx b/docs/developing/guides/api/how-to-setup-api-in-a-local-instance.mdx new file mode 100644 index 0000000000..da684eb8c3 --- /dev/null +++ b/docs/developing/guides/api/how-to-setup-api-in-a-local-instance.mdx @@ -0,0 +1,30 @@ +--- +title: "How to setup API in a local instance" +--- + +To test API in your local instance, you have the following pre-requisites: + + + + Please close the cal.com repository. You can do it by following the instructions provided in [Installation](/developing/local-development) + + + Add a staging license key that goes in as value for `CALCOM_LICENSE_KEY` in your root `.env` file. + + You can use the following as staging license key + ``` + 1a1f8138-0bfc-4f37-b4af-1e24fd145839 + ``` + + + Next, you need to install API submodule by following the instructions provided in [API submodule](/developing/local-development#api) + + + Start the cal.com server using `yarn dev` on localhost and create the test API keys by visiting `/settings/developer/api-keys` + + + Start the API server by running `yarn workspace @calcom/api dev` and start testing your API locally + + + +By default, the app server runs on port 3000 and the API server runs on port 3003 diff --git a/docs/developing/guides/appstore-and-integration/adding-your-first-app.mdx b/docs/developing/guides/appstore-and-integration/adding-your-first-app.mdx new file mode 100644 index 0000000000..b364b92db3 --- /dev/null +++ b/docs/developing/guides/appstore-and-integration/adding-your-first-app.mdx @@ -0,0 +1,25 @@ +--- +title: "Adding your first app" +--- + +Head over to the **apps** page, where you'll find our app store. We suggest firstly adding a calendar integration, then optionally a video conferencing platform, and then try out the other apps. + + + +Find the app you'd like to install, and either click **Install** in the top right of that app's card to add it right away, or click on the app's card to view more details. + +In the details page you can read more about each app, then click **Install** in the top right of the page to add it to your account. + + + +If the app requires authentication, for instance to connect your Google Account, a dialog will be opened prompting you to authenticate. Once complete, the app will be successfully installed. \ No newline at end of file diff --git a/docs/developing/guides/appstore-and-integration/build-a-greeter-app.mdx b/docs/developing/guides/appstore-and-integration/build-a-greeter-app.mdx new file mode 100644 index 0000000000..fa2d1b6b16 --- /dev/null +++ b/docs/developing/guides/appstore-and-integration/build-a-greeter-app.mdx @@ -0,0 +1,95 @@ +--- +title: "Build a greeter app" +--- + +## Building a **Greeter** app + + + + Create an app with the title "**Greeter".** Run the following command and provide the information it is looking for. + + ```bash + yarn app-store create + ``` + + + + + The app is created in Step 1 and we should install it now. + + + The app is installed but it doesn't do anything because we haven't written added any functionality to it. Let's add a button in the main navigation that greets the user. + + + 1. Create a component `greeter/components/GreeterButton.tsx` - You can name it whatever you want. + + 2. Import this component in `Shell.tsx` and add it wherever you want to so that the button is available on all pages. + + ```js + /** + * GreeterButton.tsx + * It creates a button that can be added anywhere. The button is visible only if the app is installed. + */ + import useApp from "@calcom/lib/hooks/useApp"; + import showToast from "@calcom/lib/notification"; + import { Button } from "@calcom/ui"; + + import useMeQuery from "@lib/hooks/useMeQuery"; + + export default function GreeterButton() { + const { data: user } = useMeQuery(); + const { data: greeterApp } = useApp("greeter"); + // Make sure that greeterApp is installed. We shouldn't show the button when app is not installed + if (!user || !greeterApp) { + return null; + } + return ( + + ); + } + ``` + + ```; + /** + * Shell.tsx + */ + // ... + import GreeterButton from "@calcom/app-store/greeter/components/GreeterButton"; + // ... + + ; + ``` + + + + + A sample line where I used the component in the demo + + + + + + +That's it. You now have a fully functional Greeter app. This is the simplest possible demonstration of how you can build an app and what it can do. There are simply no restrictions on what an app can achieve. \ No newline at end of file diff --git a/docs/developing/guides/appstore-and-integration/build-an-app.mdx b/docs/developing/guides/appstore-and-integration/build-an-app.mdx new file mode 100644 index 0000000000..f789a5bfe2 --- /dev/null +++ b/docs/developing/guides/appstore-and-integration/build-an-app.mdx @@ -0,0 +1,86 @@ +--- +title: "How to build an app" +--- + +Since Cal.com is open source we encourage developers to create new apps for others to use. This guide is to help you get started. + +You can create an app by simply running the following command. It creates a new directory under [`packages/app-store/`](https://github.com/calcom/cal.com/tree/main/packages/app-store)`` + +```bash +yarn create-app +``` + + +You just need to provide a few inputs and then you will have your app created within a few seconds. [See, how you can build a very simple app that greets your users.](/developing/guides/appstore-and-integration/build-a-greeter-app) + +### Steps in creating an app + + + + Create the app using the `create-app` command. The app can be installed now, but at this moment it isn't doing anything. It is just installed and ready to do what you want it to do. + + + There is pretty much no restriction on what an App can do. For example: + + - Hooking into existing functionalities to enhance them - _In all these cases you need to first check if the app is installed or not. There is_ `_useApp(appSlug)_` _react hook to do that. It gives you the app details if the app is installed._ + - Apps that need to connect with third parties like Google Calendar, Apple Calendar, Google Meet. + - A very powerful app that has its own data and pages - [Routing Forms App](https://github.com/calcom/cal.com/tree/main/packages/app-store/ee/routing_forms) is a great example of this. + - There are other powerful apps already in the [App Store](https://app.cal.com/apps) that you can get an idea of the capabilities of apps. + + + + +### Structure + +All apps can be found under [`packages/app-store`](https://github.com/calcom/cal.com/tree/main/packages/app-store). In this folder is `_baseApp` which shows the general structure of an app. + +```bash +├──_example +| ├──config.json -> Autogenerated by cli. You are free to edit it. +| ├──api +| | ├──add.ts -> Autogenerated by cli. You would want to edit it if your app needs to connect with a third party +| +| ├──components +| +| ├──static -> Add all static assets here +| | ├──icon.svg -> This is used as the logo for your App. + +| ├──index.ts +| ├──package.json +| ├──.env.example -> Specify the environmental variables (ex. auth token, API secrets) that your app will need if it's applicable +| ├──README.mdx -> Customize your app description. You can add an image slider as well. +``` + +### Other Commands + +**Deleting an app:** + +```bash +yarn app-store delete --slug APP_NAME +``` + +**Editing an app:** + +```bash +yarn app-store edit --slug APP_NAME +``` + +### Important Points + +* Make sure to have `yarn app-store:watch` command running when developing an app so that autogenerated files are always up to date. + +* If app-store cli fails at this step, try to run this command manually first. Solve that problem and then re-run cli. The command can fail because there are Prisma migrations that can't be applied automatically. + + + +A sample failure in create command + +* When you edit an app following things aren't updated. + + * README.mdx - It would still have the old description. Feel free to edit it manually. + +### Publishing Your App to the Cal.com App Store + +Once your app is working in your environment, you can publish it to Cal.com App Store by opening a PR [here](https://github.com/calcom/cal.com). + +If you need any help feel free to join us on [Discord](https://go.cal.com/discord). \ No newline at end of file diff --git a/docs/developing/guides/appstore-and-integration/how-to-show-assigned-people-from-a-crm.mdx b/docs/developing/guides/appstore-and-integration/how-to-show-assigned-people-from-a-crm.mdx new file mode 100644 index 0000000000..4792d67ad6 --- /dev/null +++ b/docs/developing/guides/appstore-and-integration/how-to-show-assigned-people-from-a-crm.mdx @@ -0,0 +1,33 @@ +--- +title: "Assign people to a call from a CRM or database" +--- + +## Option 1: Based on CRM integration + +If you are working with a CRM such as Salesforce, Hubspot, Close.com or similar, you can use our integrations to connect to your apps. + +We're using the Assignee APIs from each service to understand which person is assigned to an email of a new lead. + +By prefilling your booking link with ?email=name@acme.com our integration will look up who has spoken to name@acme.com before and skip all round-robin logic. + +Here is an example URL: i.cal.com/sales/exploration?email=name@acme.com + +This works with Embeds as well, just add the parameter to your CalLink property. + +## Option 2: Based on Emails + +If you are using a different product that we don't integrate with, or you need something more custom you can also achieve this with our Organization plan. + +We've built our Organization system in a way where every email can be turned into a scheduling link with your company subdomain: + +jane@acme.com → acme.cal.com/jane (or schedule.acme.com/jane) + +This means if you have a table + +| User | AccountExecutive | +| :----------- | :----------- | +| peer@cal.com | jane@acme.com | + +You could show peer@cal.com an embed inside your application simply by using the value from AccountExecutive to build the link: + +jane@acme.com → acme.cal.com/jane either with a string replace function or a simple regex. \ No newline at end of file diff --git a/docs/developing/guides/appstore-and-integration/integration-with-hubspot.mdx b/docs/developing/guides/appstore-and-integration/integration-with-hubspot.mdx new file mode 100644 index 0000000000..92a82572c0 --- /dev/null +++ b/docs/developing/guides/appstore-and-integration/integration-with-hubspot.mdx @@ -0,0 +1,33 @@ +--- +title: "Integration with HubSpot" +--- + +To use Cal.com with HubSpot, you only need two things: + + + + You need to have an account on both platforms + + + You'll need to go to the [HubSpot app page](https://app.cal.com/apps/hubspot) and click the "Install" button ⬇ + + + + + + + After clicking the "Install" button, you will need to select the HubSpot account you want to use Cal.com with⬇ + + + + + Then, you will need to give the listed permissions to Cal.com clicking the button "Connect app" in order to manage certain aspects of your account, in order to provide you with the functionality you are looking for. By the way, we are making our app official with HubSpot, so you will not be seeing the warning very soon ⬇ + + + + + +--- +Done! You will be redirected to Cal.com Installed Apps showing your newly installed app ready for you to take advantage of ⬇ + + diff --git a/docs/developing/guides/appstore-and-integration/oauth.mdx b/docs/developing/guides/appstore-and-integration/oauth.mdx new file mode 100644 index 0000000000..7e0be28e7e --- /dev/null +++ b/docs/developing/guides/appstore-and-integration/oauth.mdx @@ -0,0 +1,70 @@ +--- +title: "How to use OAuth to authorize apps" +--- + +As an example, you can view our OAuth flow in action on Zapier. Try to connect your cal.com account [here](https://zapier.com/apps/calcom/integrations). +To enable OAuth in one of your apps, you will need a Client ID, Client Secret, Authorization URL, Access Token Request URL, and Refresh Token Request URL. + + + +### OAuth Client Credentials +Only the cal.com team can create new OAuth clients. Please contact us at [support@cal.com](mailto:support@cal.com) with the following details: client name, redirect URI (provided by the app), app logo, and a link to the app. + +The Cal.com team will register the app and provide you with the Client ID and Client Secret. Keep these credentials confidential and secure. + +### Authorization URL +To initiate the OAuth flow, direct users to the following authorization URL: +- `https://app.cal.com/auth/oauth2/authorize` +- URL Parameters: + - *client_id* + - *state*: A securely generated random string to mitigate CSRF attacks + - *redirect_uri*: This is where users will be redirected after authorization + - *scope*: Specify the required scopes. Current scopes include READ_BOOKING and READ_PROFILE. Additional scopes can be added as needed. + +After users click *Allow*, they will be redirected to the redirect_uri with the code (authorization code) and state as URL parameters. + +### Access Token Request +Endpoint: `POST https://app.cal.com/api/auth/oauth/token` + +Request Body: +- *code*: The authorization code received in the redirect URI +- *client_id* +- *client_secret* +- *grant_type*: "authorization_code" +- *redirect_uri* + +Response: +```json +{ + access_token: “exampleAccessToken” + refresh_token: “exampleRefreshToken” +} +``` + +### Refresh Token Request +Endpoint: `POST https://app.cal.com/api/auth/oauth/refreshToken` + +Request Body: +- *grant_type*: "refresh_token" +- *client_id* +- *client_secret* + +Response: +```json +{ + access_token: “exampleAccessToken” +} +``` + +### Testing OAuth Credentials +To verify the correct setup and functionality of OAuth credentials you can use the following endpoint: +`GET https://app.cal.com/api/auth/oauth/me` + +Headers: +- Authorization: Bearer *exampleAccessToken* + + +### Authorize requests with Bearer token +Ensure that Cal.com endpoints accessed by the app support authentication via Bearer Token. +If this is not the case, it will be necessary to update the code accordingly. +Here is an example of an endpoint that supports both apiKey and Bearer token authentication: [/listBookings endpoint](https://github.com/calcom/cal.com/blob/main/packages/app-store/zapier/api/subscriptions/listBookings.ts) \ No newline at end of file diff --git a/docs/developing/guides/auth-and-provision/how-to-setup-oidc-with-okta.mdx b/docs/developing/guides/auth-and-provision/how-to-setup-oidc-with-okta.mdx new file mode 100644 index 0000000000..1eeafda6a9 --- /dev/null +++ b/docs/developing/guides/auth-and-provision/how-to-setup-oidc-with-okta.mdx @@ -0,0 +1,50 @@ +--- +title: "Setting up OIDC with okta" +--- + + + + For example, in Okta, once you create an account, you can click on Applications on the sidebar menu: + + + + + + + + + + Enter the Sign in redirect URL (or auth URL) as: + ``` + https://app.cal.com/api/auth/oidc + ``` + And the sign out URL as: + ``` + https://app.cal.com/auth/login + ``` + + + + Now you should have the Client Secret and Client ID with you. You would also need the Well Known URL which for Okta is generally of the type: + ``` + https://{yourOktaDomain}/.well-known/openid-configuration + ``` + So, if your Okta domain is `dev-123456.okta.com`, your well known URL would be: + ``` + https://dev-123456.okta.com/.well-known/openid-configuration + ``` + + + Log in with the Organization Admin user. + + + Visit `https://app.cal.com/settings/organizations/sso` and you should see something like this: + + + + Click on Configure SSO with OIDC, and then enter the Client Secret, Client ID, and Well Known URL from Step 5, and click save. + + + + + That's it. Now when you try to login with SSO, your application on Okta will handle the authentication. diff --git a/docs/developing/guides/auth-and-provision/how-to-setup-scim-with-okta.mdx b/docs/developing/guides/auth-and-provision/how-to-setup-scim-with-okta.mdx new file mode 100644 index 0000000000..0a6dcab4ed --- /dev/null +++ b/docs/developing/guides/auth-and-provision/how-to-setup-scim-with-okta.mdx @@ -0,0 +1,82 @@ + + + For example, in Okta, once you create an account, you can click on Applications on the sidebar menu: + + + + + + + Note you will have to fill in the appropriate fields for the SAML or OIDC setup to continue. + - [SAML Setup](/developing/guides/auth-and-provision/sso-setup#setting-up-saml-login) + - [OIDC Setup](/developing/guides/auth-and-provision/sso-setup#setting-up-oidc-login) + + + + Once the application is created, under General -> App Settings, click "Edit" and then the checkbox "Enable SCIM provisioning". + + + + Next, go to your instance of Cal.com and navigate to `https://app.cal.com/settings/organization/dsync` and click configure. + + + + In the "Configure Directory Sync" form, choose a directory sync name and select "Okta SCIM v2.0" as the "Directory Provider". + + + + + + + In Okta, go to your application. Navigate to the "Provisioning" tab and click "Integration" under "Settings". + + - Under "SCIM connector base URL" enter the "SCIM Base URL" from Cal.com + - Under "Unique identifier field for users" enter "email" + - Under "Supported provisioning actions" enable: + - "Import New Users and Profile Updates" + - "Push New Users" + - "Push Profile Updates" + - "Push Groups" + - Under "Authentication Mode" choose "HTTP Header" + - Under "Authentication" enter the "SCIM Bearer Token" from Cal.com + - When you hit save, it will make a test call to the "SCIM Base URL" + + + After saving, navigate to the "To App" settings, still under the "Provisioning" tab. + + + Under "Provisioning to App", click "Edit" and enable: + - "Create User" + - "Update User Attributes" + - "Deactivate User" + + + Under "\{Your application name\} Attribute Mapping," remove all fields except for: + - "username" + - "givenName" + - "familyName" + - "email" + - "displayName" + + + Set each of these properties to "Map from Okta Profile" and the related field. Under "Apply On" select "Create and Update". + + + + You can now assign users and groups to the app. + + + +## Mapping Okta Groups to Cal.com Teams + +When provisioning groups to your organization, Okta groups can be mapped to teams within your organization, and users will be auto-assigned to these teams. + +On `https://app.cal.com/settings/organization/dsync`, there is a table with the teams under your organization. Click on "Add group name" to map the Okta group to the team. + + +The group name must be spelled exactly as it is shown on Okta. + + +When you push the group to your organization, those users will automatically be added to the team. + + diff --git a/docs/developing/guides/auth-and-provision/sso-setup.mdx b/docs/developing/guides/auth-and-provision/sso-setup.mdx new file mode 100644 index 0000000000..ac440eb3ef --- /dev/null +++ b/docs/developing/guides/auth-and-provision/sso-setup.mdx @@ -0,0 +1,75 @@ +--- +title: "SSO setup" +--- + +Cal.com supports both Security Assertion Markup Language (SAML) and OpenID Connect (OIDC), two of the industry's leading authentication protocols. We prioritize your ease of access and security by providing robust Single Sign-On (SSO) capabilities. Whether you're looking for the XML-based standard of SAML or the lightweight OIDC, our platform is equipped to integrate smoothly with your preferred identity provider, ensuring both convenience and security for your users. + +### Setting up SAML login + + + + Follow the instructions here - [SAML Setup](/developing/guides/auth-and-provision/sso-setup#saml-registration-with-identity-providers) + + + Ensure that all users who need access to Cal.com have access to the IdP SAML app. + + + Keep the XML metadata from your IdP accessible, as you will need it later. + + + Visit `settings/organizations/sso`. + + + Click on the `Configure` button for `SSO with SAML`. + + + In the SAML configuration section, copy and paste the XML metadata from step 3 and click on Save. + + + Once setup is complete, provisioned users can log into Cal.com using SAML. + + + +### SAML Registration with Identity Providers + +This guide explains the settings you need to use to configure SAML with your Identity Provider. Once configured, obtain an XML metadata file and upload it on your Cal.com instance. + +> **Note:** Please do not add a trailing slash at the end of the URLs. Create them exactly as shown below. + +**Assertion consumer service URL / Single Sign-On URL / Destination URL:** https://app.cal.com/api/auth/saml/callback + +**Entity ID / Identifier / Audience URI / Audience Restriction:** [https://saml.cal.com](https://saml.cal.com) + +**Response:** Signed + +**Assertion Signature:** Signed + +**Signature Algorithm:** RSA-SHA256 + +**Assertion Encryption:** Unencrypted + +**Mapping Attributes / Attribute Statements:** + +- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier -> id +- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress -> email +- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname -> firstName +- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname -> lastName + +### Setting up OIDC login + + + + Keep handy the Client Secret, Client ID, and Well Known URL for the next steps. + + + Visit `/settings/organizations/sso` and you should see something like this: + + + + Click on Configure SSO with OIDC, enter the Client Secret, Client ID, and Well Known URL from Step 1, and click save. + + + + Now, when you try to login with SSO, your OIDC provider will handle the authentication. + + diff --git a/docs/developing/guides/email/setup-mailtrap-for-email-testing.mdx b/docs/developing/guides/email/setup-mailtrap-for-email-testing.mdx new file mode 100644 index 0000000000..b30fbd213a --- /dev/null +++ b/docs/developing/guides/email/setup-mailtrap-for-email-testing.mdx @@ -0,0 +1,39 @@ +--- +title: "Setting up mailtrap for email testing" +--- +Mailtrap is a versatile tool for testing email notifications without sending them to the real users of your application. It's especially useful for testing and ensuring that the system sends out accurate emails in a secure and efficient manner. + +To set up Mailtrap for email testing on Cal.com, follow these steps: + + + + The first step is to register an account with Mailtrap. It offers a free plan for small testing needs, and also paid plans for larger organizations. + + + After registering, create an inbox for your application. Each inbox comes with its SMTP and POP3 credentials, which can be used to send and retrieve emails. + + + Change the tab to nodemailer and copy the information to `.env`: + ``` + EMAIL_SERVER_HOST='sandbox.smtp.mailtrap.io' + EMAIL_SERVER_PORT=2525 + EMAIL_SERVER_USER='XXX' + EMAIL_SERVER_PASSWORD='XXX' + ``` + Please remember to change the user and password and replace 'XXX' above with their respective values. + + + Set the `EMAIL_FROM` environment variable in the `.env` file. For example: + ``` + EMAIL_FROM='notifications@yourselfhostedcal.com' + ``` + + + Once your instance is configured, you can start sending test emails. These emails are "trapped" in the Mailtrap inbox, enabling you to verify email content, headers, and attachments. + + + Mailtrap provides features to analyze and debug your emails. You can check whether the HTML displays correctly, ensure the email is not treated as spam, and also see if it contains any broken links, invalid format, or incorrect images. + + + +Happy testing! diff --git a/docs/developing/guides/embeds/embed-events.mdx b/docs/developing/guides/embeds/embed-events.mdx new file mode 100644 index 0000000000..0a1ce44492 --- /dev/null +++ b/docs/developing/guides/embeds/embed-events.mdx @@ -0,0 +1,38 @@ +--- +title: Embed Events +--- + +## Actions + +You can listen to an action that occurs in embedded cal link as follows. You can think of them as DOM events. We are avoiding the term "events" to not confuse it with Cal Events. + +```js + +``` + +## Following are the list of supported actions. + +| Action | Description | Properties | +|----------------------|-------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| eventTypeSelected | When user chooses an event-type from the listing. | `eventType: object` // Event Type that has been selected | +| bookingSuccessful | When the booking is successfully done. It might not be confirmed. | `confirmed: boolean` // Whether confirmation from organizer is pending or not
`eventType: object` // Event Type that has been booked
`date: string` // Date of Event
`duration: number` // Duration of booked Event
`organizer: object` // Organizer details like name, timezone, email | +| linkReady | Tells that the link is ready to be shown now. | None | +| linkFailed | Fired if link fails to load. | `code: number` // Error Code
`msg: string` // Human Readable message
`data: object` // More details to debug the error | +| __iframeReady | It is fired when the embedded iframe is ready to communicate with parent snippet. This is mostly for internal use by Embed Snippet. | None | +| __windowLoadComplete | Tells that window load for iframe is complete. | None | +| __dimensionChanged | Tells that dimensions of the content inside the iframe changed. | `iframeWidth: number`
`iframeHeight: number` | + + +Actions that start with __ are internal. + +To get more details on how Embed actually works, you can refer to this [Embed Flowchart](https://www.figma.com/file/zZ5oaUpg12Fuu5mGZrPlP5/Embed-Flowchart). \ No newline at end of file diff --git a/docs/developing/introduction.mdx b/docs/developing/introduction.mdx new file mode 100644 index 0000000000..77f6fb2dcc --- /dev/null +++ b/docs/developing/introduction.mdx @@ -0,0 +1,26 @@ +--- +title: "Introduction" +icon: "presentation-screen" +--- + + + + + + + Follow our quickstart guide to start developing with Cal.com + + + Learn how to self-host Cal.com for controlled instance + + + Learn how to use Cal.com's API to CRUD various Cal resources programmatically + + + + +## Welcome to the Cal.com Documentation! + +This is the documentation for Cal.com, an open source scheduling platform that enables you to take bookings on your own terms. Set your availability, connect your favourite calendar and video conferencing platforms, and share your link. It's that simple. + +It covers everything from setup to usage and development. Here you can explore advice and explanations for all of our features, as well as discover new tips and tricks to get the most out of the Cal.com software. This also includes the more advanced documentation covering how to build on top of Cal.com both in the codebase, by API and more. These guides will go into depth on how you can best integrate and extend Cal.com for your needs. diff --git a/docs/developing/local-development.mdx b/docs/developing/local-development.mdx new file mode 100644 index 0000000000..6b9fc08eb6 --- /dev/null +++ b/docs/developing/local-development.mdx @@ -0,0 +1,179 @@ +--- +title: "Local Development" +icon: "terminal" +--- + +Cal.com runs with pretty minimal hardware requirements by itself. The most intensive part for the software is when you actually build the software, but once it's running it's relatively lightweight. + +Cal.com works with a very large range of operating systems, as it only requires JavaScript execution to run. **Cal.com is known to work well with Windows, Mac, Linux and BSD.** Although they do work well on all of them, for production deployments we would suggest Linux as the ideal platform. Any operating system that runs Node.js should be able to work too, but these are some of the common operating systems that we know work well. + +To run Cal.com, you need to install a few things. **Node.js, yarn, Git and PostgreSQL**. We use **Prisma** for database maintenance, and is one of the dependencies. We won’t publish installation guides for these as they have their own resources available on the internet. If you're on Linux/BSD, all of these things should be readily available on your package manager. Your best bet is searching for something like **Debian 12 PostgreSQL**, which will give you a guide to installing and configuring PostgreSQL on Debian Linux 12. + + +To ensure optimal performance and compatibility, we highly recommend using Node.js version 18 for your development environment. This version provides the best balance of stability, features, and security for this project. Please make sure to update your Node.js installation if necessary. + + +## Development Setup & Production Build + +1. **First, you git clone the repository** with the following command, so you have a copy of the code. + ```git + git clone https://github.com/calcom/cal.com.git + ``` + + +If you are on windows, you would need to use the following command when cloning, with **admin privileges**: +```git +git clone -c core.symlinks=true https://github.com/calcom/cal.com.git +``` + + +2. Then, go into the directory you just cloned with + ```bash + cd cal.com + ``` + and run + ```bash + yarn + ``` + to install all of the dependencies. Essentially, dependencies are just things that Cal.com needs to install to be able to work. + +3. Then, you just need to set up a couple of things. For that, we use a `.env` file. We just need to copy and paste the `.env.example` file and rename the copy to `.env`. Here you'll have a template with comments showing you the settings you need/might want to set. + +4. Next, use the command + ```bash + openssl rand -base64 32 + ``` + (or another secret generator tool if you prefer) to generate a key and add it under `NEXTAUTH_SECRET` in the .env file. + +5. You'll also want to fill out the `.env.appStore` file similar to the `.env` file as this includes keys to enable apps. + +**Development tips** + +> Add `NEXT_PUBLIC_DEBUG=1` anywhere in your `.env` to get logging information for all the queries and mutations driven by **trpc**. + +```bash +echo 'NEXT_PUBLIC_DEBUG=1' >> .env +``` + +> For email testing, set it to "1" if you need to email checks in E2E tests locally. Make sure to run mailhog container manually or with `yarn dx`. + +```bash +echo 'E2E_TEST_MAILHOG_ENABLED=1' >> .env +``` + +### E2E Testing + +Be sure to set the environment variable `NEXTAUTH_URL` to the correct value. If you are running locally, as the documentation within `.env.example` mentions, the value should be `http://localhost:3000`. + + +In a terminal just run: +```bash +yarn test-e2e +``` +To open last HTML report run: +```bash +yarn playwright show-report test-results/reports/playwright-html-report +``` + +### Manual setup + +1. Configure environment variables in the .env file. Replace ``, ``, ``, `` with their applicable values + + ```bash + DATABASE_URL='postgresql://:@:' + ``` + +2. Set a 24 character random string in your .env file for the `CALENDSO_ENCRYPTION_KEY` (You can use a command like + ```bash + openssl rand -base64 24 + ``` + to generate one). + +3. Set up the database using the Prisma schema (found in `apps/web/prisma/schema.prisma`) + + ```bash + yarn workspace @calcom/prisma db-deploy + ``` + +4. Run (in development mode) + + ```bash + yarn dev + ``` + + +When you're testing out the enterprise features locally, you should see a warning shown in the image below, clarifying the need to purchase a license for such features in production. + + + + +## **Development quick start with** **`yarn dx`** + +> * **Requires Docker and Docker Compose to be installed** +> +> +> * Will start a local Postgres instance with a few test users - the credentials will be logged in the console +> + +```bash +yarn dx +``` + +## **Cron Jobs** + +There are a few features which require cron job setup. At cal.com, the cron jobs are found in the following directory: +```bash +/apps/web/pages/api/cron +``` + + +## App store seeder + + +We recommend using the admin UI/wizard instead of the seeder to enable app store apps + + + +## API + +#### Step 1 + +Copy the .env files from their respective example files depending on the version of the API (v1 or v2): + +```bash +cp apps/api/.env.example apps/api/{version}/.env +cp .env.example .env +``` + +#### Step 2 + +Install packages with yarn: + +```bash +yarn +``` + +### Running API server + +Run the API V1 with yarn: + +```bash +yarn workspace @calcom/api dev +``` + +Run the API V2 with yarn: +Please note API V2 requires the database running in docker + +```bash +yarn workspace @calcom/api-v2 dev +``` + + +On windows, you would need to update the script to explicitly set port to 3003 and run yarn dev under `apps/api/package.json` So, it should look something like this after the changes: + +`"dev": "set PORT=3003 && next dev"` Now, running `yarn workspace @calcom/api dev` should start the server. + + +Open [http://localhost:3003](http://localhost:3003/) with your browser to see the result. + +If you wish to test how API works locally, please [check out our guide here](/developing/guides/api/how-to-setup-api-in-a-local-instance). diff --git a/docs/developing/open-source-contribution/code-styling.mdx b/docs/developing/open-source-contribution/code-styling.mdx new file mode 100644 index 0000000000..8882970ed3 --- /dev/null +++ b/docs/developing/open-source-contribution/code-styling.mdx @@ -0,0 +1,20 @@ +--- +title: "Code styling" +icon: "pen-fancy" +--- + +Keeping our code styles consistent is key to making the repository easy to read and work with. + +We use a number of style guides written by other amazing companies, simply because they are widely used and because we like working with them. + +We don't expect you to study every single rule of each style guide, but these are great reference points as to how your code should be styled. We may reject a pull request if your code style significantly differs from these style guides however. + +### ESLint & Prettier +Calendso uses the ESLint and Prettier formatting tools, and the repository comes with defined rules for each tool. We recommend setting up both tools and using these to help automatically style your code to our guidelines. + +### JavaScript/TypeScript +We use the + for all JavaScript and Typescript code. + +### HTML & CSS +We use the [Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html) for any HTML and CSS markup. However, exceptions to the HTML guide apply where JSX differentiates from standard HTML. \ No newline at end of file diff --git a/docs/developing/open-source-contribution/contributors-guide.mdx b/docs/developing/open-source-contribution/contributors-guide.mdx new file mode 100644 index 0000000000..165119b359 --- /dev/null +++ b/docs/developing/open-source-contribution/contributors-guide.mdx @@ -0,0 +1,164 @@ +--- +title: "Contributor's Guide" +icon: "book-open" +--- + +## Introduction + +Cal.com is structured as a monorepo using [Turborepo](https://github.com/vercel/turbo). Cal.com is built on [NextJS]("https://github.com/vercel/next.js") with [Typescript]("https://www.typescriptlang.org/"), [PostgreSQL]("https://www.postgresql.org/") database and the interactions with the database are carried out using [Prisma]("https://www.prisma.io/"). We use [TRPC]("https://trpc.io/") for end-to-end typesafe procedure calls to Prisma and utilize [Zod]("https://zod.dev/") for validation and type-safe parsing of API calls. + +## Setting Up the Development Environment + +### Prerequisites + +Here is what you need to be able to run Cal.com. +- Node.js (Version: >=18.x) +- PostgreSQL +- Yarn (recommended) + +Cal.com can be set up and run on your machine locally. Please take a look at [this quick guide](https://cal.com/documentation/getting-started/local-development) for instructions on setting it up. + + + + + +## Understanding the Project Structure + +Cal.com makes use of Turborepo for a monorepo structure. This allows us to manage multiple code projects (like libraries, apps, services) and share code and resources effectively. + +You can look at [turbo.json](https://github.com/calcom/cal.com/blob/main/turbo.json) to see how we use it. In simple terms, +- Each entry in the `pipeline` section corresponds to a different part of the project. Understanding these will help in navigating the codebase. +- Knowing which tasks depend on others is crucial for understanding the build process. +- The `env` entries under each task highlight the need for specific configuration settings, which you will need to set up in your development environment. +- You should focus on the tasks related to the area you are contributing to. For example, if working on the Prisma schema, look at tasks prefixed with `@calcom/prisma` +- The various packages can be found in the [/packages](https://github.com/calcom/cal.com/tree/main/packages) folder from the root. +- The various pages and components for the web can be found in the [/apps/web](https://github.com/calcom/cal.com/tree/main/apps/web) folder from the root. +- The public API related codebase can be found in the [/apps/api](https://github.com/calcom/cal.com/tree/main/apps/api) folder from the root. +- The Cal.ai related codebase can be found in the [/apps/ai](https://github.com/calcom/cal.com/tree/main/apps/ai) folder from the root + +## How we use TRPC at cal.com + +Cal.com makes use of the TRPC calls for CRUD operations on the database by utilizing the [useQuery()](https://trpc.io/docs/client/react/useQuery) & [useMutation()](https://trpc.io/docs/client/react/useMutation) hooks. These are essentially wrappers around @tanstack/react-query and you can learn more about them here, respectively: [queries](https://tanstack.com/query/v4/docs/react/guides/queries), [mutations](https://tanstack.com/query/v4/docs/react/guides/mutations). + +Here’s an example usage of useQuery at cal.com: +```JS + const { data: webhooks } = trpc.viewer.webhook.list.useQuery(undefined, { + suspense: true, + enabled: session.status === "authenticated", + }); +``` + +Here’s an example usage of useMutation at cal.com: +```JS + const createWebhookMutation = trpc.viewer.webhook.create.useMutation({ + async onSuccess() { + showToast(t("webhook_created_successfully"), "success"); + await utils.viewer.webhook.list.invalidate(); + router.back(); + }, + onError(error) { + showToast(`${error.message}`, "error"); + }, + }); +``` +Both the examples are taken from [packages/features/webhooks/pages/webhook-new-view.tsx](https://github.com/calcom/cal.com/blob/main/packages/features/webhooks/pages/webhook-new-view.tsx) file. We use the useQuery hook for fetching data from the database. On the contrary, the useMutation hook is used for Creating/Updating/Deleting data from the database. + +The path prior to the useQuery or useMutation hook represents the actual path of the procedure: in this case, [packages/trpc/server/routers/viewer/webhook](https://github.com/calcom/cal.com/tree/main/packages/trpc/server/routers/viewer/webhook). You will find `create.handler.ts` in case of +```js +trpc.viewer.webhook.create.useMutation() +``` +& `list.handler.ts` in case of +```js +trpc.viewer.webhook.list.useQuery() +``` +in the path mentioned above. There is usually a schema file in the same directory for each handler as well (_name_.schema.ts). This is generally how we make use of the TRPC calls in the cal.com repo. + +## The Cal.com API V1 + +Our [API V1](https://github.com/calcom/cal.com/tree/main/apps/api) uses [zod validations](https://zod.dev/) to keep us typesafe and give us extensive parsing control and error handling. These validations reside in [/apps/api/lib/validations](https://github.com/calcom/cal.com/tree/main/apps/api/lib/validations). We also have [helper](https://github.com/calcom/cal.com/tree/main/apps/api/lib/helpers) and [utility](https://github.com/calcom/cal.com/tree/main/apps/api/lib/utils) functions which are used primarily within our API endpoints. +Here’s an example usage of our [zod validation in action with the respective API endpoint](https://github.com/calcom/cal.com/blob/main/apps/api/lib/validations/destination-calendar.ts): +```js +export const schemaDestinationCalendarBaseBodyParams = DestinationCalendar.pick({ + integration: true, + externalId: true, + eventTypeId: true, + bookingId: true, + userId: true, +}).partial(); + +const schemaDestinationCalendarCreateParams = z + .object({ + integration: z.string(), + externalId: z.string(), + eventTypeId: z.number().optional(), + bookingId: z.number().optional(), + userId: z.number().optional(), + }) + .strict(); + + +export const schemaDestinationCalendarCreateBodyParams = schemaDestinationCalendarBaseBodyParams.merge( + schemaDestinationCalendarCreateParams +); +``` + +And here’s the usage of this validation for parsing the request at the [respective endpoint](https://github.com/calcom/cal.com/blob/main/apps/api/pages/api/destination-calendars/_post.ts): +```js +async function postHandler(req: NextApiRequest) { + const { userId, isAdmin, prisma, body } = req; + const parsedBody = schemaDestinationCalendarCreateBodyParams.parse(body); +``` +This is the general approach of validation and parsing in our API submodule. + +## Building Apps for Cal.com + +We’ve got an app store containing apps which complement the scheduling platform. From video conferencing apps such as Zoom, Google Meet, etc. to Calendar Apps such as Google Calendar, Apple Calendar, CalDAV, etc. and even payment, automation, analytics and other miscellaneous apps, it is an amazing space to improve the scheduling experience. + +We have a CLI script in place that makes building apps a breeze. [You can learn more about it here](https://cal.com/docs/how-to-guides/how-to-build-an-app). + +## Running Tests + +### E2E-Testing + +Be sure to set the environment variable `NEXTAUTH_URL` to the correct value. If you are running locally, as the documentation within `.env.example` mentions, the value should be `http://localhost:3000`. + +In a terminal just run: +```bash +yarn test-e2e +``` + +To open the last HTML report run: + +```bash +`yarn playwright show-report test-results/reports/playwright-html-report` +``` + +#### Resolving issues + +**E2E test browsers not installed** + +If you face the following error when running `yarn test-e2e`: + + +Executable doesn't exist at /Users/alice/Library/Caches/ms-playwright/chromium-1048/chrome-mac/Chromium.app + + +You can resolve it by running: + +```bash +npx playwright install +``` +to download test browsers. + +## Contribution and Collaboration Guides + +- [Code Styling Guide](/developing/open-source-contribution/code-styling) +- [Pull Requests Guide](/developing/open-source-contribution/pull-requests) diff --git a/docs/developing/open-source-contribution/index.mdx b/docs/developing/open-source-contribution/index.mdx new file mode 100644 index 0000000000..bafdccaeea --- /dev/null +++ b/docs/developing/open-source-contribution/index.mdx @@ -0,0 +1,137 @@ +--- +title: "Introduction" +icon: "github" +--- + +Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. + +- Before jumping into a PR be sure to search [existing PRs](https://github.com/calcom/cal.com/pulls) or [issues](https://github.com/calcom/cal.com/issues) for an open or closed item that relates to your submission. + +### Priorities + +| Type of issue | Priority | +| :-------------------------------------------------------------- | :-------------------------------------------------- | +| Minor improvements, non-core feature requests | | +| Confusing UX (... but working) | | +| Core Features (Booking page, availabilty, timezone calculation) | | +| Core Bugs (Login, Booking page, Emails are not working) | | + + +### Developing + +The development branch is `main`. This is the branch that all pull +requests should be made against. The changes on the `main` +branch are tagged into a release monthly. + +To develop locally: + +1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your + own GitHub account and then + [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. +2. Create a new branch: + + ```bash + git checkout -b MY_BRANCH_NAME + ``` + +3. Install yarn: + + ```bash + npm install -g yarn + ``` + +4. Install the dependencies with: + + ```bash + yarn + ``` + +5. Start developing and watch for code changes: + + ```bash + yarn dev + ``` + +### Building + +You can build the project with: + +```bash +yarn build +``` + +Please be sure that you can make a full production build before pushing code. + +### Testing + +More info on how to add new tests coming soon. + +### Running tests + +This will run and test all flows in multiple Chromium windows to verify that no critical flow breaks: + +```bash +yarn test-e2e +``` + +### Linting + +To check the formatting of your code: + +```bash +yarn lint +``` + +If you get errors, be sure to fix them before committing. + +### Making a Pull Request + +- Be sure to [check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) while creating you PR. +- If your PR refers to or fixes an issue, be sure to add `refs #XXX` or `fixes #XXX` to the PR description. Replacing `XXX` with the respective issue number. See more about [Linking a pull request to an issue + ](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). +- Be sure to fill the PR Template accordingly. + +### Guidelines for committing yarn lockfile + +Do not commit your `yarn.lock` unless you've made changes to the `package.json`. If you've already committed `yarn.lock` unintentionally, follow these steps to undo: + +If your last commit has the `yarn.lock` file alongside other files and you only wish to uncommit the `yarn.lock`: + ```git + git checkout HEAD~1 yarn.lock + git commit -m "Revert yarn.lock changes" + ``` +If you've pushed the commit with the `yarn.lock`: + 1. Correct the commit locally using the above method. + 2. Carefully force push: + + ```git + git push origin --force + ``` + +If `yarn.lock` was committed a while ago and there have been several commits since, you can use the following steps to revert just the `yarn.lock` changes without impacting the subsequent changes: + +1. **Checkout a Previous Version**: + - Find the commit hash before the `yarn.lock` was unintentionally committed. You can do this by viewing the Git log: + ```git + git log yarn.lock + ``` + - Once you have identified the commit hash, use it to checkout the previous version of `yarn.lock`: + ```git + git checkout yarn.lock + ``` + +2. **Commit the Reverted Version**: + - After checking out the previous version of the `yarn.lock`, commit this change: + ```git + git commit -m "Revert yarn.lock to its state before unintended changes" + ``` + +3. **Proceed with Caution**: + - If you need to push this change, first pull the latest changes from your remote branch to ensure you're not overwriting other recent changes: + ```git + git pull origin + ``` + - Then push the updated branch: + ```git + git push origin + ``` \ No newline at end of file diff --git a/docs/developing/open-source-contribution/pull-requests.mdx b/docs/developing/open-source-contribution/pull-requests.mdx new file mode 100644 index 0000000000..8d69ee56d8 --- /dev/null +++ b/docs/developing/open-source-contribution/pull-requests.mdx @@ -0,0 +1,37 @@ +--- +title: "Pull requests" +icon: "code-pull-request" +--- + +### Requirements + +We have a number of requirements for PRs to ensure they are as easy to review as possible and to ensure that they are up to standard with the code. + +### Title & Content + + +Start by providing a short and concise title. Don’t put something generic (e.g. bug fixes), and instead mention more specifically what your PR achieves, for instance “Fixes dropdown not expanding on settings page”. + +For the PR description, you should go into much greater detail about what your PR adds or fixes. Firstly, the description should include a link to any relevant issues or discussions surrounding the feature or bug that your PR addresses. + +### Feature PRs + +Give a functional overview of how your feature works, including how the user can use the feature. Then, share any technical details in an overview of how the PR works (e.g. “Once the user enters their password, the password is hashed using BCrypt and stored in the Users database field”). + +### Bug Fix PRs + +Give an overview of how your PR fixes the bug both as a high-level overview and a technical explanation of what caused the issue and how your PR resolves this. + +Feel free to add a short video or screenshots of what your PR achieves. Loom is a great way of sharing short videos. + +### Code Quality & Styling + +All submitted code must match our [code styling](/code-styling) standards. We will reject pull requests that differ significantly from our standardised code styles. + +All code is automatically checked by Codacy and our linting process, and will notify you if there are any issues with the code that you submit. We require that code passes these quality checks before merging. + +### PR review process + +At least two members of the Cal.com team should review and approve any PR before it is merged. + +Once two members of the team have approved this, someone from the team will merge the PR. If you are part of the Cal.com team, you should merge your own PRs once you have received both approvals. \ No newline at end of file diff --git a/docs/essentials/code.mdx b/docs/essentials/code.mdx deleted file mode 100644 index d2a462a7a2..0000000000 --- a/docs/essentials/code.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: 'Code Blocks' -description: 'Display inline code and code blocks' -icon: 'code' ---- - -## Basic - -### Inline Code - -To denote a `word` or `phrase` as code, enclose it in backticks (`). - -``` -To denote a `word` or `phrase` as code, enclose it in backticks (`). -``` - -### Code Block - -Use [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) by enclosing code in three backticks and follow the leading ticks with the programming language of your snippet to get syntax highlighting. Optionally, you can also write the name of your code after the programming language. - -```java HelloWorld.java -class HelloWorld { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } -} -``` - -````md -```java HelloWorld.java -class HelloWorld { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } -} -``` -```` diff --git a/docs/essentials/images.mdx b/docs/essentials/images.mdx deleted file mode 100644 index 60ad42d38b..0000000000 --- a/docs/essentials/images.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: 'Images and Embeds' -description: 'Add image, video, and other HTML elements' -icon: 'image' ---- - - - -## Image - -### Using Markdown - -The [markdown syntax](https://www.markdownguide.org/basic-syntax/#images) lets you add images using the following code - -```md -![title](/path/image.jpg) -``` - -Note that the image file size must be less than 5MB. Otherwise, we recommend hosting on a service like [Cloudinary](https://cloudinary.com/) or [S3](https://aws.amazon.com/s3/). You can then use that URL and embed. - -### Using Embeds - -To get more customizability with images, you can also use [embeds](/writing-content/embed) to add images - -```html - -``` - -## Embeds and HTML elements - - - -
- - - -Mintlify supports [HTML tags in Markdown](https://www.markdownguide.org/basic-syntax/#html). This is helpful if you prefer HTML tags to Markdown syntax, and lets you create documentation with infinite flexibility. - - - -### iFrames - -Loads another HTML page within the document. Most commonly used for embedding videos. - -```html - -``` diff --git a/docs/essentials/markdown.mdx b/docs/essentials/markdown.mdx deleted file mode 100644 index c8ad9c1f3d..0000000000 --- a/docs/essentials/markdown.mdx +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: 'Markdown Syntax' -description: 'Text, title, and styling in standard markdown' -icon: 'text-size' ---- - -## Titles - -Best used for section headers. - -```md -## Titles -``` - -### Subtitles - -Best use to subsection headers. - -```md -### Subtitles -``` - - - -Each **title** and **subtitle** creates an anchor and also shows up on the table of contents on the right. - - - -## Text Formatting - -We support most markdown formatting. Simply add `**`, `_`, or `~` around text to format it. - -| Style | How to write it | Result | -| ------------- | ----------------- | --------------- | -| Bold | `**bold**` | **bold** | -| Italic | `_italic_` | _italic_ | -| Strikethrough | `~strikethrough~` | ~strikethrough~ | - -You can combine these. For example, write `**_bold and italic_**` to get **_bold and italic_** text. - -You need to use HTML to write superscript and subscript text. That is, add `` or `` around your text. - -| Text Size | How to write it | Result | -| ----------- | ------------------------ | ---------------------- | -| Superscript | `superscript` | superscript | -| Subscript | `subscript` | subscript | - -## Linking to Pages - -You can add a link by wrapping text in `[]()`. You would write `[link to google](https://google.com)` to [link to google](https://google.com). - -Links to pages in your docs need to be root-relative. Basically, you should include the entire folder path. For example, `[link to text](/writing-content/text)` links to the page "Text" in our components section. - -Relative links like `[link to text](../text)` will open slower because we cannot optimize them as easily. - -## Blockquotes - -### Singleline - -To create a blockquote, add a `>` in front of a paragraph. - -> Dorothy followed her through many of the beautiful rooms in her castle. - -```md -> Dorothy followed her through many of the beautiful rooms in her castle. -``` - -### Multiline - -> Dorothy followed her through many of the beautiful rooms in her castle. -> -> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. - -```md -> Dorothy followed her through many of the beautiful rooms in her castle. -> -> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. -``` - -### LaTeX - -Mintlify supports [LaTeX](https://www.latex-project.org) through the Latex component. - -8 x (vk x H1 - H2) = (0,1) - -```md -8 x (vk x H1 - H2) = (0,1) -``` diff --git a/docs/essentials/navigation.mdx b/docs/essentials/navigation.mdx deleted file mode 100644 index ca44bb6452..0000000000 --- a/docs/essentials/navigation.mdx +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: 'Navigation' -description: 'The navigation field in mint.json defines the pages that go in the navigation menu' -icon: 'map' ---- - -The navigation menu is the list of links on every website. - -You will likely update `mint.json` every time you add a new page. Pages do not show up automatically. - -## Navigation syntax - -Our navigation syntax is recursive which means you can make nested navigation groups. You don't need to include `.mdx` in page names. - - - -```json Regular Navigation -"navigation": [ - { - "group": "Getting Started", - "pages": ["quickstart"] - } -] -``` - -```json Nested Navigation -"navigation": [ - { - "group": "Getting Started", - "pages": [ - "quickstart", - { - "group": "Nested Reference Pages", - "pages": ["nested-reference-page"] - } - ] - } -] -``` - - - -## Folders - -Simply put your MDX files in folders and update the paths in `mint.json`. - -For example, to have a page at `https://yoursite.com/your-folder/your-page` you would make a folder called `your-folder` containing an MDX file called `your-page.mdx`. - - - -You cannot use `api` for the name of a folder unless you nest it inside another folder. Mintlify uses Next.js which reserves the top-level `api` folder for internal server calls. A folder name such as `api-reference` would be accepted. - - - -```json Navigation With Folder -"navigation": [ - { - "group": "Group Name", - "pages": ["your-folder/your-page"] - } -] -``` - -## Hidden Pages - -MDX files not included in `mint.json` will not show up in the sidebar but are accessible through the search bar and by linking directly to them. diff --git a/docs/essentials/reusable-snippets.mdx b/docs/essentials/reusable-snippets.mdx deleted file mode 100644 index a0a55297ab..0000000000 --- a/docs/essentials/reusable-snippets.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Reusable Snippets -description: Reusable, custom snippets to keep content in sync -icon: 'recycle' ---- - -import SnippetIntro from '/snippets/snippet-intro.mdx'; - - - -## Creating a custom snippet - -**Pre-condition**: You must create your snippet file in the `snippets` directory. - - - Any page in the `snippets` directory will be treated as a snippet and will not - be rendered into a standalone page. If you want to create a standalone page - from the snippet, import the snippet into another file and call it as a - component. - - -### Default export - -1. Add content to your snippet file that you want to re-use across multiple - locations. Optionally, you can add variables that can be filled in via props - when you import the snippet. - -```mdx snippets/my-snippet.mdx -Hello world! This is my content I want to reuse across pages. My keyword of the -day is {word}. -``` - - - The content that you want to reuse must be inside the `snippets` directory in - order for the import to work. - - -2. Import the snippet into your destination file. - -```mdx destination-file.mdx ---- -title: My title -description: My Description ---- - -import MySnippet from '/snippets/path/to/my-snippet.mdx'; - -## Header - -Lorem impsum dolor sit amet. - - -``` - -### Reusable variables - -1. Export a variable from your snippet file: - -```mdx snippets/path/to/custom-variables.mdx -export const myName = 'my name'; - -export const myObject = { fruit: 'strawberries' }; -``` - -2. Import the snippet from your destination file and use the variable: - -```mdx destination-file.mdx ---- -title: My title -description: My Description ---- - -import { myName, myObject } from '/snippets/path/to/custom-variables.mdx'; - -Hello, my name is {myName} and I like {myObject.fruit}. -``` - -### Reusable components - -1. Inside your snippet file, create a component that takes in props by exporting - your component in the form of an arrow function. - -```mdx snippets/custom-component.mdx -export const MyComponent = ({ title }) => ( -
-

{title}

-

... snippet content ...

-
-); -``` - - - MDX does not compile inside the body of an arrow function. Stick to HTML - syntax when you can or use a default export if you need to use MDX. - - -2. Import the snippet into your destination file and pass in the props - -```mdx destination-file.mdx ---- -title: My title -description: My Description ---- - -import { MyComponent } from '/snippets/custom-component.mdx'; - -Lorem ipsum dolor sit amet. - - -``` diff --git a/docs/essentials/settings.mdx b/docs/essentials/settings.mdx deleted file mode 100644 index d9dd2d7e1d..0000000000 --- a/docs/essentials/settings.mdx +++ /dev/null @@ -1,318 +0,0 @@ ---- -title: 'Global Settings' -description: 'Mintlify gives you complete control over the look and feel of your documentation using the mint.json file' -icon: 'gear' ---- - -Every Mintlify site needs a `mint.json` file with the core configuration settings. Learn more about the [properties](#properties) below. - -## Properties - - -Name of your project. Used for the global title. - -Example: `mintlify` - - - - - An array of groups with all the pages within that group - - - The name of the group. - - Example: `Settings` - - - - The relative paths to the markdown files that will serve as pages. - - Example: `["customization", "page"]` - - - - - - - - Path to logo image or object with path to "light" and "dark" mode logo images - - - Path to the logo in light mode - - - Path to the logo in dark mode - - - Where clicking on the logo links you to - - - - - - Path to the favicon image - - - - Hex color codes for your global theme - - - The primary color. Used for most often for highlighted content, section - headers, accents, in light mode - - - The primary color for dark mode. Used for most often for highlighted - content, section headers, accents, in dark mode - - - The primary color for important buttons - - - The color of the background in both light and dark mode - - - The hex color code of the background in light mode - - - The hex color code of the background in dark mode - - - - - - - - Array of `name`s and `url`s of links you want to include in the topbar - - - The name of the button. - - Example: `Contact us` - - - The url once you click on the button. Example: `https://mintlify.com/contact` - - - - - - - - - Link shows a button. GitHub shows the repo information at the url provided including the number of GitHub stars. - - - If `link`: What the button links to. - - If `github`: Link to the repository to load GitHub information from. - - - Text inside the button. Only required if `type` is a `link`. - - - - - - - Array of version names. Only use this if you want to show different versions - of docs with a dropdown in the navigation bar. - - - - An array of the anchors, includes the `icon`, `color`, and `url`. - - - The [Font Awesome](https://fontawesome.com/search?s=brands%2Cduotone) icon used to feature the anchor. - - Example: `comments` - - - The name of the anchor label. - - Example: `Community` - - - The start of the URL that marks what pages go in the anchor. Generally, this is the name of the folder you put your pages in. - - - The hex color of the anchor icon background. Can also be a gradient if you pass an object with the properties `from` and `to` that are each a hex color. - - - Used if you want to hide an anchor until the correct docs version is selected. - - - Pass `true` if you want to hide the anchor until you directly link someone to docs inside it. - - - One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" - - - - - - - Override the default configurations for the top-most anchor. - - - The name of the top-most anchor - - - Font Awesome icon. - - - One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" - - - - - - An array of navigational tabs. - - - The name of the tab label. - - - The start of the URL that marks what pages go in the tab. Generally, this - is the name of the folder you put your pages in. - - - - - - Configuration for API settings. Learn more about API pages at [API Components](/api-playground/demo). - - - The base url for all API endpoints. If `baseUrl` is an array, it will enable for multiple base url - options that the user can toggle. - - - - - - The authentication strategy used for all API endpoints. - - - The name of the authentication parameter used in the API playground. - - If method is `basic`, the format should be `[usernameName]:[passwordName]` - - - The default value that's designed to be a prefix for the authentication input field. - - E.g. If an `inputPrefix` of `AuthKey` would inherit the default input result of the authentication field as `AuthKey`. - - - - - - Configurations for the API playground - - - - Whether the playground is showing, hidden, or only displaying the endpoint with no added user interactivity `simple` - - Learn more at the [playground guides](/api-playground/demo) - - - - - - Enabling this flag ensures that key ordering in OpenAPI pages matches the key ordering defined in the OpenAPI file. - - This behavior will soon be enabled by default, at which point this field will be deprecated. - - - - - - - A string or an array of strings of URL(s) or relative path(s) pointing to your - OpenAPI file. - - Examples: - - ```json Absolute - "openapi": "https://example.com/openapi.json" - ``` - ```json Relative - "openapi": "/openapi.json" - ``` - ```json Multiple - "openapi": ["https://example.com/openapi1.json", "/openapi2.json", "/openapi3.json"] - ``` - - - - - - An object of social media accounts where the key:property pair represents the social media platform and the account url. - - Example: - ```json - { - "x": "https://x.com/mintlify", - "website": "https://mintlify.com" - } - ``` - - - One of the following values `website`, `facebook`, `x`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news` - - Example: `x` - - - The URL to the social platform. - - Example: `https://x.com/mintlify` - - - - - - Configurations to enable feedback buttons - - - - Enables a button to allow users to suggest edits via pull requests - - - Enables a button to allow users to raise an issue about the documentation - - - - - - Customize the dark mode toggle. - - - Set if you always want to show light or dark mode for new users. When not - set, we default to the same mode as the user's operating system. - - - Set to true to hide the dark/light mode toggle. You can combine `isHidden` with `default` to force your docs to only use light or dark mode. For example: - - - ```json Only Dark Mode - "modeToggle": { - "default": "dark", - "isHidden": true - } - ``` - - ```json Only Light Mode - "modeToggle": { - "default": "light", - "isHidden": true - } - ``` - - - - - - - - - A background image to be displayed behind every page. See example with - [Infisical](https://infisical.com/docs) and [FRPC](https://frpc.io). - diff --git a/docs/images/high-priority.svg b/docs/images/high-priority.svg new file mode 100644 index 0000000000..60cc089480 --- /dev/null +++ b/docs/images/high-priority.svg @@ -0,0 +1 @@ +High PriorityHigh Priority \ No newline at end of file diff --git a/docs/images/i958x1232-mgsIib6PZJLt_zobva8.webp b/docs/images/i958x1232-mgsIib6PZJLt_zobva8.webp new file mode 100644 index 0000000000000000000000000000000000000000..9b7ce67a684fdb4ae5bf6946a2960ef5fdd3232d GIT binary patch literal 43622 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%0Djq1RULkmuG%Xr;l}A_Prk2 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 PriorityLow Priority \ No newline at end of file diff --git a/docs/images/medium-priority.svg b/docs/images/medium-priority.svg new file mode 100644 index 0000000000..dbafed5ce6 --- /dev/null +++ b/docs/images/medium-priority.svg @@ -0,0 +1 @@ +Medium PriorityMedium Priority \ No newline at end of file diff --git a/docs/images/urgent-priority.svg b/docs/images/urgent-priority.svg new file mode 100644 index 0000000000..26dcd40709 --- /dev/null +++ b/docs/images/urgent-priority.svg @@ -0,0 +1 @@ +UrgentUrgent \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index bb2146cc0d..f8c50b52ae 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -32,6 +32,18 @@ "url": "https://app.cal.com" }, "tabs": [ + { + "name": "Developing", + "url": "developing" + }, + { + "name": "Self Hosting", + "url": "self-hosting" + }, + { + "name": "Platform", + "url": "platform" + }, { "name": "API v2 Reference", "openapi": "/api-reference/v2/openapi.json", @@ -45,11 +57,138 @@ ], "navigation": [ { - "group": "Getting started", + "group": "Getting Started", + "pages": ["developing/introduction", "developing/local-development"] + }, + { + "group": "Open Source Contribution", + "pages": [ + "developing/open-source-contribution/index", + "developing/open-source-contribution/code-styling", + "developing/open-source-contribution/pull-requests", + "developing/open-source-contribution/contributors-guide" + ] + }, + { + "group": "Guides", + "pages": [ + { + "group": "API Development", + "icon": "brackets-curly", + "pages": ["developing/guides/api/how-to-setup-api-in-a-local-instance"] + }, + { + "group": "App Store and Integration", + "icon": "grid", + "pages": [ + "developing/guides/appstore-and-integration/adding-your-first-app", + "developing/guides/appstore-and-integration/build-a-greeter-app", + "developing/guides/appstore-and-integration/build-an-app", + "developing/guides/appstore-and-integration/how-to-show-assigned-people-from-a-crm", + "developing/guides/appstore-and-integration/integration-with-hubspot", + "developing/guides/appstore-and-integration/oauth" + ] + }, + { + "group": "Auth and Provision", + "icon": "user-shield", + "pages": [ + "developing/guides/auth-and-provision/how-to-setup-oidc-with-okta", + "developing/guides/auth-and-provision/how-to-setup-scim-with-okta", + "developing/guides/auth-and-provision/sso-setup" + ] + }, + { + "group": "Email", + "icon": "inbox", + "pages": ["developing/guides/email/setup-mailtrap-for-email-testing"] + }, + { + "group": "Embeds", + "icon": "code-simple", + "pages": ["developing/guides/embeds/embed-events"] + } + ] + }, + { + "group": "Getting Started", + "pages": [ + "self-hosting/installation", + "self-hosting/database-migrations", + "self-hosting/upgrading", + "self-hosting/sso-setup", + "self-hosting/docker" + ] + }, + { + "group": "Deployments", + "pages": [ + "self-hosting/deployments/aws", + "self-hosting/deployments/azure", + "self-hosting/deployments/gcp", + "self-hosting/deployments/vercel" + ] + }, + { + "group": "Apps", + "pages": [ + { + "group": "Install Apps", + "icon": "grid-2-plus", + "pages": [ + "self-hosting/apps/install-apps/introduction", + "self-hosting/apps/install-apps/google", + "self-hosting/apps/install-apps/microsoft", + "self-hosting/apps/install-apps/zoom", + "self-hosting/apps/install-apps/daily", + "self-hosting/apps/install-apps/hubspot", + "self-hosting/apps/install-apps/sendgrid", + "self-hosting/apps/install-apps/stripe", + "self-hosting/apps/install-apps/twilio", + "self-hosting/apps/install-apps/zoho" + ] + } + ] + }, + { + "group": "Guides", + "pages": [ + { + "group": "App Store and Integration", + "icon": "grid", + "pages": ["/self-hosting/guides/appstore-and-integration/syncing-third-party-apps"] + }, + { + "group": "White Labeling", + "icon": "tag", + "pages": [ + "/self-hosting/guides/white-labeling/introduction", + "/self-hosting/guides/white-labeling/custom-css" + ] + }, + { + "group": "Sponsorship", + "icon": "money-check-dollar", + "pages": ["/self-hosting/guides/sponsorship/can-calcom-sponsor-my-open-source-project"] + } + ] + }, + { + "group": "Getting Started", + "pages": [ + "platform/introduction", + "platform/quickstart", + "platform/booking-redirects", + "platform/atoms-reference", + "platform/faq" + ] + }, + { + "group": "Getting Started", "pages": ["api-reference/v2/introduction", "api-reference/v2/v1-v2-differences"] }, { - "group": "Getting started", + "group": "Getting Started", "pages": [ "api-reference/v1/introduction", "api-reference/v1/authentication", @@ -62,5 +201,10 @@ "x": "https://x.com/calcom", "github": "https://github.com/calcom", "linkedin": "https://www.linkedin.com/company/cal-com" + }, + "feedback": { + "thumbsRating": true, + "suggestEdit": true, + "raiseIssue": true } } diff --git a/docs/platform/atoms-reference.mdx b/docs/platform/atoms-reference.mdx new file mode 100644 index 0000000000..1dac452d28 --- /dev/null +++ b/docs/platform/atoms-reference.mdx @@ -0,0 +1,231 @@ +--- +title: Atoms reference +description: Everything related to atoms from props to styling. +--- + +## Props + +Below is a list of tables that contains all the available props each atom contains. + +Please ensure all custom classnames are valid [Tailwind CSS](https://tailwindcss.com/) classnames. + +### 1. Google calendar connect atom +

+ +| Name | Required | Description | +|:--------------------|:----------|:-----------------------------------------------------------------------------------------------------------| +| className | No | To pass in custom classnames from outside for styling the atom | +| label | No | The label for the connect button | +| alreadyConnectedLabel | No | Label to display when atom is in already connected state | +| loadingLabel | No | Label to display when atom is in loading state | +| onCheckError | No | A callback function to handle errors when checking the connection status | + + +### 2. Outlook calendar connect atom +

+ +| Name | Required | Description | +|:--------------------|:----------|:-----------------------------------------------------------------------------------------------------------| +| className | No | To pass in custom classnames from outside for styling the atom | +| label | No | The label for the connect button | +| alreadyConnectedLabel | No | Label to display when atom is in already connected state | +| loadingLabel | No | Label to display when atom is in loading state | +| onCheckError | No | A callback function to handle errors when checking the connection status | +| redir | No | A custom redirect URL link where the user gets redirected after successful authentication | + + +### 3. Apple calendar connect atom +

+ +| Name | Required | Description | +|:--------------------|:----------|:-----------------------------------------------------------------------------------------------------------| +| className | No | To pass in custom classnames from outside for styling the atom | +| label | No | The label for the connect button | +| alreadyConnectedLabel | No | Label to display when atom is in already connected state | +| loadingLabel | No | Label to display when atom is in loading state | +| onCheckError | No | A callback function to handle errors when checking the connection status | + + +### 4. Availability settings atom +

+ +| Name | Required | Description | +|:--------------------|:----------|:-----------------------------------------------------------------------------------------------------------| +| id | No | The ID of the schedule which fetches a user's availability | +| labels | No | Helpful if you want to pass in custom labels for i18n | +| customClassNames | No | To pass in custom classnames from outside for styling the atom | +| onUpdateSuccess | No | A callback function to handle updating user availability successfully | +| onBeforeUpdate | No | Validates schedule before it is sent to the server; if true, the schedule is sent, else it is not | +| onUpdateError | No | A callback function that gets triggered when the user availability fails to update | +| onDeleteSuccess | No | A callback function that gets triggered when the user availability is deleted successfully | +| onDeleteError | No | A callback function that gets triggered when the user availability fails to delete | +| enableOverrides | No | Allows user to enable or disable date overrides display in the atom; defaults to disabled | + + +### 5. Booker atom +

+ +| Name | Required | Description | +|:----------------------------|:----------|:-------------------------------------------------------------------------------------------------------| +| username | Yes | Username of the person whose schedule is to be displayed | +| eventSlug | Yes | Unique slug created for a particular event | +| orgBannerUrl | No | URL of the user's current organization | +| customClassNames | No | To pass in custom classnames from outside for styling the atom | +| month | No | The exact month for displaying a user's availability; defaults to the current month | +| selectedDate | No | Default selected date for which the slot picker opens | +| hideBranding | No | For hiding any branding on the booker | +| isAway | No | Sets the booker component to the away state | +| allowsDynamicBooking | No | Boolean indicating if the booking is a dynamic booking | +| bookingData | No | Data for rescheduling a booking passed in via this prop | +| defaultFormValues | No | Prefilled values for booking form fields like name, email, guests, notes, reschedule reason, etc. | +| isTeamEvent | No | Boolean indicating if it is a team event | +| duration | No | Refers to a multiple-duration event type; selects default if not passed | +| durationConfig | No | Configures selectable options for a multi-duration event type | +| hashedLink | No | Refers to the private link from event types page | +| isInstantMeeting | No | Boolean indicating if the booking is an instant meeting | +| rescheduleUid | No | Unique ID generated during rescheduling | +| bookingUid | No | Unique ID generated during booking | +| locationUrl | No | Custom meeting link URL instead of a Cal.com link | +| firstName | No | First name of the attendee | +| lastName | No | Last name of the attendee | +| guests | No | Invite a guest to join a meeting | +| name | No | Host name | +| onCreateBookingSuccess | No | Callback function for successful booking creation | +| onCreateBookingError | No | Callback function triggered on booking creation failure | +| onCreateRecurringBookingSuccess | No | Callback function for successful recurring booking creation | +| onCreateRecurringBookingError | No | Callback function triggered on recurring booking creation failure | +| onCreateInstantBookingSuccess | No | Callback function for successful instant booking creation | +| onCreateInstantBookingError | No | Callback function triggered on instant booking creation failure | +| onReserveSlotSuccess | No | Callback function for successful slot reservation | +| onReserveSlotError | No | Callback function triggered on slot reservation failure | +| onDeleteSlotSuccess | No | Callback function for successful slot deletion | +| onDeleteSlotError | No | Callback function triggered on slot deletion failure | + + +Booker embed atom shares the same prop interface as the Booker atom. Hence, you can pass in the same props to both the Booker Embed atom and the Booker atom. + +### 6. Calendar settings atom +

+ +| Name | Required | Description | +|:------------|:----------|:-----------------------------------------------------------------------------------------------------------| +| classNames | No | To pass in custom classnames from outside for styling the atom | + + +### 7. Destination calendar settings atom +

+ +| Name | Required | Description | +|:--------------|:----------|:-----------------------------------------------------------------------------------------------------------| +| statusLoader | No | To pass in a custom component for the loading state | +| classNames | No | To pass in custom classnames from outside for styling the atom | + + +### 8. Selected calendar settings atom +

+ +| Name | Required | Description | +|:------------|:----------|:-----------------------------------------------------------------------------------------------------------| +| classNames | No | To pass in custom classnames from outside for styling the atom | + + +## Styling + +Below is a list of tables that contains all the available classnames that can be used for each atom. + +### 1. Google calendar connect atom +

+ +| Name | Description | +|:------------|:-----------------------------------------------------| +| className | Pass in custom classnames from outside for styling the button | + + +### 2. Outlook calendar connect atom +

+ +| Name | Description | +|:------------|:-----------------------------------------------------| +| className | Pass in custom classnames from outside for styling the button | + + +### 3. Apple calendar connect atom +

+ +| Name | Description | +|:------------|:-----------------------------------------------------| +| className | Pass in custom classnames from outside for styling the button | + + +### 4. Availability settings atom +

+ +Availability settings atom accepts custom styles via the **customClassNames** prop. All the props that we see below fall under this **customClassNames** prop. + +| Name | Description | +|:---------------------------|:-----------------------------------------------------------------------| +| containerClassName | Adds styling to the whole availability settings component | +| ctaClassName | Adds stylings only to certain call-to-action buttons | +| editableHeadingClassName | Editable heading or title can be styled | +| formClassName | Form which contains the days and toggles | +| timezoneSelectClassName | Adds styling to the timezone select component | +| subtitlesClassName | Styles the subtitle | +| scheduleContainer | Styles the entire schedule component | +| scheduleDay | Adds styling to just the day of a particular schedule | +| dayRanges | Adds styling to day ranges | +| timeRanges | Time ranges in the availability settings can be customized | +| labelAndSwitchContainer | Adds styling to label and switches | +| overridesModalClassNames | Adds styling to the date overrides modal | + + +### 5. Booker atom +

+ +Booker atom accepts custom styles via the **customClassNames** prop. All the props that we see below fall under this **customClassNames** prop. In terms of styling there are four major classnames for the Booker, i.e. **bookerContainer**, **eventMetaCustomClassNames**, **datePickerCustomClassNames** and **availableTimeSlotsCustomClassNames**. Most of the classnames below fall under one of these sub classes. + +| Name | Description | +|:-----------------------------|:----------------------------------------------------------------------| +| bookerContainer | Adds styling to the whole of the booker atom | +| eventMetaContainer | Styles the event meta component containing details about an event | +| eventMetaTitle | Adds styles to the event meta title | +| eventMetaTimezoneSelect | Adds styles to the event meta timezone selector | +| datePickerContainer | Adds styling to the date picker | +| datePickerTitle | Styles the date picker title | +| datePickerDays | Adds styling to all days in the date picker | +| datePickerDate | Adds styling to all dates in the date picker | +| datePickerDatesActive | Styles only the dates with available slots | +| datePickerToggle | Styles the left and right toggle buttons | +| availableTimeSlotsContainer | Adds styling to the available time slots component | +| availableTimeSlotsHeaderContainer | Styles only the header container | +| availableTimeSlotsTitle | Adds styles to the title | +| availableTimeSlotsTimeFormatToggle | Adds styles to the format toggle buttons | +| availableTimes | Styles all the available times container | + + +### 6. Calendar settings atom +

+ +| Name | Description | +|:-----------------------------------------|:--------------------------------------------------------------| +| calendarSettingsCustomClassnames | Adds styling to the entire calendar settings atom | +| destinationCalendarSettingsCustomClassnames | Adds styling only to the destination calendar container | +| selectedCalendarSettingsCustomClassnames | Adds styling only to the selected calendar container | + + +### 7. Destination calendar settings atom +

+ +| Name | Description | +|:------------|:-----------------------------------------------------| +| classNames | Pass in custom classnames from outside for styling the atom | + + +### 8. Selected calendar settings atom +

+ +| Name | Description | +|:------------|:-----------------------------------------------------| +| classNames | Pass in custom classnames from outside for styling the atom | + + +Please note that sometimes the custom classnames may fail to override the styling with the classnames that you might have passed via props. That is because the **clsx** utility function that we use to override classnames inside our components has some limitations. A simple get around to solve this issue is to just prefix your classnames with **!** property just before passing in any classname. \ No newline at end of file diff --git a/docs/self-hosting/apps/install-apps/daily.mdx b/docs/self-hosting/apps/install-apps/daily.mdx new file mode 100644 index 0000000000..9f579b5a69 --- /dev/null +++ b/docs/self-hosting/apps/install-apps/daily.mdx @@ -0,0 +1,22 @@ +--- +title: "Daily" +--- + +#### Obtaining Daily API Credentials + + + Go to [Daily](https://www.daily.co/) and sign into your account. + + + From within your dashboard, go to the [developers](https://dashboard.daily.co/developers) tab. + + + Copy the API key provided in the developers tab. + + + Now paste the API key into the `DAILY_API_KEY` field in your `.env` file. + + + If you have the [Daily Scale Plan](https://www.daily.co/pricing), set the `DAILY_SCALE_PLAN` variable to `true` in the `.env` file to use features like video recording. + + diff --git a/docs/self-hosting/apps/install-apps/google.mdx b/docs/self-hosting/apps/install-apps/google.mdx new file mode 100644 index 0000000000..4d163c059d --- /dev/null +++ b/docs/self-hosting/apps/install-apps/google.mdx @@ -0,0 +1,54 @@ +--- +title: "Google" +--- +#### Obtaining the Google API Credentials + + + + Go to [Google API Console](https://console.cloud.google.com/apis/dashboard). If you don't have a project in your Google Cloud subscription, create one before proceeding. Under the Dashboard pane, select "Enable APIs and Services". + + + In the search box, type "calendar" and select the Google Calendar API search result. + + + Enable the selected API to proceed. + + + Go to the [OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) from the side pane. Select the app type (Internal or External) and enter the basic app details on the first page. + + + On the Scopes page, select "Add or Remove Scopes". Search for Calendar.event and select the scopes with values `.../auth/calendar.events`, `.../auth/calendar.readonly`, and then click "Update". + + + On the Test Users page, add the Google account(s) you'll be using. Verify details on the last page to complete the consent screen configuration. + + + From the side pane, select [Credentials](https://console.cloud.google.com/apis/credentials) and then "Create Credentials". Choose "OAuth Client ID". + + + Choose "Web Application" as the Application Type. + + + Under Authorized redirect URI's, add the URIs: + ``` + /api/integrations/googlecalendar/callback + /api/auth/callback/google + ``` + Replace `` with the URL where your application runs. + + + The key will be created, redirecting you back to the Credentials page. Select the new client ID under "OAuth 2.0 Client IDs", then click "Download JSON". Copy the JSON file contents and paste the entire string into the `.env` and `.env.appStore` files under the `GOOGLE_API_CREDENTIALS` key. + + + In the `.env` file, set the following environment variable: + ``` + GOOGLE_LOGIN_ENABLED=false + ``` + This will configure the Google integration as an Internal app, restricting login access. + + + +### **Adding Google Calendar to Cal.com App Store** + +After adding Google credentials, you can now add the Google Calendar App to the app store. Repopulate the App store by running: + diff --git a/docs/self-hosting/apps/install-apps/hubspot.mdx b/docs/self-hosting/apps/install-apps/hubspot.mdx new file mode 100644 index 0000000000..e504acf39f --- /dev/null +++ b/docs/self-hosting/apps/install-apps/hubspot.mdx @@ -0,0 +1,45 @@ +--- +title: "HubSpot" +--- +### Obtaining HubSpot Client ID and Secret + + + + Go to [HubSpot Developer](https://developer.hubspot.com/) and sign into your account, or create a new one. + + + From the Developer account home page, go to "Manage apps". + + + Click the "Create app" button located at the top right corner of the page. + + + In the "App info" tab, fill in any information you want for your app. + + + Navigate to the "Auth" tab to set up authentication for the app. + + + Copy the Client ID and Client Secret and add them to your `.env` file under the fields: + ``` + HUBSPOT_CLIENT_ID + HUBSPOT_CLIENT_SECRET + ``` + + + Set the Redirect URL for OAuth to: + ``` + /api/integrations/hubspot/callback + ``` + Replace `` with the URL where your application is hosted. + + + In the "Scopes" section, select "Read" and "Write" for the scope called `crm.objects.contacts`. + + + Click the "Save" button at the bottom of the page. + + + You’re all set! Any booking in Cal.com will now be created as a meeting in HubSpot for your contacts. + + diff --git a/docs/self-hosting/apps/install-apps/introduction.mdx b/docs/self-hosting/apps/install-apps/introduction.mdx new file mode 100644 index 0000000000..6e2df234dd --- /dev/null +++ b/docs/self-hosting/apps/install-apps/introduction.mdx @@ -0,0 +1,35 @@ +--- +title: "Introduction" +--- + +Install desired apps in your self-hosted instance for better integration. + + + + Install google for supporting Google Calendar and Google Meet integrations + + + Install microsoft to support Exchange365 integration + + + Install Zoom to support Zoom video conferencing integration + + + Install Daily to support Cal Video integration + + + Install Hubspot to support Hubspot CRM integration + + + Install Sendgrid to support Sendgrid Email integration + + + Install Stripe to support Stripe integration for paid bookings + + + Install Twilio to support Workflows integration for sms reminders + + + Install Zoho to support Zoho CRM integration + + diff --git a/docs/self-hosting/apps/install-apps/microsoft.mdx b/docs/self-hosting/apps/install-apps/microsoft.mdx new file mode 100644 index 0000000000..6834fcefb8 --- /dev/null +++ b/docs/self-hosting/apps/install-apps/microsoft.mdx @@ -0,0 +1,29 @@ +--- +title: "Microsoft" +--- +#### Obtaining Microsoft Graph Client ID and Secret + + + + Go to [Azure App Registration](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) and select "New registration". + + + Provide a name for your application to proceed with the registration. + + + Set **Who can use this application or access this API?** to **Accounts in any organizational directory (Any Azure AD directory - Multitenant)**. + + + Set the **Web** redirect URI to: + ``` + /api/integrations/office365calendar/callback + ``` + Replace `` with the URL where your application runs. + + + Use the **Application (client) ID** as the value for **MS_GRAPH_CLIENT_ID** in your `.env` file. + + + Click on **Certificates & secrets**, create a new client secret, and use the generated value as the **MS_GRAPH_CLIENT_SECRET** in your `.env` file. + + diff --git a/docs/self-hosting/apps/install-apps/sendgrid.mdx b/docs/self-hosting/apps/install-apps/sendgrid.mdx new file mode 100644 index 0000000000..c4000eb6ad --- /dev/null +++ b/docs/self-hosting/apps/install-apps/sendgrid.mdx @@ -0,0 +1,30 @@ +--- +title: "Sendgrid" +--- + + + Go to [https://signup.sendgrid.com/](https://signup.sendgrid.com/) and create a new SendGrid account. + + + Navigate to **Settings** -> **API Keys** and create a new API key. + + + Copy the generated API key and add it to your `.env` file under the field: + ``` + SENDGRID_API_KEY + ``` + + + Go to **Settings** -> **Sender Authentication** and verify a single sender. + + + Copy the verified email address and add it to your `.env` file under the field: + ``` + SENDGRID_EMAIL + ``` + + + + +This app is **required** for Workflows + \ No newline at end of file diff --git a/docs/self-hosting/apps/install-apps/stripe.mdx b/docs/self-hosting/apps/install-apps/stripe.mdx new file mode 100644 index 0000000000..f4865ef507 --- /dev/null +++ b/docs/self-hosting/apps/install-apps/stripe.mdx @@ -0,0 +1,38 @@ +--- +title: "Stripe" +--- +### Setting up Stripe + + + + Go to Stripe and either create a new account or log into an existing one. For testing, activate the Test-Mode toggle in the top right of the Stripe dashboard. + + + Open [Stripe API Keys](https://dashboard.stripe.com/apikeys), then save the token starting with `pk_...` to `NEXT_PUBLIC_STRIPE_PUBLIC_KEY` and `sk_...` to `STRIPE_PRIVATE_KEY` in the `.env` file. + + + Go to [Stripe Connect Settings](https://dashboard.stripe.com/settings/connect) and activate OAuth for Standard Accounts. + + + Add the following redirect URL, replacing `` with your application's URL: + ``` + /api/integrations/stripepayment/callback + ``` + + + Copy your client ID (`ca_...`) to `STRIPE_CLIENT_ID` in the `.env` file. + + + Open [Stripe Webhooks](https://dashboard.stripe.com/webhooks) and add: + ``` + /api/integrations/stripepayment/webhook + ``` + as the webhook for connected applications. + + + Select all `payment_intent` events for the webhook. + + + Copy the webhook secret (`whsec_...`) to `STRIPE_WEBHOOK_SECRET` in the `.env` file. + + diff --git a/docs/self-hosting/apps/install-apps/twilio.mdx b/docs/self-hosting/apps/install-apps/twilio.mdx new file mode 100644 index 0000000000..888b4fc47b --- /dev/null +++ b/docs/self-hosting/apps/install-apps/twilio.mdx @@ -0,0 +1,70 @@ +--- +title: "Twilio" +--- + + + Go to [Twilio](https://www.twilio.com/try-twilio) and create a new account. + + + Click on 'Get a Twilio phone number' to obtain a phone number for your account. + + + Copy the Account SID and add it to your `.env` file under the field: + ``` + TWILIO_SID + ``` + + + Copy the Auth Token and add it to your `.env` file under the field: + ``` + TWILIO_TOKEN + ``` + + + Copy your Twilio phone number and add it to your `.env` file under the field: + ``` + TWILIO_PHONE_NUMBER + ``` + + + Add your own sender ID to the `.env` file under the field: + ``` + NEXT_PUBLIC_SENDER_ID + ``` + (The fallback is set to "Cal" if not specified.) + + + Navigate to **Develop** -> **Messaging** -> **Services**, then create a new messaging service. + + + Choose a name for the messaging service. This can be anything you like. + + + Click 'Add Senders' and select 'Phone number' as the sender type. + + + Add the listed phone number to the messaging service. + + + Leave all other fields as they are, then complete the setup by clicking ‘View my new Messaging Service’. + + + Copy the Messaging Service SID and add it to your `.env` file under the field: + ``` + TWILIO_MESSAGING_SID + ``` + + + Go to the Twilio dashboard, create a new Verify Service, and name it as you wish. + + + Copy the Verify Service SID and add it to your `.env` file under the field: + ``` + TWILIO_VERIFY_SID + ``` + + + + +This app is **required** for Workflows + \ No newline at end of file diff --git a/docs/self-hosting/apps/install-apps/zoho.mdx b/docs/self-hosting/apps/install-apps/zoho.mdx new file mode 100644 index 0000000000..495a5383bc --- /dev/null +++ b/docs/self-hosting/apps/install-apps/zoho.mdx @@ -0,0 +1,125 @@ +--- +title: "Zoho" +--- +#### Obtaining ZohoCRM Client ID and Secret + + + + Go to [Zoho API Console](https://api-console.zoho.com/) and sign into your account, or create a new one. + + + From the API console page, navigate to "Applications". + + + Click the "ADD CLIENT" button at the top right and select "Server-based Applications". + + + Enter any desired information in the "Client Details" tab. + + + Navigate to the "Client Secret" tab. + + + Copy the Client ID and Client Secret into your `.env` file under: + ``` + ZOHOCRM_CLIENT_ID + ZOHOCRM_CLIENT_SECRET + ``` + + + Set the Redirect URL for OAuth to: + ``` + /api/integrations/zohocrm/callback + ``` + Replace `` with your application URL. + + + In the "Settings" section, check the "Multi-DC" option if you wish to use the same OAuth credentials for all data centers. + + + Click "Save" or "UPDATE" at the bottom of the page. + + + Your ZohoCRM integration can now be easily added in the Cal.com settings. + + + +#### Obtaining Zoho Calendar Client ID and Secret + + + + Go to [Zoho API Console](https://api-console.zoho.com/) and sign into your account, or create a new one. + + + Choose "Server-based Applications" and set the Redirect URL for OAuth to: + ``` + /api/integrations/zohocalendar/callback + ``` + Replace `` with your application URL. + + + Enter any information you want in the "Client Details" tab. + + + Navigate to the "Client Secret" tab. + + + Copy the Client ID and Client Secret to your app keys in the Cal.com admin panel at: + ``` + /settings/admin/apps + ``` + + + In the "Settings" section of Zoho API Console, check the "Multi-DC" option if you wish to use the same OAuth credentials across data centers. + + + Click "Save" or "UPDATE" at the bottom of the page. + + + Your Zoho Calendar integration is now ready and can be managed at: + ``` + /settings/my-account/calendars + ``` + + + You can access your Zoho calendar at [https://calendar.zoho.com/](https://calendar.zoho.com/). + + + + +If you use multiple calendars with Cal, make sure to enable the toggle to prevent double-bookings across calendars. This setting can be found at `/settings/my-account/calendars`. + + +#### Obtaining Zoho Bigin Client ID and Secret + + + + Go to [Zoho API Console](https://api-console.zoho.com/) and sign into your account, or create a new one. + + + Click the "ADD CLIENT" button at the top right, and select "Server-based Applications". + + + Set the Redirect URL for OAuth to: + ``` + /api/integrations/zoho-bigin/callback + ``` + Replace `` with your application URL. + + + Navigate to the "Client Secret" tab. + + + Copy the Client ID and Client Secret into the `.env.appStore` file under: + ``` + ZOHO_BIGIN_CLIENT_ID + ZOHO_BIGIN_CLIENT_SECRET + ``` + + + In the "Settings" section, check the "Multi-DC" option if you wish to use the same OAuth credentials across data centers. + + + Your Zoho Bigin integration is now ready and can be added from the Cal.com app store. + + diff --git a/docs/self-hosting/apps/install-apps/zoom.mdx b/docs/self-hosting/apps/install-apps/zoom.mdx new file mode 100644 index 0000000000..e8f3a60eca --- /dev/null +++ b/docs/self-hosting/apps/install-apps/zoom.mdx @@ -0,0 +1,54 @@ +--- +title: "Zoom" +--- +**Obtaining Zoom Client ID and Secret** + + + + Go to [Zoom Marketplace](https://marketplace.zoom.us/) and sign in with your Zoom account. + + + In the upper right, click "Develop" and then "Build App". + + + Under "OAuth", select "Create". + + + Provide a name for your app. + + + Select "User-managed app" as the app type. + + + De-select the option to publish the app on the Zoom App Marketplace. + + + Click "Create" to proceed. + + + Copy the Client ID and Client Secret and add them to your `.env` file under the fields: + ``` + ZOOM_CLIENT_ID + ZOOM_CLIENT_SECRET + ``` + + + Set the Redirect URL for OAuth to: + ``` + /api/integrations/zoomvideo/callback + ``` + Replace `` with your application URL. + + + Add the redirect URL to the allow list and enable "Subdomain check". Ensure it displays "saved" below the form. + + + You don't need to provide basic information about your app. Instead, go to "Scopes", click "+ Add Scopes", then select the category "Meeting" on the left, and check the scope `meeting:write`. + + + Click "Done" to save the scope settings. + + + Your Zoom integration is now ready and can be easily added in the Cal.com settings. + + diff --git a/docs/self-hosting/database-migrations.mdx b/docs/self-hosting/database-migrations.mdx new file mode 100644 index 0000000000..264b51aca2 --- /dev/null +++ b/docs/self-hosting/database-migrations.mdx @@ -0,0 +1,69 @@ +--- +title: "Database migrations" +icon: "upload" +--- + +As described in the [upgrade guide](/self-hosting/upgrading), you should use the +``` +yarn workspace @calcom/prisma db-migrate +``` +or +``` +yarn workspace @calcom/prisma db-deploy +``` +command to update the database. + +We use database migrations in order to handle changes to the database schema in a more secure and stable way. This is actually very common. The thing is that when just changing the schema in `schema.prisma` without creating migrations, the update to the newer database schema can damage or delete all data in production mode, since the system sometimes doesn't know how to transform the data from A to B. Using migrations, each step is reproducable, transparent and can be undone in a simple way. + +### Creating migrations + +If you are modifying the codebase and make a change to the `schema.prisma` file, you must create a migration. + +To create a migration for your previously changed `schema.prisma`, simply run the following: + +``` +yarn workspace @calcom/prisma db-migrate +``` + +Now, you must create a short name for your migration to describe what changed (for example, "user_add_email_verified"). Then just add and commit it with the corresponding code that uses your new database schema. + + +Always keep an eye on what migrations Prisma is generating.** Prisma often happily will drop entire columns of data because it can't figure out what to do. + + +### Error: The database schema is not empty + +Prisma uses a database called `_prisma_migrations` to keep track of which migrations have been applied and which haven't. If your local migrations database doesn't match up with what's in the actual database, then Prisma will throw the following error: + +``` +Error: P3005 + +The database schema for `localhost:5432` is not empty. Read more about how to baseline an existing production database: https://pris.ly/d/migrate-baseline +``` + +In order to fix this, we need to tell Prisma which migrations have already been applied. + +This can be done by running the following command, replacing `migration_name` with each migration that you have already applied: + +``` +yarn prisma migrate resolve --applied migration_name +``` + +You will need to run the command for each migration that you want to mark as applied. + +### Resetting Prisma migrate + +When your local Prisma database runs out of sync with migrations on local and you are tearing your hair out, I’ve been there, so you don’t have to: + +**PostgreSQL** + +``` +DELETE FROM "_prisma_migrations"; +``` + +**Quickly re-index** + +``` +# Run the following to easily apply all migrations in the prisma/migrations directory +ls -1a prisma/migrations/ | grep 2021 | xargs -I{} prisma migrate resolve --applied {} +``` \ No newline at end of file diff --git a/docs/self-hosting/deployments/aws.mdx b/docs/self-hosting/deployments/aws.mdx new file mode 100644 index 0000000000..adeacdf453 --- /dev/null +++ b/docs/self-hosting/deployments/aws.mdx @@ -0,0 +1,46 @@ +--- +title: "AWS" +icon: "aws" +--- + +Deploying Cal.com on AWS +___ + + + + 1. Amazon Web Services account + 2. Familiarity with AWS services and management console + 3. Access to the Cal.com source code + + + 1. Create an AWS Account: If not already set up, create an account on AWS. + 2. Management Console: Log in to the AWS Management Console. + + + 1. Create a New IAM User: Set up an IAM user with the necessary permissions for deploying and managing the application. + 2. Set Up Required Services: Establish services like Amazon EC2, RDS for PostgreSQL, etc., as needed for your application. + + + 1. Clone the Repository: Get the Cal.com repository onto your local environment. + 2. Update Configuration: Modify the .env file to include your AWS resource details (like database endpoints). + + + 1. Deploy Application: Utilize AWS services such as EC2 or Elastic Beanstalk to deploy the Cal.com application. + 2. Database Configuration: Set up and connect the RDS instance to your application. + 3. Verify Deployment: Ensure the application is operational and accessible. + + + 1. DNS Setup: Update your DNS settings to point to your AWS deployment. + 2. Monitoring and Scaling: Leverage AWS monitoring tools to keep track of your application's performance and scale resources accordingly. + + + +### Best Practices + + 1. Adhere to AWS's recommended security practices. + 2. Regularly update your deployment with the latest Cal.com releases. + + +### Additional Resources + +https://docs.aws.amazon.com/ \ No newline at end of file diff --git a/docs/self-hosting/deployments/azure.mdx b/docs/self-hosting/deployments/azure.mdx new file mode 100644 index 0000000000..dcf5e2fd63 --- /dev/null +++ b/docs/self-hosting/deployments/azure.mdx @@ -0,0 +1,48 @@ +--- +title: "Azure" +icon: "microsoft" +--- + +Deploying Cal.com on Azure +___ + + + + 1. Microsoft Azure account + 2. Basic knowledge of Azure services + 3. Access to Cal.com source code + + + 1. Create an Azure Account + 2. Azure Portal: Familiarize yourself with the Azure Portal. + + + + 1. Creating Azure Resources: In the Azure Portal, create a new resource group for your Cal.com project. + + 2. Create Azure Services: Set up required services such as Azure App Service, Azure Database for PostgreSQL, etc. + #### Create Web App + + + #### Setup Database, Networking + + + #### Setup Monitoring + + + + 1. Clone Repository: Clone the Cal.com repository to your local machine. + 2. Configuration Files: Update the .env file with necessary Azure configurations (e.g., database connection strings). + + + 1. Deploying Web App: Use Azure App Service to deploy the Cal.com web application. + 2. Database Setup: Deploy and configure the Azure Database for PostgreSQL with Cal.com. + 3. Deployment Verification: Ensure that the application is running smoothly post-deployment. + + + + + 1. DNS Configuration: Configure your DNS settings to point to the Azure deployment. + 2. Monitor and Scale: Utilize Azure monitoring tools to keep track of performance and scale resources as needed. + + \ No newline at end of file diff --git a/docs/self-hosting/deployments/gcp.mdx b/docs/self-hosting/deployments/gcp.mdx new file mode 100644 index 0000000000..b40d83b5a7 --- /dev/null +++ b/docs/self-hosting/deployments/gcp.mdx @@ -0,0 +1,109 @@ +--- +title: "GCP" +icon: "google" +--- + +Deploying Cal.com on Google Cloud Platform (GCP) + +In this guide, we will go over the steps to deploy cal.com on Google Cloud Platform (GCP). We will cover how to create a virtual machine, configure it, install Docker, and finally deploy the cal.com application. + + + + ### Go to the GCP Console + + First, open the Google Cloud Platform console by visiting the [https://console.cloud.google.com/](https://console.cloud.google.com/) website. + + ### Create a New Project + + If you haven't already, create a new project by clicking on the "Select a project" dropdown menu and selecting "New Project". Enter a name for your project and click on the "Create" button. + + + + + ### Create a New VM Instance + + Click on the navigation menu icon (three horizontal lines) and select "Compute Engine" from the list. Then, click on "VM instances" in the sub-menu. + + Click on the "Create" button to create a new virtual machine. + + + + + ### Select the Machine Type + + Choose the machine type that best suits your needs. Ideally, 2 vCPUs and 2-4GB RAM is enough. + + ### Set Up Networking + + Make sure the "Networking" tab is selected and click on the "Add network" button. Choose the "Default" network and click on the "Add" button. + + + ### Create the Instance + + Review the details of your virtual machine and click on the "Create" button to create the instance. + + + Note the public IP of the VM. + + + Once your virtual machine is created, you need to configure it to allow traffic on port 80. + + ### Open Port 80 + + Click on the navigation menu icon (three horizontal lines) and select "Compute Engine" from the list. Then, click on "VM instances" in the sub-menu. Find your newly created instance and click on its name to enter its details page. + + Click on the "Firewalls" tab and then click on the "Add firewall rule" button. Select "Allow all" as the source and destination, set the protocol to "tcp" and the ports to "80". Click on the "Add" button to save the changes. + + + Now that your virtual machine is configured, you need to install Docker on it. + + ### Connect to Your Instance + + Open a terminal window on your local machine and use SSH to connect to your virtual machine. You can find the external IP address or DNS name of your instance in the GCP console. Use the following command to connect to your instance: + ```bash + gcloud ssh --project=[PROJECT_ID] --zone=[ZONE] [INSTANCE_NAME] + ``` + Replace `[PROJECT_ID]` with your project ID, `[ZONE]` with the zone where your instance is located, and `[INSTANCE_NAME]` with the name of your instance. + You can also use web based SSH. + + + + + ### Install Docker + + Once connected, update the package list and install Docker using the following commands: + ```bash + sudo apt-get update + sudo apt-get install docker.io + ``` + ### Start the Docker Service + + Start the Docker service using the following command: + ```bash + sudo systemctl start docker + ``` + + + Now that Docker is installed and running, you can deploy cal.com on your virtual machine. + + ### Pull the Docker Image + + Use the following command to pull the cal.com Docker image from Docker Hub: + ```bash + docker pull cal/calcom + ``` + ### Run the Docker Container + + Run the Docker container using the following command: + ```bash + docker run -d -p 80:80 cal/cal.com + ``` + This command maps port 80 on your local machine to port 80 inside the container, so you can access cal.com from outside the container. + + ### Access Cal.com + + Open a web browser and navigate to `http://localhost`. You should now be able to access the cal.com homepage. + + + +Congratulations! You have successfully deployed cal.com on Google Cloud Platform. \ No newline at end of file diff --git a/docs/self-hosting/deployments/vercel.mdx b/docs/self-hosting/deployments/vercel.mdx new file mode 100644 index 0000000000..2e51d3cd41 --- /dev/null +++ b/docs/self-hosting/deployments/vercel.mdx @@ -0,0 +1,76 @@ +--- +title: "Vercel" +icon: "triangle" +iconType: "solid" +--- + +## Requirements + +You need a PostgresDB database hosted somewhere. [Supabase](https://supabase.com/) offer a great free option while [Heroku](https://www.heroku.com/) offers a low-cost option. + +## Getting Started + +### Local settings + + + + ``` + git clone https://github.com/\/cal.com.git + ``` + + + Copy the `.env.example` file in `apps/web`, rename it to `.env` and fill it with your settings ([See manual setup](https://github.com/calcom/cal.com#manual-setup) and [Obtaining the Google API Credentials](https://github.com/calcom/cal.com#obtaining-the-google-api-credentials)) + + + ``` + yarn install + ``` + + + Schema is located in at `packages/prisma/schema.prisma`. + + ``` + yarn workspace @calcom/prisma db-deploy + ``` + + + To look at or modify the database content + ``` + yarn db-studio + ``` + + + Click on the `User` model to add a new user record. + + + Fill out the fields (remembering to encrypt your password with [BCrypt](https://bcrypt-generator.com/)) and click `Save 1 Record` to create your first user. + + + Open a browser to [port 3000](http://localhost:3000/) on your localhost and login with your just created, first user. + + + + +Sometimes, yarn install might fail during deployment on Vercel, in which case, you can use ```YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install``` as the install command instead. + + +#### Deployment + + + + + + + + + + + + Override to: + ``` + cd ../.. && yarn build --include-dependencies --no-deps + ``` + + + + \ No newline at end of file diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx new file mode 100644 index 0000000000..81c2502d81 --- /dev/null +++ b/docs/self-hosting/docker.mdx @@ -0,0 +1,86 @@ +--- +title: "Docker" +icon: "docker" +--- + +The Docker configuration for Cal is an effort powered by people within the community. Cal.com, Inc. does not provide official support for Docker, but we will accept fixes and documentation. Use at your own risk. + +If you want to contribute to the Docker repository, [reply here](https://github.com/calcom/docker/discussions/32). + +The Docker configuration can be found [in our docker repository](https://github.com/calcom/docker). + +### Requirements + +Make sure you have `docker` & `docker compose` installed on the server / system. + +Note: `docker compose` without the hyphen is now the primary method of using docker-compose, per the Docker documentation. + +### Getting Started + +1. Clone calcom-docker + + ``` + git clone --recursive https://github.com/calcom/docker.git calcom-docker + ``` + +2. Change into the directory + + ``` + cd calcom-docker + ``` + +3. Update the calcom submodule + + ``` + git submodule update --remote --init + ``` + +4. Rename `.env.example` to `.env` and then update `.env` + +5. Build and start Cal.com via docker compose + + ``` + docker compose up --build + ``` + +6. (First Run) Open a browser to [http://localhost:5555](http://localhost:5555/) to look at or modify the database content. + + a. Click on the `User` model to add a new user record. + + b. Fill out the fields (remembering to encrypt your password with [BCrypt](https://bcrypt-generator.com/)) and click `Save 1 Record` to create your first user. + +7. Open a browser to [http://localhost:3000](http://localhost:3000/) and login with your just created, first user. + +### Configuration + +#### Build-time variables + +These variables must be provided at the time of the docker build, and can be provided by updating the .env file. Changing these is not required for evaluation, but may be required for running in production. Currently, if you require changes to these variables, you must follow the instructions to build and publish your own image. + +* NEXT_PUBLIC_WEBAPP_URL + +* NEXT_PUBLIC_LICENSE_CONSENT + +* NEXT_PUBLIC_TELEMETRY_KEY + +#### Important Run-time variables + +* NEXTAUTH_SECRET + +### Git Submodules + +This repository uses a git submodule. + +To update the calcom submodule, use the following command: + +``` +git submodule update --remote --init +``` + +For more advanced usage, please refer to the git documentation: [https://git-scm.com/book/en/v2/Git-Tools-Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) + +### Troubleshooting + +* SSL edge termination: If running behind a load balancer which handles SSL certificates, you will need to add the environmental variable `NODE_TLS_REJECT_UNAUTHORIZED=0` to prevent requests from being rejected. Only do this if you know what you are doing and trust the services/load-balancers directing traffic to your service. + +* Failed to commit changes: Invalid 'prisma.user.create()': Certain versions may have trouble creating a user if the field `metadata` is empty. Using an empty json object `{}` as the field value should resolve this issue. Also, the `id` field will autoincrement, so you may also try leaving the value of `id` as empty. \ No newline at end of file diff --git a/docs/self-hosting/guides/appstore-and-integration/syncing-third-party-apps.mdx b/docs/self-hosting/guides/appstore-and-integration/syncing-third-party-apps.mdx new file mode 100644 index 0000000000..db22b03802 --- /dev/null +++ b/docs/self-hosting/guides/appstore-and-integration/syncing-third-party-apps.mdx @@ -0,0 +1,53 @@ +--- +title: "How to sync third party apps" +--- + + + + 1. `CALCOM_WEBHOOK_SECRET` + - You can generate this by running `openssl rand -base64 32`. This is required when sending 3rd party app credentials from your platform to your instance of Cal.com. + 2. On your self-hosted instance of Cal.com visit settings/admin/apps under an admin account. Here you can enable/disable apps on Cal.com and set the app keys (client id, client secret, etc.). These keys should match the ones on your platform. + 3. `CALCOM_WEBHOOK_HEADER_NAME` + - The header name is expected to contain the webhook secret. The default is `calcom-webhook-secret` + 5. `CALCOM_CREDENTIAL_SYNC_ENDPOINT` + - The endpoint on your platform that your instance of [Cal.com](http://Cal.com) will make a request to if the 3rd party app credentials are expired. + 5. `CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY` + - When sending 3rd party app credentials between your platform and your instance Cal.com, we expect these to be encrypted using AES256. When you encrypt the 3rd party app credentials, ensure the same key is used. + + + + When a user adds a 3rd party app on your platform, you should send the credentials that are created to your instance of [Cal.com](http://Cal.com) to `${CALCOM_WEBAPP_URL}/api/webhook/app-credential` . The payload should contain the following + + ```javascript + { + // UserId of the Cal.com user + userId: number; + + // The app slug that is on Cal.com. + // Can be found in the Cal.com database in the App table + appSlug: string; + + // The credentials from the 3rd party app. (ex. Access token, refresh token). + // Ideally it should contain the access token and expiry date + // AES256 encrypted with CALCOM_APP_CREDENTIAL_ENCRYPTION_KEY + keys: string; + } + ``` + + + When [Cal.com](http://Cal.com) needs to refresh the app credentials it will make a request to `CALCOM_CREDENTIAL_SYNC_ENDPOINT`. The request contains the following. + + ```javascript + { + calcomUserId: string; + // App slug on Cal.com + appSlug: string; + } + ``` + + + + + +This only works if you have integrated [Cal.com](http://cal.com/) into your platform. Users **must** consent to give access to your platform, and you are simply using [Cal.com](http://cal.com/)'s code within your platform. + \ No newline at end of file diff --git a/docs/self-hosting/guides/sponsorship/can-calcom-sponsor-my-open-source-project.mdx b/docs/self-hosting/guides/sponsorship/can-calcom-sponsor-my-open-source-project.mdx new file mode 100644 index 0000000000..c219cf80ab --- /dev/null +++ b/docs/self-hosting/guides/sponsorship/can-calcom-sponsor-my-open-source-project.mdx @@ -0,0 +1,40 @@ +--- +title: "Can Cal.com sponsor my open source project?" +--- + +Cal.com is a commercial open source company and thereby we fully support other open source projects, whether it's commercial or non-commercial. We're friends of many other COSS companies. + +## Sponsorship Criteria + +- The project should be non-commercial or a commercial open source company with less than $2M in ARR +- The project should be open source +- Include our "Book us with Cal.com" banner in your footer and link to a Cal.com booking link of your choice +- Include our "Book us with Cal.com" banner in your open source repository's README.md file and link to a Cal.com booking link of your choice +- Add the UTM tag with the format for the banners: ?utm_source=banner&utm_campaign=oss + +![](/docs/images/book-with-cal-dark_osayta.svg) +or +![](/docs/images/book-with-cal-light_kqtoky.svg) + +### HTML Code: + +```html +Book us with Cal.com +``` +or +```html +Book us with Cal.com +``` + +## How to apply + +Please note that we have a limited number of openings for OSS sponsorships each month. If you're interested in applying for a sponsorship for your open source software project, please follow these steps: + +- Ensure that you meet all the sponsorship criteria mentioned above. +- Send an email to peer@cal.com and explain what you are working on and confirm that you meet all the sponsorship criteria (watch out, Peer may invest a small angel check in your COSS company 👀) +- Explaining why Cal.com's free tier or open core is insufficient for your project +- Include your Cal.com team slug and provide a link to your open source repository in the email. + + +Please note that sponsorship requests are reviewed on a monthly basis, so allow some time before reaching out for updates. We reserve the right to reject any project at will without reason. + diff --git a/docs/self-hosting/guides/white-labeling/custom-css.mdx b/docs/self-hosting/guides/white-labeling/custom-css.mdx new file mode 100644 index 0000000000..a0f468ba54 --- /dev/null +++ b/docs/self-hosting/guides/white-labeling/custom-css.mdx @@ -0,0 +1,36 @@ +--- +title: "How to add custom CSS" +--- + + +Adding or modifying CSS counts as changing the code, so as per [our license](https://github.com/calcom/cal.com/blob/main/LICENSE) **you must either open-source your modified version or purchase an enterprise license.** + + +Cal.com uses [TailwindCSS](https://tailwindcss.com/) as a replacement for traditional CSS styling within the application, but some people prefer to add CSS styles themselves. + +CSS files should be stored in the `styles` directory within the codebase. + +Within the `styles` directory, you will find two CSS files, `fonts.css` and `global.css`. We suggest not to add to these files, and instead create new CSS files and import them into the application. This helps reduce conflicts when pulling in changes that we've made to either of the existing CSS files. + +### Adding new stylesheets + +Firstly, create the CSS file inside the `styles` directory. + +Then, open the `pages/_app.tsx` file, and you will see the following two lines: + +```js +import "../styles/fonts.css"; +import "../styles/globals.css"; +``` + +Duplicate one of these import statements and change the path to link to your new CSS stylesheet, like so: + +```js +import "../styles/your-new-stylesheet.css"; +``` + + +These styles will apply to all pages and components in your application. + + +Due to the global nature of stylesheets, and to avoid conflicts, you may **only import them inside** **`pages/_app.tsx`**. \ No newline at end of file diff --git a/docs/self-hosting/guides/white-labeling/introduction.mdx b/docs/self-hosting/guides/white-labeling/introduction.mdx new file mode 100644 index 0000000000..3db5626e53 --- /dev/null +++ b/docs/self-hosting/guides/white-labeling/introduction.mdx @@ -0,0 +1,29 @@ +--- +title: "How to white label the self hosted instance" +--- + +If you're on the Ultimate or Platform plan of Cal.com, we have made it very easy for you to white label your self-hosted instance. + + + + Please update the following env variables to match your desired branding: + ``` + NEXT_PUBLIC_APP_NAME="acme.com" + NEXT_PUBLIC_SUPPORT_MAIL_ADDRESS="support@acme.com" + NEXT_PUBLIC_COMPANY_NAME="ACME inc." + ``` + + + The images for the logo are placed in `/web/public` . You can update the logo by changing the value of the following constants in `/packages/lib/constants.ts` file: + 1. LOGO + 2. LOGO_ICON + to the file name of your logo and logo icon, or the paths with file name to your respective logo and logo icon images from `/web/public`. + + + The instance wide theme can be easily updated thanks to the color tokens we have in place. Simply modify the hex-codes within the `apps/web/styles/globals.css` file against the [color tokens](https://cal.com/docs/enterprise-features/instance-wide-theming), and the entire application can be consistently whitelabeled to your brand identity. + + + + +If you're using custom SMTP server for emails, you might need to set `NODE_EXTRA_CA_CERTS` env variable equal to the path of your CA certificate so that it can be recognized by the calcom application. + \ No newline at end of file diff --git a/docs/self-hosting/installation.mdx b/docs/self-hosting/installation.mdx new file mode 100644 index 0000000000..e01c8f8741 --- /dev/null +++ b/docs/self-hosting/installation.mdx @@ -0,0 +1,142 @@ +--- +title: "Installation" +icon: "play" +--- + +There are multiple ways in which you can deploy Cal.com, providing support for customers who want to implement Cal.com within their existing infrastructure stack. Let's go through them one-by-one. You can find the instructions for deployment in our README file, which is the section you see when you scroll down in our GitHub repository, or if you've got a copy of Cal.com downloaded already, you can open the file contained in the downloaded repository called `README.md`. + +## Requirements + +Cal.com runs with pretty minimal hardware requirements by itself. The most intensive part for the software is when you actually build the software, but once it's running it's relatively lightweight. + +Cal.com works with a very large range of operating systems, as it only requires JavaScript execution to run. **Cal.com is known to work well with Windows, Mac, Linux and BSD.** Although they do work well on all of them, for production deployments we would suggest Linux as the ideal platform. Any operating system that runs Node.js should be able to work too, but these are some of the common operating systems that we know work well. + +To run Cal.com, you need to install a few things. **Node.js, yarn, Git and PostgreSQL**. We use **Prisma** for database maintenance, and is one of the dependencies. We won’t publish installation guides for these as they have their own resources available on the internet. If you're on Linux/BSD, all of these things should be readily available on your package manager. Your best bet is searching for something like **Debian 12 PostgreSQL**, which will give you a guide to installing and configuring PostgreSQL on Debian Linux 12. + + +To ensure optimal performance and compatibility, we highly recommend using Node.js version 18 for your development environment. This version provides the best balance of stability, features, and security for this project. Please make sure to update your Node.js installation if necessary. + + +## Prisma Data Proxy + +To use cal.com with prisma data proxy, you can: +1. Set in the .env file + ``` + PRISMA_GENERATE_DATAPROXY=true + ``` +2. Set DATABASE_URL in the .env file equal to the prisma connection string + ``` + DATABASE_URL="prisma://****:****@****" + ``` + +## Production Build + +1. First, you git clone the repository with the following command, so you have a copy of the code. + ``` + git clone https://github.com/calcom/cal.com.git + ``` + + +If you are on windows, you would need to use the following command when cloning, with **admin privileges**: +``` +git clone -c core.symlinks=true https://github.com/calcom/cal.com.git +``` + + +2. Then, go into the directory you just cloned with + ``` + cd cal.com + ``` + and run + ``` + yarn + ``` + to install all of the dependencies. Essentially, dependencies are just things that Cal.com needs to install to be able to work. + +3. Then, you just need to set up a couple of things. For that, we use a `.env` file. We just need to copy and paste the `.env.example` file and rename the copy to `.env`. Here you'll have a template with comments showing you the settings you need/might want to set. + + +For preview deployments on **Vercel**, please leave the following environment variables empty: + - **NEXTAUTH_URL** + - **NEXT_PUBLIC_WEBSITE_URL** + - **NEXT_PUBLIC_WEBAPP_URL** + + +4. Next, use the command + ``` + openssl rand -base64 32 + ``` + (or another secret generator tool if you prefer) to generate a key and add it under `NEXTAUTH_SECRET` in the .env file. + +5. You'll also want to fill out the `.env.appStore` file similar to the `.env` file as this includes keys to enable apps. + +#### Production Build + +For a production build, **please make sure** to set up [E2E testing](/developing/local-development#e2e-testing) and [Upgrading](/self-hosting/upgrading) the database from earlier version, and the proceed to build as follows: + +``` +yarn build +yarn start +``` + + Please make sure to upgrade your database before you build for production + +## Cron Jobs + +There are a few features which require cron job setup. When self-hosting, you would probably need to set up cron jobs according to the hosting platform you are using. +For instance, if you are hosting on Vercel, you would need to set up cron jobs by following [this document](https://vercel.com/guides/how-to-setup-cron-jobs-on-vercel). + +At cal.com, the cron jobs are found in the following directory: +``` +/apps/web/pages/api/cron +``` + + +## App store seeder + + +We recommend using the admin UI/wizard instead of the seeder to enable app store apps + + + +## API + +#### Step 1 + +Copy the .env files from their respective example files: + +``` +cp apps/api/.env.example apps/api/V2/.env +cp .env.example .env +``` + +#### Step 2 + +Install packages with yarn: + +``` +yarn +``` + +### Running API server + +Build & Run the API V2 with yarn: + +``` +yarn workspace @calcom/api-v2 build +yarn workspace @calcom/api-v2 start +``` + +## Vercel + +As Cal.com is written in Next.js, Vercel is the perfect platform to host this on (Vercel built Next.js). You can simply follow the [instructions provided by Vercel to get started](https://vercel.com/docs/concepts/get-started). All you do is fork your own version of the repository, click new on Vercel and select the repository. It'll pretty much do the rest for you. The one thing you will need to do is set the environment variables (the .env file). As you can't create a .env file on Vercel, you can go into the settings and manually add each variable. Use the .env.example file for reference as to what you should add. You can learn more about [setting environment variables on Vercel here](https://vercel.com/docs/concepts/projects/environment-variables). + +## Other environments + +Cal.com effectively is just a Next.js application, so any possible solution you find online pertaining to Next.js applications should work. One example is Netlify, which is pretty similar to Vercel. It says it supports Next.js, so you can deploy Cal.com on Netlify. Refer to Netlify's docs on Next.js projects for more info. Another example is on a self hosted instance people may want to configure complex reverse proxies, SSL gateways and all sorts of other stuff. We can't officially support every configuration, but for any edge case where you may want to deploy Cal.com with X, just refer to X's docs on Next.js applications and you should be fine. + +That's it. Your new self hosted Cal.com instance should now be up and running. + + + For more details on specific deployment instructions for AWS, Azure, etc. + diff --git a/docs/self-hosting/sso-setup.mdx b/docs/self-hosting/sso-setup.mdx new file mode 100644 index 0000000000..8d3b42d050 --- /dev/null +++ b/docs/self-hosting/sso-setup.mdx @@ -0,0 +1,68 @@ +--- +title: "SSO setup" +icon: "user-shield" +--- + +Cal.com supports both Security Assertion Markup Language (SAML) and OpenID Connect (OIDC), two of the industry's leading authentication protocols. We prioritize your ease of access and security by providing robust Single Sign-On (SSO) capabilities. Whether you're looking for the XML-based standard of SAML or the lightweight OIDC, our platform is equipped to integrate smoothly with your preferred identity provider, ensuring both convenience and security for your users. + + +You need an Enterprise License to avail this when self-hosting + + +### Setting up SAML login + +1. Set SAML_DATABASE_URL to a Postgres database. Please use a different database than the main Cal instance since the migrations are separate for this database. For example `postgresql://postgres:@localhost:5450/cal-saml`. If you are using a self-signed certificate for Postgres then use the `sslmode=no-verify` query param in the database URL. For example `postgresql://postgres:@localhost:5450/cal-saml?sslmode=no-verify`. + +2. Set SAML_ADMINS to a comma separated list of admin emails from where the SAML metadata can be uploaded and configured. + +3. Create a SAML application with your Identity Provider (IdP) using the instructions here - [SAML Setup](/self-hosting/sso-setup#saml-registration-with-identity-providers) + +4. Remember to configure access to the IdP SAML app for all your users (who need access to Cal). + +5. You will need the XML metadata from your IdP later, so keep it accessible. + +6. Log in to one of the admin accounts configured in SAML_ADMINS and then navigate to Settings -> Security. + +7. You should see a SAML configuration section, copy and paste the XML metadata from step 5 and click on Save. + +8. Your provisioned users can now log into Cal using SAML. + +### SAML Registration with Identity Providers + +This guide explains the settings you need to use to configure SAML with your Identity Provider. Once this is set up you should get an XML metadata file that should then be uploaded on your Cal.com self-hosted instance. + +> **Note:** Please do not add a trailing slash at the end of the URLs. Create them exactly as shown below. + +**Assertion consumer service URL / Single Sign-On URL / Destination URL:** https://\/api/auth/saml/callback [Replace the placeholder with the URL for your self-hosted Cal instance] + +**Entity ID / Identifier / Audience URI / Audience Restriction:** [https://saml.cal.com](https://saml.cal.com) + +**Response:** Signed + +**Assertion Signature:** Signed + +**Signature Algorithm:** RSA-SHA256 + +**Assertion Encryption:** Unencrypted + +**Mapping Attributes / Attribute Statements:** + +http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier -> id + +http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress -> email + +http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname -> firstName + +http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname -> lastName + +### Setting up OIDC login + +1. Set SAML_DATABASE_URL to a Postgres database. Please use a different database than the main Cal instance since the migrations are separate for this database. For example `postgresql://postgres:@localhost:5450/cal-saml`. If you are using a self-signed certificate for Postgres then use the `sslmode=no-verify` query param in the database URL. For example `postgresql://postgres:@localhost:5450/cal-saml?sslmode=no-verify`. +2. Set SAML_ADMINS to a comma separated list of admin emails who can configure the OIDC. +3. Keep handy the Client Secret, Client ID and Well Known URL with you for the next step. +4. Spin up cal.com on your server and login with the Admin user (the email ID of which was provided in step 2 for SAML_ADMINS environment variable). +5. Visit `{BASE_URL}/settings/security/sso` and you should see something like this: + +6. Click on Configure SSO with OIDC, and then enter the Client Secret, Client ID and Well known URL from the Step 3, and click save. + +7. That's it. Now when you try to login with SSO, your OIDC provider will handle the auth. \ No newline at end of file diff --git a/docs/self-hosting/upgrading.mdx b/docs/self-hosting/upgrading.mdx new file mode 100644 index 0000000000..c34d6d209d --- /dev/null +++ b/docs/self-hosting/upgrading.mdx @@ -0,0 +1,53 @@ +--- +title: "Upgrading" +icon: "cloud-check" +--- + +Pull the current version: + +``` +git pull +``` + +Check if dependencies got added/updated/removed + +``` +yarn +``` + +Apply database migrations by running **one of** the following commands: + +In a development environment, run: + +``` +yarn workspace @calcom/prisma db-migrate +``` + +(this can clear your development database in some cases) + +In a production environment, run: + +``` +yarn workspace @calcom/prisma db-deploy +``` + +Check for `.env` variables changes + +``` +yarn predev +``` + +Start the server. In a development environment, just do: + +``` +yarn dev +``` + +For a production build, run for example: + +``` +yarn build +yarn start +``` + +Enjoy the new version. \ No newline at end of file