diff --git a/packages/backend/server/src/core/doc/adapters/workspace.ts b/packages/backend/server/src/core/doc/adapters/workspace.ts index 1f0c8a23339ee..23976c7f0486b 100644 --- a/packages/backend/server/src/core/doc/adapters/workspace.ts +++ b/packages/backend/server/src/core/doc/adapters/workspace.ts @@ -132,11 +132,6 @@ export class PgWorkspaceDocStorageAdapter extends DocStorageAdapter { async deleteSpace(workspaceId: string) { const ident = { where: { workspaceId } }; await this.db.$transaction([ - this.db.workspace.deleteMany({ - where: { - id: workspaceId, - }, - }), this.db.snapshot.deleteMany(ident), this.db.update.deleteMany(ident), this.db.snapshotHistory.deleteMany(ident), @@ -344,6 +339,17 @@ export class PgWorkspaceDocStorageAdapter extends DocStorageAdapter { return false; } + const historyMaxAge = await this.options + .historyMaxAge(snapshot.spaceId) + .catch( + () => + 0 /* edgecase: user deleted but owned workspaces not handled correctly */ + ); + + if (historyMaxAge === 0) { + return false; + } + await this.db.snapshotHistory .create({ select: { @@ -355,9 +361,7 @@ export class PgWorkspaceDocStorageAdapter extends DocStorageAdapter { timestamp: new Date(snapshot.timestamp), blob: Buffer.from(snapshot.bin), createdBy: snapshot.editor, - expiredAt: new Date( - Date.now() + (await this.options.historyMaxAge(snapshot.spaceId)) - ), + expiredAt: new Date(Date.now() + historyMaxAge), }, }) .catch(() => { diff --git a/packages/backend/server/src/core/doc/job.ts b/packages/backend/server/src/core/doc/job.ts index 13c73afbb28bd..509f4eeb31b3f 100644 --- a/packages/backend/server/src/core/doc/job.ts +++ b/packages/backend/server/src/core/doc/job.ts @@ -2,7 +2,13 @@ import { Injectable, Logger, OnModuleInit, Optional } from '@nestjs/common'; import { Cron, CronExpression, SchedulerRegistry } from '@nestjs/schedule'; import { PrismaClient } from '@prisma/client'; -import { CallTimer, Config, metrics } from '../../fundamentals'; +import { + CallTimer, + Config, + type EventPayload, + metrics, + OnEvent, +} from '../../fundamentals'; import { PgWorkspaceDocStorageAdapter } from './adapters/workspace'; @Injectable() @@ -73,4 +79,11 @@ export class DocStorageCronJob implements OnModuleInit { .gauge('updates_queue_count') .record(await this.db.update.count()); } + + @OnEvent('user.deleted') + async clearUserWorkspaces(payload: EventPayload<'user.deleted'>) { + for (const workspace of payload.ownedWorkspaces) { + await this.workspace.deleteSpace(workspace); + } + } } diff --git a/packages/backend/server/src/core/user/index.ts b/packages/backend/server/src/core/user/index.ts index e130d325fd9cb..fc0361e991d5e 100644 --- a/packages/backend/server/src/core/user/index.ts +++ b/packages/backend/server/src/core/user/index.ts @@ -1,12 +1,13 @@ import { Module } from '@nestjs/common'; +import { PermissionModule } from '../permission'; import { StorageModule } from '../storage'; import { UserAvatarController } from './controller'; import { UserManagementResolver, UserResolver } from './resolver'; import { UserService } from './service'; @Module({ - imports: [StorageModule], + imports: [StorageModule, PermissionModule], providers: [UserResolver, UserService, UserManagementResolver], controllers: [UserAvatarController], exports: [UserService], diff --git a/packages/backend/server/src/core/user/service.ts b/packages/backend/server/src/core/user/service.ts index f2b10f79e7ba7..06ee0e9ecaba4 100644 --- a/packages/backend/server/src/core/user/service.ts +++ b/packages/backend/server/src/core/user/service.ts @@ -11,6 +11,7 @@ import { WrongSignInCredentials, WrongSignInMethod, } from '../../fundamentals'; +import { PermissionService } from '../permission'; import { Quota_FreePlanV1_1 } from '../quota/schema'; import { validators } from '../utils/validators'; @@ -34,7 +35,8 @@ export class UserService { private readonly config: Config, private readonly crypto: CryptoHelper, private readonly prisma: PrismaClient, - private readonly emitter: EventEmitter + private readonly emitter: EventEmitter, + private readonly permission: PermissionService ) {} get userCreatingData() { @@ -276,12 +278,13 @@ export class UserService { } async deleteUser(id: string) { + const ownedWorkspaces = await this.permission.getOwnedWorkspaces(id); const user = await this.prisma.user.delete({ where: { id } }); - this.emitter.emit('user.deleted', user); + this.emitter.emit('user.deleted', { ...user, ownedWorkspaces }); } @OnEvent('user.updated') - async onUserUpdated(user: EventPayload<'user.deleted'>) { + async onUserUpdated(user: EventPayload<'user.updated'>) { const { enabled, customerIo } = this.config.metrics; if (enabled && customerIo?.token) { const payload = { diff --git a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts index f0318c9618df9..7371452e70d3e 100644 --- a/packages/backend/server/src/core/workspaces/resolvers/workspace.ts +++ b/packages/backend/server/src/core/workspaces/resolvers/workspace.ts @@ -30,7 +30,7 @@ import { UserNotFound, } from '../../../fundamentals'; import { CurrentUser, Public } from '../../auth'; -import type { Editor } from '../../doc'; +import { type Editor, PgWorkspaceDocStorageAdapter } from '../../doc'; import { DocContentService } from '../../doc-renderer'; import { Permission, PermissionService } from '../../permission'; import { QuotaManagementService, QuotaQueryType } from '../../quota'; @@ -86,7 +86,8 @@ export class WorkspaceResolver { private readonly event: EventEmitter, private readonly blobStorage: WorkspaceBlobStorage, private readonly mutex: RequestMutex, - private readonly doc: DocContentService + private readonly doc: DocContentService, + private readonly workspaceStorage: PgWorkspaceDocStorageAdapter ) {} @ResolveField(() => Permission, { @@ -352,6 +353,7 @@ export class WorkspaceResolver { id, }, }); + await this.workspaceStorage.deleteSpace(id); this.event.emit('workspace.deleted', id); diff --git a/packages/backend/server/src/fundamentals/event/def.ts b/packages/backend/server/src/fundamentals/event/def.ts index ff1081463fd60..33db14b7a2832 100644 --- a/packages/backend/server/src/fundamentals/event/def.ts +++ b/packages/backend/server/src/fundamentals/event/def.ts @@ -19,7 +19,11 @@ export interface DocEvents { export interface UserEvents { updated: Payload>; - deleted: Payload; + deleted: Payload< + User & { + ownedWorkspaces: Workspace['id'][]; + } + >; } /**