feat(server): support selfhost licenses (#8947)

This commit is contained in:
forehalo
2025-01-22 10:21:07 +00:00
parent 22e424d7de
commit 994d758c07
31 changed files with 1653 additions and 127 deletions
@@ -1,7 +1,8 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { type EventPayload } from '../../base';
import type { EventPayload } from '../../base';
import { FeatureManagementService } from '../../core/features';
import { PermissionService } from '../../core/permission';
import {
QuotaManagementService,
@@ -9,18 +10,21 @@ import {
QuotaType,
} from '../../core/quota';
import { WorkspaceService } from '../../core/workspaces/resolvers';
import { SubscriptionPlan } from './types';
@Injectable()
export class TeamQuotaOverride {
export class QuotaOverride {
constructor(
private readonly quota: QuotaService,
private readonly manager: QuotaManagementService,
private readonly permission: PermissionService,
private readonly workspace: WorkspaceService
private readonly workspace: WorkspaceService,
private readonly feature: FeatureManagementService,
private readonly quotaService: QuotaService
) {}
@OnEvent('workspace.subscription.activated')
async onSubscriptionUpdated({
async onWorkspaceSubscriptionUpdated({
workspaceId,
plan,
recurring,
@@ -36,7 +40,7 @@ export class TeamQuotaOverride {
workspaceId,
`${recurring} team subscription activated`
);
await this.manager.updateWorkspaceConfig(
await this.quota.updateWorkspaceConfig(
workspaceId,
QuotaType.TeamPlanV1,
{ memberLimit: quantity }
@@ -55,16 +59,67 @@ export class TeamQuotaOverride {
}
@OnEvent('workspace.subscription.canceled')
async onSubscriptionCanceled({
async onWorkspaceSubscriptionCanceled({
workspaceId,
plan,
}: EventPayload<'workspace.subscription.canceled'>) {
switch (plan) {
case 'team':
case SubscriptionPlan.Team:
await this.manager.removeTeamWorkspace(workspaceId);
break;
default:
break;
}
}
@OnEvent('user.subscription.activated')
async onUserSubscriptionUpdated({
userId,
plan,
recurring,
}: EventPayload<'user.subscription.activated'>) {
switch (plan) {
case SubscriptionPlan.AI:
await this.feature.addCopilot(userId, 'subscription activated');
break;
case SubscriptionPlan.Pro:
await this.quotaService.switchUserQuota(
userId,
recurring === 'lifetime'
? QuotaType.LifetimeProPlanV1
: QuotaType.ProPlanV1,
'subscription activated'
);
break;
default:
break;
}
}
@OnEvent('user.subscription.canceled')
async onUserSubscriptionCanceled({
userId,
plan,
}: EventPayload<'user.subscription.canceled'>) {
switch (plan) {
case SubscriptionPlan.AI:
await this.feature.removeCopilot(userId);
break;
case SubscriptionPlan.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.quotaService.getUserQuota(userId);
if (quota.feature.name !== QuotaType.LifetimeProPlanV1) {
await this.quotaService.switchUserQuota(
userId,
QuotaType.FreePlanV1,
'subscription canceled'
);
}
break;
}
default:
break;
}
}
}