mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-04 02:49:57 +08:00
chore: cleanup legacy logic (#15072)
This commit is contained in:
@@ -187,9 +187,7 @@ export class AuthResolver {
|
||||
@Mutation(() => Boolean)
|
||||
async sendChangeEmail(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Args('callbackUrl') callbackUrl: string,
|
||||
// @deprecated
|
||||
@Args('email', { nullable: true }) _email?: string
|
||||
@Args('callbackUrl') callbackUrl: string
|
||||
) {
|
||||
if (!user.emailVerified) {
|
||||
throw new EmailVerificationRequired();
|
||||
|
||||
@@ -299,18 +299,13 @@ export class CommentResolver {
|
||||
@CurrentUser() me: UserType,
|
||||
@Parent() workspace: WorkspaceType,
|
||||
@Args('docId') docId: string,
|
||||
@Args({
|
||||
name: 'pagination',
|
||||
})
|
||||
@Args({ name: 'pagination' })
|
||||
pagination: PaginationInput
|
||||
): Promise<PaginatedCommentChangeObjectType> {
|
||||
// DEPRECATED-0.26-COMPAT(realtime): remove after server no longer supports 0.26.x clients.
|
||||
await this.assertPermission(
|
||||
me,
|
||||
{
|
||||
workspaceId: workspace.id,
|
||||
docId,
|
||||
},
|
||||
{ workspaceId: workspace.id, docId },
|
||||
'Doc.Comments.Read'
|
||||
);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Config,
|
||||
CryptoHelper,
|
||||
getOrGenRequestId,
|
||||
safeFetch,
|
||||
UserFriendlyError,
|
||||
} from '../../base';
|
||||
import { Models } from '../../models';
|
||||
@@ -303,7 +304,17 @@ export class RpcDocReader extends DatabaseDocReader {
|
||||
if (body) {
|
||||
requestInit.body = body;
|
||||
}
|
||||
const res = await fetch(url, requestInit);
|
||||
const res = await safeFetch(url, requestInit, {
|
||||
timeoutMs: 10_000,
|
||||
maxRedirects: 0,
|
||||
maxBytes: 50 * 1024 * 1024,
|
||||
allowedHeaders: [
|
||||
'content-type',
|
||||
'x-access-token',
|
||||
'x-cloud-trace-context',
|
||||
],
|
||||
allowPrivateTargetOrigin: true,
|
||||
});
|
||||
if (!res.ok) {
|
||||
if (res.status === 404) {
|
||||
return null;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { Entitlement, Prisma, PrismaClient } from '@prisma/client';
|
||||
|
||||
import { BadRequest, CryptoHelper, EventBus } from '../../base';
|
||||
import { resolveEntitlementV1 } from '../../native';
|
||||
import { checkLicenseHealth, resolveEntitlementV1 } from '../../native';
|
||||
import {
|
||||
SubscriptionPlan,
|
||||
SubscriptionRecurring,
|
||||
@@ -47,15 +47,7 @@ export interface SelfhostLicenseEntitlementInput {
|
||||
license?: Buffer | null;
|
||||
}
|
||||
|
||||
interface RemoteSelfhostLicense {
|
||||
plan: string;
|
||||
recurring: string;
|
||||
quantity: number;
|
||||
endAt: number;
|
||||
}
|
||||
|
||||
const REMOTE_SELFHOST_LICENSE_REVALIDATE_INTERVAL = 1000 * 60 * 10;
|
||||
const REMOTE_SELFHOST_LICENSE_HEALTH_TIMEOUT = 10_000;
|
||||
|
||||
declare global {
|
||||
interface Events {
|
||||
@@ -844,44 +836,25 @@ export class EntitlementService {
|
||||
return cached.entitlement;
|
||||
}
|
||||
|
||||
const endpoint =
|
||||
process.env.AFFINE_PRO_SERVER_ENDPOINT ?? 'https://app.affine.pro';
|
||||
const signal = AbortSignal.timeout(REMOTE_SELFHOST_LICENSE_HEALTH_TIMEOUT);
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${endpoint}/api/team/licenses/${entitlement.subjectId}/health`,
|
||||
{
|
||||
signal,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-validate-key': metadata.validateKey,
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!res.ok) {
|
||||
if (res.status >= 500) {
|
||||
const res = await checkLicenseHealth({
|
||||
licenseKey: entitlement.subjectId,
|
||||
validateKey: metadata.validateKey,
|
||||
});
|
||||
if (res.error) {
|
||||
if (res.error.status >= 500) {
|
||||
return this.remoteSelfhostFallbackEntitlement(entitlement);
|
||||
}
|
||||
|
||||
await this.markRemoteSelfhostLicenseNeedsReupload(
|
||||
entitlement,
|
||||
`Remote license health check failed: ${res.status}`
|
||||
`Remote license health check failed: ${res.error.status}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const payload = (await res
|
||||
.json()
|
||||
.catch(() => null)) as RemoteSelfhostLicense | null;
|
||||
if (!payload) {
|
||||
return this.remoteSelfhostFallbackEntitlement(entitlement);
|
||||
}
|
||||
const expiresAt = this.remoteSelfhostLicenseExpiresAt(payload.endAt);
|
||||
if (
|
||||
payload.plan !== SubscriptionPlan.SelfHostedTeam ||
|
||||
payload.quantity < 1 ||
|
||||
!expiresAt
|
||||
) {
|
||||
const license = res.license;
|
||||
if (!license || license.plan !== SubscriptionPlan.SelfHostedTeam) {
|
||||
await this.markRemoteSelfhostLicenseNeedsReupload(
|
||||
entitlement,
|
||||
'Remote license health payload is invalid.'
|
||||
@@ -889,17 +862,17 @@ export class EntitlementService {
|
||||
return null;
|
||||
}
|
||||
|
||||
const validateKey =
|
||||
res.headers.get('x-next-validate-key') ?? metadata.validateKey;
|
||||
const expiresAt = new Date(license.expiresAt);
|
||||
const validateKey = license.validateKey || metadata.validateKey;
|
||||
const [updated] = await Promise.all([
|
||||
this.db.entitlement.update({
|
||||
where: { id: entitlement.id },
|
||||
data: {
|
||||
status: 'active',
|
||||
quantity: this.normalizedQuantity(payload.quantity),
|
||||
quantity: this.normalizedQuantity(license.quantity),
|
||||
metadata: {
|
||||
...metadata,
|
||||
recurring: payload.recurring,
|
||||
recurring: license.recurring,
|
||||
validateKey,
|
||||
remoteValidated: true,
|
||||
errorCode: null,
|
||||
@@ -913,8 +886,8 @@ export class EntitlementService {
|
||||
.updateMany({
|
||||
where: { key: entitlement.subjectId },
|
||||
data: {
|
||||
quantity: this.normalizedQuantity(payload.quantity),
|
||||
recurring: payload.recurring,
|
||||
quantity: this.normalizedQuantity(license.quantity),
|
||||
recurring: license.recurring,
|
||||
validateKey,
|
||||
validatedAt: new Date(),
|
||||
expiredAt: expiresAt,
|
||||
@@ -950,14 +923,6 @@ export class EntitlementService {
|
||||
return cached.entitlement;
|
||||
}
|
||||
|
||||
private remoteSelfhostLicenseExpiresAt(endAt: unknown) {
|
||||
const expiresAt = new Date(endAt as string | number | Date);
|
||||
if (!Number.isFinite(expiresAt.getTime()) || expiresAt <= new Date()) {
|
||||
return null;
|
||||
}
|
||||
return expiresAt;
|
||||
}
|
||||
|
||||
private async markRemoteSelfhostLicenseNeedsReupload(
|
||||
entitlement: Entitlement,
|
||||
reason: string
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
AdminFeatureManagementResolver,
|
||||
UserFeatureResolver,
|
||||
} from './resolver';
|
||||
import { EarlyAccessType, FeatureService } from './service';
|
||||
import { FeatureService } from './service';
|
||||
|
||||
@Module({
|
||||
imports: [EntitlementModule],
|
||||
@@ -18,5 +18,5 @@ import { EarlyAccessType, FeatureService } from './service';
|
||||
})
|
||||
export class FeatureModule {}
|
||||
|
||||
export { EarlyAccessType, FeatureService };
|
||||
export { FeatureService };
|
||||
export { AvailableUserFeatureConfig } from './types';
|
||||
|
||||
@@ -4,11 +4,6 @@ import { Models } from '../../models';
|
||||
|
||||
const STAFF = ['@toeverything.info', '@affine.pro'];
|
||||
|
||||
export enum EarlyAccessType {
|
||||
App = 'app',
|
||||
AI = 'ai',
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class FeatureService {
|
||||
protected logger = new Logger(FeatureService.name);
|
||||
@@ -32,15 +27,4 @@ export class FeatureService {
|
||||
addAdmin(userId: string) {
|
||||
return this.models.userFeature.add(userId, 'administrator', 'Admin user');
|
||||
}
|
||||
|
||||
// ======== Early Access ========
|
||||
async isEarlyAccessUser(
|
||||
userId: string,
|
||||
type: EarlyAccessType = EarlyAccessType.App
|
||||
) {
|
||||
return await this.models.userFeature.has(
|
||||
userId,
|
||||
type === EarlyAccessType.App ? 'early_access' : 'ai_early_access'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,10 @@ import { Feature, UserFeatureName } from '../../models';
|
||||
@Injectable()
|
||||
export class AvailableUserFeatureConfig {
|
||||
availableUserFeatures(): Set<UserFeatureName> {
|
||||
return new Set([Feature.Admin, Feature.EarlyAccess, Feature.AIEarlyAccess]);
|
||||
return new Set([Feature.Admin]);
|
||||
}
|
||||
|
||||
configurableUserFeatures(): Set<UserFeatureName> {
|
||||
return new Set(
|
||||
env.selfhosted
|
||||
? [Feature.Admin]
|
||||
: [Feature.EarlyAccess, Feature.AIEarlyAccess, Feature.Admin]
|
||||
);
|
||||
return new Set([Feature.Admin]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
Args,
|
||||
ID,
|
||||
Int,
|
||||
Mutation,
|
||||
ResolveField,
|
||||
Resolver,
|
||||
} from '@nestjs/graphql';
|
||||
import { Args, ID, Mutation, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
MentionUserDocAccessDenied,
|
||||
@@ -45,16 +38,6 @@ export class UserNotificationResolver {
|
||||
return paginate(notifications, 'createdAt', pagination, totalCount);
|
||||
}
|
||||
|
||||
@ResolveField(() => Int, {
|
||||
description: 'Get user notification count',
|
||||
deprecationReason:
|
||||
'Use realtime subscription "notification.count.changed" instead.',
|
||||
})
|
||||
async notificationCount(@CurrentUser() me: UserType): Promise<number> {
|
||||
// DEPRECATED-0.26-COMPAT(realtime): remove after server no longer supports 0.26.x clients.
|
||||
return await this.service.countByUserId(me.id);
|
||||
}
|
||||
|
||||
@Mutation(() => ID, {
|
||||
description: 'mention user in a doc',
|
||||
})
|
||||
|
||||
@@ -155,7 +155,6 @@ test('quota service exposes history period in seconds', async t => {
|
||||
usedStorageQuota: 0,
|
||||
memberCount: 1,
|
||||
overcapacityMemberCount: 0,
|
||||
usedSize: 0,
|
||||
}).historyPeriod,
|
||||
'30 days'
|
||||
);
|
||||
|
||||
@@ -76,7 +76,6 @@ export class QuotaService {
|
||||
usedStorageQuota: Number(state.usedStorageQuota),
|
||||
memberCount: state.memberCount,
|
||||
overcapacityMemberCount: state.overcapacityMemberCount,
|
||||
usedSize: Number(state.usedStorageQuota),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -118,12 +118,4 @@ export class WorkspaceQuotaType implements Partial<WorkspaceQuota> {
|
||||
|
||||
@Field()
|
||||
humanReadable!: WorkspaceQuotaHumanReadableType;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Field(() => SafeIntResolver, {
|
||||
deprecationReason: 'use `usedStorageQuota` instead',
|
||||
})
|
||||
usedSize!: number;
|
||||
}
|
||||
|
||||
@@ -166,16 +166,6 @@ export class InviteResult {
|
||||
})
|
||||
inviteId?: string;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Field(() => Boolean, {
|
||||
description: 'Invite email sent success',
|
||||
deprecationReason: 'Notification will be sent asynchronously',
|
||||
defaultValue: true,
|
||||
})
|
||||
sentSuccess?: boolean;
|
||||
|
||||
@Field(() => GraphQLJSONObject, {
|
||||
nullable: true,
|
||||
description: 'Invite error',
|
||||
|
||||
Reference in New Issue
Block a user