refactor(server): separate page visibility from workspace permission (#4836)

This commit is contained in:
liuyi
2023-11-06 11:49:39 +08:00
committed by GitHub
parent e8987457ab
commit f491ff94cc
19 changed files with 796 additions and 362 deletions

View File

@@ -81,7 +81,7 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
@MessageBody() workspaceId: string,
@ConnectedSocket() client: Socket
): Promise<EventResponse<{ clientId: string }>> {
const canWrite = await this.permissions.tryCheck(
const canWrite = await this.permissions.tryCheckWorkspace(
workspaceId,
user.id,
Permission.Write
@@ -181,7 +181,10 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
}
): Promise<{ missing: string; state?: string } | false> {
if (!client.rooms.has(workspaceId)) {
const canRead = await this.permissions.tryCheck(workspaceId, user.id);
const canRead = await this.permissions.tryCheckWorkspace(
workspaceId,
user.id
);
if (!canRead) {
return false;
}
@@ -266,7 +269,10 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
}
): Promise<EventResponse<{ missing: string; state?: string }>> {
if (!client.rooms.has(workspaceId)) {
const canRead = await this.permissions.tryCheck(workspaceId, user.id);
const canRead = await this.permissions.tryCheckWorkspace(
workspaceId,
user.id
);
if (!canRead) {
return {
error: new AccessDeniedError(workspaceId),

View File

@@ -4,12 +4,17 @@ import { DocModule } from '../doc';
import { UsersService } from '../users';
import { WorkspacesController } from './controller';
import { PermissionService } from './permission';
import { WorkspaceResolver } from './resolver';
import { PagePermissionResolver, WorkspaceResolver } from './resolver';
@Module({
imports: [DocModule.forFeature()],
controllers: [WorkspacesController],
providers: [WorkspaceResolver, PermissionService, UsersService],
providers: [
WorkspaceResolver,
PermissionService,
UsersService,
PagePermissionResolver,
],
exports: [PermissionService],
})
export class WorkspaceModule {}

View File

@@ -4,15 +4,20 @@ import { Prisma } from '@prisma/client';
import { PrismaService } from '../../prisma';
import { Permission } from './types';
export enum PublicPageMode {
Page,
Edgeless,
}
@Injectable()
export class PermissionService {
constructor(private readonly prisma: PrismaService) {}
/// Start regin: workspace permission
async get(ws: string, user: string) {
const data = await this.prisma.userWorkspacePermission.findFirst({
const data = await this.prisma.workspaceUserPermission.findFirst({
where: {
workspaceId: ws,
subPageId: null,
userId: user,
accepted: true,
},
@@ -22,7 +27,7 @@ export class PermissionService {
}
async getWorkspaceOwner(workspaceId: string) {
return this.prisma.userWorkspacePermission.findFirstOrThrow({
return this.prisma.workspaceUserPermission.findFirstOrThrow({
where: {
workspaceId,
type: Permission.Owner,
@@ -34,7 +39,7 @@ export class PermissionService {
}
async tryGetWorkspaceOwner(workspaceId: string) {
return this.prisma.userWorkspacePermission.findFirst({
return this.prisma.workspaceUserPermission.findFirst({
where: {
workspaceId,
type: Permission.Owner,
@@ -46,77 +51,76 @@ export class PermissionService {
}
async isAccessible(ws: string, id: string, user?: string): Promise<boolean> {
if (user) {
const hasPermission = await this.tryCheck(ws, user);
if (hasPermission) return true;
// workspace
if (ws === id) {
return this.tryCheckWorkspace(ws, user, Permission.Read);
}
// check if this is a public workspace
const count = await this.prisma.workspace.count({
where: { id: ws, public: true },
});
if (count > 0) {
return true;
}
// check whether this is a public subpage
const workspace = await this.prisma.userWorkspacePermission.findMany({
where: {
workspaceId: ws,
userId: null,
},
});
const subpages = workspace
.map(ws => ws.subPageId)
.filter((v): v is string => !!v);
if (subpages.length > 0 && ws === id) {
// rootDoc is always accessible when there is a public subpage
return true;
} else {
// check if this is a public subpage
return subpages.some(subpage => id === subpage);
}
return this.tryCheckPage(ws, id, user);
}
async check(
async checkWorkspace(
ws: string,
user: string,
user?: string,
permission: Permission = Permission.Read
) {
if (!(await this.tryCheck(ws, user, permission))) {
if (!(await this.tryCheckWorkspace(ws, user, permission))) {
throw new ForbiddenException('Permission denied');
}
}
async tryCheck(
async tryCheckWorkspace(
ws: string,
user: string,
user?: string,
permission: Permission = Permission.Read
) {
// If the permission is read, we should check if the workspace is public
if (permission === Permission.Read) {
const data = await this.prisma.workspace.count({
const count = await this.prisma.workspace.count({
where: { id: ws, public: true },
});
if (data > 0) {
// workspace is public
// accessible
if (count > 0) {
return true;
}
const publicPage = await this.prisma.workspacePage.findFirst({
select: {
pageId: true,
},
where: {
workspaceId: ws,
public: true,
},
});
// has any public pages
if (publicPage) {
return true;
}
}
const data = await this.prisma.userWorkspacePermission.count({
where: {
workspaceId: ws,
subPageId: null,
userId: user,
accepted: true,
type: {
gte: permission,
if (user) {
// normally check if the user has the permission
const count = await this.prisma.workspaceUserPermission.count({
where: {
workspaceId: ws,
userId: user,
accepted: true,
type: {
gte: permission,
},
},
},
});
});
return data > 0;
return count > 0;
}
// unsigned in, workspace is not public
// unaccessible
return false;
}
async grant(
@@ -124,10 +128,9 @@ export class PermissionService {
user: string,
permission: Permission = Permission.Read
): Promise<string> {
const data = await this.prisma.userWorkspacePermission.findFirst({
const data = await this.prisma.workspaceUserPermission.findFirst({
where: {
workspaceId: ws,
subPageId: null,
userId: user,
accepted: true,
},
@@ -136,9 +139,12 @@ export class PermissionService {
if (data) {
const [p] = await this.prisma.$transaction(
[
this.prisma.userWorkspacePermission.update({
this.prisma.workspaceUserPermission.update({
where: {
id: data.id,
workspaceId_userId: {
workspaceId: ws,
userId: user,
},
},
data: {
type: permission,
@@ -147,7 +153,7 @@ export class PermissionService {
// If the new permission is owner, we need to revoke old owner
permission === Permission.Owner
? this.prisma.userWorkspacePermission.updateMany({
? this.prisma.workspaceUserPermission.updateMany({
where: {
workspaceId: ws,
type: Permission.Owner,
@@ -166,11 +172,10 @@ export class PermissionService {
return p.id;
}
return this.prisma.userWorkspacePermission
return this.prisma.workspaceUserPermission
.create({
data: {
workspaceId: ws,
subPageId: null,
userId: user,
type: permission,
},
@@ -178,10 +183,10 @@ export class PermissionService {
.then(p => p.id);
}
async getInvitationById(inviteId: string, workspaceId: string) {
return this.prisma.userWorkspacePermission.findUniqueOrThrow({
async getWorkspaceInvitation(invitationId: string, workspaceId: string) {
return this.prisma.workspaceUserPermission.findUniqueOrThrow({
where: {
id: inviteId,
id: invitationId,
workspaceId,
},
include: {
@@ -190,11 +195,11 @@ export class PermissionService {
});
}
async acceptById(ws: string, id: string) {
const result = await this.prisma.userWorkspacePermission.updateMany({
async acceptWorkspaceInvitation(invitationId: string, workspaceId: string) {
const result = await this.prisma.workspaceUserPermission.updateMany({
where: {
id,
workspaceId: ws,
id: invitationId,
workspaceId: workspaceId,
},
data: {
accepted: true,
@@ -204,27 +209,10 @@ export class PermissionService {
return result.count > 0;
}
async accept(ws: string, user: string) {
const result = await this.prisma.userWorkspacePermission.updateMany({
async revokeWorkspace(ws: string, user: string) {
const result = await this.prisma.workspaceUserPermission.deleteMany({
where: {
workspaceId: ws,
subPageId: null,
userId: user,
accepted: false,
},
data: {
accepted: true,
},
});
return result.count > 0;
}
async revoke(ws: string, user: string) {
const result = await this.prisma.userWorkspacePermission.deleteMany({
where: {
workspaceId: ws,
subPageId: null,
userId: user,
type: {
// We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading
@@ -235,56 +223,177 @@ export class PermissionService {
return result.count > 0;
}
/// End regin: workspace permission
async isPageAccessible(ws: string, page: string, user?: string) {
const data = await this.prisma.userWorkspacePermission.findFirst({
/// Start regin: page permission
async checkPagePermission(
ws: string,
page: string,
user?: string,
permission = Permission.Read
) {
if (!(await this.tryCheckPage(ws, page, user, permission))) {
throw new ForbiddenException('Permission denied');
}
}
async tryCheckPage(
ws: string,
page: string,
user?: string,
permission = Permission.Read
) {
// check whether page is public
const count = await this.prisma.workspacePage.count({
where: {
workspaceId: ws,
subPageId: page,
userId: user,
pageId: page,
public: true,
},
});
return data?.accepted || false;
// page is public
// accessible
if (count > 0) {
return true;
}
if (user) {
const count = await this.prisma.workspacePageUserPermission.count({
where: {
workspaceId: ws,
pageId: page,
userId: user,
accepted: true,
type: {
gte: permission,
},
},
});
// page shared to user
// accessible
if (count > 0) {
return true;
}
}
// check whether user has workspace related permission
return this.tryCheckWorkspace(ws, user, permission);
}
async publishPage(ws: string, page: string, mode = PublicPageMode.Page) {
return this.prisma.workspacePage.upsert({
where: {
workspaceId_pageId: {
workspaceId: ws,
pageId: page,
},
},
update: {
mode,
},
create: {
workspaceId: ws,
pageId: page,
mode,
public: true,
},
});
}
async revokePublicPage(ws: string, page: string) {
const workspacePage = await this.prisma.workspacePage.findUnique({
where: {
workspaceId_pageId: {
workspaceId: ws,
pageId: page,
},
},
});
if (!workspacePage) {
throw new Error('Page is not public');
}
return this.prisma.workspacePage.update({
where: {
workspaceId_pageId: {
workspaceId: ws,
pageId: page,
},
},
data: {
public: false,
},
});
}
async grantPage(
ws: string,
page: string,
user?: string,
user: string,
permission: Permission = Permission.Read
) {
const data = await this.prisma.userWorkspacePermission.findFirst({
const data = await this.prisma.workspacePageUserPermission.findFirst({
where: {
workspaceId: ws,
subPageId: page,
pageId: page,
userId: user,
accepted: true,
},
});
if (data) {
return data.accepted;
const [p] = await this.prisma.$transaction(
[
this.prisma.workspacePageUserPermission.update({
where: {
id: data.id,
},
data: {
type: permission,
},
}),
// If the new permission is owner, we need to revoke old owner
permission === Permission.Owner
? this.prisma.workspacePageUserPermission.updateMany({
where: {
workspaceId: ws,
pageId: page,
type: Permission.Owner,
userId: {
not: user,
},
},
data: {
type: Permission.Admin,
},
})
: null,
].filter(Boolean) as Prisma.PrismaPromise<any>[]
);
return p.id;
}
return this.prisma.userWorkspacePermission
return this.prisma.workspacePageUserPermission
.create({
data: {
workspaceId: ws,
subPageId: page,
pageId: page,
userId: user,
// if provide user id, user need to accept the invitation
accepted: user ? false : true,
type: permission,
},
})
.then(ret => ret.accepted);
.then(p => p.id);
}
async revokePage(ws: string, page: string, user?: string) {
const result = await this.prisma.userWorkspacePermission.deleteMany({
async revokePage(ws: string, page: string, user: string) {
const result = await this.prisma.workspacePageUserPermission.deleteMany({
where: {
workspaceId: ws,
subPageId: page,
pageId: page,
userId: user,
type: {
// We shouldn't revoke owner permission, should auto deleted by workspace/user delete cascading
@@ -295,4 +404,5 @@ export class PermissionService {
return result.count > 0;
}
/// End regin: page permission
}

View File

@@ -25,7 +25,11 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import type { User, Workspace } from '@prisma/client';
import type {
User,
Workspace,
WorkspacePage as PrismaWorkspacePage,
} from '@prisma/client';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { applyUpdate, Doc } from 'yjs';
@@ -39,7 +43,7 @@ import { MailService } from '../auth/mailer';
import { AuthService } from '../auth/service';
import { UsersService } from '../users';
import { UserType } from '../users/resolver';
import { PermissionService } from './permission';
import { PermissionService, PublicPageMode } from './permission';
import { Permission } from './types';
import { defaultWorkspaceAvatar } from './utils';
@@ -172,28 +176,11 @@ export class WorkspaceResolver {
complexity: 2,
})
memberCount(@Parent() workspace: WorkspaceType) {
return this.prisma.userWorkspacePermission.count({
where: {
workspaceId: workspace.id,
userId: {
not: null,
},
},
});
}
@ResolveField(() => [String], {
description: 'Shared pages of workspace',
complexity: 2,
})
async sharedPages(@Parent() workspace: WorkspaceType) {
const data = await this.prisma.userWorkspacePermission.findMany({
return this.prisma.workspaceUserPermission.count({
where: {
workspaceId: workspace.id,
},
});
return data.map(item => item.subPageId).filter(Boolean);
}
@ResolveField(() => UserType, {
@@ -215,12 +202,9 @@ export class WorkspaceResolver {
@Args('skip', { type: () => Int, nullable: true }) skip?: number,
@Args('take', { type: () => Int, nullable: true }) take?: number
) {
const data = await this.prisma.userWorkspacePermission.findMany({
const data = await this.prisma.workspaceUserPermission.findMany({
where: {
workspaceId: workspace.id,
userId: {
not: null,
},
},
skip,
take: take || 8,
@@ -265,7 +249,7 @@ export class WorkspaceResolver {
complexity: 2,
})
async workspaces(@CurrentUser() user: User) {
const data = await this.prisma.userWorkspacePermission.findMany({
const data = await this.prisma.workspaceUserPermission.findMany({
where: {
userId: user.id,
accepted: true,
@@ -309,7 +293,7 @@ export class WorkspaceResolver {
description: 'Get workspace by id',
})
async workspace(@CurrentUser() user: UserType, @Args('id') id: string) {
await this.permissions.check(id, user.id);
await this.permissions.checkWorkspace(id, user.id);
const workspace = await this.prisma.workspace.findUnique({ where: { id } });
if (!workspace) {
@@ -343,7 +327,7 @@ export class WorkspaceResolver {
const workspace = await this.prisma.workspace.create({
data: {
public: false,
users: {
permissions: {
create: {
type: Permission.Owner,
user: {
@@ -378,7 +362,7 @@ export class WorkspaceResolver {
@Args({ name: 'input', type: () => UpdateWorkspaceInput })
{ id, ...updates }: UpdateWorkspaceInput
) {
await this.permissions.check(id, user.id, Permission.Admin);
await this.permissions.checkWorkspace(id, user.id, Permission.Admin);
return this.prisma.workspace.update({
where: {
@@ -390,7 +374,7 @@ export class WorkspaceResolver {
@Mutation(() => Boolean)
async deleteWorkspace(@CurrentUser() user: UserType, @Args('id') id: string) {
await this.permissions.check(id, user.id, Permission.Owner);
await this.permissions.checkWorkspace(id, user.id, Permission.Owner);
await this.prisma.workspace.delete({
where: {
@@ -422,7 +406,11 @@ export class WorkspaceResolver {
@Args('permission', { type: () => Permission }) permission: Permission,
@Args('sendInviteMail', { nullable: true }) sendInviteMail: boolean
) {
await this.permissions.check(workspaceId, user.id, Permission.Admin);
await this.permissions.checkWorkspace(
workspaceId,
user.id,
Permission.Admin
);
if (permission === Permission.Owner) {
throw new ForbiddenException('Cannot change owner');
@@ -431,7 +419,7 @@ export class WorkspaceResolver {
const target = await this.users.findUserByEmail(email);
if (target) {
const originRecord = await this.prisma.userWorkspacePermission.findFirst({
const originRecord = await this.prisma.workspaceUserPermission.findFirst({
where: {
workspaceId,
userId: target.id,
@@ -463,7 +451,10 @@ export class WorkspaceResolver {
},
});
} catch (e) {
const ret = await this.permissions.revoke(workspaceId, target.id);
const ret = await this.permissions.revokeWorkspace(
workspaceId,
target.id
);
if (!ret) {
this.logger.fatal(
@@ -502,7 +493,10 @@ export class WorkspaceResolver {
},
});
} catch (e) {
const ret = await this.permissions.revoke(workspaceId, user.id);
const ret = await this.permissions.revokeWorkspace(
workspaceId,
user.id
);
if (!ret) {
this.logger.fatal(
@@ -532,7 +526,7 @@ export class WorkspaceResolver {
description: 'Update workspace',
})
async getInviteInfo(@Args('inviteId') inviteId: string) {
const workspaceId = await this.prisma.userWorkspacePermission
const workspaceId = await this.prisma.workspaceUserPermission
.findUniqueOrThrow({
where: {
id: inviteId,
@@ -556,7 +550,7 @@ export class WorkspaceResolver {
const metaJSON = doc.getMap('meta').toJSON();
const owner = await this.permissions.getWorkspaceOwner(workspaceId);
const invitee = await this.permissions.getInvitationById(
const invitee = await this.permissions.getWorkspaceInvitation(
inviteId,
workspaceId
);
@@ -588,9 +582,13 @@ export class WorkspaceResolver {
@Args('workspaceId') workspaceId: string,
@Args('userId') userId: string
) {
await this.permissions.check(workspaceId, user.id, Permission.Admin);
await this.permissions.checkWorkspace(
workspaceId,
user.id,
Permission.Admin
);
return this.permissions.revoke(workspaceId, userId);
return this.permissions.revokeWorkspace(workspaceId, userId);
}
@Mutation(() => Boolean)
@@ -619,15 +617,7 @@ export class WorkspaceResolver {
});
}
return this.permissions.acceptById(workspaceId, inviteId);
}
@Mutation(() => Boolean)
async acceptInvite(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string
) {
return this.permissions.accept(workspaceId, user.id);
return this.permissions.acceptWorkspaceInvitation(inviteId, workspaceId);
}
@Mutation(() => Boolean)
@@ -637,7 +627,7 @@ export class WorkspaceResolver {
@Args('workspaceName') workspaceName: string,
@Args('sendLeaveMail', { nullable: true }) sendLeaveMail: boolean
) {
await this.permissions.check(workspaceId, user.id);
await this.permissions.checkWorkspace(workspaceId, user.id);
const owner = await this.permissions.getWorkspaceOwner(workspaceId);
@@ -654,50 +644,7 @@ export class WorkspaceResolver {
});
}
return this.permissions.revoke(workspaceId, user.id);
}
@Mutation(() => Boolean)
async sharePage(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
const docId = new DocID(pageId, workspaceId);
if (docId.isWorkspace) {
throw new ForbiddenException('Expect page not to be workspace');
}
const userWorkspace = await this.prisma.userWorkspacePermission.findFirst({
where: {
userId: user.id,
workspaceId: docId.workspace,
},
});
if (!userWorkspace?.accepted) {
throw new ForbiddenException('Permission denied');
}
return this.permissions.grantPage(docId.workspace, docId.guid);
}
@Mutation(() => Boolean)
async revokePage(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
const docId = new DocID(pageId, workspaceId);
if (docId.isWorkspace) {
throw new ForbiddenException('Expect page not to be workspace');
}
await this.permissions.check(docId.workspace, user.id, Permission.Admin);
return this.permissions.revokePage(docId.workspace, docId.guid);
return this.permissions.revokeWorkspace(workspaceId, user.id);
}
@Query(() => [String], {
@@ -707,7 +654,7 @@ export class WorkspaceResolver {
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string
) {
await this.permissions.check(workspaceId, user.id);
await this.permissions.checkWorkspace(workspaceId, user.id);
return this.storage.listBlobs(workspaceId);
}
@@ -717,14 +664,14 @@ export class WorkspaceResolver {
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string
) {
await this.permissions.check(workspaceId, user.id);
await this.permissions.checkWorkspace(workspaceId, user.id);
return this.storage.blobsSize([workspaceId]).then(size => ({ size }));
}
@Query(() => WorkspaceBlobSizes)
async collectAllBlobSizes(@CurrentUser() user: UserType) {
const workspaces = await this.prisma.userWorkspacePermission
const workspaces = await this.prisma.workspaceUserPermission
.findMany({
where: {
userId: user.id,
@@ -751,7 +698,7 @@ export class WorkspaceResolver {
@Args('workspaceId') workspaceId: string,
@Args('size', { type: () => Float }) size: number
) {
const canWrite = await this.permissions.tryCheck(
const canWrite = await this.permissions.tryCheckWorkspace(
workspaceId,
user.id,
Permission.Write
@@ -775,7 +722,11 @@ export class WorkspaceResolver {
@Args({ name: 'blob', type: () => GraphQLUpload })
blob: FileUpload
) {
await this.permissions.check(workspaceId, user.id, Permission.Write);
await this.permissions.checkWorkspace(
workspaceId,
user.id,
Permission.Write
);
// quota was apply to owner's account
const { user: owner } =
@@ -831,8 +782,151 @@ export class WorkspaceResolver {
@Args('workspaceId') workspaceId: string,
@Args('hash') hash: string
) {
await this.permissions.check(workspaceId, user.id);
await this.permissions.checkWorkspace(workspaceId, user.id);
return this.storage.deleteBlob(workspaceId, hash);
}
}
registerEnumType(PublicPageMode, {
name: 'PublicPageMode',
description: 'The mode which the public page default in',
});
@ObjectType()
class WorkspacePage implements Partial<PrismaWorkspacePage> {
@Field(() => String, { name: 'id' })
pageId!: string;
@Field()
workspaceId!: string;
@Field(() => PublicPageMode)
mode!: PublicPageMode;
@Field()
public!: boolean;
}
@UseGuards(CloudThrottlerGuard)
@Auth()
@Resolver(() => WorkspaceType)
export class PagePermissionResolver {
constructor(
private readonly prisma: PrismaService,
private readonly permission: PermissionService
) {}
/**
* @deprecated
*/
@ResolveField(() => [String], {
description: 'Shared pages of workspace',
complexity: 2,
deprecationReason: 'use WorkspaceType.publicPages',
})
async sharedPages(@Parent() workspace: WorkspaceType) {
const data = await this.prisma.workspacePage.findMany({
where: {
workspaceId: workspace.id,
public: true,
},
});
return data.map(row => row.pageId);
}
@ResolveField(() => [WorkspacePage], {
description: 'Public pages of a workspace',
complexity: 2,
})
async publicPages(@Parent() workspace: WorkspaceType) {
return this.prisma.workspacePage.findMany({
where: {
workspaceId: workspace.id,
public: true,
},
});
}
/**
* @deprecated
*/
@Mutation(() => Boolean, {
name: 'sharePage',
deprecationReason: 'renamed to publicPage',
})
async deprecatedSharePage(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
await this.publishPage(user, workspaceId, pageId, PublicPageMode.Page);
return true;
}
@Mutation(() => WorkspacePage)
async publishPage(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string,
@Args({
name: 'mode',
type: () => PublicPageMode,
nullable: true,
defaultValue: PublicPageMode.Page,
})
mode: PublicPageMode
) {
const docId = new DocID(pageId, workspaceId);
if (docId.isWorkspace) {
throw new ForbiddenException('Expect page not to be workspace');
}
await this.permission.checkWorkspace(
workspaceId,
user.id,
Permission.Admin
);
return this.permission.publishPage(docId.workspace, docId.guid, mode);
}
/**
* @deprecated
*/
@Mutation(() => Boolean, {
name: 'revokePage',
deprecationReason: 'use revokePublicPage',
})
async deprecatedRevokePage(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
await this.revokePublicPage(user, workspaceId, pageId);
return true;
}
@Mutation(() => WorkspacePage)
async revokePublicPage(
@CurrentUser() user: UserType,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
const docId = new DocID(pageId, workspaceId);
if (docId.isWorkspace) {
throw new ForbiddenException('Expect page not to be workspace');
}
await this.permission.checkWorkspace(
docId.workspace,
user.id,
Permission.Admin
);
return this.permission.revokePublicPage(docId.workspace, docId.guid);
}
}