mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-19 02:56:23 +08:00
feat(core): optimize team workspace member management (#9737)
close AF-2106 AF-2077 AF-2089 feat(core): handle need more seat status feat(core): prevent invite members when team plan is canceled
This commit is contained in:
@@ -12,6 +12,7 @@ export { AccountLoggedIn } from './events/account-logged-in';
|
||||
export { AccountLoggedOut } from './events/account-logged-out';
|
||||
export { ServerInitialized } from './events/server-initialized';
|
||||
export { ValidatorProvider } from './provider/validator';
|
||||
export { AcceptInviteService } from './services/accept-invite';
|
||||
export { AuthService } from './services/auth';
|
||||
export { CaptchaService } from './services/captcha';
|
||||
export { DefaultServerService } from './services/default-server';
|
||||
@@ -52,6 +53,7 @@ import { WorkspaceInvoices } from './entities/workspace-invoices';
|
||||
import { WorkspaceSubscription } from './entities/workspace-subscription';
|
||||
import { ValidatorProvider } from './provider/validator';
|
||||
import { ServerScope } from './scopes/server';
|
||||
import { AcceptInviteService } from './services/accept-invite';
|
||||
import { AuthService } from './services/auth';
|
||||
import { CaptchaService } from './services/captcha';
|
||||
import { CloudDocMetaService } from './services/cloud-doc-meta';
|
||||
@@ -71,8 +73,10 @@ import { UserQuotaService } from './services/user-quota';
|
||||
import { WorkspaceInvoicesService } from './services/workspace-invoices';
|
||||
import { WorkspaceServerService } from './services/workspace-server';
|
||||
import { WorkspaceSubscriptionService } from './services/workspace-subscription';
|
||||
import { AcceptInviteStore } from './stores/accept-invite';
|
||||
import { AuthStore } from './stores/auth';
|
||||
import { CloudDocMetaStore } from './stores/cloud-doc-meta';
|
||||
import { InviteInfoStore } from './stores/invite-info';
|
||||
import { InvoicesStore } from './stores/invoices';
|
||||
import { SelfhostGenerateLicenseStore } from './stores/selfhost-generate-license';
|
||||
import { SelfhostLicenseStore } from './stores/selfhost-license';
|
||||
@@ -136,7 +140,10 @@ export function configureCloudModule(framework: Framework) {
|
||||
.store(InvoicesStore, [GraphQLService])
|
||||
.entity(Invoices, [InvoicesStore])
|
||||
.service(SelfhostGenerateLicenseService, [SelfhostGenerateLicenseStore])
|
||||
.store(SelfhostGenerateLicenseStore, [GraphQLService]);
|
||||
.store(SelfhostGenerateLicenseStore, [GraphQLService])
|
||||
.store(InviteInfoStore, [GraphQLService])
|
||||
.service(AcceptInviteService, [AcceptInviteStore, InviteInfoStore])
|
||||
.store(AcceptInviteStore, [GraphQLService]);
|
||||
|
||||
framework
|
||||
.scope(WorkspaceScope)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import type { GetInviteInfoQuery } from '@affine/graphql';
|
||||
import {
|
||||
backoffRetry,
|
||||
catchErrorInto,
|
||||
effect,
|
||||
fromPromise,
|
||||
LiveData,
|
||||
onComplete,
|
||||
onStart,
|
||||
Service,
|
||||
} from '@toeverything/infra';
|
||||
import { EMPTY, exhaustMap, mergeMap } from 'rxjs';
|
||||
|
||||
import { isBackendError, isNetworkError } from '../error';
|
||||
import type { AcceptInviteStore } from '../stores/accept-invite';
|
||||
import type { InviteInfoStore } from '../stores/invite-info';
|
||||
|
||||
export type InviteInfo = GetInviteInfoQuery['getInviteInfo'];
|
||||
|
||||
export class AcceptInviteService extends Service {
|
||||
constructor(
|
||||
private readonly store: AcceptInviteStore,
|
||||
private readonly inviteInfoStore: InviteInfoStore
|
||||
) {
|
||||
super();
|
||||
}
|
||||
inviteInfo$ = new LiveData<InviteInfo | undefined>(undefined);
|
||||
accepted$ = new LiveData<boolean>(false);
|
||||
loading$ = new LiveData(false);
|
||||
error$ = new LiveData<any>(null);
|
||||
|
||||
readonly revalidate = effect(
|
||||
exhaustMap(({ inviteId }: { inviteId: string }) => {
|
||||
if (!inviteId) {
|
||||
return EMPTY;
|
||||
}
|
||||
return fromPromise(async () => {
|
||||
return await this.inviteInfoStore.getInviteInfo(inviteId);
|
||||
}).pipe(
|
||||
mergeMap(res => {
|
||||
this.inviteInfo$.setValue(res);
|
||||
return fromPromise(async () => {
|
||||
return await this.store.acceptInvite(
|
||||
res.workspace.id,
|
||||
inviteId,
|
||||
true
|
||||
);
|
||||
});
|
||||
}),
|
||||
mergeMap(res => {
|
||||
this.accepted$.next(res);
|
||||
return EMPTY;
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isNetworkError,
|
||||
count: Infinity,
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isBackendError,
|
||||
count: 3,
|
||||
}),
|
||||
catchErrorInto(this.error$),
|
||||
onStart(() => {
|
||||
this.loading$.setValue(true);
|
||||
this.inviteInfo$.setValue(undefined);
|
||||
this.accepted$.setValue(false);
|
||||
}),
|
||||
onComplete(() => {
|
||||
this.loading$.setValue(false);
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
override dispose(): void {
|
||||
this.revalidate.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { acceptInviteByInviteIdMutation } from '@affine/graphql';
|
||||
import { Store } from '@toeverything/infra';
|
||||
|
||||
import type { GraphQLService } from '../services/graphql';
|
||||
|
||||
export class AcceptInviteStore extends Store {
|
||||
constructor(private readonly gqlService: GraphQLService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async acceptInvite(
|
||||
workspaceId: string,
|
||||
inviteId: string,
|
||||
sendAcceptMail?: boolean,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const data = await this.gqlService.gql({
|
||||
query: acceptInviteByInviteIdMutation,
|
||||
|
||||
variables: {
|
||||
workspaceId,
|
||||
inviteId,
|
||||
sendAcceptMail,
|
||||
},
|
||||
context: { signal },
|
||||
});
|
||||
|
||||
return data.acceptInviteById;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { getInviteInfoQuery } from '@affine/graphql';
|
||||
import { Store } from '@toeverything/infra';
|
||||
|
||||
import type { GraphQLService } from '../services/graphql';
|
||||
|
||||
export class InviteInfoStore extends Store {
|
||||
constructor(private readonly gqlService: GraphQLService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async getInviteInfo(inviteId?: string, signal?: AbortSignal) {
|
||||
if (!inviteId) {
|
||||
throw new Error('No inviteId');
|
||||
}
|
||||
const data = await this.gqlService.gql({
|
||||
query: getInviteInfoQuery,
|
||||
variables: {
|
||||
inviteId,
|
||||
},
|
||||
context: { signal },
|
||||
});
|
||||
|
||||
return data.getInviteInfo;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user