mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-01 17:50:50 +08:00
fix: enforce quota for comment attachments (#15149)
## Summary This change includes comment attachments in workspace storage usage and checks workspace storage quota before accepting a new comment attachment upload. ## Impact Comment attachments already had a per-file size limit, but they were not counted in the same workspace storage usage path as other uploaded blobs. A user with comment permission could keep adding attachments without those bytes participating in workspace storage quota calculations. ## Fix - Count comment attachment bytes in workspace storage usage reconciliation. - Check the workspace quota before storing a new comment attachment. - Return the existing comment attachment quota error when the upload would exceed limits. ## Validation - `git diff --check` - Full test/lint suite was not run locally because dependencies are not installed in this checkout. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Workspace attachment uploads now respect storage and file quota limits more accurately. * Workspace storage tracking now includes comment attachments, improving quota enforcement. * **Bug Fixes** * Attachment uploads now fail with a clear quota error when a workspace is out of space or blob capacity. * Storage usage calculations now better reflect actual workspace content, including non-deleted files. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: failsafesecurity <190101117+failsafesecurity@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { ServerConfigModule } from '../config';
|
||||
import { PermissionModule } from '../permission';
|
||||
import { QuotaServiceModule } from '../quota';
|
||||
import { StorageModule } from '../storage';
|
||||
import { CommentRealtimeModule } from './realtime.module';
|
||||
import { CommentResolver } from './resolver';
|
||||
@@ -9,6 +10,7 @@ import { CommentResolver } from './resolver';
|
||||
@Module({
|
||||
imports: [
|
||||
PermissionModule,
|
||||
QuotaServiceModule,
|
||||
StorageModule,
|
||||
ServerConfigModule,
|
||||
CommentRealtimeModule,
|
||||
|
||||
@@ -26,6 +26,7 @@ import { Comment, DocMode, Models, Reply } from '../../models';
|
||||
import { CurrentUser } from '../auth/session';
|
||||
import { ServerFeature, ServerService } from '../config';
|
||||
import { DocAction, PermissionAccess } from '../permission';
|
||||
import { QuotaService } from '../quota';
|
||||
import { RealtimePublisher } from '../realtime';
|
||||
import { CommentAttachmentStorage } from '../storage';
|
||||
import { UserType } from '../user';
|
||||
@@ -56,6 +57,7 @@ export class CommentResolver {
|
||||
private readonly service: CommentService,
|
||||
private readonly ac: PermissionAccess,
|
||||
private readonly commentAttachmentStorage: CommentAttachmentStorage,
|
||||
private readonly quota: QuotaService,
|
||||
private readonly queue: JobQueue,
|
||||
private readonly models: Models,
|
||||
private readonly server: ServerService,
|
||||
@@ -354,13 +356,19 @@ export class CommentResolver {
|
||||
'Doc.Comments.Create'
|
||||
);
|
||||
|
||||
// TODO(@fengmk2): should check total attachment quota in the future version
|
||||
const buffer = await readableToBuffer(attachment.createReadStream());
|
||||
// max attachment size is 10MB
|
||||
if (buffer.length > 10 * 1024 * 1024) {
|
||||
throw new CommentAttachmentQuotaExceeded();
|
||||
}
|
||||
|
||||
const checkExceeded =
|
||||
await this.quota.getWorkspaceQuotaCalculator(workspaceId);
|
||||
const result = checkExceeded(buffer.length);
|
||||
if (result?.blobQuotaExceeded || result?.storageQuotaExceeded) {
|
||||
throw new CommentAttachmentQuotaExceeded();
|
||||
}
|
||||
|
||||
const key = randomUUID();
|
||||
await this.commentAttachmentStorage.put(
|
||||
workspaceId,
|
||||
|
||||
@@ -287,17 +287,30 @@ export class QuotaStateService {
|
||||
}
|
||||
|
||||
private async getWorkspaceStorageUsage(workspaceId: string) {
|
||||
const sum = await this.db.blob.aggregate({
|
||||
where: {
|
||||
workspaceId,
|
||||
deletedAt: null,
|
||||
},
|
||||
_sum: {
|
||||
size: true,
|
||||
},
|
||||
});
|
||||
const [blobSum, commentAttachmentSum] = await Promise.all([
|
||||
this.db.blob.aggregate({
|
||||
where: {
|
||||
workspaceId,
|
||||
deletedAt: null,
|
||||
},
|
||||
_sum: {
|
||||
size: true,
|
||||
},
|
||||
}),
|
||||
this.db.commentAttachment.aggregate({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
_sum: {
|
||||
size: true,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return BigInt(sum._sum.size ?? 0);
|
||||
return (
|
||||
BigInt(blobSum._sum.size ?? 0) +
|
||||
BigInt(commentAttachmentSum._sum.size ?? 0)
|
||||
);
|
||||
}
|
||||
|
||||
private hasStandaloneWorkspaceQuota(plan: string) {
|
||||
|
||||
Reference in New Issue
Block a user