mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00:00
fix(server): cannot revalidate licenses (#9982)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||||
import { InstalledLicense, PrismaClient } from '@prisma/client';
|
import { InstalledLicense, PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ interface License {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LicenseService {
|
export class LicenseService implements OnModuleInit {
|
||||||
private readonly logger = new Logger(LicenseService.name);
|
private readonly logger = new Logger(LicenseService.name);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -34,6 +34,55 @@ export class LicenseService {
|
|||||||
private readonly models: Models
|
private readonly models: Models
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
if (this.config.isSelfhosted) {
|
||||||
|
this.event.on(
|
||||||
|
'workspace.subscription.activated',
|
||||||
|
this.onWorkspaceSubscriptionUpdated
|
||||||
|
);
|
||||||
|
this.event.on(
|
||||||
|
'workspace.subscription.canceled',
|
||||||
|
this.onWorkspaceSubscriptionCanceled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly onWorkspaceSubscriptionUpdated = async ({
|
||||||
|
workspaceId,
|
||||||
|
plan,
|
||||||
|
recurring,
|
||||||
|
quantity,
|
||||||
|
}: Events['workspace.subscription.activated']) => {
|
||||||
|
switch (plan) {
|
||||||
|
case SubscriptionPlan.SelfHostedTeam:
|
||||||
|
await this.models.workspaceFeature.add(
|
||||||
|
workspaceId,
|
||||||
|
'team_plan_v1',
|
||||||
|
`${recurring} team subscription activated`,
|
||||||
|
{
|
||||||
|
memberLimit: quantity,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await this.permission.refreshSeatStatus(workspaceId, quantity);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly onWorkspaceSubscriptionCanceled = async ({
|
||||||
|
workspaceId,
|
||||||
|
plan,
|
||||||
|
}: Events['workspace.subscription.canceled']) => {
|
||||||
|
switch (plan) {
|
||||||
|
case SubscriptionPlan.SelfHostedTeam:
|
||||||
|
await this.models.workspaceFeature.remove(workspaceId, 'team_plan_v1');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async getLicense(workspaceId: string) {
|
async getLicense(workspaceId: string) {
|
||||||
return this.db.installedLicense.findUnique({
|
return this.db.installedLicense.findUnique({
|
||||||
select: {
|
select: {
|
||||||
@@ -208,10 +257,14 @@ export class LicenseService {
|
|||||||
|
|
||||||
@Cron(CronExpression.EVERY_10_MINUTES)
|
@Cron(CronExpression.EVERY_10_MINUTES)
|
||||||
async licensesHealthCheck() {
|
async licensesHealthCheck() {
|
||||||
|
if (!this.config.isSelfhosted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const licenses = await this.db.installedLicense.findMany({
|
const licenses = await this.db.installedLicense.findMany({
|
||||||
where: {
|
where: {
|
||||||
validatedAt: {
|
validatedAt: {
|
||||||
lte: new Date(Date.now() - 1000 * 60 * 60),
|
lte: new Date(Date.now() - 1000 * 60 * 60 /* 1h */),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -224,7 +277,12 @@ export class LicenseService {
|
|||||||
private async revalidateLicense(license: InstalledLicense) {
|
private async revalidateLicense(license: InstalledLicense) {
|
||||||
try {
|
try {
|
||||||
const res = await this.fetch<License>(
|
const res = await this.fetch<License>(
|
||||||
`/api/team/licenses/${license.key}/health`
|
`/api/team/licenses/${license.key}/health`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'x-validate-key': license.validateKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.db.installedLicense.update({
|
await this.db.installedLicense.update({
|
||||||
@@ -272,18 +330,23 @@ export class LicenseService {
|
|||||||
init?: RequestInit
|
init?: RequestInit
|
||||||
): Promise<T & { res: Response }> {
|
): Promise<T & { res: Response }> {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('https://app.affine.pro' + path, {
|
const res = await fetch(
|
||||||
...init,
|
process.env.AFFINE_PRO_SERVER_ENDPOINT ??
|
||||||
headers: {
|
'https://app.affine.pro' + path,
|
||||||
'Content-Type': 'application/json',
|
{
|
||||||
},
|
...init,
|
||||||
});
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...init?.headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const body = (await res.json()) as UserFriendlyError;
|
const body = (await res.json()) as UserFriendlyError;
|
||||||
throw new UserFriendlyError(
|
throw new UserFriendlyError(
|
||||||
body.type as any,
|
body.type as any,
|
||||||
body.name as any,
|
body.name.toLowerCase() as any,
|
||||||
body.message,
|
body.message,
|
||||||
body.data
|
body.data
|
||||||
);
|
);
|
||||||
@@ -306,42 +369,4 @@ export class LicenseService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent('workspace.subscription.activated')
|
|
||||||
async onWorkspaceSubscriptionUpdated({
|
|
||||||
workspaceId,
|
|
||||||
plan,
|
|
||||||
recurring,
|
|
||||||
quantity,
|
|
||||||
}: Events['workspace.subscription.activated']) {
|
|
||||||
switch (plan) {
|
|
||||||
case SubscriptionPlan.SelfHostedTeam:
|
|
||||||
await this.models.workspaceFeature.add(
|
|
||||||
workspaceId,
|
|
||||||
'team_plan_v1',
|
|
||||||
`${recurring} team subscription activated`,
|
|
||||||
{
|
|
||||||
memberLimit: quantity,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await this.permission.refreshSeatStatus(workspaceId, quantity);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnEvent('workspace.subscription.canceled')
|
|
||||||
async onWorkspaceSubscriptionCanceled({
|
|
||||||
workspaceId,
|
|
||||||
plan,
|
|
||||||
}: Events['workspace.subscription.canceled']) {
|
|
||||||
switch (plan) {
|
|
||||||
case SubscriptionPlan.SelfHostedTeam:
|
|
||||||
await this.models.workspaceFeature.remove(workspaceId, 'team_plan_v1');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,21 +215,21 @@ export const DEFAULT_PRICES = new Map([
|
|||||||
// team
|
// team
|
||||||
[
|
[
|
||||||
`${SubscriptionPlan.Team}_${SubscriptionRecurring.Monthly}`,
|
`${SubscriptionPlan.Team}_${SubscriptionRecurring.Monthly}`,
|
||||||
{ product: 'AFFiNE Team(per seat)', price: 1500 },
|
{ product: 'AFFiNE Team(per seat)', price: 1440 },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
`${SubscriptionPlan.Team}_${SubscriptionRecurring.Yearly}`,
|
`${SubscriptionPlan.Team}_${SubscriptionRecurring.Yearly}`,
|
||||||
{ product: 'AFFiNE Team(per seat)', price: 14400 },
|
{ product: 'AFFiNE Team(per seat)', price: 12000 },
|
||||||
],
|
],
|
||||||
|
|
||||||
// selfhost team
|
// selfhost team
|
||||||
[
|
[
|
||||||
`${SubscriptionPlan.SelfHostedTeam}_${SubscriptionRecurring.Monthly}`,
|
`${SubscriptionPlan.SelfHostedTeam}_${SubscriptionRecurring.Monthly}`,
|
||||||
{ product: 'AFFiNE Self-hosted Team(per seat)', price: 1500 },
|
{ product: 'AFFiNE Self-hosted Team(per seat)', price: 1440 },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
`${SubscriptionPlan.SelfHostedTeam}_${SubscriptionRecurring.Yearly}`,
|
`${SubscriptionPlan.SelfHostedTeam}_${SubscriptionRecurring.Yearly}`,
|
||||||
{ product: 'AFFiNE Self-hosted Team(per seat)', price: 14400 },
|
{ product: 'AFFiNE Self-hosted Team(per seat)', price: 12000 },
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user