feat(server): support lifetime subscription (#7405)

closes CLOUD-48

- [x] lifetime subscription quota
- [ ] tests
This commit is contained in:
forehalo
2024-07-08 07:41:26 +00:00
parent 7235779b02
commit de91027852
17 changed files with 447 additions and 165 deletions
@@ -155,6 +155,25 @@ export const Quotas: Quota[] = [
copilotActionLimit: 10,
},
},
{
feature: QuotaType.LifetimeProPlanV1,
type: FeatureKind.Quota,
version: 1,
configs: {
// quota name
name: 'Lifetime Pro',
// single blob limit 100MB
blobLimit: 100 * OneMB,
// total blob limit 1TB
storageQuota: 1024 * OneGB,
// history period of validity 30 days
historyPeriod: 30 * OneDay,
// member limit 10
memberLimit: 10,
// copilot action limit 10
copilotActionLimit: 10,
},
},
];
export function getLatestQuota(type: QuotaType) {
@@ -165,6 +184,7 @@ export function getLatestQuota(type: QuotaType) {
export const FreePlan = getLatestQuota(QuotaType.FreePlanV1);
export const ProPlan = getLatestQuota(QuotaType.ProPlanV1);
export const LifetimeProPlan = getLatestQuota(QuotaType.LifetimeProPlanV1);
export const Quota_FreePlanV1_1 = {
feature: Quotas[5].feature,
@@ -3,7 +3,6 @@ import { PrismaClient } from '@prisma/client';
import type { EventPayload } from '../../fundamentals';
import { OnEvent, PrismaTransaction } from '../../fundamentals';
import { SubscriptionPlan } from '../../plugins/payment/types';
import { FeatureManagementService } from '../features/management';
import { FeatureKind } from '../features/types';
import { QuotaConfig } from './quota';
@@ -152,15 +151,18 @@ export class QuotaService {
async onSubscriptionUpdated({
userId,
plan,
recurring,
}: EventPayload<'user.subscription.activated'>) {
switch (plan) {
case SubscriptionPlan.AI:
case 'ai':
await this.feature.addCopilot(userId, 'subscription activated');
break;
case SubscriptionPlan.Pro:
case 'pro':
await this.switchUserQuota(
userId,
QuotaType.ProPlanV1,
recurring === 'lifetime'
? QuotaType.LifetimeProPlanV1
: QuotaType.ProPlanV1,
'subscription activated'
);
break;
@@ -175,16 +177,22 @@ export class QuotaService {
plan,
}: EventPayload<'user.subscription.canceled'>) {
switch (plan) {
case SubscriptionPlan.AI:
case 'ai':
await this.feature.removeCopilot(userId);
break;
case SubscriptionPlan.Pro:
await this.switchUserQuota(
userId,
QuotaType.FreePlanV1,
'subscription canceled'
);
case 'pro': {
// edge case: when user switch from recurring Pro plan to `Lifetime` plan,
// a subscription canceled event will be triggered because `Lifetime` plan is not subscription based
const quota = await this.getUserQuota(userId);
if (quota.feature.name !== QuotaType.LifetimeProPlanV1) {
await this.switchUserQuota(
userId,
QuotaType.FreePlanV1,
'subscription canceled'
);
}
break;
}
default:
break;
}
@@ -17,6 +17,7 @@ import { ByteUnit, OneDay, OneKB } from './constant';
export enum QuotaType {
FreePlanV1 = 'free_plan_v1',
ProPlanV1 = 'pro_plan_v1',
LifetimeProPlanV1 = 'lifetime_pro_plan_v1',
// only for test, smaller quota
RestrictedPlanV1 = 'restricted_plan_v1',
}
@@ -25,6 +26,7 @@ const quotaPlan = z.object({
feature: z.enum([
QuotaType.FreePlanV1,
QuotaType.ProPlanV1,
QuotaType.LifetimeProPlanV1,
QuotaType.RestrictedPlanV1,
]),
configs: z.object({