diff --git a/packages/backend/server/src/base/error/def.ts b/packages/backend/server/src/base/error/def.ts index 87b620f0b3651..79345ef300d0e 100644 --- a/packages/backend/server/src/base/error/def.ts +++ b/packages/backend/server/src/base/error/def.ts @@ -166,7 +166,7 @@ function generateErrorArgs(name: string, args: ErrorArgs) { export function generateUserFriendlyErrors() { const output = [ - '/* eslint-disable */', + '/* oxlint-disable */', '// AUTO GENERATED FILE', `import { createUnionType, Field, ObjectType, registerEnumType } from '@nestjs/graphql';`, '', @@ -374,10 +374,6 @@ export const USER_FRIENDLY_ERRORS = { args: { spaceId: 'string' }, message: ({ spaceId }) => `Owner of Space ${spaceId} not found.`, }, - cant_change_space_owner: { - type: 'action_forbidden', - message: 'You are not allowed to change the owner of a Space.', - }, doc_not_found: { type: 'resource_not_found', args: { spaceId: 'string', docId: 'string' }, diff --git a/packages/backend/server/src/base/error/errors.gen.ts b/packages/backend/server/src/base/error/errors.gen.ts index 01273cb870505..a17feb5b84ac5 100644 --- a/packages/backend/server/src/base/error/errors.gen.ts +++ b/packages/backend/server/src/base/error/errors.gen.ts @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* oxlint-disable */ // AUTO GENERATED FILE import { createUnionType, Field, ObjectType, registerEnumType } from '@nestjs/graphql'; @@ -240,12 +240,6 @@ export class SpaceOwnerNotFound extends UserFriendlyError { super('internal_server_error', 'space_owner_not_found', message, args); } } - -export class CantChangeSpaceOwner extends UserFriendlyError { - constructor(message?: string) { - super('action_forbidden', 'cant_change_space_owner', message); - } -} @ObjectType() class DocNotFoundDataType { @Field() spaceId!: string @@ -630,7 +624,6 @@ export enum ErrorNames { ALREADY_IN_SPACE, SPACE_ACCESS_DENIED, SPACE_OWNER_NOT_FOUND, - CANT_CHANGE_SPACE_OWNER, DOC_NOT_FOUND, DOC_ACCESS_DENIED, VERSION_REJECTED, diff --git a/packages/backend/server/src/base/event/def.ts b/packages/backend/server/src/base/event/def.ts index 910443480e767..7f253d9343a59 100644 --- a/packages/backend/server/src/base/event/def.ts +++ b/packages/backend/server/src/base/event/def.ts @@ -10,6 +10,12 @@ export interface WorkspaceEvents { workspaceId: Workspace['id']; }>; requestApproved: Payload<{ inviteId: string }>; + roleChanged: Payload<{ + userId: User['id']; + workspaceId: Workspace['id']; + permission: number; + }>; + ownerTransferred: Payload<{ email: string; workspaceId: Workspace['id'] }>; updated: Payload<{ workspaceId: Workspace['id']; count: number }>; }; deleted: Payload; diff --git a/packages/backend/server/src/base/mailer/mail.service.ts b/packages/backend/server/src/base/mailer/mail.service.ts index cf1f1cde21613..f078851fe6b62 100644 --- a/packages/backend/server/src/base/mailer/mail.service.ts +++ b/packages/backend/server/src/base/mailer/mail.service.ts @@ -6,7 +6,12 @@ import { URLHelper } from '../helpers'; import { metrics } from '../metrics'; import type { MailerService, Options } from './mailer'; import { MAILER_SERVICE } from './mailer'; -import { emailTemplate } from './template'; +import { + emailTemplate, + getRoleChangedTemplate, + type RoleChangedMailParams, +} from './template'; + @Injectable() export class MailService { constructor( @@ -311,4 +316,22 @@ export class MailService { }); return this.sendMail({ to, subject: title, html }); } + + async sendRoleChangedEmail(to: string, ws: RoleChangedMailParams) { + const { subject, title, content } = getRoleChangedTemplate(ws); + const html = emailTemplate({ title, content }); + console.log({ subject, title, content, to }); + return this.sendMail({ to, subject, html }); + } + + async sendOwnerTransferred(to: string, ws: { name: string }) { + const { name: workspaceName } = ws; + const title = `Your ownership of ${workspaceName} has been transferred`; + + const html = emailTemplate({ + title: 'Ownership transferred', + content: `You have transferred ownership of ${workspaceName}. You are now a admin in this workspace.`, + }); + return this.sendMail({ to, subject: title, html }); + } } diff --git a/packages/backend/server/src/base/mailer/template.ts b/packages/backend/server/src/base/mailer/template.ts index 3e50a317187e7..d790adfcc92a9 100644 --- a/packages/backend/server/src/base/mailer/template.ts +++ b/packages/backend/server/src/base/mailer/template.ts @@ -219,3 +219,38 @@ export const emailTemplate = ({ `; }; + +type RoleChangedMail = { + subject: string; + title: string; + content: string; +}; + +export type RoleChangedMailParams = { + name: string; + role: 'owner' | 'admin' | 'member' | 'readonly'; +}; + +export const getRoleChangedTemplate = ( + ws: RoleChangedMailParams +): RoleChangedMail => { + const { name, role } = ws; + let subject = `You are now an ${role} of ${name}`; + let title = 'Role update in workspace'; + let content = `Your role in ${name} has been changed to ${role}. You can continue to collaborate in this workspace.`; + + switch (role) { + case 'owner': + title = 'Welcome, new workspace owner!'; + content = `You have been assigned as the owner of ${name}. As a workspace owner, you have full control over this team workspace.`; + break; + case 'admin': + title = `You've been promoted to admin.`; + content = `You have been promoted to admin of ${name}. As an admin, you can help the workspace owner manage members in this workspace.`; + break; + default: + subject = `Your role has been changed in ${name}`; + break; + } + return { subject, title, content }; +}; diff --git a/packages/backend/server/src/core/permission/service.ts b/packages/backend/server/src/core/permission/service.ts index 2ff219545dd41..717be512ebeb9 100644 --- a/packages/backend/server/src/core/permission/service.ts +++ b/packages/backend/server/src/core/permission/service.ts @@ -322,10 +322,6 @@ export class PermissionService { this.prisma.workspaceUserPermission.update({ where: { workspaceId_userId: { workspaceId: ws, userId: user }, - // only update permission: - // 1. if the new permission is owner and original permission is admin - // 2. if the original permission is not owner - type: toBeOwner ? Permission.Admin : { not: Permission.Owner }, }, data: { type: permission }, }), diff --git a/packages/backend/server/src/core/workspaces/resolvers/service.ts b/packages/backend/server/src/core/workspaces/resolvers/service.ts index 3743608be32c1..14cf1bb64a636 100644 --- a/packages/backend/server/src/core/workspaces/resolvers/service.ts +++ b/packages/backend/server/src/core/workspaces/resolvers/service.ts @@ -2,9 +2,9 @@ import { Injectable, Logger } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; import { getStreamAsBuffer } from 'get-stream'; -import { Cache, MailService } from '../../../base'; +import { Cache, MailService, UserNotFound } from '../../../base'; import { DocContentService } from '../../doc-renderer'; -import { PermissionService } from '../../permission'; +import { Permission, PermissionService } from '../../permission'; import { WorkspaceBlobStorage } from '../../storage'; import { UserService } from '../../user'; @@ -17,6 +17,13 @@ export type InviteInfo = { inviteeUserId?: string; }; +const PermissionToRole = { + [Permission.Read]: 'readonly' as const, + [Permission.Write]: 'member' as const, + [Permission.Admin]: 'admin' as const, + [Permission.Owner]: 'owner' as const, +}; + @Injectable() export class WorkspaceService { private readonly logger = new Logger(WorkspaceService.name); @@ -78,6 +85,27 @@ export class WorkspaceService { }; } + private async getInviteeEmailTarget(inviteId: string) { + const { workspaceId, inviteeUserId } = await this.getInviteInfo(inviteId); + if (!inviteeUserId) { + this.logger.error(`Invitee user not found for inviteId: ${inviteId}`); + return; + } + const workspace = await this.getWorkspaceInfo(workspaceId); + const invitee = await this.user.findUserById(inviteeUserId); + if (!invitee) { + this.logger.error( + `Invitee user not found in workspace: ${workspaceId}, userId: ${inviteeUserId}` + ); + return; + } + + return { + email: invitee.email, + workspace, + }; + } + async sendAcceptedEmail(inviteId: string) { const { workspaceId, inviterUserId, inviteeUserId } = await this.getInviteInfo(inviteId); @@ -167,24 +195,21 @@ export class WorkspaceService { await this.mailer.sendReviewDeclinedEmail(email, { name: workspaceName }); } - private async getInviteeEmailTarget(inviteId: string) { - const { workspaceId, inviteeUserId } = await this.getInviteInfo(inviteId); - if (!inviteeUserId) { - this.logger.error(`Invitee user not found for inviteId: ${inviteId}`); - return; - } - const workspace = await this.getWorkspaceInfo(workspaceId); - const invitee = await this.user.findUserById(inviteeUserId); - if (!invitee) { - this.logger.error( - `Invitee user not found in workspace: ${workspaceId}, userId: ${inviteeUserId}` - ); - return; - } + async sendRoleChangedEmail( + userId: string, + ws: { id: string; role: Permission } + ) { + const user = await this.user.findUserById(userId); + if (!user) throw new UserNotFound(); + const workspace = await this.getWorkspaceInfo(ws.id); + await this.mailer.sendRoleChangedEmail(user?.email, { + name: workspace.name, + role: PermissionToRole[ws.role], + }); + } - return { - email: invitee.email, - workspace, - }; + async sendOwnerTransferred(email: string, ws: { id: string }) { + const workspace = await this.getWorkspaceInfo(ws.id); + await this.mailer.sendOwnerTransferred(email, { name: workspace.name }); } } diff --git a/packages/backend/server/src/core/workspaces/resolvers/team.ts b/packages/backend/server/src/core/workspaces/resolvers/team.ts index ab0f1d5069654..17b1605b9387e 100644 --- a/packages/backend/server/src/core/workspaces/resolvers/team.ts +++ b/packages/backend/server/src/core/workspaces/resolvers/team.ts @@ -18,6 +18,7 @@ import { RequestMutex, TooManyRequest, URLHelper, + UserFriendlyError, } from '../../../base'; import { CurrentUser } from '../../auth'; import { Permission, PermissionService } from '../../permission'; @@ -311,7 +312,17 @@ export class TeamWorkspaceResolver { ); if (result) { - // TODO(@darkskygit): send team role changed mail + this.event.emit('workspace.members.roleChanged', { + userId, + workspaceId, + permission, + }); + if (permission === Permission.Owner) { + this.event.emit('workspace.members.ownerTransferred', { + email: user.email, + workspaceId, + }); + } } return result; @@ -320,6 +331,10 @@ export class TeamWorkspaceResolver { } } catch (e) { this.logger.error('failed to invite user', e); + // pass through user friendly error + if (e instanceof UserFriendlyError) { + return e; + } return new TooManyRequest(); } } @@ -353,4 +368,28 @@ export class TeamWorkspaceResolver { // send approve mail await this.workspaceService.sendReviewApproveEmail(inviteId); } + + @OnEvent('workspace.members.roleChanged') + async onRoleChanged({ + userId, + workspaceId, + permission, + }: EventPayload<'workspace.members.roleChanged'>) { + // send role changed mail + await this.workspaceService.sendRoleChangedEmail(userId, { + id: workspaceId, + role: permission, + }); + } + + @OnEvent('workspace.members.ownerTransferred') + async onOwnerTransferred({ + email, + workspaceId, + }: EventPayload<'workspace.members.ownerTransferred'>) { + // send role changed mail + await this.workspaceService.sendOwnerTransferred(email, { + id: workspaceId, + }); + } } diff --git a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts index eeaae23df0a89..8036ae24e7659 100644 --- a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts +++ b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts @@ -17,7 +17,6 @@ import type { FileUpload } from '../../../base'; import { AlreadyInSpace, Cache, - CantChangeSpaceOwner, DocNotFound, EventEmitter, InternalServerError, @@ -383,8 +382,13 @@ export class WorkspaceResolver { @CurrentUser() user: CurrentUser, @Args('workspaceId') workspaceId: string, @Args('email') email: string, - @Args('permission', { type: () => Permission }) permission: Permission, - @Args('sendInviteMail', { nullable: true }) sendInviteMail: boolean + @Args('sendInviteMail', { nullable: true }) sendInviteMail: boolean, + @Args('permission', { + type: () => Permission, + nullable: true, + deprecationReason: 'never used', + }) + _permission?: Permission ) { await this.permissions.checkWorkspace( workspaceId, @@ -392,10 +396,6 @@ export class WorkspaceResolver { Permission.Admin ); - if (permission === Permission.Owner) { - throw new CantChangeSpaceOwner(); - } - try { // lock to prevent concurrent invite and grant const lockFlag = `invite:${workspaceId}`; @@ -428,7 +428,7 @@ export class WorkspaceResolver { const inviteId = await this.permissions.grant( workspaceId, target.id, - permission + Permission.Write ); if (sendInviteMail) { try { diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index aa59d72efc53f..3ec9dee256c99 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -220,7 +220,6 @@ enum ErrorNames { BLOB_QUOTA_EXCEEDED CANNOT_DELETE_ALL_ADMIN_ACCOUNT CANNOT_DELETE_OWN_ACCOUNT - CANT_CHANGE_SPACE_OWNER CANT_UPDATE_ONETIME_PAYMENT_SUBSCRIPTION CAPTCHA_VERIFICATION_FAILED COPILOT_ACTION_TAKEN @@ -526,7 +525,7 @@ type Mutation { """Create a chat session""" forkCopilotSession(options: ForkChatSessionInput!): String! grantMember(permission: Permission!, userId: String!, workspaceId: String!): String! - invite(email: String!, permission: Permission!, sendInviteMail: Boolean, workspaceId: String!): String! + invite(email: String!, permission: Permission @deprecated(reason: "never used"), sendInviteMail: Boolean, workspaceId: String!): String! inviteBatch(emails: [String!]!, sendInviteMail: Boolean, workspaceId: String!): [InviteResult!]! leaveWorkspace(sendLeaveMail: Boolean, workspaceId: String!, workspaceName: String @deprecated(reason: "no longer used")): Boolean! publishPage(mode: PublicPageMode = Page, pageId: String!, workspaceId: String!): WorkspacePage! diff --git a/packages/backend/server/tests/copilot.e2e.ts b/packages/backend/server/tests/copilot.e2e.ts index 1cb8b95f757d1..49262127d9a98 100644 --- a/packages/backend/server/tests/copilot.e2e.ts +++ b/packages/backend/server/tests/copilot.e2e.ts @@ -147,13 +147,7 @@ test('should create session correctly', async t => { ); }); - const inviteId = await inviteUser( - app, - token, - id, - 'darksky@affine.pro', - 'Admin' - ); + const inviteId = await inviteUser(app, token, id, 'darksky@affine.pro'); await acceptInviteById(app, id, inviteId, false); await assertCreateSession( id, @@ -240,13 +234,7 @@ test('should fork session correctly', async t => { } ); - const inviteId = await inviteUser( - app, - token, - id, - 'test@affine.pro', - 'Admin' - ); + const inviteId = await inviteUser(app, token, id, 'test@affine.pro'); await acceptInviteById(app, id, inviteId, false); await assertForkSession( newToken, @@ -609,8 +597,7 @@ test('should reject request that user have not permission', async t => { app, anotherToken, workspaceId, - 'darksky@affine.pro', - 'Admin' + 'darksky@affine.pro' ); await acceptInviteById(app, workspaceId, inviteId, false); diff --git a/packages/backend/server/tests/mailer.spec.ts b/packages/backend/server/tests/mailer.spec.ts index df7a63ebe8e40..ec2451d1de030 100644 --- a/packages/backend/server/tests/mailer.spec.ts +++ b/packages/backend/server/tests/mailer.spec.ts @@ -44,14 +44,7 @@ test('should send invite email', async t => { const stub = Sinon.stub(mail, 'sendMail'); - await inviteUser( - app, - u1.token.token, - workspace.id, - u2.email, - 'Admin', - true - ); + await inviteUser(app, u1.token.token, workspace.id, u2.email, true); t.true(stub.calledOnce); diff --git a/packages/backend/server/tests/team.e2e.ts b/packages/backend/server/tests/team.e2e.ts index bd208c6824f08..b770d5c2d379e 100644 --- a/packages/backend/server/tests/team.e2e.ts +++ b/packages/backend/server/tests/team.e2e.ts @@ -120,7 +120,6 @@ const init = async ( owner.token.token, workspace.id, member.email, - permission, shouldSendEmail ); await acceptInviteById(app, workspace.id, inviteId, shouldSendEmail); @@ -133,10 +132,16 @@ const init = async ( owner.token.token, teamWorkspace.id, member.email, - permission, shouldSendEmail ); await acceptInviteById(app, teamWorkspace.id, inviteId, shouldSendEmail); + await grantMember( + app, + owner.token.token, + teamWorkspace.id, + member.id, + permission + ); } return member; @@ -437,8 +442,11 @@ test('should be able to manage invite link', async t => { read, } = await init(app, 4); - for (const workspace of [ws, tws]) { - for (const manager of [owner, admin]) { + for (const [workspace, managers] of [ + [ws, [owner]], + [tws, [owner, admin]], + ] as const) { + for (const manager of managers) { const { link } = await createInviteLink( app, manager.token.token, @@ -646,16 +654,21 @@ test('should be able to emit events', async t => { const { teamWorkspace: tws, inviteBatch } = await init(app, 4); await inviteBatch(['m1@affine.pro', 'm2@affine.pro']); - t.true( - event.emit.calledOnceWith('workspace.members.updated', { + const [membersUpdated] = event.emit + .getCalls() + .map(call => call.args) + .toReversed(); + t.deepEqual(membersUpdated, [ + 'workspace.members.updated', + { workspaceId: tws.id, count: 6, - }) - ); + }, + ]); } { - const { teamWorkspace: tws, owner, createInviteLink } = await init(app, 10); + const { teamWorkspace: tws, owner, createInviteLink } = await init(app); const [, invite] = await createInviteLink(tws); const user = await invite('m3@affine.pro'); const { members } = await getWorkspace(app, owner.token.token, tws.id); @@ -679,4 +692,39 @@ test('should be able to emit events', async t => { 'should emit review requested event' ); } + + { + const { teamWorkspace: tws, owner, read } = await init(app); + await grantMember(app, owner.token.token, tws.id, read.id, 'Admin'); + t.deepEqual( + event.emit.lastCall.args, + [ + 'workspace.members.roleChanged', + { userId: read.id, workspaceId: tws.id, permission: Permission.Admin }, + ], + 'should emit role changed event' + ); + + await grantMember(app, owner.token.token, tws.id, read.id, 'Owner'); + const [ownerTransferred, roleChanged] = event.emit + .getCalls() + .map(call => call.args) + .toReversed(); + t.deepEqual( + roleChanged, + [ + 'workspace.members.roleChanged', + { userId: read.id, workspaceId: tws.id, permission: Permission.Owner }, + ], + 'should emit role changed event' + ); + t.deepEqual( + ownerTransferred, + [ + 'workspace.members.ownerTransferred', + { email: owner.email, workspaceId: tws.id }, + ], + 'should emit owner transferred event' + ); + } }); diff --git a/packages/backend/server/tests/utils/invite.ts b/packages/backend/server/tests/utils/invite.ts index 218ddad371709..4d99d1fc90bf1 100644 --- a/packages/backend/server/tests/utils/invite.ts +++ b/packages/backend/server/tests/utils/invite.ts @@ -3,14 +3,12 @@ import request from 'supertest'; import type { InvitationType } from '../../src/core/workspaces'; import { gql } from './common'; -import { PermissionEnum } from './utils'; export async function inviteUser( app: INestApplication, token: string, workspaceId: string, email: string, - permission: PermissionEnum, sendInviteMail = false ): Promise { const res = await request(app.getHttpServer()) @@ -20,7 +18,7 @@ export async function inviteUser( .send({ query: ` mutation { - invite(workspaceId: "${workspaceId}", email: "${email}", permission: ${permission}, sendInviteMail: ${sendInviteMail}) + invite(workspaceId: "${workspaceId}", email: "${email}", sendInviteMail: ${sendInviteMail}) } `, }) diff --git a/packages/backend/server/tests/workspace-invite.e2e.ts b/packages/backend/server/tests/workspace-invite.e2e.ts index a69930eb0f08e..5274e26e49acc 100644 --- a/packages/backend/server/tests/workspace-invite.e2e.ts +++ b/packages/backend/server/tests/workspace-invite.e2e.ts @@ -52,13 +52,7 @@ test('should invite a user', async t => { const workspace = await createWorkspace(app, u1.token.token); - const invite = await inviteUser( - app, - u1.token.token, - workspace.id, - u2.email, - 'Admin' - ); + const invite = await inviteUser(app, u1.token.token, workspace.id, u2.email); t.truthy(invite, 'failed to invite user'); }); @@ -68,13 +62,7 @@ test('should leave a workspace', async t => { const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1'); const workspace = await createWorkspace(app, u1.token.token); - const id = await inviteUser( - app, - u1.token.token, - workspace.id, - u2.email, - 'Admin' - ); + const id = await inviteUser(app, u1.token.token, workspace.id, u2.email); await acceptInviteById(app, workspace.id, id, false); const leave = await leaveWorkspace(app, u2.token.token, workspace.id); @@ -89,7 +77,7 @@ test('should revoke a user', async t => { const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1'); const workspace = await createWorkspace(app, u1.token.token); - await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin'); + await inviteUser(app, u1.token.token, workspace.id, u2.email); const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id); t.is(currWorkspace.members.length, 2, 'failed to invite user'); @@ -104,7 +92,7 @@ test('should create user if not exist', async t => { const workspace = await createWorkspace(app, u1.token.token); - await inviteUser(app, u1.token.token, workspace.id, 'u2@affine.pro', 'Admin'); + await inviteUser(app, u1.token.token, workspace.id, 'u2@affine.pro'); const u2 = await user.findUserByEmail('u2@affine.pro'); t.not(u2, undefined, 'failed to create user'); @@ -118,24 +106,12 @@ test('should invite a user by link', async t => { const workspace = await createWorkspace(app, u1.token.token); - const invite = await inviteUser( - app, - u1.token.token, - workspace.id, - u2.email, - 'Admin' - ); + const invite = await inviteUser(app, u1.token.token, workspace.id, u2.email); const accept = await acceptInviteById(app, workspace.id, invite); t.true(accept, 'failed to accept invite'); - const invite1 = await inviteUser( - app, - u1.token.token, - workspace.id, - u2.email, - 'Admin' - ); + const invite1 = await inviteUser(app, u1.token.token, workspace.id, u2.email); t.is(invite, invite1, 'repeat the invitation must return same id'); @@ -159,7 +135,6 @@ test('should send email', async t => { u1.token.token, workspace.id, u2.email, - 'Admin', true ); @@ -224,20 +199,8 @@ test('should support pagination for member', async t => { const u3 = await signUp(app, 'u3', 'u3@affine.pro', '1'); const workspace = await createWorkspace(app, u1.token.token); - const invite1 = await inviteUser( - app, - u1.token.token, - workspace.id, - u2.email, - 'Admin' - ); - const invite2 = await inviteUser( - app, - u1.token.token, - workspace.id, - u3.email, - 'Admin' - ); + const invite1 = await inviteUser(app, u1.token.token, workspace.id, u2.email); + const invite2 = await inviteUser(app, u1.token.token, workspace.id, u3.email); await acceptInviteById(app, workspace.id, invite1, false); await acceptInviteById(app, workspace.id, invite2, false); @@ -267,13 +230,7 @@ test('should limit member count correctly', async t => { const workspace = await createWorkspace(app, u1.token.token); await Promise.allSettled( Array.from({ length: 10 }).map(async (_, i) => - inviteUser( - app, - u1.token.token, - workspace.id, - `u${i}@affine.pro`, - 'Admin' - ) + inviteUser(app, u1.token.token, workspace.id, `u${i}@affine.pro`) ) ); diff --git a/packages/backend/server/tests/workspace.e2e.ts b/packages/backend/server/tests/workspace.e2e.ts index 59d6b170d03d8..9738509d50937 100644 --- a/packages/backend/server/tests/workspace.e2e.ts +++ b/packages/backend/server/tests/workspace.e2e.ts @@ -134,7 +134,7 @@ test('should share a page', async t => { await acceptInviteById( app, workspace.id, - await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin') + await inviteUser(app, u1.token.token, workspace.id, u2.email) ); const invited = await publishPage(app, u2.token.token, workspace.id, 'page2'); t.is(invited.id, 'page2', 'failed to share page'); @@ -211,7 +211,7 @@ test('should can get workspace doc', async t => { await acceptInviteById( app, workspace.id, - await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin') + await inviteUser(app, u1.token.token, workspace.id, u2.email) ); const res2 = await request(app.getHttpServer()) diff --git a/packages/frontend/core/src/modules/permissions/entities/permission.ts b/packages/frontend/core/src/modules/permissions/entities/permission.ts index e9e4eb761efba..58855a1aab553 100644 --- a/packages/frontend/core/src/modules/permissions/entities/permission.ts +++ b/packages/frontend/core/src/modules/permissions/entities/permission.ts @@ -75,15 +75,10 @@ export class WorkspacePermission extends Entity { }) ); - async inviteMember( - email: string, - permission: Permission, - sendInviteMail?: boolean - ) { + async inviteMember(email: string, sendInviteMail?: boolean) { return await this.store.inviteMember( this.workspaceService.workspace.id, email, - permission, sendInviteMail ); } diff --git a/packages/frontend/core/src/modules/permissions/stores/permission.ts b/packages/frontend/core/src/modules/permissions/stores/permission.ts index 34d128e4d034c..b7f61d56da312 100644 --- a/packages/frontend/core/src/modules/permissions/stores/permission.ts +++ b/packages/frontend/core/src/modules/permissions/stores/permission.ts @@ -38,7 +38,6 @@ export class WorkspacePermissionStore extends Store { async inviteMember( workspaceId: string, email: string, - permission: Permission, sendInviteMail = false ) { if (!this.workspaceServerService.server) { @@ -49,7 +48,6 @@ export class WorkspacePermissionStore extends Store { variables: { workspaceId, email, - permission, sendInviteMail, }, }); diff --git a/packages/frontend/graphql/src/graphql/index.ts b/packages/frontend/graphql/src/graphql/index.ts index cada1672ad8ec..744491dae4629 100644 --- a/packages/frontend/graphql/src/graphql/index.ts +++ b/packages/frontend/graphql/src/graphql/index.ts @@ -1367,11 +1367,10 @@ export const inviteByEmailMutation = { definitionName: 'invite', containsFile: false, query: ` -mutation inviteByEmail($workspaceId: String!, $email: String!, $permission: Permission!, $sendInviteMail: Boolean) { +mutation inviteByEmail($workspaceId: String!, $email: String!, $sendInviteMail: Boolean) { invite( workspaceId: $workspaceId email: $email - permission: $permission sendInviteMail: $sendInviteMail ) }`, diff --git a/packages/frontend/graphql/src/graphql/workspace-intive-by-email.gql b/packages/frontend/graphql/src/graphql/workspace-intive-by-email.gql index fbe94cb2340c2..27032351ff8f5 100644 --- a/packages/frontend/graphql/src/graphql/workspace-intive-by-email.gql +++ b/packages/frontend/graphql/src/graphql/workspace-intive-by-email.gql @@ -1,13 +1,11 @@ mutation inviteByEmail( $workspaceId: String! $email: String! - $permission: Permission! $sendInviteMail: Boolean ) { invite( workspaceId: $workspaceId email: $email - permission: $permission sendInviteMail: $sendInviteMail ) } diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index a48adf3315d8b..cc2b381f7ddee 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -1,4 +1,4 @@ -/* eslint-disable */ + export type Maybe = T | null; export type InputMaybe = T | null; export type Exact = { @@ -298,7 +298,6 @@ export enum ErrorNames { BLOB_QUOTA_EXCEEDED = 'BLOB_QUOTA_EXCEEDED', CANNOT_DELETE_ALL_ADMIN_ACCOUNT = 'CANNOT_DELETE_ALL_ADMIN_ACCOUNT', CANNOT_DELETE_OWN_ACCOUNT = 'CANNOT_DELETE_OWN_ACCOUNT', - CANT_CHANGE_SPACE_OWNER = 'CANT_CHANGE_SPACE_OWNER', CANT_UPDATE_ONETIME_PAYMENT_SUBSCRIPTION = 'CANT_UPDATE_ONETIME_PAYMENT_SUBSCRIPTION', CAPTCHA_VERIFICATION_FAILED = 'CAPTCHA_VERIFICATION_FAILED', COPILOT_ACTION_TAKEN = 'COPILOT_ACTION_TAKEN', @@ -729,7 +728,7 @@ export interface MutationGrantMemberArgs { export interface MutationInviteArgs { email: Scalars['String']['input']; - permission: Permission; + permission?: InputMaybe; sendInviteMail?: InputMaybe; workspaceId: Scalars['String']['input']; } @@ -2647,7 +2646,6 @@ export type RemoveWorkspaceFeatureMutation = { export type InviteByEmailMutationVariables = Exact<{ workspaceId: Scalars['String']['input']; email: Scalars['String']['input']; - permission: Permission; sendInviteMail?: InputMaybe; }>;