Files
calendar/apps/api/v2/src/env.ts
T
MorganGitHubunknown <>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
c60addc528 feat: add PlatformOrganizationBillingTasker with sync and trigger.dev versions (#26803)
* feat: add PlatformOrganizationBillingTasker with sync and trigger.dev versions

- Create PlatformOrganizationBillingTasker extending Tasker base class
- Add sync tasker (PlatformOrganizationBillingSyncTasker) for synchronous execution
- Add trigger.dev tasker (PlatformOrganizationBillingTriggerTasker) for async execution
- Create trigger.dev task for incrementing subscription usage
- Add PlatformOrganizationBillingTaskService with business logic
- Add PlatformOrganizationBillingRepository for data access
- Add createSubscriptionUsageRecord method to StripeBillingService
- Follow BookingEmailAndSmsTasker pattern for consistency

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* refactor: move repository methods per PR feedback

- Move findPlatformOrgByUserId to OrganizationRepository
- Create new PlatformBillingRepository for billing queries
- Remove PlatformOrganizationBillingRepository (consolidated)
- Update task service and trigger.dev task to use new repositories

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* feat: add DI using @evyweb/ioctopus for platform billing tasker

- Create di/tasker/ directory following BookingEmailAndSmsTasker pattern
- Add tokens.ts with DI tokens for all billing tasker classes
- Add module files for PlatformBillingRepository, TaskService, SyncTasker, TriggerTasker, Tasker
- Add container files with getter functions
- Update trigger.dev task to use DI container instead of manual instantiation

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* refactor: add select statement to PlatformBillingRepository.findByTeamId

Only select subscriptionId field since that's the only field used by the task service

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* feat: add NestJS DI modules for platform billing tasker in API v2

- Add platform billing tasker exports to platform-libraries/organizations.ts
- Export IBillingProviderService type from platform-libraries
- Create StripeBillingProviderService wrapper implementing IBillingProviderService
- Create PrismaPlatformBillingRepository extending PlatformBillingRepository
- Create NestJS service wrappers for all platform billing tasker classes
- Create PlatformBillingTaskerModule with all providers and exports
- Update PlatformOrganizationBillingTaskService to use Pick<IBillingProviderService>

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* feat: handle cancel and reschedule usage

* fix: restore IsUserInBillingOrg to billing module providers

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* chore: add idempotency key

* fix: just log error]

* fix: logger and typo

* fix: address Cubic AI review feedback (high confidence issues)

- Fix grammar error: 'Delayed task are' -> 'Delayed tasks are' in PlatformOrganizationBillingSyncTasker.ts (3 occurrences)
- Re-throw error after logging in increment-usage.ts to enable trigger.dev retry mechanism

Co-Authored-By: unknown <>

* fix: remove duplicate code and use DI instead

* fix: remove orThrow on find first in findPlatformOrgByUserId

* refactor: prevent usage increment task from re-throwing errors

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-20 15:26:50 +09:00

64 lines
1.9 KiB
TypeScript

import { logLevels } from "@/lib/logger";
export type Environment = {
NODE_ENV: "development" | "production";
API_PORT: string;
API_URL: string;
DATABASE_READ_URL: string;
DATABASE_WRITE_URL: string;
NEXTAUTH_SECRET: string;
DATABASE_URL: string;
JWT_SECRET: string;
SENTRY_DSN: string;
SENTRY_TRACES_SAMPLE_RATE?: number;
SENTRY_PROFILES_SAMPLE_RATE?: number;
LOG_LEVEL: keyof typeof logLevels;
REDIS_URL: string;
STRIPE_API_KEY: string;
STRIPE_WEBHOOK_SECRET: string;
WEB_APP_URL: string;
IS_E2E: string;
CALCOM_LICENSE_KEY: string;
GET_LICENSE_KEY_URL: string;
API_KEY_PREFIX: string;
DOCS_URL: string;
RATE_LIMIT_DEFAULT_TTL_MS: number;
RATE_LIMIT_DEFAULT_LIMIT_API_KEY: number;
RATE_LIMIT_DEFAULT_LIMIT_OAUTH_CLIENT: number;
RATE_LIMIT_DEFAULT_LIMIT_ACCESS_TOKEN: number;
RATE_LIMIT_DEFAULT_LIMIT: number;
RATE_LIMIT_DEFAULT_BLOCK_DURATION_MS: number;
AXIOM_DATASET: string;
AXIOM_TOKEN: string;
STRIPE_TEAM_MONTHLY_PRICE_ID: string;
IS_TEAM_BILLING_ENABLED: boolean;
// Used to enable/disable the rewrite of /api/v2 to /v2, active by default.
REWRITE_API_V2_PREFIX: string;
DATABASE_READ_POOL_MAX: number;
DATABASE_WRITE_POOL_MAX: number;
DATABASE_READ_WORKER_POOL_MAX: number;
DATABASE_WRITE_WORKER_POOL_MAX: number;
ENABLE_SLOTS_WORKERS: string;
SLOTS_WORKER_POOL_SIZE: string;
USE_POOL: string;
VERCEL: string;
ENABLE_ASYNC_TASKER: string;
};
export const getEnv = <K extends keyof Environment>(key: K, fallback?: Environment[K]): Environment[K] => {
const value = process.env[key] as Environment[K] | undefined;
if (value === undefined) {
// handle fallback falsy cases that should still be used as value
if (fallback === false || fallback === "" || fallback === 0) {
return fallback;
}
if (fallback) {
return fallback;
}
throw new Error(`Missing environment variable: ${key}.`);
}
return value;
};