mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-10 13:38:49 +08:00
feat: reduce backend (#14251)
#### PR Dependency Tree * **PR #14251** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Current user profile now exposes access tokens, revealed tokens, and detailed calendar accounts/subscriptions. * Workspace now exposes permissions, calendars, calendar events, and a workspace-scoped blob upload part URL. * New document-update mutation for applying doc updates. * **API Changes** * validateAppConfig is now a query (mutation deprecated). * Several legacy top-level calendar/blob endpoints deprecated in favor of user/workspace fields. * **Refactor** * Calendar, blob-upload and access-token surfaces reorganized to use user/workspace-centric fields. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AccessTokenResolver } from './resolver';
|
||||
import { AccessTokenResolver, UserAccessTokenResolver } from './resolver';
|
||||
|
||||
@Module({
|
||||
providers: [AccessTokenResolver],
|
||||
providers: [AccessTokenResolver, UserAccessTokenResolver],
|
||||
})
|
||||
export class AccessTokenModule {}
|
||||
|
||||
@@ -3,34 +3,17 @@ import {
|
||||
Field,
|
||||
InputType,
|
||||
Mutation,
|
||||
ObjectType,
|
||||
Parent,
|
||||
Query,
|
||||
ResolveField,
|
||||
Resolver,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import { ActionForbidden } from '../../base';
|
||||
import { Models } from '../../models';
|
||||
import { CurrentUser } from '../auth/session';
|
||||
|
||||
@ObjectType()
|
||||
class AccessToken {
|
||||
@Field()
|
||||
id!: string;
|
||||
|
||||
@Field()
|
||||
name!: string;
|
||||
|
||||
@Field()
|
||||
createdAt!: Date;
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
expiresAt!: Date | null;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
class RevealedAccessToken extends AccessToken {
|
||||
@Field()
|
||||
token!: string;
|
||||
}
|
||||
import { UserType } from '../user';
|
||||
import { AccessToken, RevealedAccessToken } from './types';
|
||||
|
||||
@InputType()
|
||||
class GenerateAccessTokenInput {
|
||||
@@ -45,12 +28,16 @@ class GenerateAccessTokenInput {
|
||||
export class AccessTokenResolver {
|
||||
constructor(private readonly models: Models) {}
|
||||
|
||||
@Query(() => [AccessToken])
|
||||
@Query(() => [AccessToken], {
|
||||
deprecationReason: 'use currentUser.accessTokens',
|
||||
})
|
||||
async accessTokens(@CurrentUser() user: CurrentUser): Promise<AccessToken[]> {
|
||||
return await this.models.accessToken.list(user.id);
|
||||
}
|
||||
|
||||
@Query(() => [RevealedAccessToken])
|
||||
@Query(() => [RevealedAccessToken], {
|
||||
deprecationReason: 'use currentUser.revealedAccessTokens',
|
||||
})
|
||||
async revealedAccessTokens(
|
||||
@CurrentUser() user: CurrentUser
|
||||
): Promise<RevealedAccessToken[]> {
|
||||
@@ -78,3 +65,30 @@ export class AccessTokenResolver {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Resolver(() => UserType)
|
||||
export class UserAccessTokenResolver {
|
||||
constructor(private readonly models: Models) {}
|
||||
|
||||
@ResolveField(() => [AccessToken])
|
||||
async accessTokens(
|
||||
@CurrentUser() currentUser: CurrentUser,
|
||||
@Parent() user: UserType
|
||||
): Promise<AccessToken[]> {
|
||||
if (!currentUser || currentUser.id !== user.id) {
|
||||
throw new ActionForbidden();
|
||||
}
|
||||
return await this.models.accessToken.list(user.id);
|
||||
}
|
||||
|
||||
@ResolveField(() => [RevealedAccessToken])
|
||||
async revealedAccessTokens(
|
||||
@CurrentUser() currentUser: CurrentUser,
|
||||
@Parent() user: UserType
|
||||
): Promise<RevealedAccessToken[]> {
|
||||
if (!currentUser || currentUser.id !== user.id) {
|
||||
throw new ActionForbidden();
|
||||
}
|
||||
return await this.models.accessToken.list(user.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class AccessToken {
|
||||
@Field()
|
||||
id!: string;
|
||||
|
||||
@Field()
|
||||
name!: string;
|
||||
|
||||
@Field()
|
||||
createdAt!: Date;
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
expiresAt!: Date | null;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class RevealedAccessToken extends AccessToken {
|
||||
@Field()
|
||||
token!: string;
|
||||
}
|
||||
@@ -230,13 +230,31 @@ export class AppConfigResolver {
|
||||
return await this.service.updateConfig(me.id, updates);
|
||||
}
|
||||
|
||||
@Mutation(() => [AppConfigValidateResult], {
|
||||
@Query(() => [AppConfigValidateResult], {
|
||||
description: 'validate app configuration',
|
||||
})
|
||||
async validateAppConfig(
|
||||
@Args('updates', { type: () => [UpdateAppConfigInput] })
|
||||
updates: UpdateAppConfigInput[]
|
||||
): Promise<AppConfigValidateResult[]> {
|
||||
return this.validateConfigInternal(updates);
|
||||
}
|
||||
|
||||
@Mutation(() => [AppConfigValidateResult], {
|
||||
description: 'validate app configuration',
|
||||
deprecationReason: 'use Query.validateAppConfig',
|
||||
name: 'validateAppConfig',
|
||||
})
|
||||
async validateAppConfigMutation(
|
||||
@Args('updates', { type: () => [UpdateAppConfigInput] })
|
||||
updates: UpdateAppConfigInput[]
|
||||
): Promise<AppConfigValidateResult[]> {
|
||||
return this.validateConfigInternal(updates);
|
||||
}
|
||||
|
||||
private validateConfigInternal(
|
||||
updates: UpdateAppConfigInput[]
|
||||
): AppConfigValidateResult[] {
|
||||
const errors = this.service.validateConfig(updates);
|
||||
|
||||
return updates.map(update => {
|
||||
|
||||
@@ -156,6 +156,19 @@ export class WorkspaceBlobResolver {
|
||||
return this.storage.totalSize(workspace.id);
|
||||
}
|
||||
|
||||
@ResolveField(() => BlobUploadPart, {
|
||||
description: 'Get blob upload part url',
|
||||
})
|
||||
async blobUploadPartUrl(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Parent() workspace: WorkspaceType,
|
||||
@Args('key') key: string,
|
||||
@Args('uploadId') uploadId: string,
|
||||
@Args('partNumber', { type: () => Int }) partNumber: number
|
||||
): Promise<BlobUploadPart> {
|
||||
return this.getUploadPart(user, workspace.id, key, uploadId, partNumber);
|
||||
}
|
||||
|
||||
@Query(() => WorkspaceBlobSizes, {
|
||||
deprecationReason: 'use `user.quotaUsage` instead',
|
||||
})
|
||||
@@ -399,13 +412,40 @@ export class WorkspaceBlobResolver {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Mutation(() => BlobUploadPart)
|
||||
@Mutation(() => BlobUploadPart, {
|
||||
deprecationReason: 'use WorkspaceType.blobUploadPartUrl',
|
||||
})
|
||||
async getBlobUploadPartUrl(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Args('workspaceId') workspaceId: string,
|
||||
@Args('key') key: string,
|
||||
@Args('uploadId') uploadId: string,
|
||||
@Args('partNumber', { type: () => Int }) partNumber: number
|
||||
): Promise<BlobUploadPart> {
|
||||
return this.getUploadPart(user, workspaceId, key, uploadId, partNumber);
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async abortBlobUpload(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Args('workspaceId') workspaceId: string,
|
||||
@Args('key') key: string,
|
||||
@Args('uploadId') uploadId: string
|
||||
) {
|
||||
await this.ac
|
||||
.user(user.id)
|
||||
.workspace(workspaceId)
|
||||
.assert('Workspace.Blobs.Write');
|
||||
|
||||
return this.storage.abortMultipartUpload(workspaceId, key, uploadId);
|
||||
}
|
||||
|
||||
private async getUploadPart(
|
||||
user: CurrentUser,
|
||||
workspaceId: string,
|
||||
key: string,
|
||||
uploadId: string,
|
||||
partNumber: number
|
||||
): Promise<BlobUploadPart> {
|
||||
await this.ac
|
||||
.user(user.id)
|
||||
@@ -429,21 +469,6 @@ export class WorkspaceBlobResolver {
|
||||
};
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async abortBlobUpload(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
@Args('workspaceId') workspaceId: string,
|
||||
@Args('key') key: string,
|
||||
@Args('uploadId') uploadId: string
|
||||
) {
|
||||
await this.ac
|
||||
.user(user.id)
|
||||
.workspace(workspaceId)
|
||||
.assert('Workspace.Blobs.Write');
|
||||
|
||||
return this.storage.abortMultipartUpload(workspaceId, key, uploadId);
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean)
|
||||
async deleteBlob(
|
||||
@CurrentUser() user: CurrentUser,
|
||||
|
||||
Reference in New Issue
Block a user