Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
937a9a0276 | ||
|
|
7a01e0427d | ||
|
|
3336f8f017 | ||
|
|
724ce9f446 | ||
|
|
55737a0ad3 | ||
|
|
06695f41ee | ||
|
|
32f5f8b670 | ||
|
|
78e7aa860a | ||
|
|
e4a2ebb545 | ||
|
|
244dfd30ff | ||
|
|
d990bacf3b | ||
|
|
5d139a7ba8 | ||
|
|
091d3cdd17 | ||
|
|
a8d95d54e7 | ||
|
|
af8d38e3a6 | ||
|
|
fd91c5da79 | ||
|
|
452ea6a993 | ||
|
|
0223db1f7c | ||
|
|
da7c5ec407 | ||
|
|
d391f35389 | ||
|
|
ef498c6ed6 | ||
|
|
bec1453148 | ||
|
|
a2b83bbb03 | ||
|
|
d84dce0f05 | ||
|
|
9896875006 | ||
|
|
f12a6b27b7 | ||
|
|
f3eeb2a50f | ||
|
|
49017ad4b7 | ||
|
|
09bbb19fe1 | ||
|
|
dd3dd32a3e | ||
|
|
75c3c596c2 | ||
|
|
59d45afc56 | ||
|
|
070548555c | ||
|
|
b520238d33 | ||
|
|
e04a8f0344 | ||
|
|
9acc21d882 | ||
|
|
fbff9a9a3f | ||
|
|
de41f20145 | ||
|
|
81b34420dc | ||
|
|
71c657f9c1 | ||
|
|
eb580e91de | ||
|
|
896ba104fd | ||
|
|
d08c3fdc71 | ||
|
|
74e5f4bf37 | ||
|
|
61a8543e08 | ||
|
|
82c7e595ea | ||
|
|
e19e829ff7 | ||
|
|
0fc79f93de | ||
|
|
0ad82bdf52 | ||
|
|
b4c62b3c43 | ||
|
|
0df68cce3b | ||
|
|
2d90def37b | ||
|
|
602fd6879f | ||
|
|
6ea13b7c9a | ||
|
|
e169d9e8d1 | ||
|
|
0e8fa22b09 | ||
|
|
c50e04e18b | ||
|
|
9bb13a0b47 | ||
|
|
25c0955aa5 | ||
|
+24 |
0a15483c46 | ||
|
|
5aa922559a | ||
|
|
9abf004880 | ||
|
|
253f57bce1 | ||
|
|
a55ffca013 | ||
|
|
86addf3793 | ||
|
|
68599fc189 | ||
|
|
0e29c518d0 | ||
|
|
8edf55f946 | ||
|
|
85425649d4 | ||
|
|
b39cbea82b | ||
|
|
b1d31613d9 | ||
|
|
d6605d6dfd | ||
|
|
0f1f0a29e6 | ||
|
|
e4e1d91068 | ||
|
|
848318a771 | ||
|
|
aaf630ed18 | ||
|
|
ece74cbc49 | ||
|
|
3c5a007125 | ||
|
|
9694aa443f | ||
|
|
7e557889b6 | ||
|
|
9e17042a72 | ||
|
|
4b764436e5 | ||
|
|
c42571df03 | ||
|
|
a30555018f | ||
|
|
0c2d1bff2b | ||
|
|
7a790aaabe | ||
|
|
8fe8beb686 | ||
|
|
71b0dd2aa3 | ||
|
|
28c793c3fe | ||
|
|
af6899d122 | ||
|
|
f4baf88ed5 | ||
|
|
09ca75eb1f | ||
|
|
3aff79e7d1 | ||
|
|
a7405db0d4 | ||
|
|
802fbdf491 | ||
|
|
2803b5072c | ||
|
|
502a3241e4 | ||
|
|
a4bc0793d7 | ||
|
|
4af209fc9d | ||
|
|
76b33165e5 | ||
|
|
5109075494 | ||
|
|
52791599e1 | ||
|
|
54db9a42a9 | ||
|
|
ce204d7a98 | ||
|
|
44170b267f | ||
|
|
cb30e5f1e0 | ||
|
|
d6d2aae67f | ||
|
|
b06e46342c | ||
|
|
4cf482f860 | ||
|
|
f2478ea7d3 | ||
|
|
0e110022fb | ||
|
|
ee80577cc3 | ||
|
|
8402dfa3a2 | ||
|
|
4585199b5e | ||
|
|
ae2446f42f | ||
|
|
2f6d109a70 | ||
|
|
7ff884ca7d | ||
|
|
9230b2d2ca | ||
|
|
1a645f3110 | ||
|
|
4eb11e76ea | ||
|
|
e3a5ce5f6e | ||
|
|
12e171823b | ||
|
|
3c723d6f5b | ||
|
|
1482d9a9af | ||
|
|
0efe25feb4 | ||
|
|
602192e026 | ||
|
|
aeb54e6401 | ||
|
|
b8c1d623f3 | ||
|
|
30e4d769e3 | ||
|
|
3babfa7cf3 | ||
|
|
e45ee8fab3 | ||
|
|
3c96e122b4 | ||
|
|
f21a915814 | ||
|
|
3467e2272f | ||
|
|
844592e15e | ||
|
|
bdacb18d5c | ||
|
|
1b5d50c45c | ||
|
|
1c39dbad3d | ||
|
|
e60454750e | ||
|
|
286f696302 | ||
|
|
8deaea3edd | ||
|
|
7d497a621f | ||
|
|
5c8e0483b1 | ||
|
|
3cefa94a94 | ||
|
|
30549d5c14 | ||
|
|
b87972399e | ||
|
|
0784df6a31 | ||
|
|
fc062878ed | ||
|
|
33c1d80a42 | ||
|
|
1064e1181d | ||
|
|
dfd683ffc4 | ||
|
|
0972307998 | ||
|
|
157f633ef2 | ||
|
|
cc1c3b210a | ||
|
|
fcfd47e7d2 | ||
|
|
284c4c9084 | ||
|
|
9fd3594f6c | ||
|
|
531371bf55 | ||
|
|
99fd4fe7a4 | ||
|
|
a677d0d2c4 | ||
|
|
d8cee20d0c | ||
|
|
a7ce12e22f | ||
|
|
88b67a8121 | ||
|
|
7e2a1a23b9 | ||
|
|
a115a3458e | ||
|
|
ae503acccc | ||
|
|
61e7c89f36 | ||
|
|
60ba7a5ca2 | ||
|
|
bfa718e193 | ||
|
|
b29a52d0b0 | ||
|
|
c21d9d08ab | ||
|
|
615e1dcfe8 | ||
|
|
a24f9b76f6 | ||
|
|
62f8ac1c6d | ||
|
|
09b91d5a7e | ||
|
|
01ac2e640a | ||
|
|
e39bf37f91 | ||
|
|
1f845944ac | ||
|
|
7f53db601d | ||
|
|
1cc9f1d25d | ||
|
|
c6ee482478 | ||
|
|
5292dd2e66 | ||
|
|
f59d62f03c | ||
|
|
cb02268e7f | ||
|
|
e33453c50d | ||
|
|
15ffeec745 | ||
|
|
b7252ed79c | ||
|
|
9b04df9bbe | ||
|
|
657be16a4e | ||
|
|
b0afb15939 | ||
|
|
3a9b8a9bf6 | ||
|
|
92a919069d | ||
|
|
0071d16575 | ||
|
|
23d22ee7ea | ||
|
|
3a4f944aac | ||
|
|
3eadae0aad | ||
|
|
0cc2fe2d7f | ||
|
|
0970c77009 | ||
|
|
5b733765f4 | ||
|
|
83fe4d6625 | ||
|
|
91beeeb525 | ||
|
|
81c0b1ffe9 | ||
|
|
aedc88f975 | ||
|
|
67256b5b6e | ||
|
|
ef02d64f90 | ||
|
|
8685862617 | ||
|
|
7422488b5d | ||
|
|
c3e1481d6a | ||
|
|
a1d1883b34 | ||
|
|
f2ae94a190 | ||
|
|
b82a597645 | ||
|
|
5ba0ee8e85 | ||
|
|
1463f97f14 | ||
|
|
34fea35e83 | ||
|
|
2d75048037 | ||
|
|
b549735334 | ||
|
|
86c379817e | ||
|
|
5d1976295c | ||
|
|
e9b1fa1f50 | ||
|
|
80080dda7a | ||
|
|
27c25e1f95 | ||
|
|
858be0f5d8 | ||
|
|
68d0090f50 | ||
|
|
a43fa13b11 | ||
|
|
2bf6da30b3 | ||
|
|
d85e0b51e4 | ||
|
|
55a317d677 | ||
|
|
5fff8fc58c | ||
|
|
0d68767d0a | ||
|
|
2051e87436 | ||
|
|
77a157a12b | ||
|
|
c4f25f2c44 | ||
|
|
d967facb63 | ||
|
|
c6dad39fb0 | ||
|
|
eec7f4d7d4 | ||
|
|
95d5824ba9 | ||
|
|
5f95a4d041 | ||
|
|
8e97444129 | ||
|
|
8d8264b5f5 | ||
|
|
2ad81221d0 | ||
|
|
3d1ac4523f | ||
|
|
b38807cb0f | ||
|
|
11fb387370 | ||
|
|
1546f9115d | ||
|
|
40ecd5dd9d | ||
|
|
ac6ac14a23 | ||
|
|
c6534160e4 | ||
|
|
aa2db562b8 | ||
|
|
b7ed314be2 | ||
|
|
d102268b33 | ||
|
|
f4397d6593 | ||
|
|
192603a1fa | ||
|
|
a3d969680f | ||
|
|
adc3b3aec6 | ||
|
|
92286e2453 | ||
|
|
51b2a15cb0 | ||
|
|
fa9a099dd6 | ||
|
|
83ceb46cca | ||
|
|
75d5576e17 | ||
|
|
0a6dde4fcc | ||
|
|
73b5839284 | ||
|
|
1a42b94b13 | ||
|
|
acb910f680 | ||
|
|
38e857f9a9 | ||
|
|
b8149dee36 | ||
|
|
f583ef40f2 | ||
|
|
71574488c5 | ||
|
|
008e0e43a2 | ||
|
|
da22eda662 | ||
|
|
f94d696885 | ||
|
|
9e84eb1b2c | ||
|
|
4c549811a1 | ||
|
|
05502f0cfb | ||
|
|
e436b8e06b | ||
|
|
8146cf8c93 | ||
|
|
c05f595ef0 | ||
|
|
0a795f5363 | ||
|
|
5b50a469b2 | ||
|
|
c08365f09c | ||
|
|
477e102388 | ||
|
|
e1b10b4ec5 | ||
|
|
ff6db3c572 | ||
|
|
4fa3fdef97 | ||
|
|
f29f6632e8 | ||
|
|
36747988e6 | ||
|
|
3dd312b88b | ||
|
|
3c9e9329b0 | ||
|
|
8617f43a8e | ||
|
|
5f0eb652b0 | ||
|
|
26d7301885 | ||
|
|
ffc4d22ff1 | ||
|
|
ee286a600f | ||
|
|
72c493ee21 | ||
|
|
5bd0428d18 | ||
|
|
c7089b9db6 | ||
|
|
81ad00a816 | ||
|
|
b5730da56d | ||
|
|
9455d14ad5 | ||
|
|
a056160404 | ||
|
|
6d53cd84f8 | ||
|
|
30cb8f745d | ||
|
|
bafa3c97ac | ||
|
|
bb48ccbad9 | ||
|
|
0a68c6d048 | ||
|
|
bb9f8e5d38 | ||
|
|
c99bb190f1 | ||
|
|
f0d2a45fdf | ||
|
|
e5f9315ee6 | ||
|
|
f7782b2080 | ||
|
|
e1f2dbab84 | ||
|
|
c2edbbcea7 | ||
|
|
c7e78cc181 | ||
|
|
7ba1557d1d | ||
|
|
d5b45b757a | ||
|
|
bb3b262bd7 | ||
|
|
d4f41e803a | ||
|
|
74eafa59e1 | ||
|
|
e6cd016cad | ||
|
|
7b0d5959cb | ||
|
|
7702ded85e | ||
|
|
5acb3e8f97 | ||
|
|
41046aaf27 | ||
|
|
7cffb30daa | ||
|
|
26bacb6c8e | ||
|
|
01b5c1f11e | ||
|
|
f5ba04599b | ||
|
|
994e602c8d | ||
|
|
a776c72d48 | ||
|
|
6adb5b413d | ||
|
|
b183a5779a | ||
|
|
16c1cf7b23 | ||
|
|
cf4ee142de | ||
|
|
a5d99bbec9 | ||
|
|
5d0468a986 | ||
|
|
7fef9db4ce | ||
|
|
296a367955 | ||
|
|
1ff2d51ee5 | ||
|
|
092cb0f691 | ||
|
|
0c74a98721 | ||
|
|
2082a4f4ed | ||
|
|
f06ef84f61 | ||
|
|
1080d7f88f | ||
|
|
4ebd52c9af | ||
|
|
e0ac95578c | ||
|
|
473012f2c0 | ||
|
|
1293588ac4 | ||
|
|
c0c40185fc | ||
|
|
3348e7be2f | ||
|
|
97dcb2a2a9 | ||
|
|
5dad78f8fe | ||
|
|
88cc83a0aa | ||
|
|
9dddf5e525 | ||
|
|
9df9f9bb6b | ||
|
|
c4803be323 | ||
|
|
34d360a4ec | ||
|
|
0e6f44e2b9 | ||
|
|
e377997d49 | ||
|
|
a0451e34c1 | ||
|
|
d2b4760e3a | ||
|
|
8469c91973 | ||
|
|
0990549e7f | ||
|
|
e884348807 | ||
|
|
5684ce2d28 | ||
|
|
bd8ac12f14 | ||
|
|
512628e8ab | ||
|
|
903a154dbc | ||
|
|
1a3d54a0d0 | ||
|
|
a5970cb1a8 | ||
|
|
925dc04f1c | ||
|
|
271f31730d | ||
|
|
684f4a795c | ||
|
|
836429348c | ||
|
|
a55138348f | ||
|
|
66b5dfd796 | ||
|
|
bba68e09a9 | ||
|
|
bab182960f | ||
|
|
f09153b3bb | ||
|
|
3e492eb17b | ||
|
|
fe5fe8d53b | ||
|
|
0cc5937104 | ||
|
|
fde3b49126 | ||
|
|
556787b93b | ||
|
|
dcb244d7be | ||
|
|
8d7fb75f71 | ||
|
|
4fe75771b8 | ||
|
|
63f537a08f | ||
|
|
bed593c835 | ||
|
|
f367c5ed3d | ||
|
|
af1382c5ba | ||
|
|
eeb0fc795d | ||
|
|
4aa040d96e | ||
|
|
12466fb064 | ||
|
|
42a3448abb | ||
|
|
92ff49f6a1 | ||
|
|
c4aa555610 | ||
|
|
a45d77de51 | ||
|
|
0421dd401a | ||
|
|
094189fc3b | ||
|
|
677a964d4f | ||
|
|
2357c919c1 | ||
|
|
27b7cccfa6 | ||
|
|
ab2354f2a4 | ||
|
|
538e5017c5 | ||
|
|
2e3060b738 | ||
|
|
e9d9aebd88 | ||
|
|
7d1c69801e | ||
|
|
4235b32349 | ||
|
|
2ca031e25c | ||
|
|
9f734b8964 | ||
|
|
d6927c5049 | ||
|
|
910f5f906f | ||
|
|
71f955e256 | ||
|
|
5e60654aa9 | ||
|
|
b6175b3dde | ||
|
|
a0a731b357 | ||
|
|
fdaa6961f8 | ||
|
|
d91e74ec13 | ||
|
|
0af780aac9 | ||
|
|
41b0589ab0 | ||
|
|
00e96ac7ad | ||
|
|
63feb721ea | ||
|
|
43c1bd63ca | ||
|
|
8bc265faaa | ||
|
|
f33ea91ab0 | ||
|
|
cc0ff17d97 | ||
|
|
4d128971bb | ||
|
|
f9f82283fb | ||
|
|
111135a605 | ||
|
|
04e577582d | ||
|
|
6643d3c20b | ||
|
|
3c42301c19 | ||
|
|
20517be280 | ||
|
|
fa45a85088 | ||
|
|
c839e40a3e | ||
|
|
9422e28598 | ||
|
|
d804b3947d | ||
|
|
3fa710fc1c | ||
|
|
f9359aaa7b | ||
|
|
7c7ca79d48 | ||
|
|
753cb5803d | ||
|
|
210bc9fc8c | ||
|
|
3d8b5390df | ||
|
|
a477f337dc | ||
|
|
9db9204088 | ||
|
|
fe46b81c63 | ||
|
|
17db644be1 | ||
|
|
e4037be79a | ||
|
|
ebe6a60366 | ||
|
|
fa9c6ea0ef | ||
|
|
9b22f44cba | ||
|
|
c84f75df99 | ||
|
|
8f9a4fc78f | ||
|
|
7b30ade284 | ||
|
|
24a4646fdc | ||
|
|
0860036607 | ||
|
|
d080a0ef2d | ||
|
|
a6eab74fd1 | ||
|
|
0a2e275908 | ||
|
|
3b05ebc5b5 | ||
|
|
e5b4d2fb80 | ||
|
|
00a2a3d8a8 | ||
|
|
30ba168fc3 | ||
|
|
12eec186b2 | ||
|
|
4417d649ea | ||
|
|
7058da70e6 | ||
|
|
2cb383c337 | ||
|
|
fe5eca8eb3 | ||
|
|
7c6468953a | ||
|
|
28a0553d18 | ||
|
|
ffcb04fa99 |
No files matched your search
+16
-6
@@ -23,8 +23,9 @@ CALCOM_LICENSE_KEY=
|
|||||||
|
|
||||||
# - DATABASE ************************************************************************************************
|
# - DATABASE ************************************************************************************************
|
||||||
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
|
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
|
||||||
UPSTASH_REDIS_REST_URL=
|
# Needed to run migrations while using a connection pooler like PgBouncer
|
||||||
UPSTASH_REDIS_REST_TOKEN=
|
# Use the same one as DATABASE_URL if you're not using a connection pooler
|
||||||
|
DATABASE_DIRECT_URL="postgresql://postgres:@localhost:5450/calendso"
|
||||||
INSIGHTS_DATABASE_URL=
|
INSIGHTS_DATABASE_URL=
|
||||||
|
|
||||||
# Uncomment to enable a dedicated connection pool for Prisma using Prisma Data Proxy
|
# Uncomment to enable a dedicated connection pool for Prisma using Prisma Data Proxy
|
||||||
@@ -134,8 +135,8 @@ NEXT_PUBLIC_SENDGRID_SENDER_NAME=
|
|||||||
NEXT_PUBLIC_SENTRY_DSN=
|
NEXT_PUBLIC_SENTRY_DSN=
|
||||||
|
|
||||||
# Formbricks Experience Management Integration
|
# Formbricks Experience Management Integration
|
||||||
FORMBRICKS_HOST_URL=https://app.formbricks.com
|
NEXT_PUBLIC_FORMBRICKS_HOST_URL=https://app.formbricks.com
|
||||||
FORMBRICKS_ENVIRONMENT_ID=
|
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=
|
||||||
FORMBRICKS_FEEDBACK_SURVEY_ID=
|
FORMBRICKS_FEEDBACK_SURVEY_ID=
|
||||||
|
|
||||||
# AvatarAPI
|
# AvatarAPI
|
||||||
@@ -229,6 +230,7 @@ NEXT_PUBLIC_COMPANY_NAME="Cal.com, Inc."
|
|||||||
# NEXT_PUBLIC_DISABLE_SIGNUP=true
|
# NEXT_PUBLIC_DISABLE_SIGNUP=true
|
||||||
NEXT_PUBLIC_DISABLE_SIGNUP=
|
NEXT_PUBLIC_DISABLE_SIGNUP=
|
||||||
|
|
||||||
|
# Set this to 'non-strict' to enable CSP for support pages. 'strict' isn't supported yet. Also, check the README for details.
|
||||||
# Content Security Policy
|
# Content Security Policy
|
||||||
CSP_POLICY=
|
CSP_POLICY=
|
||||||
|
|
||||||
@@ -236,6 +238,8 @@ CSP_POLICY=
|
|||||||
EDGE_CONFIG=
|
EDGE_CONFIG=
|
||||||
|
|
||||||
NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes
|
NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes
|
||||||
|
NEXT_PUBLIC_BOOKER_NUMBER_OF_DAYS_TO_LOAD=0 # Override the booker to only load X number of days worth of data
|
||||||
|
|
||||||
# Control time intervals on a user's Schedule availability
|
# Control time intervals on a user's Schedule availability
|
||||||
NEXT_PUBLIC_AVAILABILITY_SCHEDULE_INTERVAL=
|
NEXT_PUBLIC_AVAILABILITY_SCHEDULE_INTERVAL=
|
||||||
|
|
||||||
@@ -289,9 +293,9 @@ E2E_TEST_CALCOM_GCAL_KEYS=
|
|||||||
# Used for self-hosters that are implementing Cal.com into their applications that already have certain integrations
|
# Used for self-hosters that are implementing Cal.com into their applications that already have certain integrations
|
||||||
# Under settings/admin/apps ensure that all app secrets are set the same as the parent application
|
# Under settings/admin/apps ensure that all app secrets are set the same as the parent application
|
||||||
# You can use: `openssl rand -base64 32` to generate one
|
# You can use: `openssl rand -base64 32` to generate one
|
||||||
CALCOM_WEBHOOK_SECRET=""
|
CALCOM_CREDENTIAL_SYNC_SECRET=""
|
||||||
# This is the header name that will be used to verify the webhook secret. Should be in lowercase
|
# This is the header name that will be used to verify the webhook secret. Should be in lowercase
|
||||||
CALCOM_WEBHOOK_HEADER_NAME="calcom-webhook-secret"
|
CALCOM_CREDENTIAL_SYNC_HEADER_NAME="calcom-credential-sync-secret"
|
||||||
CALCOM_CREDENTIAL_SYNC_ENDPOINT=""
|
CALCOM_CREDENTIAL_SYNC_ENDPOINT=""
|
||||||
# Key should match on Cal.com and your application
|
# Key should match on Cal.com and your application
|
||||||
# must be 24 bytes for AES256 encryption algorithm
|
# must be 24 bytes for AES256 encryption algorithm
|
||||||
@@ -336,3 +340,9 @@ APP_ROUTER_TEAMS_ENABLED=0
|
|||||||
|
|
||||||
# disable setry server source maps
|
# disable setry server source maps
|
||||||
SENTRY_DISABLE_SERVER_WEBPACK_PLUGIN=1
|
SENTRY_DISABLE_SERVER_WEBPACK_PLUGIN=1
|
||||||
|
|
||||||
|
# api v2
|
||||||
|
NEXT_PUBLIC_API_V2_URL="http://localhost:5555/api/v2"
|
||||||
|
|
||||||
|
# Ratelimiting via unkey
|
||||||
|
UNKEY_ROOT_KEY=
|
||||||
@@ -13,7 +13,7 @@ Fixes # (issue)
|
|||||||
<!-- Please provide all documents that are important to understand the reason of that PR. -->
|
<!-- Please provide all documents that are important to understand the reason of that PR. -->
|
||||||
|
|
||||||
- If there is a requirement document, please, share it here.
|
- If there is a requirement document, please, share it here.
|
||||||
- If there is ab UI/UX design document, please, share it here.
|
- If there is a UI/UX design document, please, share it here.
|
||||||
|
|
||||||
## Type of change
|
## Type of change
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ runs:
|
|||||||
key-1: ${{ hashFiles('packages/prisma/schema.prisma', 'packages/prisma/migrations/**/**.sql', 'packages/prisma/*.ts') }}
|
key-1: ${{ hashFiles('packages/prisma/schema.prisma', 'packages/prisma/migrations/**/**.sql', 'packages/prisma/*.ts') }}
|
||||||
key-2: ${{ github.event.pull_request.number || github.ref }}
|
key-2: ${{ github.event.pull_request.number || github.ref }}
|
||||||
DATABASE_URL: ${{ inputs.DATABASE_URL }}
|
DATABASE_URL: ${{ inputs.DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ inputs.DATABASE_URL }}
|
||||||
E2E_TEST_CALCOM_QA_EMAIL: ${{ inputs.E2E_TEST_CALCOM_QA_EMAIL }}
|
E2E_TEST_CALCOM_QA_EMAIL: ${{ inputs.E2E_TEST_CALCOM_QA_EMAIL }}
|
||||||
E2E_TEST_CALCOM_QA_PASSWORD: ${{ inputs.E2E_TEST_CALCOM_QA_PASSWORD }}
|
E2E_TEST_CALCOM_QA_PASSWORD: ${{ inputs.E2E_TEST_CALCOM_QA_PASSWORD }}
|
||||||
E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS: ${{ inputs.E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS }}
|
E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS: ${{ inputs.E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS }}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
name: Manually Delete BuildJet Cache
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
cache_key:
|
||||||
|
description: "BuildJet Cache Key to Delete"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
jobs:
|
||||||
|
manually-delete-buildjet-cache:
|
||||||
|
runs-on: buildjet-2vcpu-ubuntu-2204
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- uses: buildjet/cache-delete@v1
|
||||||
|
with:
|
||||||
|
cache_key: ${{ inputs.cache_key }}
|
||||||
@@ -10,7 +10,10 @@ jobs:
|
|||||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:12.1
|
image: postgres:13.13
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_DB: calendso
|
POSTGRES_DB: calendso
|
||||||
@@ -18,6 +21,9 @@ jobs:
|
|||||||
- 5432:5432
|
- 5432:5432
|
||||||
mailhog:
|
mailhog:
|
||||||
image: mailhog/mailhog:v1.0.1
|
image: mailhog/mailhog:v1.0.1
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
ports:
|
ports:
|
||||||
- 8025:8025
|
- 8025:8025
|
||||||
- 1025:1025
|
- 1025:1025
|
||||||
@@ -39,6 +45,7 @@ jobs:
|
|||||||
- uses: ./.github/actions/cache-db
|
- uses: ./.github/actions/cache-db
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
E2E_TEST_CALCOM_QA_EMAIL: ${{ secrets.E2E_TEST_CALCOM_QA_EMAIL }}
|
E2E_TEST_CALCOM_QA_EMAIL: ${{ secrets.E2E_TEST_CALCOM_QA_EMAIL }}
|
||||||
E2E_TEST_CALCOM_QA_PASSWORD: ${{ secrets.E2E_TEST_CALCOM_QA_PASSWORD }}
|
E2E_TEST_CALCOM_QA_PASSWORD: ${{ secrets.E2E_TEST_CALCOM_QA_PASSWORD }}
|
||||||
E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS: ${{ secrets.E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS }}
|
E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS: ${{ secrets.E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS }}
|
||||||
@@ -50,6 +57,7 @@ jobs:
|
|||||||
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
||||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ jobs:
|
|||||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:12.1
|
image: postgres:13.13
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_DB: calendso
|
POSTGRES_DB: calendso
|
||||||
@@ -41,6 +44,7 @@ jobs:
|
|||||||
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
||||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ jobs:
|
|||||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:12.1
|
image: postgres:13.13
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_DB: calendso
|
POSTGRES_DB: calendso
|
||||||
@@ -18,6 +21,9 @@ jobs:
|
|||||||
- 5432:5432
|
- 5432:5432
|
||||||
mailhog:
|
mailhog:
|
||||||
image: mailhog/mailhog:v1.0.1
|
image: mailhog/mailhog:v1.0.1
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
ports:
|
ports:
|
||||||
- 8025:8025
|
- 8025:8025
|
||||||
- 1025:1025
|
- 1025:1025
|
||||||
@@ -44,6 +50,7 @@ jobs:
|
|||||||
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
||||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ jobs:
|
|||||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:12.1
|
image: postgres:13.13
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_DB: calendso
|
POSTGRES_DB: calendso
|
||||||
@@ -18,6 +21,9 @@ jobs:
|
|||||||
- 5432:5432
|
- 5432:5432
|
||||||
mailhog:
|
mailhog:
|
||||||
image: mailhog/mailhog:v1.0.1
|
image: mailhog/mailhog:v1.0.1
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
ports:
|
ports:
|
||||||
- 8025:8025
|
- 8025:8025
|
||||||
- 1025:1025
|
- 1025:1025
|
||||||
@@ -42,6 +48,7 @@ jobs:
|
|||||||
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
|
||||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.EQUITY_BEE_TEAM_LABELER_ACTION_TOKEN }}
|
repo-token: ${{ secrets.EQUITY_BEE_TEAM_LABELER_ACTION_TOKEN }}
|
||||||
organization-name: calcom
|
organization-name: calcom
|
||||||
ignore-labels: "admin, app-store, ai, authentication, automated-testing, devops, platform, billing, bookings, caldav, calendar-apps, ci, console, crm-apps, docs, documentation, emails, embeds, event-types, i18n, impersonation, manual-testing, ui, performance, ops-stack, organizations, public-api, routing-forms, seats, teams, webhooks, workflows, zapier"
|
ignore-labels: "admin, app-store, ai, authentication, automated-testing, devops, billing, bookings, caldav, calendar-apps, ci, console, crm-apps, docs, documentation, emails, embeds, event-types, i18n, impersonation, manual-testing, ui, performance, ops-stack, organizations, public-api, routing-forms, seats, teams, webhooks, workflows, zapier"
|
||||||
apply-labels-from-issue:
|
apply-labels-from-issue:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ env:
|
|||||||
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
||||||
@@ -28,6 +29,7 @@ env:
|
|||||||
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
|
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
|
NEXT_PUBLIC_API_V2_URL: ${{ secrets.CI_NEXT_PUBLIC_API_V2_URL }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ env:
|
|||||||
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }}
|
||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
|
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
E2E_TEST_CALCOM_QA_EMAIL: ${{ secrets.E2E_TEST_CALCOM_QA_EMAIL }}
|
E2E_TEST_CALCOM_QA_EMAIL: ${{ secrets.E2E_TEST_CALCOM_QA_EMAIL }}
|
||||||
@@ -32,6 +33,7 @@ env:
|
|||||||
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
|
SENDGRID_EMAIL: ${{ secrets.CI_SENDGRID_EMAIL }}
|
||||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||||
|
NEXT_PUBLIC_API_V2_URL: ${{ secrets.CI_NEXT_PUBLIC_API_V2_URL }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -40,7 +42,10 @@ jobs:
|
|||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:12.1
|
image: postgres:13.13
|
||||||
|
credentials:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_DB: calendso
|
POSTGRES_DB: calendso
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ public
|
|||||||
packages/prisma/zod
|
packages/prisma/zod
|
||||||
packages/prisma/enums
|
packages/prisma/enums
|
||||||
apps/web/public/embed
|
apps/web/public/embed
|
||||||
|
apps/api/v2/swagger/documentation.json
|
||||||
+226
-56
@@ -1,9 +1,10 @@
|
|||||||
// This transform config was generated by Snaplet.
|
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
||||||
// Snaplet found fields that may contain personally identifiable information (PII)
|
/// <reference path=".snaplet/snaplet.d.ts" />
|
||||||
// and used that to populate this file.
|
// This config was generated by Snaplet make sure to check it over before using it.
|
||||||
import { copycat as c } from "@snaplet/copycat";
|
import { copycat as c } from "@snaplet/copycat";
|
||||||
|
import { defineConfig } from "snaplet";
|
||||||
|
|
||||||
import type { Transform } from "./structure";
|
// c.setHashKey(REPLACE_ME_WITH_YOUR_HASH_KEY);
|
||||||
|
|
||||||
function hasStringProp<T extends string>(x: unknown, key: T): x is { [key in T]: string } {
|
function hasStringProp<T extends string>(x: unknown, key: T): x is { [key in T]: string } {
|
||||||
return !!x && typeof x === "object" && key in x;
|
return !!x && typeof x === "object" && key in x;
|
||||||
@@ -41,57 +42,226 @@ function replaceSensitiveKeys(record: object) {
|
|||||||
|
|
||||||
const generateUsername = (x: string) => `${c.firstName(x)}-${c.lastName(x)}${c.int(x, { min: 2, max: 99 })}`;
|
const generateUsername = (x: string) => `${c.firstName(x)}-${c.lastName(x)}${c.int(x, { min: 2, max: 99 })}`;
|
||||||
|
|
||||||
const config: Transform = () => ({
|
export default defineConfig({
|
||||||
public: {
|
transform: {
|
||||||
ApiKey: ({ row }) => ({
|
$mode: "unsafe",
|
||||||
hashedKey: c.uuid(row.hashedKey),
|
public: {
|
||||||
}),
|
Account({ row }) {
|
||||||
App: ({ row }) => ({
|
return {
|
||||||
keys: replaceSensitiveKeys(row.keys),
|
refresh_token: c.uuid(row.refresh_token),
|
||||||
}),
|
access_token: c.uuid(row.access_token),
|
||||||
Attendee: ({ row }) => ({
|
expires_at: c.int(row.expires_at, {
|
||||||
email: c.email(row.email),
|
min: 0,
|
||||||
name: c.fullName(row.name),
|
max: Math.pow(4, 8) - 1,
|
||||||
timeZone: c.timezone(row.timeZone),
|
}),
|
||||||
}),
|
token_type: c.uuid(row.token_type),
|
||||||
Credential: ({ row }) => ({
|
id_token: c.uuid(row.id_token),
|
||||||
key: typeof row.key === "string" ? c.uuid(row.key) : replaceSensitiveKeys(row.key),
|
};
|
||||||
}),
|
},
|
||||||
EventType: ({ row }) => ({
|
ApiKey: ({ row }) => ({
|
||||||
slug: generateSlug(row.slug),
|
hashedKey: c.uuid(row.hashedKey),
|
||||||
timeZone: c.timezone(row.timeZone),
|
note: c.fullName(row.note),
|
||||||
eventName: c.words(row.eventName, { max: 3 }),
|
createdAt: c.dateString(row.createdAt, {
|
||||||
}),
|
minYear: 2020,
|
||||||
ResetPasswordRequest: ({ row }) => ({
|
}),
|
||||||
email: c.email(row.email),
|
}),
|
||||||
}),
|
App: ({ row }) => ({
|
||||||
Schedule: ({ row }) => ({
|
keys: replaceSensitiveKeys(row.keys),
|
||||||
timeZone: c.timezone(row.timeZone),
|
}),
|
||||||
}),
|
App_RoutingForms_Form({ row }) {
|
||||||
Team: ({ row }) => ({
|
return {
|
||||||
bio: c.sentence(row.bio),
|
name: c.fullName(row.name),
|
||||||
name: c.words(row.name, { max: 2 }),
|
};
|
||||||
slug: generateSlug(row.slug),
|
},
|
||||||
}),
|
Attendee: ({ row }) => ({
|
||||||
users: ({ row }) =>
|
email: c.email(row.email),
|
||||||
row.role !== "ADMIN"
|
name: c.fullName(row.name),
|
||||||
? {
|
timeZone: c.timezone(row.timeZone),
|
||||||
bio: c.sentence(row.bio),
|
locale: c.fullName(row.locale),
|
||||||
email: c.email(row.email),
|
}),
|
||||||
name: c.fullName(row.name),
|
Availability({ row }) {
|
||||||
password: c.password(row.password),
|
return {
|
||||||
timeZone: c.timezone(row.timeZone),
|
startTime: c
|
||||||
username: generateUsername(row.username),
|
.dateString(row.startTime, {
|
||||||
}
|
minYear: 2020,
|
||||||
: row,
|
})
|
||||||
VerificationToken: ({ row }) => ({
|
.slice(11, 19),
|
||||||
token: c.uuid(row.token),
|
endTime: c
|
||||||
}),
|
.dateString(row.endTime, {
|
||||||
Account: ({ row }) => ({
|
minYear: 2020,
|
||||||
access_token: c.uuid(row.access_token),
|
})
|
||||||
refresh_token: c.uuid(row.refresh_token),
|
.slice(11, 19),
|
||||||
}),
|
};
|
||||||
|
},
|
||||||
|
Booking({ row }) {
|
||||||
|
return {
|
||||||
|
title: c.fullName(row.title),
|
||||||
|
startTime: c.dateString(row.startTime, {
|
||||||
|
minYear: 2020,
|
||||||
|
}),
|
||||||
|
endTime: c.dateString(row.endTime, {
|
||||||
|
minYear: 2020,
|
||||||
|
}),
|
||||||
|
location: c.sentence(row.location),
|
||||||
|
metadata: {
|
||||||
|
[c.word(row.metadata)]: c.words(row.metadata),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Credential: ({ row }) => ({
|
||||||
|
key: typeof row.key === "string" ? c.uuid(row.key) : replaceSensitiveKeys(row.key),
|
||||||
|
}),
|
||||||
|
EventType: ({ row }) => ({
|
||||||
|
slug: generateSlug(row.slug),
|
||||||
|
timeZone: c.timezone(row.timeZone),
|
||||||
|
eventName: c.words(row.eventName, { max: 3 }),
|
||||||
|
currency: c.sentence(row.currency),
|
||||||
|
}),
|
||||||
|
EventTypeCustomInput({ row }) {
|
||||||
|
return {
|
||||||
|
label: c.fullName(row.label),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Feature({ row }) {
|
||||||
|
return {
|
||||||
|
slug: c.uuid(row.slug),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
InstantMeetingToken({ row }) {
|
||||||
|
return {
|
||||||
|
token: c.uuid(row.token),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
OAuthClient({ row }) {
|
||||||
|
return {
|
||||||
|
name: c.fullName(row.name),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
OutOfOfficeEntry({ row }) {
|
||||||
|
return {
|
||||||
|
start: c.dateString(row.start, {
|
||||||
|
minYear: 2020,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Payment({ row }) {
|
||||||
|
return {
|
||||||
|
amount: c.int(row.amount, {
|
||||||
|
min: 0,
|
||||||
|
max: Math.pow(4, 8) - 1,
|
||||||
|
}),
|
||||||
|
currency: c.sentence(row.currency),
|
||||||
|
data: {
|
||||||
|
[c.word(row.data)]: c.words(row.data),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
ResetPasswordRequest: ({ row }) => ({
|
||||||
|
email: c.email(row.email),
|
||||||
|
}),
|
||||||
|
Schedule: ({ row }) => ({
|
||||||
|
name: c.fullName(row.name),
|
||||||
|
timeZone: c.timezone(row.timeZone),
|
||||||
|
}),
|
||||||
|
Session({ row }) {
|
||||||
|
return {
|
||||||
|
sessionToken: c.uuid(row.sessionToken),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Team: ({ row }) => ({
|
||||||
|
bio: c.sentence(row.bio),
|
||||||
|
name: c.words(row.name, { max: 2 }),
|
||||||
|
slug: generateSlug(row.slug),
|
||||||
|
timeZone: c.timezone(row.timeZone),
|
||||||
|
logoUrl: c.username(row.logoUrl),
|
||||||
|
}),
|
||||||
|
TempOrgRedirect({ row }) {
|
||||||
|
return {
|
||||||
|
from: c.dateString(row.from, {
|
||||||
|
maxYear: 1999,
|
||||||
|
}),
|
||||||
|
toUrl: c.city(row.toUrl),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
VerificationToken({ row }) {
|
||||||
|
return {
|
||||||
|
id: c
|
||||||
|
.int(row.id, {
|
||||||
|
min: 1,
|
||||||
|
max: Math.pow(4, 8) - 1,
|
||||||
|
})
|
||||||
|
.toString(),
|
||||||
|
identifier: c.uuid(row.identifier),
|
||||||
|
token: c.uuid(row.token),
|
||||||
|
expires: c.dateString(row.expires, {
|
||||||
|
minYear: 2020,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
VerifiedNumber({ row }) {
|
||||||
|
return {
|
||||||
|
phoneNumber: c.phoneNumber(row.phoneNumber),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
Webhook({ row }) {
|
||||||
|
return {
|
||||||
|
subscriberUrl: c.url(row.subscriberUrl),
|
||||||
|
secret: c.streetAddress(row.secret),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
WebhookScheduledTriggers({ row }) {
|
||||||
|
return {
|
||||||
|
jobName: c.fullName(row.jobName),
|
||||||
|
payload: c.password(row.payload),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
WorkflowStep({ row }) {
|
||||||
|
return {
|
||||||
|
sendTo: c.oneOf(row.sendTo, [
|
||||||
|
"Man",
|
||||||
|
"Woman",
|
||||||
|
"Transgender",
|
||||||
|
"Non-binary/non-conforming",
|
||||||
|
"Not specified",
|
||||||
|
]),
|
||||||
|
sender: c.oneOf(row.sender, [
|
||||||
|
"Man",
|
||||||
|
"Woman",
|
||||||
|
"Transgender",
|
||||||
|
"Non-binary/non-conforming",
|
||||||
|
"Not specified",
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
users: ({ row }) =>
|
||||||
|
row.role !== "ADMIN"
|
||||||
|
? {
|
||||||
|
bio: c.sentence(row.bio),
|
||||||
|
email: c.email(row.email),
|
||||||
|
name: c.fullName(row.name),
|
||||||
|
password: c.password(row.password),
|
||||||
|
timeZone: c.timezone(row.timeZone),
|
||||||
|
username: generateUsername(row.username),
|
||||||
|
metadata: {
|
||||||
|
[c.word(row.metadata)]: c.words(row.metadata),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: row,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
subset: {
|
||||||
|
enabled: true,
|
||||||
|
version: "3",
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
table: "public.users",
|
||||||
|
rowLimit: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
keepDisconnectedTables: false,
|
||||||
|
followNullableRelations: true,
|
||||||
|
maxCyclesLoop: 0,
|
||||||
|
eager: false,
|
||||||
|
taskSortAlgorithm: "children",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default config;
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Contact: security@cal.com
|
||||||
|
Preferred-Languages: en
|
||||||
|
Canonical: https://cal.com/.well-known/security.txt
|
||||||
|
Policy: https://github.com/calcom/cal.com/security/policy
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
diff --git a/plugin/timezone.js b/plugin/timezone.js
|
||||||
|
index fb6112a96f03f53ba78162ac323dce17f635161d..991e4e5410a32f5a69cb6e0e9476a9c529657aae 100644
|
||||||
|
--- a/plugin/timezone.js
|
||||||
|
+++ b/plugin/timezone.js
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_timezone=e()}(this,(function(){"use strict";var t={year:0,month:1,day:2,hour:3,minute:4,second:5},e={};return function(n,i,o){var r,a=function(t,n,i){void 0===i&&(i={});var o=new Date(t),r=function(t,n){void 0===n&&(n={});var i=n.timeZoneName||"short",o=t+"|"+i,r=e[o];return r||(r=new Intl.DateTimeFormat("en-US",{hour12:!1,timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:i}),e[o]=r),r}(n,i);return r.formatToParts(o)},u=function(e,n){for(var i=a(e,n),r=[],u=0;u<i.length;u+=1){var f=i[u],s=f.type,m=f.value,c=t[s];c>=0&&(r[c]=parseInt(m,10))}var d=r[3],l=24===d?0:d,v=r[0]+"-"+r[1]+"-"+r[2]+" "+l+":"+r[4]+":"+r[5]+":000",h=+e;return(o.utc(v).valueOf()-(h-=h%1e3))/6e4},f=i.prototype;f.tz=function(t,e){void 0===t&&(t=r);var n=this.utcOffset(),i=this.toDate(),a=i.toLocaleString("en-US",{timeZone:t}),u=Math.round((i-new Date(a))/1e3/60),f=o(a).$set("millisecond",this.$ms).utcOffset(15*-Math.round(i.getTimezoneOffset()/15)-u,!0);if(e){var s=f.utcOffset();f=f.add(n-s,"minute")}return f.$x.$timezone=t,f},f.offsetName=function(t){var e=this.$x.$timezone||o.tz.guess(),n=a(this.valueOf(),e,{timeZoneName:t}).find((function(t){return"timezonename"===t.type.toLowerCase()}));return n&&n.value};var s=f.startOf;f.startOf=function(t,e){if(!this.$x||!this.$x.$timezone)return s.call(this,t,e);var n=o(this.format("YYYY-MM-DD HH:mm:ss:SSS"));return s.call(n,t,e).tz(this.$x.$timezone,!0)},o.tz=function(t,e,n){var i=n&&e,a=n||e||r,f=u(+o(),a);if("string"!=typeof t)return o(t).tz(a);var s=function(t,e,n){var i=t-60*e*1e3,o=u(i,n);if(e===o)return[i,e];var r=u(i-=60*(o-e)*1e3,n);return o===r?[i,o]:[t-60*Math.min(o,r)*1e3,Math.max(o,r)]}(o.utc(t,i).valueOf(),f,a),m=s[0],c=s[1],d=o(m).utcOffset(c);return d.$x.$timezone=a,d},o.tz.guess=function(){return Intl.DateTimeFormat().resolvedOptions().timeZone},o.tz.setDefault=function(t){r=t}}}));
|
||||||
|
\ No newline at end of file
|
||||||
|
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_timezone=e()}(this,(function(){"use strict";function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t},t.apply(this,arguments)}var e={year:0,month:1,day:2,hour:3,minute:4,second:5},n={year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"},i={},r={};return function(o,a,u){var f,s=function(e,r,o){void 0===o&&(o={});var a=new Date(e),u=function(e,r){void 0===r&&(r={});var o=r.timeZoneName||"short",a=e+"|"+o,u=i[a];return u||(u=new Intl.DateTimeFormat("en-US",t({},n,{hour12:!1,timeZone:e,timeZoneName:o})),i[a]=u),u}(r,o);return u.formatToParts(a)},m=function(t,n){for(var i=s(t,n),r=[],o=0;o<i.length;o+=1){var a=i[o],f=a.type,m=a.value,c=e[f];c>=0&&(r[c]=parseInt(m,10))}var l=r[3],d=24===l?0:l,h=r[0]+"-"+r[1]+"-"+r[2]+" "+d+":"+r[4]+":"+r[5]+":000",v=+t;return(u.utc(h).valueOf()-(v-=v%1e3))/6e4},c=a.prototype;c.tz=function(e,i){void 0===e&&(e=f);var o=this.utcOffset(),a=this.toDate(),s=function(e){var i=r[e];return i||(i=new Intl.DateTimeFormat("en-US",t({},n,{timeZone:e})),r[e]=i),i}(e).format(a),m=Math.round((a-new Date(s))/1e3/60),c=u(s,{locale:this.$L}).$set("millisecond",this.$ms).utcOffset(15*-Math.round(a.getTimezoneOffset()/15)-m,!0);if(i){var l=c.utcOffset();c=c.add(o-l,"minute")}return c.$x.$timezone=e,c},c.offsetName=function(t){var e=this.$x.$timezone||u.tz.guess(),n=s(this.valueOf(),e,{timeZoneName:t}).find((function(t){return"timezonename"===t.type.toLowerCase()}));return n&&n.value};var l=c.startOf;c.startOf=function(t,e){if(!this.$x||!this.$x.$timezone)return l.call(this,t,e);var n=u(this.format("YYYY-MM-DD HH:mm:ss:SSS"),{locale:this.$L});return l.call(n,t,e).tz(this.$x.$timezone,!0)},u.tz=function(t,e,n){var i=n&&e,r=n||e||f,o=m(+u(),r);if("string"!=typeof t)return u(t).tz(r);var a=function(t,e,n){var i=t-60*e*1e3,r=m(i,n);if(e===r)return[i,e];var o=m(i-=60*(r-e)*1e3,n);return r===o?[i,r]:[t-60*Math.min(r,o)*1e3,Math.max(r,o)]}(u.utc(t,i).valueOf(),o,r),s=a[0],c=a[1],l=u(s).utcOffset(c);return l.$x.$timezone=r,l},u.tz.guess=function(){return Intl.DateTimeFormat().resolvedOptions().timeZone},u.tz.setDefault=function(t){f=t}}}));
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
diff --git a/plugin/timezone.js b/plugin/timezone.js
|
||||||
|
index fb6112a96f03f53ba78162ac323dce17f635161d..991e4e5410a32f5a69cb6e0e9476a9c529657aae 100644
|
||||||
|
--- a/plugin/timezone.js
|
||||||
|
+++ b/plugin/timezone.js
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_timezone=e()}(this,(function(){"use strict";var t={year:0,month:1,day:2,hour:3,minute:4,second:5},e={};return function(n,i,o){var r,a=function(t,n,i){void 0===i&&(i={});var o=new Date(t),r=function(t,n){void 0===n&&(n={});var i=n.timeZoneName||"short",o=t+"|"+i,r=e[o];return r||(r=new Intl.DateTimeFormat("en-US",{hour12:!1,timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:i}),e[o]=r),r}(n,i);return r.formatToParts(o)},u=function(e,n){for(var i=a(e,n),r=[],u=0;u<i.length;u+=1){var f=i[u],s=f.type,m=f.value,c=t[s];c>=0&&(r[c]=parseInt(m,10))}var d=r[3],l=24===d?0:d,v=r[0]+"-"+r[1]+"-"+r[2]+" "+l+":"+r[4]+":"+r[5]+":000",h=+e;return(o.utc(v).valueOf()-(h-=h%1e3))/6e4},f=i.prototype;f.tz=function(t,e){void 0===t&&(t=r);var n=this.utcOffset(),i=this.toDate(),a=i.toLocaleString("en-US",{timeZone:t}),u=Math.round((i-new Date(a))/1e3/60),f=o(a).$set("millisecond",this.$ms).utcOffset(15*-Math.round(i.getTimezoneOffset()/15)-u,!0);if(e){var s=f.utcOffset();f=f.add(n-s,"minute")}return f.$x.$timezone=t,f},f.offsetName=function(t){var e=this.$x.$timezone||o.tz.guess(),n=a(this.valueOf(),e,{timeZoneName:t}).find((function(t){return"timezonename"===t.type.toLowerCase()}));return n&&n.value};var s=f.startOf;f.startOf=function(t,e){if(!this.$x||!this.$x.$timezone)return s.call(this,t,e);var n=o(this.format("YYYY-MM-DD HH:mm:ss:SSS"));return s.call(n,t,e).tz(this.$x.$timezone,!0)},o.tz=function(t,e,n){var i=n&&e,a=n||e||r,f=u(+o(),a);if("string"!=typeof t)return o(t).tz(a);var s=function(t,e,n){var i=t-60*e*1e3,o=u(i,n);if(e===o)return[i,e];var r=u(i-=60*(o-e)*1e3,n);return o===r?[i,o]:[t-60*Math.min(o,r)*1e3,Math.max(o,r)]}(o.utc(t,i).valueOf(),f,a),m=s[0],c=s[1],d=o(m).utcOffset(c);return d.$x.$timezone=a,d},o.tz.guess=function(){return Intl.DateTimeFormat().resolvedOptions().timeZone},o.tz.setDefault=function(t){r=t}}}));
|
||||||
|
\ No newline at end of file
|
||||||
|
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_timezone=e()}(this,(function(){"use strict";function t(){return t=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t},t.apply(this,arguments)}var e={year:0,month:1,day:2,hour:3,minute:4,second:5},n={year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"},i={},r={};return function(o,a,u){var f,s=function(e,r,o){void 0===o&&(o={});var a=new Date(e),u=function(e,r){void 0===r&&(r={});var o=r.timeZoneName||"short",a=e+"|"+o,u=i[a];return u||(u=new Intl.DateTimeFormat("en-US",t({},n,{hour12:!1,timeZone:e,timeZoneName:o})),i[a]=u),u}(r,o);return u.formatToParts(a)},m=function(t,n){for(var i=s(t,n),r=[],o=0;o<i.length;o+=1){var a=i[o],f=a.type,m=a.value,c=e[f];c>=0&&(r[c]=parseInt(m,10))}var l=r[3],d=24===l?0:l,h=r[0]+"-"+r[1]+"-"+r[2]+" "+d+":"+r[4]+":"+r[5]+":000",v=+t;return(u.utc(h).valueOf()-(v-=v%1e3))/6e4},c=a.prototype;c.tz=function(e,i){void 0===e&&(e=f);var o=this.utcOffset(),a=this.toDate(),s=function(e){var i=r[e];return i||(i=new Intl.DateTimeFormat("en-US",t({},n,{timeZone:e})),r[e]=i),i}(e).format(a),m=Math.round((a-new Date(s))/1e3/60),c=u(s,{locale:this.$L}).$set("millisecond",this.$ms).utcOffset(15*-Math.round(a.getTimezoneOffset()/15)-m,!0);if(i){var l=c.utcOffset();c=c.add(o-l,"minute")}return c.$x.$timezone=e,c},c.offsetName=function(t){var e=this.$x.$timezone||u.tz.guess(),n=s(this.valueOf(),e,{timeZoneName:t}).find((function(t){return"timezonename"===t.type.toLowerCase()}));return n&&n.value};var l=c.startOf;c.startOf=function(t,e){if(!this.$x||!this.$x.$timezone)return l.call(this,t,e);var n=u(this.format("YYYY-MM-DD HH:mm:ss:SSS"),{locale:this.$L});return l.call(n,t,e).tz(this.$x.$timezone,!0)},u.tz=function(t,e,n){var i=n&&e,r=n||e||f,o=m(+u(),r);if("string"!=typeof t)return u(t).tz(r);var a=function(t,e,n){var i=t-60*e*1e3,r=m(i,n);if(e===r)return[i,e];var o=m(i-=60*(r-e)*1e3,n);return r===o?[i,r]:[t-60*Math.min(r,o)*1e3,Math.max(r,o)]}(u.utc(t,i).valueOf(),o,r),s=a[0],c=a[1],l=u(s).utcOffset(c);return l.$x.$timezone=r,l},u.tz.guess=function(){return Intl.DateTimeFormat().resolvedOptions().timeZone},u.tz.setDefault=function(t){f=t}}}));
|
||||||
@@ -2,7 +2,8 @@ Copyright (c) 2020-present Cal.com, Inc.
|
|||||||
|
|
||||||
Portions of this software are licensed as follows:
|
Portions of this software are licensed as follows:
|
||||||
|
|
||||||
* All content that resides under https://github.com/calcom/cal.com/tree/main/packages/features/ee and https://github.com/calcom/cal.com/tree/main/apps/api/ directory of this repository (Commercial License) is licensed under the license defined in "ee/LICENSE".
|
* All content that resides under https://github.com/calcom/cal.com/tree/main/packages/features/ee and
|
||||||
|
https://github.com/calcom/cal.com/tree/main/apps/api/v2/src/ee directory of this repository (Commercial License) is licensed under the license defined in "ee/LICENSE".
|
||||||
* All third party components incorporated into the Cal.com Software are licensed under the original license provided by the owner of the applicable component.
|
* All third party components incorporated into the Cal.com Software are licensed under the original license provided by the owner of the applicable component.
|
||||||
* Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.
|
* Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ test.describe("Org", () => {
|
|||||||
const response = await page.goto("https://i.cal.com/embed");
|
const response = await page.goto("https://i.cal.com/embed");
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
await page.screenshot({ path: "screenshot.jpg" });
|
await page.screenshot({ path: "screenshot.jpg" });
|
||||||
await expectPageToBeServerSideRendered(page);
|
await expectPageToBeRenderedWithEmbedSsr(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Org User(Peer) Page should be embeddable", async ({ page }) => {
|
test("Org User(Peer) Page should be embeddable", async ({ page }) => {
|
||||||
const response = await page.goto("https://i.cal.com/peer/embed");
|
const response = await page.goto("https://i.cal.com/peer/embed");
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
await expect(page.locator("text=Peer Richelsen")).toBeVisible();
|
await expect(page.locator("text=Peer Richelsen")).toBeVisible();
|
||||||
await expectPageToBeServerSideRendered(page);
|
await expectPageToBeRenderedWithEmbedSsr(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Org User Event(peer/meet) Page should be embeddable", async ({ page }) => {
|
test("Org User Event(peer/meet) Page should be embeddable", async ({ page }) => {
|
||||||
@@ -23,14 +23,14 @@ test.describe("Org", () => {
|
|||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
await expect(page.locator('[data-testid="decrementMonth"]')).toBeVisible();
|
await expect(page.locator('[data-testid="decrementMonth"]')).toBeVisible();
|
||||||
await expect(page.locator('[data-testid="incrementMonth"]')).toBeVisible();
|
await expect(page.locator('[data-testid="incrementMonth"]')).toBeVisible();
|
||||||
await expectPageToBeServerSideRendered(page);
|
await expectPageToBeRenderedWithEmbedSsr(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Org Team Profile(/sales) page should be embeddable", async ({ page }) => {
|
test("Org Team Profile(/sales) page should be embeddable", async ({ page }) => {
|
||||||
const response = await page.goto("https://i.cal.com/sales/embed");
|
const response = await page.goto("https://i.cal.com/sales/embed");
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
await expect(page.locator("text=Cal.com Sales")).toBeVisible();
|
await expect(page.locator("text=Cal.com Sales")).toBeVisible();
|
||||||
await expectPageToBeServerSideRendered(page);
|
await expectPageToBeRenderedWithEmbedSsr(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Org Team Event page(/sales/hippa) should be embeddable", async ({ page }) => {
|
test("Org Team Event page(/sales/hippa) should be embeddable", async ({ page }) => {
|
||||||
@@ -38,9 +38,10 @@ test.describe("Org", () => {
|
|||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
await expect(page.locator('[data-testid="decrementMonth"]')).toBeVisible();
|
await expect(page.locator('[data-testid="decrementMonth"]')).toBeVisible();
|
||||||
await expect(page.locator('[data-testid="incrementMonth"]')).toBeVisible();
|
await expect(page.locator('[data-testid="incrementMonth"]')).toBeVisible();
|
||||||
await expectPageToBeServerSideRendered(page);
|
await expectPageToBeRenderedWithEmbedSsr(page);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Dynamic Group Booking", () => {
|
test.describe("Dynamic Group Booking", () => {
|
||||||
test("Dynamic Group booking link should load", async ({ page }) => {
|
test("Dynamic Group booking link should load", async ({ page }) => {
|
||||||
const users = [
|
const users = [
|
||||||
@@ -63,12 +64,39 @@ test.describe("Org", () => {
|
|||||||
expect((await page.locator('[data-testid="event-meta"] [data-testid="avatar"]').all()).length).toBe(3);
|
expect((await page.locator('[data-testid="event-meta"] [data-testid="avatar"]').all()).length).toBe(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Organization Homepage - Has Engineering and Marketing Teams", async ({ page }) => {
|
||||||
|
const response = await page.goto("https://i.cal.com");
|
||||||
|
expect(response?.status()).toBe(200);
|
||||||
|
await expect(page.locator("text=Cal.com")).toBeVisible();
|
||||||
|
await expect(page.locator("text=Engineering")).toBeVisible();
|
||||||
|
await expect(page.locator("text=Marketing")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe("Browse the Engineering Team", async () => {
|
||||||
|
test("By User Navigation", async ({ page }) => {
|
||||||
|
await page.goto("https://i.cal.com");
|
||||||
|
await page.click('text="Engineering"');
|
||||||
|
await expect(page.locator("text=Cal.com Engineering")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("By /team/engineering", async ({ page }) => {
|
||||||
|
await page.goto("https://i.cal.com/team/engineering");
|
||||||
|
await expect(page.locator("text=Cal.com Engineering")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("By /engineering", async ({ page }) => {
|
||||||
|
await page.goto("https://i.cal.com/engineering");
|
||||||
|
await expect(page.locator("text=Cal.com Engineering")).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// This ensures that the route is actually mapped to a page that is using withEmbedSsr
|
// This ensures that the route is actually mapped to a page that is using withEmbedSsr
|
||||||
async function expectPageToBeServerSideRendered(page: Page) {
|
async function expectPageToBeRenderedWithEmbedSsr(page: Page) {
|
||||||
expect(
|
expect(
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
|
//@ts-expect-error - __NEXT_DATA__ is a global variable defined by Next.js
|
||||||
return window.__NEXT_DATA__.props.pageProps.isEmbed;
|
return window.__NEXT_DATA__.props.pageProps.isEmbed;
|
||||||
})
|
})
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
"author": "Cal.com Inc.",
|
"author": "Cal.com Inc.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@calcom/prisma": "*",
|
"@calcom/prisma": "*",
|
||||||
|
"@langchain/core": "^0.1.26",
|
||||||
|
"@langchain/openai": "^0.0.14",
|
||||||
"@t3-oss/env-nextjs": "^0.6.1",
|
"@t3-oss/env-nextjs": "^0.6.1",
|
||||||
"langchain": "^0.0.131",
|
"langchain": "^0.1.17",
|
||||||
"mailparser": "^3.6.5",
|
"mailparser": "^3.6.5",
|
||||||
"next": "^13.5.4",
|
"next": "^13.5.4",
|
||||||
"supports-color": "8.1.1",
|
"supports-color": "8.1.1",
|
||||||
"zod": "^3.22.2"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mailparser": "^3.4.0"
|
"@types/mailparser": "^3.4.0"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DynamicStructuredTool } from "langchain/tools";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import type { UserList } from "~/src/types/user";
|
import type { UserList } from "~/src/types/user";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DynamicStructuredTool } from "langchain/tools";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { env } from "../env.mjs";
|
import { env } from "../env.mjs";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DynamicStructuredTool } from "langchain/tools";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { env } from "../env.mjs";
|
import { env } from "../env.mjs";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DynamicStructuredTool } from "langchain/tools";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { env } from "../env.mjs";
|
import { env } from "../env.mjs";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DynamicStructuredTool } from "langchain/tools";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { env } from "../env.mjs";
|
import { env } from "../env.mjs";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DynamicStructuredTool } from "langchain/tools";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { env } from "~/src/env.mjs";
|
import { env } from "~/src/env.mjs";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DynamicStructuredTool } from "langchain/tools";
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { env } from "../env.mjs";
|
import { env } from "../env.mjs";
|
||||||
|
|||||||
+65
-59
@@ -1,5 +1,6 @@
|
|||||||
import { initializeAgentExecutorWithOptions } from "langchain/agents";
|
import { ChatPromptTemplate } from "@langchain/core/prompts";
|
||||||
import { ChatOpenAI } from "langchain/chat_models/openai";
|
import { ChatOpenAI } from "@langchain/openai";
|
||||||
|
import { AgentExecutor, createOpenAIFunctionsAgent } from "langchain/agents";
|
||||||
|
|
||||||
import { env } from "../env.mjs";
|
import { env } from "../env.mjs";
|
||||||
import createBookingIfAvailable from "../tools/createBooking";
|
import createBookingIfAvailable from "../tools/createBooking";
|
||||||
@@ -13,7 +14,7 @@ import type { User, UserList } from "../types/user";
|
|||||||
import type { WorkingHours } from "../types/workingHours";
|
import type { WorkingHours } from "../types/workingHours";
|
||||||
import now from "./now";
|
import now from "./now";
|
||||||
|
|
||||||
const gptModel = "gpt-4-0613";
|
const gptModel = "gpt-4-0125-preview";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core of the Cal.ai booking agent: a LangChain Agent Executor.
|
* Core of the Cal.ai booking agent: a LangChain Agent Executor.
|
||||||
@@ -47,67 +48,72 @@ const agent = async (
|
|||||||
/**
|
/**
|
||||||
* Initialize the agent executor with arguments.
|
* Initialize the agent executor with arguments.
|
||||||
*/
|
*/
|
||||||
const executor = await initializeAgentExecutorWithOptions(tools, model, {
|
const prompt =
|
||||||
agentArgs: {
|
ChatPromptTemplate.fromTemplate(`You are Cal.ai - a bleeding edge scheduling assistant that interfaces via email.
|
||||||
prefix: `You are Cal.ai - a bleeding edge scheduling assistant that interfaces via email.
|
Make sure your final answers are definitive, complete and well formatted.
|
||||||
Make sure your final answers are definitive, complete and well formatted.
|
Sometimes, tools return errors. In this case, try to handle the error intelligently or ask the user for more information.
|
||||||
Sometimes, tools return errors. In this case, try to handle the error intelligently or ask the user for more information.
|
Tools will always handle times in UTC, but times sent to users should be formatted per that user's timezone.
|
||||||
Tools will always handle times in UTC, but times sent to users should be formatted per that user's timezone.
|
In responses to users, always summarize necessary context and open the door to follow ups. For example "I have booked your chat with @username for 3pm on Wednesday, December 20th, 2023 EST. Please let me know if you need to reschedule."
|
||||||
In responses to users, always summarize necessary context and open the door to follow ups. For example "I have booked your chat with @username for 3pm on Wednesday, December 20th, 2023 EST. Please let me know if you need to reschedule."
|
If you can't find a referenced user, ask the user for their email or @username. Make sure to specify that usernames require the @username format. Users don't know other users' userIds.
|
||||||
If you can't find a referenced user, ask the user for their email or @username. Make sure to specify that usernames require the @username format. Users don't know other users' userIds.
|
|
||||||
|
|
||||||
The primary user's id is: ${userId}
|
The primary user's id is: ${userId}
|
||||||
The primary user's username is: ${user.username}
|
The primary user's username is: ${user.username}
|
||||||
The current time in the primary user's timezone is: ${now(user.timeZone, {
|
The current time in the primary user's timezone is: ${now(user.timeZone, {
|
||||||
weekday: "long",
|
weekday: "long",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "numeric",
|
minute: "numeric",
|
||||||
})}
|
})}
|
||||||
The primary user's time zone is: ${user.timeZone}
|
The primary user's time zone is: ${user.timeZone}
|
||||||
The primary user's event types are: ${user.eventTypes
|
The primary user's event types are: ${user.eventTypes
|
||||||
.map((e: EventType) => `ID: ${e.id}, Slug: ${e.slug}, Title: ${e.title}, Length: ${e.length};`)
|
.map((e: EventType) => `ID: ${e.id}, Slug: ${e.slug}, Title: ${e.title}, Length: ${e.length};`)
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
The primary user's working hours are: ${user.workingHours
|
The primary user's working hours are: ${user.workingHours
|
||||||
.map(
|
.map(
|
||||||
(w: WorkingHours) =>
|
(w: WorkingHours) =>
|
||||||
`Days: ${w.days.join(", ")}, Start Time (minutes in UTC): ${
|
`Days: ${w.days.join(", ")}, Start Time (minutes in UTC): ${
|
||||||
w.startTime
|
w.startTime
|
||||||
}, End Time (minutes in UTC): ${w.endTime};`
|
}, End Time (minutes in UTC): ${w.endTime};`
|
||||||
)
|
)
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
${
|
${
|
||||||
users.length
|
users.length
|
||||||
? `The email references the following @usernames and emails: ${users
|
? `The email references the following @usernames and emails: ${users
|
||||||
.map(
|
.map(
|
||||||
(u) =>
|
(u) =>
|
||||||
`${
|
`${
|
||||||
(u.id ? `, id: ${u.id}` : "id: (non user)") +
|
(u.id ? `, id: ${u.id}` : "id: (non user)") +
|
||||||
(u.username
|
(u.username
|
||||||
? u.type === "fromUsername"
|
? u.type === "fromUsername"
|
||||||
? `, username: @${u.username}`
|
? `, username: @${u.username}`
|
||||||
: ", username: REDACTED"
|
: ", username: REDACTED"
|
||||||
: ", (no username)") +
|
: ", (no username)") +
|
||||||
(u.email
|
(u.email
|
||||||
? u.type === "fromEmail"
|
? u.type === "fromEmail"
|
||||||
? `, email: ${u.email}`
|
? `, email: ${u.email}`
|
||||||
: ", email: REDACTED"
|
: ", email: REDACTED"
|
||||||
: ", (no email)")
|
: ", (no email)")
|
||||||
};`
|
};`
|
||||||
)
|
)
|
||||||
.join("\n")}`
|
.join("\n")}`
|
||||||
: ""
|
: ""
|
||||||
}
|
}`);
|
||||||
`,
|
const agent = await createOpenAIFunctionsAgent({
|
||||||
},
|
llm: model,
|
||||||
agentType: "openai-functions",
|
prompt,
|
||||||
|
tools,
|
||||||
|
});
|
||||||
|
|
||||||
|
const executor = new AgentExecutor({
|
||||||
|
agent,
|
||||||
|
tools,
|
||||||
returnIntermediateSteps: env.NODE_ENV === "development",
|
returnIntermediateSteps: env.NODE_ENV === "development",
|
||||||
verbose: env.NODE_ENV === "development",
|
verbose: env.NODE_ENV === "development",
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await executor.call({ input });
|
const result = await executor.invoke({ input });
|
||||||
const { output } = result;
|
const { output } = result;
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
"../../packages/types/window.d.ts"
|
||||||
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const http = require("http");
|
||||||
|
const connect = require("connect");
|
||||||
|
const { createProxyMiddleware } = require("http-proxy-middleware");
|
||||||
|
|
||||||
|
const apiProxyV1 = createProxyMiddleware({
|
||||||
|
target: "http://localhost:3003",
|
||||||
|
});
|
||||||
|
|
||||||
|
const apiProxyV2 = createProxyMiddleware({
|
||||||
|
target: "http://localhost:3004",
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = connect();
|
||||||
|
app.use("/", apiProxyV1);
|
||||||
|
|
||||||
|
app.use("/v2", apiProxyV2);
|
||||||
|
|
||||||
|
http.createServer(app).listen(3002);
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import type { NextMiddleware } from "next-api-middleware";
|
|
||||||
|
|
||||||
import { CONSOLE_URL } from "@calcom/lib/constants";
|
|
||||||
import { customPrisma } from "@calcom/prisma";
|
|
||||||
|
|
||||||
const LOCAL_CONSOLE_URL = process.env.NEXT_PUBLIC_CONSOLE_URL || CONSOLE_URL;
|
|
||||||
|
|
||||||
// This replaces the prisma client for the custom one if the key is valid
|
|
||||||
export const customPrismaClient: NextMiddleware = async (req, res, next) => {
|
|
||||||
const {
|
|
||||||
query: { key },
|
|
||||||
} = req;
|
|
||||||
// If no custom api Id is provided, attach to request the regular cal.com prisma client.
|
|
||||||
if (!key) {
|
|
||||||
req.prisma = customPrisma();
|
|
||||||
await next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a key, we check if the deployment matching the key, has a databaseUrl value set.
|
|
||||||
const databaseUrl = await fetch(`${LOCAL_CONSOLE_URL}/api/deployments/database?key=${key}`)
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((res) => res.databaseUrl);
|
|
||||||
|
|
||||||
if (!databaseUrl) {
|
|
||||||
res.status(400).json({ error: "no databaseUrl set up at your instance yet" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
req.prisma = customPrisma({ datasources: { db: { url: databaseUrl } } });
|
|
||||||
/* @note:
|
|
||||||
In order to skip verifyApiKey for customPrisma requests,
|
|
||||||
we pass isAdmin true, and userId 0, if we detect them later,
|
|
||||||
we skip verifyApiKey logic and pass onto next middleware instead.
|
|
||||||
*/
|
|
||||||
req.isAdmin = true;
|
|
||||||
req.isCustomPrisma = true;
|
|
||||||
// We don't need the key from here and on. Prevents unrecognized key errors.
|
|
||||||
delete req.query.key;
|
|
||||||
await next();
|
|
||||||
await req.prisma.$disconnect();
|
|
||||||
// @ts-expect-error testing
|
|
||||||
delete req.prisma;
|
|
||||||
};
|
|
||||||
+9
-39
@@ -1,46 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@calcom/api",
|
"name": "@calcom/api-proxy",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Public API for Cal.com",
|
"description": "",
|
||||||
"main": "index.ts",
|
"main": "index.js",
|
||||||
"repository": "git@github.com:calcom/api.git",
|
|
||||||
"author": "Cal.com Inc.",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "next build",
|
"dev": "node ./index.js"
|
||||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
|
||||||
"dev": "PORT=3002 next dev",
|
|
||||||
"lint": "eslint . --ignore-path .gitignore",
|
|
||||||
"lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
|
|
||||||
"start": "PORT=3002 next start",
|
|
||||||
"docker-start-api": "PORT=80 next start",
|
|
||||||
"type-check": "tsc --pretty --noEmit"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@calcom/tsconfig": "*",
|
|
||||||
"@calcom/types": "*",
|
|
||||||
"node-mocks-http": "^1.11.0"
|
|
||||||
},
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@calcom/app-store": "*",
|
"connect": "^3.7.0",
|
||||||
"@calcom/core": "*",
|
"http": "^0.0.1-security",
|
||||||
"@calcom/dayjs": "*",
|
"http-proxy-middleware": "^2.0.6"
|
||||||
"@calcom/emails": "*",
|
|
||||||
"@calcom/features": "*",
|
|
||||||
"@calcom/lib": "*",
|
|
||||||
"@calcom/prisma": "*",
|
|
||||||
"@calcom/trpc": "*",
|
|
||||||
"@sentry/nextjs": "^7.73.0",
|
|
||||||
"bcryptjs": "^2.4.3",
|
|
||||||
"memory-cache": "^0.2.0",
|
|
||||||
"next": "^13.5.4",
|
|
||||||
"next-api-middleware": "^1.0.1",
|
|
||||||
"next-axiom": "^0.17.0",
|
|
||||||
"next-swagger-doc": "^0.3.6",
|
|
||||||
"next-validations": "^0.2.0",
|
|
||||||
"typescript": "^4.9.4",
|
|
||||||
"tzdata": "^1.0.30",
|
|
||||||
"uuid": "^8.3.2",
|
|
||||||
"zod": "^3.22.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import * as Sentry from "@sentry/nextjs";
|
|
||||||
|
|
||||||
Sentry.init({
|
|
||||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
||||||
});
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { describe, vi, it, expect, afterEach } from "vitest";
|
|
||||||
|
|
||||||
import { middlewareOrder } from "../../../lib/helpers/withMiddleware";
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.resetAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Not sure if there is much point testing this order is actually applied via an integration test:
|
|
||||||
// It is tested internally https://github.com/htunnicliff/next-api-middleware/blob/368b12aa30e79f4bd7cfe7aacc18da263cc3de2f/lib/label.spec.ts#L62
|
|
||||||
describe("API - withMiddleware test", () => {
|
|
||||||
it("Custom prisma should be before verifyApiKey", async () => {
|
|
||||||
const customPrismaClientIndex = middlewareOrder.indexOf("customPrismaClient");
|
|
||||||
const verifyApiKeyIndex = middlewareOrder.indexOf("verifyApiKey");
|
|
||||||
expect(customPrismaClientIndex).toBeLessThan(verifyApiKeyIndex);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
|||||||
import * as Sentry from "@sentry/nextjs";
|
import { captureException as SentryCaptureException } from "@sentry/nextjs";
|
||||||
import type { NextMiddleware } from "next-api-middleware";
|
import type { NextMiddleware } from "next-api-middleware";
|
||||||
|
|
||||||
import { redactError } from "@calcom/lib/redactError";
|
import { redactError } from "@calcom/lib/redactError";
|
||||||
@@ -9,7 +9,7 @@ export const captureErrors: NextMiddleware = async (_req, res, next) => {
|
|||||||
// middleware and the API route handler
|
// middleware and the API route handler
|
||||||
await next();
|
await next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Sentry.captureException(error);
|
SentryCaptureException(error);
|
||||||
const redactedError = redactError(error);
|
const redactedError = redactError(error);
|
||||||
if (redactedError instanceof Error) {
|
if (redactedError instanceof Error) {
|
||||||
res.status(400).json({ message: redactedError.message, error: redactedError });
|
res.status(400).json({ message: redactedError.message, error: redactedError });
|
||||||
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,7 @@
|
|||||||
import type { NextMiddleware } from "next-api-middleware";
|
import type { NextMiddleware } from "next-api-middleware";
|
||||||
|
|
||||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||||
|
import { API_KEY_RATE_LIMIT } from "@calcom/lib/rateLimit";
|
||||||
|
|
||||||
export const rateLimitApiKey: NextMiddleware = async (req, res, next) => {
|
export const rateLimitApiKey: NextMiddleware = async (req, res, next) => {
|
||||||
if (!req.query.apiKey) return res.status(401).json({ message: "No apiKey provided" });
|
if (!req.query.apiKey) return res.status(401).json({ message: "No apiKey provided" });
|
||||||
@@ -9,6 +10,11 @@ export const rateLimitApiKey: NextMiddleware = async (req, res, next) => {
|
|||||||
await checkRateLimitAndThrowError({
|
await checkRateLimitAndThrowError({
|
||||||
identifier: req.query.apiKey as string,
|
identifier: req.query.apiKey as string,
|
||||||
rateLimitingType: "api",
|
rateLimitingType: "api",
|
||||||
|
onRateLimiterResponse: (response) => {
|
||||||
|
res.setHeader("X-RateLimit-Limit", API_KEY_RATE_LIMIT);
|
||||||
|
res.setHeader("X-RateLimit-Remaining", response.remaining);
|
||||||
|
res.setHeader("X-RateLimit-Reset", response.reset);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
File renamed without changes.
@@ -3,6 +3,7 @@ import type { NextMiddleware } from "next-api-middleware";
|
|||||||
import { hashAPIKey } from "@calcom/features/ee/api-keys/lib/apiKeys";
|
import { hashAPIKey } from "@calcom/features/ee/api-keys/lib/apiKeys";
|
||||||
import checkLicense from "@calcom/features/ee/common/server/checkLicense";
|
import checkLicense from "@calcom/features/ee/common/server/checkLicense";
|
||||||
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { isAdminGuard } from "../utils/isAdmin";
|
import { isAdminGuard } from "../utils/isAdmin";
|
||||||
|
|
||||||
@@ -16,15 +17,9 @@ export const dateNotInPast = function (date: Date) {
|
|||||||
|
|
||||||
// This verifies the apiKey and sets the user if it is valid.
|
// This verifies the apiKey and sets the user if it is valid.
|
||||||
export const verifyApiKey: NextMiddleware = async (req, res, next) => {
|
export const verifyApiKey: NextMiddleware = async (req, res, next) => {
|
||||||
const { prisma, isCustomPrisma, isAdmin } = req;
|
|
||||||
const hasValidLicense = await checkLicense(prisma);
|
const hasValidLicense = await checkLicense(prisma);
|
||||||
if (!hasValidLicense && IS_PRODUCTION)
|
if (!hasValidLicense && IS_PRODUCTION)
|
||||||
return res.status(401).json({ error: "Invalid or missing CALCOM_LICENSE_KEY environment variable" });
|
return res.status(401).json({ error: "Invalid or missing CALCOM_LICENSE_KEY environment variable" });
|
||||||
// If the user is an admin and using a license key (from customPrisma), skip the apiKey check.
|
|
||||||
if (isCustomPrisma && isAdmin) {
|
|
||||||
await next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check if the apiKey query param is provided.
|
// Check if the apiKey query param is provided.
|
||||||
if (!req.query.apiKey) return res.status(401).json({ message: "No apiKey provided" });
|
if (!req.query.apiKey) return res.status(401).json({ message: "No apiKey provided" });
|
||||||
// remove the prefix from the user provided api_key. If no env set default to "cal_"
|
// remove the prefix from the user provided api_key. If no env set default to "cal_"
|
||||||
@@ -43,6 +38,5 @@ export const verifyApiKey: NextMiddleware = async (req, res, next) => {
|
|||||||
req.userId = apiKey.userId;
|
req.userId = apiKey.userId;
|
||||||
// save the isAdmin boolean here for later use
|
// save the isAdmin boolean here for later use
|
||||||
req.isAdmin = await isAdminGuard(req);
|
req.isAdmin = await isAdminGuard(req);
|
||||||
req.isCustomPrisma = false;
|
|
||||||
await next();
|
await next();
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import type { NextMiddleware } from "next-api-middleware";
|
||||||
|
|
||||||
|
import { APP_CREDENTIAL_SHARING_ENABLED } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
export const verifyCredentialSyncEnabled: NextMiddleware = async (req, res, next) => {
|
||||||
|
const { isAdmin } = req;
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return res.status(403).json({ error: "Only admin API keys can access credential syncing endpoints" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!APP_CREDENTIAL_SHARING_ENABLED) {
|
||||||
|
return res.status(501).json({ error: "Credential syncing is not enabled" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
req.headers[process.env.CALCOM_CREDENTIAL_SYNC_HEADER_NAME || "calcom-credential-sync-secret"] !==
|
||||||
|
process.env.CALCOM_CREDENTIAL_SYNC_SECRET
|
||||||
|
) {
|
||||||
|
return res.status(401).json({ message: "Invalid credential sync secret" });
|
||||||
|
}
|
||||||
|
|
||||||
|
await next();
|
||||||
|
};
|
||||||
@@ -2,7 +2,6 @@ import { label } from "next-api-middleware";
|
|||||||
|
|
||||||
import { addRequestId } from "./addRequestid";
|
import { addRequestId } from "./addRequestid";
|
||||||
import { captureErrors } from "./captureErrors";
|
import { captureErrors } from "./captureErrors";
|
||||||
import { customPrismaClient } from "./customPrisma";
|
|
||||||
import { extendRequest } from "./extendRequest";
|
import { extendRequest } from "./extendRequest";
|
||||||
import {
|
import {
|
||||||
HTTP_POST,
|
HTTP_POST,
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
} from "./httpMethods";
|
} from "./httpMethods";
|
||||||
import { rateLimitApiKey } from "./rateLimitApiKey";
|
import { rateLimitApiKey } from "./rateLimitApiKey";
|
||||||
import { verifyApiKey } from "./verifyApiKey";
|
import { verifyApiKey } from "./verifyApiKey";
|
||||||
|
import { verifyCredentialSyncEnabled } from "./verifyCredentialSyncEnabled";
|
||||||
import { withPagination } from "./withPagination";
|
import { withPagination } from "./withPagination";
|
||||||
|
|
||||||
const middleware = {
|
const middleware = {
|
||||||
@@ -26,25 +26,22 @@ const middleware = {
|
|||||||
addRequestId,
|
addRequestId,
|
||||||
verifyApiKey,
|
verifyApiKey,
|
||||||
rateLimitApiKey,
|
rateLimitApiKey,
|
||||||
customPrismaClient,
|
|
||||||
extendRequest,
|
extendRequest,
|
||||||
pagination: withPagination,
|
pagination: withPagination,
|
||||||
captureErrors,
|
captureErrors,
|
||||||
|
verifyCredentialSyncEnabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Middleware = keyof typeof middleware;
|
type Middleware = keyof typeof middleware;
|
||||||
|
|
||||||
const middlewareOrder =
|
const middlewareOrder = [
|
||||||
// The order here, determines the order of execution
|
// The order here, determines the order of execution
|
||||||
[
|
"extendRequest",
|
||||||
"extendRequest",
|
"captureErrors",
|
||||||
"captureErrors",
|
"verifyApiKey",
|
||||||
// - Put customPrismaClient before verifyApiKey always.
|
"rateLimitApiKey",
|
||||||
"customPrismaClient",
|
"addRequestId",
|
||||||
"verifyApiKey",
|
] as Middleware[]; // <-- Provide a list of middleware to call automatically
|
||||||
"rateLimitApiKey",
|
|
||||||
"addRequestId",
|
|
||||||
] as Middleware[]; // <-- Provide a list of middleware to call automatically
|
|
||||||
|
|
||||||
const withMiddleware = label(middleware, middlewareOrder);
|
const withMiddleware = label(middleware, middlewareOrder);
|
||||||
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,9 +1,10 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
import { UserPermissionRole } from "@calcom/prisma/enums";
|
import { UserPermissionRole } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
export const isAdminGuard = async (req: NextApiRequest) => {
|
export const isAdminGuard = async (req: NextApiRequest) => {
|
||||||
const { userId, prisma } = req;
|
const { userId } = req;
|
||||||
const user = await prisma.user.findUnique({ where: { id: userId } });
|
const user = await prisma.user.findUnique({ where: { id: userId } });
|
||||||
return user?.role === UserPermissionRole.ADMIN;
|
return user?.role === UserPermissionRole.ADMIN;
|
||||||
};
|
};
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,18 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const CalendarSchema = z.object({
|
||||||
|
externalId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
primary: z.boolean(),
|
||||||
|
readOnly: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const IntegrationSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
appId: z.string(),
|
||||||
|
userId: z.number(),
|
||||||
|
integration: z.string(),
|
||||||
|
calendars: z.array(CalendarSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaConnectedCalendarsReadPublic = z.array(IntegrationSchema);
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
|
|
||||||
|
const userId = z.string().transform((val) => {
|
||||||
|
const userIdInt = parseInt(val);
|
||||||
|
|
||||||
|
if (isNaN(userIdInt)) {
|
||||||
|
throw new HttpError({ message: "userId is not a valid number", statusCode: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return userIdInt;
|
||||||
|
});
|
||||||
|
const appSlug = z.string();
|
||||||
|
const credentialId = z.string().transform((val) => {
|
||||||
|
const credentialIdInt = parseInt(val);
|
||||||
|
|
||||||
|
if (isNaN(credentialIdInt)) {
|
||||||
|
throw new HttpError({ message: "credentialId is not a valid number", statusCode: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentialIdInt;
|
||||||
|
});
|
||||||
|
const encryptedKey = z.string();
|
||||||
|
|
||||||
|
export const schemaCredentialGetParams = z.object({
|
||||||
|
userId,
|
||||||
|
appSlug: appSlug.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaCredentialPostParams = z.object({
|
||||||
|
userId,
|
||||||
|
createSelectedCalendar: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => {
|
||||||
|
return val === "true";
|
||||||
|
}),
|
||||||
|
createDestinationCalendar: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => {
|
||||||
|
return val === "true";
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaCredentialPostBody = z.object({
|
||||||
|
appSlug,
|
||||||
|
encryptedKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaCredentialPatchParams = z.object({
|
||||||
|
userId,
|
||||||
|
credentialId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaCredentialPatchBody = z.object({
|
||||||
|
encryptedKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaCredentialDeleteParams = z.object({
|
||||||
|
userId,
|
||||||
|
credentialId,
|
||||||
|
});
|
||||||
File renamed without changes.
File renamed without changes.
@@ -62,6 +62,7 @@ export const schemaEventTypeBaseBodyParams = EventType.pick({
|
|||||||
bookingLimits: true,
|
bookingLimits: true,
|
||||||
onlyShowFirstAvailableSlot: true,
|
onlyShowFirstAvailableSlot: true,
|
||||||
durationLimits: true,
|
durationLimits: true,
|
||||||
|
assignAllTeamMembers: true,
|
||||||
})
|
})
|
||||||
.merge(
|
.merge(
|
||||||
z.object({
|
z.object({
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,7 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { baseApiParams } from "./baseApiParams";
|
||||||
|
|
||||||
|
export const schemaQuerySlug = baseApiParams.extend({
|
||||||
|
slug: z.string().optional(),
|
||||||
|
});
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import tzdata from "tzdata";
|
import tzdata from "tzdata";
|
||||||
import * as z from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
// @note: This is a custom validation that checks if the timezone is valid and exists in the tzdb library
|
// @note: This is a custom validation that checks if the timezone is valid and exists in the tzdb library
|
||||||
export const timeZone = z.string().refine((tz: string) => Object.keys(tzdata.zones).includes(tz), {
|
export const timeZone = z.string().refine((tz: string) => Object.keys(tzdata.zones).includes(tz), {
|
||||||
File renamed without changes.
@@ -74,6 +74,7 @@ export const schemaUserBaseBodyParams = User.pick({
|
|||||||
timeZone: true,
|
timeZone: true,
|
||||||
weekStart: true,
|
weekStart: true,
|
||||||
theme: true,
|
theme: true,
|
||||||
|
appTheme: true,
|
||||||
defaultScheduleId: true,
|
defaultScheduleId: true,
|
||||||
locale: true,
|
locale: true,
|
||||||
hideBranding: true,
|
hideBranding: true,
|
||||||
@@ -100,6 +101,7 @@ const schemaUserEditParams = z.object({
|
|||||||
hideBranding: z.boolean().optional(),
|
hideBranding: z.boolean().optional(),
|
||||||
timeZone: timeZone.optional(),
|
timeZone: timeZone.optional(),
|
||||||
theme: z.nativeEnum(theme).optional().nullable(),
|
theme: z.nativeEnum(theme).optional().nullable(),
|
||||||
|
appTheme: z.nativeEnum(theme).optional().nullable(),
|
||||||
timeFormat: z.nativeEnum(timeFormat).optional(),
|
timeFormat: z.nativeEnum(timeFormat).optional(),
|
||||||
defaultScheduleId: z
|
defaultScheduleId: z
|
||||||
.number()
|
.number()
|
||||||
@@ -122,6 +124,7 @@ const schemaUserCreateParams = z.object({
|
|||||||
hideBranding: z.boolean().optional(),
|
hideBranding: z.boolean().optional(),
|
||||||
timeZone: timeZone.optional(),
|
timeZone: timeZone.optional(),
|
||||||
theme: z.nativeEnum(theme).optional().nullable(),
|
theme: z.nativeEnum(theme).optional().nullable(),
|
||||||
|
appTheme: z.nativeEnum(theme).optional().nullable(),
|
||||||
timeFormat: z.nativeEnum(timeFormat).optional(),
|
timeFormat: z.nativeEnum(timeFormat).optional(),
|
||||||
defaultScheduleId: z
|
defaultScheduleId: z
|
||||||
.number()
|
.number()
|
||||||
@@ -159,6 +162,7 @@ export const schemaUserReadPublic = User.pick({
|
|||||||
weekStart: true,
|
weekStart: true,
|
||||||
endTime: true,
|
endTime: true,
|
||||||
bufferTime: true,
|
bufferTime: true,
|
||||||
|
appTheme: true,
|
||||||
theme: true,
|
theme: true,
|
||||||
defaultScheduleId: true,
|
defaultScheduleId: true,
|
||||||
locale: true,
|
locale: true,
|
||||||
File renamed without changes.
File renamed without changes.
@@ -4,7 +4,7 @@ const i18nConfig = require("@calcom/config/next-i18next.config");
|
|||||||
/** @type {import("next-i18next").UserConfig} */
|
/** @type {import("next-i18next").UserConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
...i18nConfig,
|
...i18nConfig,
|
||||||
localePath: path.resolve("../web/public/static/locales"),
|
localePath: path.resolve("../../web/public/static/locales"),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
File renamed without changes.
-4
@@ -1,8 +1,6 @@
|
|||||||
import type { Session } from "next-auth";
|
import type { Session } from "next-auth";
|
||||||
import type { NextApiRequest as BaseNextApiRequest } from "next/types";
|
import type { NextApiRequest as BaseNextApiRequest } from "next/types";
|
||||||
|
|
||||||
import type { PrismaClient } from "@calcom/prisma";
|
|
||||||
|
|
||||||
export type * from "next/types";
|
export type * from "next/types";
|
||||||
|
|
||||||
export declare module "next" {
|
export declare module "next" {
|
||||||
@@ -11,11 +9,9 @@ export declare module "next" {
|
|||||||
|
|
||||||
userId: number;
|
userId: number;
|
||||||
method: string;
|
method: string;
|
||||||
prisma: PrismaClient;
|
|
||||||
// session: { user: { id: number } };
|
// session: { user: { id: number } };
|
||||||
// query: Partial<{ [key: string]: string | string[] }>;
|
// query: Partial<{ [key: string]: string | string[] }>;
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
isCustomPrisma: boolean;
|
|
||||||
pagination: { take: number; skip: number };
|
pagination: { take: number; skip: number };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@calcom/api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Public API for Cal.com",
|
||||||
|
"main": "index.ts",
|
||||||
|
"repository": "git@github.com:calcom/api.git",
|
||||||
|
"author": "Cal.com Inc.",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "next build",
|
||||||
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
||||||
|
"dev": "PORT=3003 next dev",
|
||||||
|
"lint": "eslint . --ignore-path .gitignore",
|
||||||
|
"lint:fix": "eslint . --ext .ts,.js,.tsx,.jsx --fix",
|
||||||
|
"start": "PORT=3003 next start",
|
||||||
|
"docker-start-api": "PORT=80 next start",
|
||||||
|
"type-check": "tsc --pretty --noEmit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@calcom/tsconfig": "*",
|
||||||
|
"@calcom/types": "*",
|
||||||
|
"node-mocks-http": "^1.11.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@calcom/app-store": "*",
|
||||||
|
"@calcom/core": "*",
|
||||||
|
"@calcom/dayjs": "*",
|
||||||
|
"@calcom/emails": "*",
|
||||||
|
"@calcom/features": "*",
|
||||||
|
"@calcom/lib": "*",
|
||||||
|
"@calcom/prisma": "*",
|
||||||
|
"@calcom/trpc": "*",
|
||||||
|
"@sentry/nextjs": "^7.73.0",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
|
"memory-cache": "^0.2.0",
|
||||||
|
"next": "^13.5.4",
|
||||||
|
"next-api-middleware": "^1.0.1",
|
||||||
|
"next-axiom": "^0.17.0",
|
||||||
|
"next-swagger-doc": "^0.3.6",
|
||||||
|
"next-validations": "^0.2.0",
|
||||||
|
"typescript": "^4.9.4",
|
||||||
|
"tzdata": "^1.0.30",
|
||||||
|
"uuid": "^8.3.2",
|
||||||
|
"zod": "^3.22.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -1,11 +1,12 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
||||||
|
|
||||||
export async function authMiddleware(req: NextApiRequest) {
|
export async function authMiddleware(req: NextApiRequest) {
|
||||||
const { userId, isAdmin, prisma } = req;
|
const { userId, isAdmin } = req;
|
||||||
const { id } = schemaQueryIdAsString.parse(req.query);
|
const { id } = schemaQueryIdAsString.parse(req.query);
|
||||||
// Admin can check any api key
|
// Admin can check any api key
|
||||||
if (isAdmin) return;
|
if (isAdmin) return;
|
||||||
+2
-1
@@ -1,11 +1,12 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
||||||
|
|
||||||
async function deleteHandler(req: NextApiRequest) {
|
async function deleteHandler(req: NextApiRequest) {
|
||||||
const { prisma, query } = req;
|
const { query } = req;
|
||||||
const { id } = schemaQueryIdAsString.parse(query);
|
const { id } = schemaQueryIdAsString.parse(query);
|
||||||
await prisma.apiKey.delete({ where: { id } });
|
await prisma.apiKey.delete({ where: { id } });
|
||||||
return { message: `ApiKey with id: ${id} deleted` };
|
return { message: `ApiKey with id: ${id} deleted` };
|
||||||
+2
-1
@@ -1,12 +1,13 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { apiKeyPublicSchema } from "~/lib/validations/api-key";
|
import { apiKeyPublicSchema } from "~/lib/validations/api-key";
|
||||||
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
||||||
|
|
||||||
async function getHandler(req: NextApiRequest) {
|
async function getHandler(req: NextApiRequest) {
|
||||||
const { prisma, query } = req;
|
const { query } = req;
|
||||||
const { id } = schemaQueryIdAsString.parse(query);
|
const { id } = schemaQueryIdAsString.parse(query);
|
||||||
const api_key = await prisma.apiKey.findUniqueOrThrow({ where: { id } });
|
const api_key = await prisma.apiKey.findUniqueOrThrow({ where: { id } });
|
||||||
return { api_key: apiKeyPublicSchema.parse(api_key) };
|
return { api_key: apiKeyPublicSchema.parse(api_key) };
|
||||||
+2
-1
@@ -1,12 +1,13 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { apiKeyEditBodySchema, apiKeyPublicSchema } from "~/lib/validations/api-key";
|
import { apiKeyEditBodySchema, apiKeyPublicSchema } from "~/lib/validations/api-key";
|
||||||
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
import { schemaQueryIdAsString } from "~/lib/validations/shared/queryIdString";
|
||||||
|
|
||||||
async function patchHandler(req: NextApiRequest) {
|
async function patchHandler(req: NextApiRequest) {
|
||||||
const { prisma, body } = req;
|
const { body } = req;
|
||||||
const { id } = schemaQueryIdAsString.parse(req.query);
|
const { id } = schemaQueryIdAsString.parse(req.query);
|
||||||
const data = apiKeyEditBodySchema.parse(body);
|
const data = apiKeyEditBodySchema.parse(body);
|
||||||
const api_key = await prisma.apiKey.update({ where: { id }, data });
|
const api_key = await prisma.apiKey.update({ where: { id }, data });
|
||||||
File renamed without changes.
@@ -2,6 +2,7 @@ import type { Prisma } from "@prisma/client";
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
import type { Ensure } from "@calcom/types/utils";
|
import type { Ensure } from "@calcom/types/utils";
|
||||||
|
|
||||||
import { apiKeyPublicSchema } from "~/lib/validations/api-key";
|
import { apiKeyPublicSchema } from "~/lib/validations/api-key";
|
||||||
@@ -29,7 +30,7 @@ function hasReqArgs(req: CustomNextApiRequest): req is Ensure<CustomNextApiReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getHandler(req: CustomNextApiRequest) {
|
async function getHandler(req: CustomNextApiRequest) {
|
||||||
const { userId, isAdmin, prisma } = req;
|
const { userId, isAdmin } = req;
|
||||||
req.args = isAdmin ? {} : { where: { userId } };
|
req.args = isAdmin ? {} : { where: { userId } };
|
||||||
// Proof of concept: allowing mutation in exchange of composability
|
// Proof of concept: allowing mutation in exchange of composability
|
||||||
handleAdminRequests(req);
|
handleAdminRequests(req);
|
||||||
@@ -5,11 +5,12 @@ import { v4 } from "uuid";
|
|||||||
import { generateUniqueAPIKey } from "@calcom/features/ee/api-keys/lib/apiKeys";
|
import { generateUniqueAPIKey } from "@calcom/features/ee/api-keys/lib/apiKeys";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { apiKeyCreateBodySchema, apiKeyPublicSchema } from "~/lib/validations/api-key";
|
import { apiKeyCreateBodySchema, apiKeyPublicSchema } from "~/lib/validations/api-key";
|
||||||
|
|
||||||
async function postHandler(req: NextApiRequest) {
|
async function postHandler(req: NextApiRequest) {
|
||||||
const { userId, isAdmin, prisma } = req;
|
const { userId, isAdmin } = req;
|
||||||
const { neverExpires, userId: bodyUserId, ...input } = apiKeyCreateBodySchema.parse(req.body);
|
const { neverExpires, userId: bodyUserId, ...input } = apiKeyCreateBodySchema.parse(req.body);
|
||||||
const [hashedKey, apiKey] = generateUniqueAPIKey();
|
const [hashedKey, apiKey] = generateUniqueAPIKey();
|
||||||
const args: Prisma.ApiKeyCreateArgs = {
|
const args: Prisma.ApiKeyCreateArgs = {
|
||||||
File renamed without changes.
+2
-1
@@ -1,11 +1,12 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
||||||
|
|
||||||
async function authMiddleware(req: NextApiRequest) {
|
async function authMiddleware(req: NextApiRequest) {
|
||||||
const { userId, isAdmin, prisma } = req;
|
const { userId, isAdmin } = req;
|
||||||
const query = schemaQueryIdParseInt.parse(req.query);
|
const query = schemaQueryIdParseInt.parse(req.query);
|
||||||
// @note: Here we make sure to only return attendee's of the user's own bookings if the user is not an admin.
|
// @note: Here we make sure to only return attendee's of the user's own bookings if the user is not an admin.
|
||||||
if (isAdmin) return;
|
if (isAdmin) return;
|
||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
|
|||||||
* description: Authorization information is missing or invalid.
|
* description: Authorization information is missing or invalid.
|
||||||
*/
|
*/
|
||||||
export async function deleteHandler(req: NextApiRequest) {
|
export async function deleteHandler(req: NextApiRequest) {
|
||||||
const { prisma, query } = req;
|
const { query } = req;
|
||||||
const { id } = schemaQueryIdParseInt.parse(query);
|
const { id } = schemaQueryIdParseInt.parse(query);
|
||||||
await prisma.attendee.delete({ where: { id } });
|
await prisma.attendee.delete({ where: { id } });
|
||||||
return { message: `Attendee with id: ${id} deleted successfully` };
|
return { message: `Attendee with id: ${id} deleted successfully` };
|
||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { schemaAttendeeReadPublic } from "~/lib/validations/attendee";
|
import { schemaAttendeeReadPublic } from "~/lib/validations/attendee";
|
||||||
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
||||||
@@ -35,7 +36,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
|
|||||||
* description: Attendee was not found
|
* description: Attendee was not found
|
||||||
*/
|
*/
|
||||||
export async function getHandler(req: NextApiRequest) {
|
export async function getHandler(req: NextApiRequest) {
|
||||||
const { prisma, query } = req;
|
const { query } = req;
|
||||||
const { id } = schemaQueryIdParseInt.parse(query);
|
const { id } = schemaQueryIdParseInt.parse(query);
|
||||||
const attendee = await prisma.attendee.findUnique({ where: { id } });
|
const attendee = await prisma.attendee.findUnique({ where: { id } });
|
||||||
return { attendee: schemaAttendeeReadPublic.parse(attendee) };
|
return { attendee: schemaAttendeeReadPublic.parse(attendee) };
|
||||||
+3
-2
@@ -3,6 +3,7 @@ import type { z } from "zod";
|
|||||||
|
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { schemaAttendeeEditBodyParams, schemaAttendeeReadPublic } from "~/lib/validations/attendee";
|
import { schemaAttendeeEditBodyParams, schemaAttendeeReadPublic } from "~/lib/validations/attendee";
|
||||||
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
|
||||||
@@ -53,7 +54,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export async function patchHandler(req: NextApiRequest) {
|
export async function patchHandler(req: NextApiRequest) {
|
||||||
const { prisma, query, body } = req;
|
const { query, body } = req;
|
||||||
const { id } = schemaQueryIdParseInt.parse(query);
|
const { id } = schemaQueryIdParseInt.parse(query);
|
||||||
const data = schemaAttendeeEditBodyParams.parse(body);
|
const data = schemaAttendeeEditBodyParams.parse(body);
|
||||||
await checkPermissions(req, data);
|
await checkPermissions(req, data);
|
||||||
@@ -62,7 +63,7 @@ export async function patchHandler(req: NextApiRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function checkPermissions(req: NextApiRequest, body: z.infer<typeof schemaAttendeeEditBodyParams>) {
|
async function checkPermissions(req: NextApiRequest, body: z.infer<typeof schemaAttendeeEditBodyParams>) {
|
||||||
const { isAdmin, prisma } = req;
|
const { isAdmin } = req;
|
||||||
if (isAdmin) return;
|
if (isAdmin) return;
|
||||||
const { userId } = req;
|
const { userId } = req;
|
||||||
const { bookingId } = body;
|
const { bookingId } = body;
|
||||||
Loaded 100 of 1630 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user