Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion apps/api/v2/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { AppLoggerMiddleware } from "@/middleware/app.logger.middleware";
import { RewriterMiddleware } from "@/middleware/app.rewrites.middleware";
import { JsonBodyMiddleware } from "@/middleware/body/json.body.middleware";
import { RawBodyMiddleware } from "@/middleware/body/raw.body.middleware";
import { ResponseInterceptor } from "@/middleware/request-ids/request-id.interceptor";
import { RequestIdMiddleware } from "@/middleware/request-ids/request-id.middleware";
import { AuthModule } from "@/modules/auth/auth.module";
import { EndpointsModule } from "@/modules/endpoints.module";
import { JwtModule } from "@/modules/jwt/jwt.module";
Expand All @@ -11,7 +13,7 @@ import { RedisModule } from "@/modules/redis/redis.module";
import { RedisService } from "@/modules/redis/redis.service";
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { RouterModule } from "@nestjs/core";
import { APP_INTERCEPTOR, RouterModule } from "@nestjs/core";
import { seconds, ThrottlerModule } from "@nestjs/throttler";
import { ThrottlerStorageRedisService } from "nestjs-throttler-storage-redis";

Expand Down Expand Up @@ -52,6 +54,12 @@ import { AppController } from "./app.controller";
RouterModule.register([{ path: "/v2", module: EndpointsModule }]),
],
controllers: [AppController],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: ResponseInterceptor,
},
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): void {
Expand All @@ -63,6 +71,8 @@ export class AppModule implements NestModule {
})
.apply(JsonBodyMiddleware)
.forRoutes("*")
.apply(RequestIdMiddleware)
.forRoutes("*")
.apply(AppLoggerMiddleware)
.forRoutes("*")
.apply(RewriterMiddleware)
Expand Down
4 changes: 4 additions & 0 deletions apps/api/v2/src/filters/http-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const statusCode = exception.getStatus();
const requestId = request.headers["X-Request-Id"];

this.logger.error(`Http Exception Filter: ${exception?.message}`, {
exception,
body: request.body,
headers: request.headers,
url: request.url,
method: request.method,
requestId,
});

response.status(statusCode).json({
status: ERROR_STATUS,
timestamp: new Date().toISOString(),
Expand Down
3 changes: 3 additions & 0 deletions apps/api/v2/src/filters/prisma-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ export class PrismaExceptionFilter implements ExceptionFilter {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const requestId = request.headers["X-Request-Id"];

this.logger.error(`PrismaError: ${error.message}`, {
error,
body: request.body,
headers: request.headers,
url: request.url,
method: request.method,
requestId,
});
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
status: ERROR_STATUS,
Expand Down
2 changes: 2 additions & 0 deletions apps/api/v2/src/filters/sentry-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ export class SentryFilter extends BaseExceptionFilter {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const requestId = request.headers["X-Request-Id"];

this.logger.error(`Sentry Exception Filter: ${exception?.message}`, {
exception,
body: request.body,
headers: request.headers,
url: request.url,
method: request.method,
requestId,
});

// capture if client has been init
Expand Down
3 changes: 3 additions & 0 deletions apps/api/v2/src/filters/trpc-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ export class TRPCExceptionFilter implements ExceptionFilter {
break;
}

const requestId = request.headers["X-Request-Id"];

this.logger.error(`TRPC Exception Filter: ${exception?.message}`, {
exception,
body: request.body,
headers: request.headers,
url: request.url,
method: request.method,
requestId,
});

response.status(statusCode).json({
Expand Down
2 changes: 2 additions & 0 deletions apps/api/v2/src/filters/zod-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ export class ZodExceptionFilter implements ExceptionFilter {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const requestId = request.headers["X-Request-Id"];

this.logger.error(`ZodError: ${error.message}`, {
error,
body: request.body,
headers: request.headers,
url: request.url,
method: request.method,
requestId,
});

response.status(HttpStatus.BAD_REQUEST).json({
Expand Down
16 changes: 16 additions & 0 deletions apps/api/v2/src/middleware/request-ids/request-id.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common";
import { Request, Response } from "express";

@Injectable()
export class ResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const ctx = context.switchToHttp();
const request = ctx.getRequest<Request>();
const response = ctx.getResponse<Response>();

const requestId = request.headers["X-Request-Id"] ?? "unknown-request-id";
response.setHeader("X-Request-Id", requestId.toString());

return next.handle();
}
}
11 changes: 11 additions & 0 deletions apps/api/v2/src/middleware/request-ids/request-id.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
import { v4 as uuid } from "uuid";

@Injectable()
export class RequestIdMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
req.headers["X-Request-Id"] = uuid();
next();
}
}
15 changes: 13 additions & 2 deletions apps/api/v2/src/modules/billing/controllers/billing.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,19 @@ export class BillingController {

if (event.type === "customer.subscription.created" || event.type === "customer.subscription.updated") {
const subscription = event.data.object as Stripe.Subscription;
if (!subscription.metadata?.teamId) {
return {
status: "success",
};
}

const teamId = Number.parseInt(subscription.metadata.teamId);
const plan = subscription.metadata.plan;
if (!plan || !teamId) {
throw new Error("Invalid webhook received.");
this.logger.log("Webhook received but not pertaining to Platform, discarding.");
return {
status: "success",
};
}

await this.billingService.setSubscriptionForTeam(
Expand All @@ -121,6 +130,8 @@ export class BillingController {
};
}

throw new BadRequestException(`Unhandled event type ${event.type}`);
return {
status: "success",
};
}
}
44 changes: 29 additions & 15 deletions apps/api/v2/src/modules/billing/services/billing.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,36 @@ export class BillingService {
}

async increaseUsageForTeam(teamId: number) {
// TODO - if we support multiple subscription items per team, we may need to track which plan they're
// subscribed to so we can do one less query.
const billingSubscription = await this.billingRepository.getBillingForTeam(teamId);
if (!billingSubscription || !billingSubscription?.subscriptionId) {
throw new Error(`Failed to increase usage for team ${teamId}`);
}
try {
const billingSubscription = await this.billingRepository.getBillingForTeam(teamId);
if (!billingSubscription || !billingSubscription?.subscriptionId) {
this.logger.error("Team did not have stripe subscription associated to it", {
teamId,
});
return void 0;
}

const stripeSubscription = await this.stripeService.stripe.subscriptions.retrieve(
billingSubscription.subscriptionId
);
const items = stripeSubscription.items.data[0]; // first (and only) subscription item.
await this.stripeService.stripe.subscriptionItems.createUsageRecord(items.id, {
action: "increment",
quantity: 1,
timestamp: "now",
});
const stripeSubscription = await this.stripeService.stripe.subscriptions.retrieve(
billingSubscription.subscriptionId
);
const item = stripeSubscription.items.data[0];
// legacy plans are licensed, we cannot create usage records against them
if (item.price?.recurring?.usage_type === "licensed") {
return void 0;
}

await this.stripeService.stripe.subscriptionItems.createUsageRecord(item.id, {
action: "increment",
quantity: 1,
timestamp: "now",
});
} catch (error) {
// don't fail the request, log it.
this.logger.error("Failed to increase usage for team", {
teamId: teamId,
error,
});
}
}

async increaseUsageByClientId(clientId: string) {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "4.0.5",
"version": "4.0.6",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/ar/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,8 +937,6 @@
"verify_wallet": "تأكيد المحفظة",
"create_events_on": "إنشاء أحداث في",
"enterprise_license": "هذه هي ميزة للمؤسسات",
"enterprise_license_description": "لتمكين هذه الميزة، احصل على مفتاح نشر في وحدة التحكم {{consoleUrl}} وأضفه إلى وحدة التحكم الخاصة بك. nv باسم CALCOM_LICENSE_KEY. إذا كان لدى فريقك بالفعل ترخيص، يرجى الاتصال بـ {{supportMail}} للحصول على المساعدة.",
"enterprise_license_development": "يمكنك اختبار هذه الميزة في وضع التطوير. لاستخدام الإنتاج، يرجى مطالبة المسؤول بالذهاب إلى <2>/auth/setup</2> لإدخال مفتاح الترخيص.",
"missing_license": "الترخيص مفقود",
"next_steps": "الخطوات التالية",
"acquire_commercial_license": "الحصول على ترخيص تجاري",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/cs/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,8 +937,6 @@
"verify_wallet": "Ověřit peněženku",
"create_events_on": "Vytvořit události v:",
"enterprise_license": "Jedná se o firemní funkci",
"enterprise_license_description": "Pokud chcete povolit tuto funkci, získejte klíč pro nasazení v konzoli {{consoleUrl}} a přidejte ho do souboru .env jako CALCOM_LICENSE_KEY. Pokud váš tým již licenci má, kontaktujte prosím {{supportMail}} a požádejte o pomoc.",
"enterprise_license_development": "Tuto funkci můžete vyzkoušet v režimu vývoje. Produkční použití vyžaduje od správce zadání licenčního klíče v <2>/auth/setup</2>.",
"missing_license": "Chybějící licence",
"next_steps": "Další kroky",
"acquire_commercial_license": "Získat komerční licenci",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/de/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -980,8 +980,6 @@
"verify_wallet": "Wallet verifizieren",
"create_events_on": "Erstelle Termine in:",
"enterprise_license": "Das ist eine Enterprise-Funktion",
"enterprise_license_description": "Um diese Funktion zu aktivieren, holen Sie sich einen Deployment-Schlüssel von der {{consoleUrl}}-Konsole und fügen Sie ihn als CALCOM_LICENSE_KEY zu Ihrer .env hinzu. Wenn Ihr Team bereits eine Lizenz hat, wenden Sie sich bitte an {{supportMail}} für Hilfe.",
"enterprise_license_development": "Sie können diese Funktion im Entwicklungsmodus testen. Zur Nutzung im regulären Arbeitsumfeld, besuchen Sie bitte <2>/auth/setup</2>, um einen Lizenzschlüssel einzugeben.",
"missing_license": "Lizenz fehlt",
"next_steps": "Nächste Schritte",
"acquire_commercial_license": "Eine kommerzielle Lizenz erwerben",
Expand Down
6 changes: 3 additions & 3 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -994,8 +994,8 @@
"verify_wallet": "Verify Wallet",
"create_events_on": "Create events on",
"enterprise_license": "This is an enterprise feature",
"enterprise_license_description": "To enable this feature, have an administrator go to <2>/auth/setup</2> to enter a license key. If a license key is already in place, please contact <5>{{SUPPORT_MAIL_ADDRESS}}</5> for help.",
"enterprise_license_development": "You can test this feature on development mode. For production usage please have an administrator go to <2>/auth/setup</2> to enter a license key.",
"enterprise_license_locally": "You can test this feature locally but not on production.",
"enterprise_license_sales": "To upgrade to the enterprise edition, please reach out to our sales team. If a license key is already in place, please contact support@cal.com for help.",
"missing_license": "Missing License",
"next_steps": "Next Steps",
"acquire_commercial_license": "Acquire a commercial license",
Expand Down Expand Up @@ -2022,7 +2022,7 @@
"invite_as": "Invite as",
"form_updated_successfully": "Form updated successfully.",
"disable_attendees_confirmation_emails": "Disable default confirmation emails for attendees",
"disable_attendees_confirmation_emails_description": "At least one workflow is active on this event type that sends an email to the attendees when the event is booked.",
"disable_attendees_confirmation_emails_description": "At least one workflow is active on this event type that sends a booking confirmation to the attendees.",
"disable_host_confirmation_emails": "Disable default confirmation emails for host",
"disable_host_confirmation_emails_description": "At least one workflow is active on this event type that sends an email to the host when the event is booked.",
"add_an_override": "Add an override",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/es/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -936,8 +936,6 @@
"verify_wallet": "Verificar billetera",
"create_events_on": "Crear eventos en",
"enterprise_license": "Esta es una función empresarial",
"enterprise_license_description": "Para habilitar esta función, obtenga una clave de despliegue en la consola {{consoleUrl}} y añádala a su .env como CALCOM_LICENSE_KEY. Si su equipo ya tiene una licencia, póngase en contacto con {{supportMail}} para obtener ayuda.",
"enterprise_license_development": "Puede probar esta función en el modo de desarrollo. Para el uso de producción, haga que un administrador vaya a <2>/auth/setup</2> para ingresar una clave de licencia.",
"missing_license": "Falta la licencia",
"next_steps": "Pasos siguientes",
"acquire_commercial_license": "Adquirir una licencia comercial",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/fr/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -983,8 +983,6 @@
"verify_wallet": "Vérifier le portefeuille",
"create_events_on": "Créer des événements dans :",
"enterprise_license": "Il s'agit d'une fonctionnalité d'entreprise",
"enterprise_license_description": "Pour activer cette fonctionnalité, demandez à un administrateur d'accéder à <2>/auth/setup</2> pour saisir une clé de licence. Si une clé de licence est déjà en place, veuillez contacter <5>{{SUPPORT_MAIL_ADDRESS}}</5> pour obtenir de l'aide.",
"enterprise_license_development": "Vous pouvez tester cette fonctionnalité en mode développement. Pour une utilisation en production, veuillez demander à un administrateur d'aller à <2>/auth/setup</2> pour entrer une clé de licence.",
"missing_license": "Licence manquante",
"next_steps": "Prochaines étapes",
"acquire_commercial_license": "Obtenir une licence commerciale",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/he/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -988,8 +988,6 @@
"verify_wallet": "אימות הארנק",
"create_events_on": "ליצור אירועים ב-",
"enterprise_license": "תכונה זו זמינה רק במינוי Enterprise",
"enterprise_license_description": "כדי להפעיל את יכולת זו, בקש ממנהל לגשת אל הקישור {{setupUrl}} והקלדת הרישיון. אם כבר יש רישיון מוגדר, צור קשר עם {{supportMail}} לעזרה.",
"enterprise_license_development": "ניתן לבדוק את התכונה הזו במצב פיתוח. לשימוש מצב הייצור, מנהל/ת מערכת צריך/ה לעבור אל <2>/auth/setup</2> ולהזין מפתח רישיון.",
"missing_license": "חסר רישיון",
"next_steps": "השלבים הבאים",
"acquire_commercial_license": "רכישת רישיון לשימוש מסחרי",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/hu/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -989,8 +989,6 @@
"verify_wallet": "A Tárca ellenőrzése",
"create_events_on": "Események létrehozása",
"enterprise_license": "Ez egy vállalati szolgáltatás",
"enterprise_license_description": "A funkció engedélyezéséhez kérjen meg egy rendszergazdát, hogy lépjen a <2>/auth/setup</2> oldalra, és adja meg a licenckulcsot. Ha már van licenckulcs, segítségért forduljon a következőhöz: <5>{{SUPPORT_MAIL_ADDRESS}}</5>.",
"enterprise_license_development": "Ezt a funkciót fejlesztési módban tesztelheti. Éles használathoz kérjen meg egy rendszergazdát, hogy lépjen be a <2>/auth/setup</2> oldalra, és adja meg a licenckulcsot.",
"missing_license": "Hiányzó licenc",
"next_steps": "Következő lépések",
"acquire_commercial_license": "Szerezzen kereskedelmi licenc-et",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/it/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -980,8 +980,6 @@
"verify_wallet": "Verifica Wallet",
"create_events_on": "Crea eventi su:",
"enterprise_license": "Questa è una funzione Enterprise",
"enterprise_license_description": "Per abilitare questa funzione, ottenere una chiave di distribuzione presso la console {{consoleUrl}} e aggiungerla al proprio .env come CALCOM_LICENSE_KEY. Nel caso il team possieda già una licenza, contattare {{supportMail}} per assistenza.",
"enterprise_license_development": "Puoi testare questa funzionalità in modalità sviluppo. Per l'utilizzo in produzione, l'amministratore deve accedere a <2>/auth/setup</2> e immettere la chiave di licenza.",
"missing_license": "Licenza mancante",
"next_steps": "Prossimi Passi",
"acquire_commercial_license": "Acquista una licenza commerciale",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/ja/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,8 +937,6 @@
"verify_wallet": "ウォレットを確認する",
"create_events_on": "以下にイベントを作成する:",
"enterprise_license": "これは企業向けの機能です",
"enterprise_license_description": "この機能を有効にするには、{{consoleUrl}} コンソールでデプロイメントキーを入手して .env に CALCOM_LICENSE_KEY として追加してください。既にチームがライセンスを持っている場合は {{supportMail}} にお問い合わせください。",
"enterprise_license_development": "この機能は開発モードでテストできます。本番環境で使用するには、管理者に <2>/auth/setup</2> にアクセスするよう依頼し、ライセンスキーを入力してもらってください。",
"missing_license": "ライセンスが見つかりません",
"next_steps": "次のステップ",
"acquire_commercial_license": "商用ライセンスを取得する",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/ko/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,8 +937,6 @@
"verify_wallet": "지갑 인증",
"create_events_on": "이벤트 생성일:",
"enterprise_license": "엔터프라이즈 기능입니다",
"enterprise_license_description": "이 기능을 활성화하려면 {{consoleUrl}} 콘솔에서 배포 키를 가져와 .env에 CALCOM_LICENSE_KEY로 추가하세요. 팀에 이미 라이선스가 있는 경우 {{supportMail}}에 문의하여 도움을 받으세요.",
"enterprise_license_development": "개발 모드에서 이 기능을 테스트할 수 있습니다. 프로덕션 용도의 경우 관리자가 <2>/auth/setup</2>으로 이동하여 라이선스 키를 입력하도록 하십시오.",
"missing_license": "라이선스 없음",
"next_steps": "다음 단계",
"acquire_commercial_license": "상용 라이선스 취득하기",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/public/static/locales/nl/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -937,8 +937,6 @@
"verify_wallet": "Wallet verifiëren",
"create_events_on": "Maak gebeurtenissen aan in de",
"enterprise_license": "Dit is een bedrijfsfunctie",
"enterprise_license_description": "Om deze functie in te schakelen, krijgt u een implementatiesleutel op de {{consoleUrl}}-console en voegt u deze toe aan uw .env als CALCOM_LICENSE_KEY. Als uw team al een licentie heeft, neem dan contact op met {{supportMail}} voor hulp.",
"enterprise_license_development": "U kunt deze functie testen in de in ontwikkelingsmodus. Voor productiegebruik moet een beheerder naar <2>/auth/setup</2> gaan om een licentiecode in te voeren.",
"missing_license": "Ontbrekende licentie",
"next_steps": "Volgende stappen",
"acquire_commercial_license": "Verkrijg een commerciële licentie",
Expand Down
Loading