chore: cleanup legacy logic (#15072)

This commit is contained in:
DarkSky
2026-06-03 16:20:15 +08:00
committed by GitHub
parent 8c0e1ba04e
commit 81760fd45c
142 changed files with 3351 additions and 2449 deletions
@@ -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'
);
+12 -1
View File
@@ -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',