Extract prisma to it's own package (#1823)

* Moved prisma to packages

* Add missing prisma configs

* Extracts common libs and types

* Build and pipeline fixes

* Adds missing package

* Prisma scripts cleanup

* Updates lint staged

* Type fixes

* Sort imports

* Updates yarn lock file

* Fixes for yarn dx

* Revert "Sort imports"

This reverts commit 076109decab9b9ba307fc03696c3b0da5c4896f3.

* Formatting

* Prevent double TS version

* Fix conflict

* Extracted e2e configs
This commit is contained in:
Omar López 2022-02-15 13:30:52 -07:00 committed by GitHub
parent fdf7516cdd
commit fe35cf6570
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
128 changed files with 349 additions and 223 deletions

View File

@ -1 +1,2 @@
# It now lives at `apps/web/.env.example` # It now lives at `apps/web/.env.example`
# DATABASE_URL got moved to `packages/prisma/.env.example`

View File

@ -1,2 +1,2 @@
node_modules node_modules
apps/web/prisma/zod packages/prisma/zod

View File

@ -93,6 +93,7 @@ jobs:
with: with:
name: videos name: videos
path: | path: |
test-results
playwright/screenshots playwright/screenshots
playwright/videos playwright/videos
playwright/results playwright/results

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ node_modules
# testing # testing
coverage coverage
/test-results/
playwright/videos playwright/videos
playwright/screenshots playwright/screenshots
playwright/artifacts playwright/artifacts

View File

@ -13,4 +13,4 @@ public
.prettierignore .prettierignore
.DS_Store .DS_Store
.eslintignore .eslintignore
apps/web/prisma/zod packages/prisma/zod

View File

@ -7,7 +7,7 @@ module.exports = {
semi: true, semi: true,
printWidth: 110, printWidth: 110,
arrowParens: "always", arrowParens: "always",
importOrder: ["^@ee/(.*)$", "^@lib/(.*)$", "^@components/(.*)$", "^@(server|trpc)/(.*)$", "^[./]"], importOrder: ["^@(calcom|ee)/(.*)$", "^@lib/(.*)$", "^@components/(.*)$", "^@(server|trpc)/(.*)$", "^[./]"],
importOrderSeparation: true, importOrderSeparation: true,
plugins: [require("./merged-prettier-plugin")], plugins: [require("./merged-prettier-plugin")],
}; };

View File

@ -106,6 +106,7 @@ Here is what you need to be able to run Cal.
```sh ```sh
cp apps/web/.env.example apps/web/.env cp apps/web/.env.example apps/web/.env
cp packages/prisma/.env.example packages/prisma/.env
``` ```
1. Install packages with yarn 1. Install packages with yarn

View File

@ -13,22 +13,27 @@
The official product, support and developer documentation, containing information and guides about using the product as well as support for self-hosted installations. This documentation site runs on [Nextra](https://nextra.vercel.app), so you may refer to their documentation should you need information on anything that isn't covered here. The official product, support and developer documentation, containing information and guides about using the product as well as support for self-hosted installations. This documentation site runs on [Nextra](https://nextra.vercel.app), so you may refer to their documentation should you need information on anything that isn't covered here.
## Prerequisites ## Prerequisites
- Git - Git
- Node.js & npm - Node.js & npm
- Yarn - Yarn
## Installation ## Installation
Firstly, clone the repository using Git: Firstly, clone the repository using Git:
```console ```console
git clone https://github.com/calcom/docs.git git clone https://github.com/calcom/docs.git
``` ```
Now, you can install the dependencies with yarn: Now, you can install the dependencies with yarn:
```console ```console
yarn install yarn install
``` ```
## Editing ## Editing
To create, edit and delete documentation pages, you can simply create markdown (.mdx) files in the `pages/` folder. You can edit Markdown with any text editor, but VS Code and WebStorm have side-by-side previews so you can see your formatted content whilst writing markdown. To create, edit and delete documentation pages, you can simply create markdown (.mdx) files in the `pages/` folder. You can edit Markdown with any text editor, but VS Code and WebStorm have side-by-side previews so you can see your formatted content whilst writing markdown.
You will also need to add it as an entry to the `meta.json` file found in whichever directory that the .mdx file is in. You will also need to add it as an entry to the `meta.json` file found in whichever directory that the .mdx file is in.
@ -52,6 +57,7 @@ This command generates static content into the `build` directory and can be serv
## How to easily contribute ## How to easily contribute
## Existing Page ## Existing Page
1. From the documentation's GitHub repository, head to the folder called 'pages' and open it. 1. From the documentation's GitHub repository, head to the folder called 'pages' and open it.
2. From here you can view all current pages on the documentation site. Select the page you would like to contribute to. 2. From here you can view all current pages on the documentation site. Select the page you would like to contribute to.
3. You should now be able to view the page you have selected. Located at the top right of the page will be a pencil icon. Pressing this will bring you up an editor to edit and make changes. You can add formatting using the buttons at the top, which will automatically insert the relevant markdown content needed to style the text. 3. You should now be able to view the page you have selected. Located at the top right of the page will be a pencil icon. Pressing this will bring you up an editor to edit and make changes. You can add formatting using the buttons at the top, which will automatically insert the relevant markdown content needed to style the text.

View File

@ -23,13 +23,14 @@ Here is what you need to be able to run Cal.
1. Go to the project folder 1. Go to the project folder
```sh ```sh
cd calendso cd cal.com
``` ```
1. Copy `.env.example` to `.env` 1. Copy `.env.example` to `.env`
```sh ```sh
cp .env.example .env cp apps/web/.env.example apps/web/.env
cp packages/prisma/.env.example packages/prisma/.env
``` ```
1. Install packages with yarn 1. Install packages with yarn
@ -42,57 +43,72 @@ Here is what you need to be able to run Cal.
> - **Requires Docker and Docker Compose to be installed** > - **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 > - Will start a local Postgres instance with a few test users - the credentials will be logged in the console
```sh ```sh
yarn dx yarn dx
``` ```
### Manual setup ### Manual setup
1. Configure environment variables in the .env file. Replace `<user>`, `<pass>`, `<db-host>`, `<db-port>` with their applicable values 1. Configure database in the `packages/prisma/.env` file. Replace `<user>`, `<pass>`, `<db-host>`, `<db-port>` with their applicable values
```text ```text
DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>' DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>'
``` ```
<details> <details>
<summary>If you don't know how to configure the DATABASE_URL, then follow the steps here to create a quick DB using Heroku</summary>
1. Create a free account with [Heroku](https://www.heroku.com/). <summary>
If you don't know how to configure the DATABASE_URL, then follow the steps here to create a quick DB
using Heroku
</summary>
2. Create a new app. 1. Create a free account with [Heroku](https://www.heroku.com/).
<img width="306" alt="Create an App" src="https://user-images.githubusercontent.com/16905768/115322780-b3d58c00-a17e-11eb-8a52-b758fb0ea942.png" />
3. In your new app, go to `Overview` and next to `Installed add-ons`, click `Configure Add-ons`. We need this to set up our database. 2. Create a new app.
![image](https://user-images.githubusercontent.com/16905768/115323232-a53ba480-a17f-11eb-98db-58e2f8c52426.png)
4. Once you clicked on `Configure Add-ons`, click on `Find more add-ons` and search for `postgres`. One of the options will be `Heroku Postgres` - click on that option. <img
![image](https://user-images.githubusercontent.com/16905768/115323126-5beb5500-a17f-11eb-8030-7380310807a9.png) width="306"
alt="Create an App"
src="https://user-images.githubusercontent.com/16905768/115322780-b3d58c00-a17e-11eb-8a52-b758fb0ea942.png"
/>
5. Once the pop-up appears, click `Submit Order Form` - plan name should be `Hobby Dev - Free`. 3. In your new app, go to `Overview` and next to `Installed add-ons`, click `Configure Add-ons`. We need this to set up our database.
<img width="512" alt="Submit Order Form" src="https://user-images.githubusercontent.com/16905768/115323265-b4baed80-a17f-11eb-99f0-d67f019aa6df.png" /> ![image](https://user-images.githubusercontent.com/16905768/115323232-a53ba480-a17f-11eb-98db-58e2f8c52426.png)
6. Once you completed the above steps, click on your newly created `Heroku Postgres` and go to its `Settings`. 4. Once you clicked on `Configure Add-ons`, click on `Find more add-ons` and search for `postgres`. One of the options will be `Heroku Postgres` - click on that option.
![image](https://user-images.githubusercontent.com/16905768/115323367-e92ea980-a17f-11eb-9ff4-dec95f2ec349.png) ![image](https://user-images.githubusercontent.com/16905768/115323126-5beb5500-a17f-11eb-8030-7380310807a9.png)
7. In `Settings`, copy your URI to your Cal.com .env file and replace the `postgresql://<user>:<pass>@<db-host>:<db-port>` with it. 5. Once the pop-up appears, click `Submit Order Form` - plan name should be `Hobby Dev - Free`.
![image](https://user-images.githubusercontent.com/16905768/115323556-4591c900-a180-11eb-9808-2f55d2aa3995.png)
![image](https://user-images.githubusercontent.com/16905768/115323697-7a9e1b80-a180-11eb-9f08-a742b1037f90.png)
8. To view your DB, once you add new data in Prisma, you can use [Heroku Data Explorer](https://heroku-data-explorer.herokuapp.com/). <img
</details> width="512"
alt="Submit Order Form"
src="https://user-images.githubusercontent.com/16905768/115323265-b4baed80-a17f-11eb-99f0-d67f019aa6df.png"
/>
1. Set a 32 character random string in your .env file for the `CALENDSO_ENCRYPTION_KEY` (You can use a command like `openssl rand -base64 24` to generate one). 6. Once you completed the above steps, click on your newly created `Heroku Postgres` and go to its `Settings`.
1. Set up the database using the Prisma schema (found in `prisma/schema.prisma`) ![image](https://user-images.githubusercontent.com/16905768/115323367-e92ea980-a17f-11eb-9ff4-dec95f2ec349.png)
```sh 7. In `Settings`, copy your URI to your Cal.com .env file and replace the `postgresql://<user>:<pass>@<db-host>:<db-port>` with it.
npx prisma migrate deploy ![image](https://user-images.githubusercontent.com/16905768/115323556-4591c900-a180-11eb-9808-2f55d2aa3995.png)
``` ![image](https://user-images.githubusercontent.com/16905768/115323697-7a9e1b80-a180-11eb-9f08-a742b1037f90.png)
1. Run (in development mode) 8. To view your DB, once you add new data in Prisma, you can use [Heroku Data Explorer](https://heroku-data-explorer.herokuapp.com/).
</details>
```sh 1. Set a 32 character random string in your `apps/web/.env` file for the `CALENDSO_ENCRYPTION_KEY` (You can use a command like `openssl rand -base64 24` to generate one).
yarn dev --scope=@calcom/web 1. Set up the database using the Prisma schema (found in `packages/prisma/schema.prisma`)
```
```sh
npx prisma migrate deploy
```
1. Run (in development mode)
```sh
yarn dev --scope=@calcom/web
```
### Setting up your first user ### Setting up your first user

View File

@ -7,8 +7,7 @@
# - Acquire a commercial license to remove these terms by visiting: cal.com/sales # - Acquire a commercial license to remove these terms by visiting: cal.com/sales
NEXT_PUBLIC_LICENSE_CONSENT='' NEXT_PUBLIC_LICENSE_CONSENT=''
# DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>/<db-name>' # ⚠️ ⚠️ ⚠️ DATABASE_URL got moved to `packages/prisma/.env.example` ⚠️ ⚠️ ⚠️
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
# Needed to enable Google Calendar integration and Login with Google # Needed to enable Google Calendar integration and Login with Google
# @see https://github.com/calendso/calendso#obtaining-the-google-api-credentials # @see https://github.com/calendso/calendso#obtaining-the-google-api-credentials

View File

@ -0,0 +1 @@
export * from "@calcom/prisma/client";

View File

@ -2,11 +2,12 @@ import { ChevronDownIcon, PlusIcon } from "@heroicons/react/solid";
import { zodResolver } from "@hookform/resolvers/zod/dist/zod"; import { zodResolver } from "@hookform/resolvers/zod/dist/zod";
import { SchedulingType } from "@prisma/client"; import { SchedulingType } from "@prisma/client";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { createEventTypeInput } from "prisma/zod/eventtypeCustom";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import type { z } from "zod"; import type { z } from "zod";
import { createEventTypeInput } from "@calcom/prisma/zod/eventtypeCustom";
import { HttpError } from "@lib/core/http/error"; import { HttpError } from "@lib/core/http/error";
import { useLocale } from "@lib/hooks/useLocale"; import { useLocale } from "@lib/hooks/useLocale";
import { useToggleQuery } from "@lib/hooks/useToggleQuery"; import { useToggleQuery } from "@lib/hooks/useToggleQuery";

View File

@ -1,126 +1 @@
import { Availability } from "@prisma/client"; export * from "@calcom/lib/availability";
import dayjs, { ConfigType } from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import { Schedule, TimeRange, WorkingHours } from "./types/schedule";
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(customParseFormat);
// sets the desired time in current date, needs to be current date for proper DST translation
export const defaultDayRange: TimeRange = {
start: new Date(new Date().setUTCHours(9, 0, 0, 0)),
end: new Date(new Date().setUTCHours(17, 0, 0, 0)),
};
export const DEFAULT_SCHEDULE: Schedule = [
[],
[defaultDayRange],
[defaultDayRange],
[defaultDayRange],
[defaultDayRange],
[defaultDayRange],
[],
];
export function getAvailabilityFromSchedule(schedule: Schedule): Availability[] {
return schedule.reduce((availability: Availability[], times: TimeRange[], day: number) => {
const addNewTime = (time: TimeRange) =>
({
days: [day],
startTime: time.start,
endTime: time.end,
} as Availability);
const filteredTimes = times.filter((time) => {
let idx;
if (
(idx = availability.findIndex(
(schedule) => schedule.startTime === time.start && schedule.endTime === time.end
)) !== -1
) {
availability[idx].days.push(day);
return false;
}
return true;
});
filteredTimes.forEach((time) => {
availability.push(addNewTime(time));
});
return availability;
}, [] as Availability[]);
}
export const MINUTES_IN_DAY = 60 * 24;
export const MINUTES_DAY_END = MINUTES_IN_DAY - 1;
export const MINUTES_DAY_START = 0;
/**
* Allows "casting" availability (days, startTime, endTime) given in UTC to a timeZone or utcOffset
*/
export function getWorkingHours(
relativeTimeUnit: {
timeZone?: string;
utcOffset?: number;
},
availability: { days: number[]; startTime: ConfigType; endTime: ConfigType }[]
) {
// clearly bail when availability is not set, set everything available.
if (!availability.length) {
return [
{
days: [0, 1, 2, 3, 4, 5, 6],
// shorthand for: dayjs().startOf("day").tz(timeZone).diff(dayjs.utc().startOf("day"), "minutes")
startTime: MINUTES_DAY_START,
endTime: MINUTES_DAY_END,
},
];
}
const utcOffset = relativeTimeUnit.utcOffset ?? dayjs().tz(relativeTimeUnit.timeZone).utcOffset();
const workingHours = availability.reduce((workingHours: WorkingHours[], schedule) => {
// Get times localised to the given utcOffset/timeZone
const startTime =
dayjs.utc(schedule.startTime).get("hour") * 60 +
dayjs.utc(schedule.startTime).get("minute") -
utcOffset;
const endTime =
dayjs.utc(schedule.endTime).get("hour") * 60 + dayjs.utc(schedule.endTime).get("minute") - utcOffset;
// add to working hours, keeping startTime and endTimes between bounds (0-1439)
const sameDayStartTime = Math.max(MINUTES_DAY_START, Math.min(MINUTES_DAY_END, startTime));
const sameDayEndTime = Math.max(MINUTES_DAY_START, Math.min(MINUTES_DAY_END, endTime));
if (sameDayStartTime !== sameDayEndTime) {
workingHours.push({
days: schedule.days,
startTime: sameDayStartTime,
endTime: sameDayEndTime,
});
}
// check for overflow to the previous day
if (startTime < MINUTES_DAY_START || endTime < MINUTES_DAY_START) {
workingHours.push({
days: schedule.days.map((day) => day - 1),
startTime: startTime + MINUTES_IN_DAY,
endTime: Math.min(endTime + MINUTES_IN_DAY, MINUTES_DAY_END),
});
}
// else, check for overflow in the next day
else if (startTime > MINUTES_DAY_END || endTime > MINUTES_DAY_END) {
workingHours.push({
days: schedule.days.map((day) => day + 1),
startTime: Math.max(startTime - MINUTES_IN_DAY, MINUTES_DAY_START),
endTime: endTime - MINUTES_IN_DAY,
});
}
return workingHours;
}, []);
workingHours.sort((a, b) => a.startTime - b.startTime);
return workingHours;
}

View File

@ -1,10 +1 @@
export enum LocationType { export * from "@calcom/lib/location";
InPerson = "inPerson",
Phone = "phone",
GoogleMeet = "integrations:google:meet",
Zoom = "integrations:zoom",
Daily = "integrations:daily",
Jitsi = "integrations:jitsi",
Huddle01 = "integrations:huddle01",
Tandem = "integrations:tandem",
}

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-var-requires */
const withTM = require("@vercel/edge-functions-ui/transpile")([]); const withTM = require("@vercel/edge-functions-ui/transpile")(["@calcom/lib", "@calcom/prisma"]);
const { i18n } = require("./next-i18next.config"); const { i18n } = require("./next-i18next.config");
// So we can test deploy previews preview // So we can test deploy previews preview

View File

@ -8,25 +8,14 @@
"analyze:browser": "BUNDLE_ANALYZE=browser next build", "analyze:browser": "BUNDLE_ANALYZE=browser next build",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next", "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"dev": "next dev", "dev": "next dev",
"db-up": "docker-compose up -d", "dx": "next dev",
"db-migrate": "yarn prisma migrate deploy",
"db-deploy": "yarn prisma migrate deploy",
"db-seed": "yarn prisma db seed",
"db-nuke": "docker-compose down --volumes --remove-orphans",
"db-setup": "run-s db-up db-migrate db-seed",
"db-reset": "run-s db-nuke db-setup",
"deploy": "run-s build db-deploy",
"dx": "env-cmd run-s db-setup dev",
"test": "jest", "test": "jest",
"test-e2e": "cd ../.. && yarn playwright test", "test-e2e": "cd ../.. && yarn playwright test --config=tests/config/playwright.config.ts --project=chromium",
"playwright-report": "playwright show-report playwright/reports/playwright-html-report", "playwright-report": "playwright show-report playwright/reports/playwright-html-report",
"test-codegen": "yarn playwright codegen http://localhost:3000", "test-codegen": "yarn playwright codegen http://localhost:3000",
"type-check": "tsc --pretty --noEmit", "type-check": "tsc --pretty --noEmit",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"generate-schemas": "prisma generate",
"postinstall": "yarn generate-schemas",
"pre-commit": "lint-staged",
"lint": "eslint . --ext .ts,.js,.tsx,.jsx", "lint": "eslint . --ext .ts,.js,.tsx,.jsx",
"lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix", "lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
"check-changed-files": "ts-node scripts/ts-check-changed-files.ts" "check-changed-files": "ts-node scripts/ts-check-changed-files.ts"
@ -37,6 +26,9 @@
}, },
"dependencies": { "dependencies": {
"@boxyhq/saml-jackson": "0.3.6", "@boxyhq/saml-jackson": "0.3.6",
"@calcom/lib": "*",
"@calcom/prisma": "*",
"@calcom/tsconfig": "*",
"@daily-co/daily-js": "^0.21.0", "@daily-co/daily-js": "^0.21.0",
"@headlessui/react": "^1.4.2", "@headlessui/react": "^1.4.2",
"@heroicons/react": "^1.0.5", "@heroicons/react": "^1.0.5",
@ -45,7 +37,6 @@
"@jitsu/sdk-js": "^2.2.4", "@jitsu/sdk-js": "^2.2.4",
"@metamask/providers": "^8.1.1", "@metamask/providers": "^8.1.1",
"@next/bundle-analyzer": "11.1.2", "@next/bundle-analyzer": "11.1.2",
"@prisma/client": "3.0.2",
"@radix-ui/react-avatar": "^0.1.0", "@radix-ui/react-avatar": "^0.1.0",
"@radix-ui/react-collapsible": "^0.1.0", "@radix-ui/react-collapsible": "^0.1.0",
"@radix-ui/react-dialog": "^0.1.0", "@radix-ui/react-dialog": "^0.1.0",
@ -144,30 +135,15 @@
"eslint-plugin-react": "^7.27.1", "eslint-plugin-react": "^7.27.1",
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.3.0",
"jest": "^26.0.0", "jest": "^26.0.0",
"lint-staged": "^11.1.2",
"mockdate": "^3.0.5", "mockdate": "^3.0.5",
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.4.4", "postcss": "^8.4.4",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"prettier-plugin-tailwindcss": "^0.1.6", "prettier-plugin-tailwindcss": "^0.1.6",
"prisma": "3.0.2",
"tailwindcss": "^3.0.0", "tailwindcss": "^3.0.0",
"ts-jest": "^26.0.0", "ts-jest": "^26.0.0",
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"typescript": "^4.5.2", "typescript": "^4.5.3"
"zod-prisma": "^0.5.4"
},
"lint-staged": {
"./{*,{ee,pages,components,lib}/**/*}.{js,ts,jsx,tsx}": [
"prettier --write",
"eslint"
],
"./prisma/schema.prisma": [
"prisma format"
]
},
"prisma": {
"seed": "ts-node ./prisma/seed.ts"
} }
} }

View File

@ -1,13 +1,14 @@
import { EventTypeCustomInput, MembershipRole, PeriodType, Prisma } from "@prisma/client"; import { EventTypeCustomInput, MembershipRole, PeriodType, Prisma } from "@prisma/client";
import { z } from "zod";
import { import {
_AvailabilityModel, _AvailabilityModel,
_DestinationCalendarModel, _DestinationCalendarModel,
_EventTypeCustomInputModel, _EventTypeCustomInputModel,
_EventTypeModel, _EventTypeModel,
} from "prisma/zod"; } from "@calcom/prisma/zod";
import { stringOrNumber } from "prisma/zod-utils"; import { stringOrNumber } from "@calcom/prisma/zod-utils";
import { createEventTypeInput } from "prisma/zod/eventtypeCustom"; import { createEventTypeInput } from "@calcom/prisma/zod/eventtypeCustom";
import { z } from "zod";
import { createProtectedRouter } from "@server/createRouter"; import { createProtectedRouter } from "@server/createRouter";
import { viewerRouter } from "@server/routers/viewer"; import { viewerRouter } from "@server/routers/viewer";

View File

@ -1,5 +1,5 @@
{ {
"extends": "../../packages/tsconfig/nextjs.json", "extends": "@calcom/tsconfig/nextjs.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {

View File

@ -17,6 +17,7 @@
"format": "prettier --write \"**/*.{ts,tsx,md}\"", "format": "prettier --write \"**/*.{ts,tsx,md}\"",
"heroku-postbuild": "turbo run @calcom/web#build", "heroku-postbuild": "turbo run @calcom/web#build",
"lint": "turbo run lint", "lint": "turbo run lint",
"pre-commit": "lint-staged",
"prepare": "husky install", "prepare": "husky install",
"start": "turbo run start", "start": "turbo run start",
"test": "turbo run test", "test": "turbo run test",
@ -41,6 +42,9 @@
], ],
"*.json": [ "*.json": [
"prettier --write" "prettier --write"
],
"packages/prisma/schema.prisma": [
"prisma format"
] ]
}, },
"engines": { "engines": {

11
packages/lib/auth.ts Normal file
View File

@ -0,0 +1,11 @@
import { compare, hash } from "bcryptjs";
export async function hashPassword(password: string) {
const hashedPassword = await hash(password, 12);
return hashedPassword;
}
export async function verifyPassword(password: string, hashedPassword: string) {
const isValid = await compare(password, hashedPassword);
return isValid;
}

View File

@ -0,0 +1,126 @@
import dayjs, { ConfigType } from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import type { Availability } from "@calcom/prisma/client";
import type { Schedule, TimeRange, WorkingHours } from "@calcom/types/schedule";
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(customParseFormat);
// sets the desired time in current date, needs to be current date for proper DST translation
export const defaultDayRange: TimeRange = {
start: new Date(new Date().setUTCHours(9, 0, 0, 0)),
end: new Date(new Date().setUTCHours(17, 0, 0, 0)),
};
export const DEFAULT_SCHEDULE: Schedule = [
[],
[defaultDayRange],
[defaultDayRange],
[defaultDayRange],
[defaultDayRange],
[defaultDayRange],
[],
];
export function getAvailabilityFromSchedule(schedule: Schedule): Availability[] {
return schedule.reduce((availability: Availability[], times: TimeRange[], day: number) => {
const addNewTime = (time: TimeRange) =>
({
days: [day],
startTime: time.start,
endTime: time.end,
} as Availability);
const filteredTimes = times.filter((time) => {
let idx;
if (
(idx = availability.findIndex(
(schedule) => schedule.startTime === time.start && schedule.endTime === time.end
)) !== -1
) {
availability[idx].days.push(day);
return false;
}
return true;
});
filteredTimes.forEach((time) => {
availability.push(addNewTime(time));
});
return availability;
}, [] as Availability[]);
}
export const MINUTES_IN_DAY = 60 * 24;
export const MINUTES_DAY_END = MINUTES_IN_DAY - 1;
export const MINUTES_DAY_START = 0;
/**
* Allows "casting" availability (days, startTime, endTime) given in UTC to a timeZone or utcOffset
*/
export function getWorkingHours(
relativeTimeUnit: {
timeZone?: string;
utcOffset?: number;
},
availability: { days: number[]; startTime: ConfigType; endTime: ConfigType }[]
) {
// clearly bail when availability is not set, set everything available.
if (!availability.length) {
return [
{
days: [0, 1, 2, 3, 4, 5, 6],
// shorthand for: dayjs().startOf("day").tz(timeZone).diff(dayjs.utc().startOf("day"), "minutes")
startTime: MINUTES_DAY_START,
endTime: MINUTES_DAY_END,
},
];
}
const utcOffset = relativeTimeUnit.utcOffset ?? dayjs().tz(relativeTimeUnit.timeZone).utcOffset();
const workingHours = availability.reduce((workingHours: WorkingHours[], schedule) => {
// Get times localised to the given utcOffset/timeZone
const startTime =
dayjs.utc(schedule.startTime).get("hour") * 60 +
dayjs.utc(schedule.startTime).get("minute") -
utcOffset;
const endTime =
dayjs.utc(schedule.endTime).get("hour") * 60 + dayjs.utc(schedule.endTime).get("minute") - utcOffset;
// add to working hours, keeping startTime and endTimes between bounds (0-1439)
const sameDayStartTime = Math.max(MINUTES_DAY_START, Math.min(MINUTES_DAY_END, startTime));
const sameDayEndTime = Math.max(MINUTES_DAY_START, Math.min(MINUTES_DAY_END, endTime));
if (sameDayStartTime !== sameDayEndTime) {
workingHours.push({
days: schedule.days,
startTime: sameDayStartTime,
endTime: sameDayEndTime,
});
}
// check for overflow to the previous day
if (startTime < MINUTES_DAY_START || endTime < MINUTES_DAY_START) {
workingHours.push({
days: schedule.days.map((day) => day - 1),
startTime: startTime + MINUTES_IN_DAY,
endTime: Math.min(endTime + MINUTES_IN_DAY, MINUTES_DAY_END),
});
}
// else, check for overflow in the next day
else if (startTime > MINUTES_DAY_END || endTime > MINUTES_DAY_END) {
workingHours.push({
days: schedule.days.map((day) => day + 1),
startTime: Math.max(startTime - MINUTES_IN_DAY, MINUTES_DAY_START),
endTime: endTime - MINUTES_IN_DAY,
});
}
return workingHours;
}, []);
workingHours.sort((a, b) => a.startTime - b.startTime);
return workingHours;
}

10
packages/lib/location.ts Normal file
View File

@ -0,0 +1,10 @@
export enum LocationType {
InPerson = "inPerson",
Phone = "phone",
GoogleMeet = "integrations:google:meet",
Zoom = "integrations:zoom",
Daily = "integrations:daily",
Jitsi = "integrations:jitsi",
Huddle01 = "integrations:huddle01",
Tandem = "integrations:tandem",
}

16
packages/lib/package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "@calcom/lib",
"version": "0.0.0",
"main": "./index.ts",
"types": "./index.ts",
"license": "MIT",
"dependencies": {
"bcryptjs": "^2.4.3",
"dayjs": "^1.10.6",
"dayjs-business-time": "^1.0.4"
},
"devDependencies": {
"@calcom/tsconfig": "*",
"typescript": "^4.5.3"
}
}

View File

@ -0,0 +1,5 @@
{
"extends": "@calcom/tsconfig/base.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}

View File

@ -0,0 +1,2 @@
# DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>/<db-name>'
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"

1
packages/prisma/client/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from ".prisma/client/index.d";

1
packages/prisma/client/runtime.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "@prisma/client/runtime";

13
packages/prisma/index.ts Normal file
View File

@ -0,0 +1,13 @@
import { PrismaClient } from "@prisma/client";
export const prisma =
globalThis.prisma ||
new PrismaClient({
log: ["query", "error", "warn"],
});
if (process.env.NODE_ENV !== "production") {
globalThis.prisma = prisma;
}
export default prisma;

View File

@ -0,0 +1,34 @@
{
"name": "@calcom/prisma",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "yarn generate-schemas",
"clean": "rm -rf .turbo && rm -rf node_modules",
"db-deploy": "yarn prisma migrate deploy",
"db-migrate": "yarn prisma migrate dev",
"db-nuke": "docker-compose down --volumes --remove-orphans",
"db-reset": "run-s db-nuke db-setup",
"db-seed": "yarn prisma db seed",
"db-setup": "run-s db-up db-deploy db-seed",
"db-up": "docker-compose up -d",
"deploy": "run-s build db-deploy",
"dx": "yarn db-setup",
"generate-schemas": "prisma generate"
},
"devDependencies": {
"npm-run-all": "^4.1.5",
"prisma": "3.0.2",
"ts-node": "^10.2.1",
"zod-prisma": "^0.5.4"
},
"dependencies": {
"@calcom/lib": "*",
"@prisma/client": "3.0.2"
},
"main": "index.ts",
"types": "index.d.ts",
"prisma": {
"seed": "ts-node ./seed.ts"
}
}

View File

@ -2,8 +2,8 @@ import { MembershipRole, Prisma, PrismaClient, UserPlan } from "@prisma/client";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { uuid } from "short-uuid"; import { uuid } from "short-uuid";
import { hashPassword } from "../lib/auth"; import { hashPassword } from "@calcom/lib/auth";
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "../lib/availability"; import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
const prisma = new PrismaClient(); const prisma = new PrismaClient();

Some files were not shown because too many files have changed in this diff Show More