feat(billing): cap each owner's stored file volume

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-02 15:44:36 +03:30
co-authored by Cursor
parent e6e90aabf3
commit 28938e8f77
20 changed files with 647 additions and 22 deletions
+8 -1
View File
@@ -19,6 +19,7 @@ import * as path from 'path';
import { PrismaService } from '../prisma/prisma.service';
import { buildSearchWhere, ci } from '../common/search';
import { LocalFileStorage } from '../storage/local-file.storage';
import { StorageQuotaService } from '../billing/storage-quota.service';
import { GamificationService } from '../gamification/gamification.service';
import { asCustomFields, toJsonCustomFields } from '../common/custom-fields';
import {
@@ -114,6 +115,7 @@ export class NotesService {
private readonly prisma: PrismaService,
private readonly files: LocalFileStorage,
private readonly game: GamificationService,
private readonly storage: StorageQuotaService,
) {}
async list(
@@ -444,10 +446,12 @@ export class NotesService {
): Promise<void> {
const note = await this.prisma.note.findFirst({
where: { id, organizationId, ...noteAccessWhere(actor) },
include: { attachments: { select: { storageKey: true } } },
include: { attachments: { select: { storageKey: true, sizeBytes: true } } },
});
if (!note) throw new NotFoundException('یادداشت پیدا نشد');
const bytes = note.attachments.reduce((sum, file) => sum + file.sizeBytes, 0);
if (bytes) await this.storage.addLocal(organizationId, -bytes);
for (const att of note.attachments) {
await this.files.remove(att.storageKey);
}
@@ -491,6 +495,7 @@ export class NotesService {
.join(organizationId, 'notes', noteId, `${attachmentId}${ext}`)
.replace(/\\/g, '/');
await this.storage.assertCanAdd(organizationId, file.size);
await this.files.save(storageKey, file.buffer);
try {
@@ -506,6 +511,7 @@ export class NotesService {
uploadedById: userId,
},
});
await this.storage.addLocal(organizationId, file.size);
return this.toAttachmentDto(row);
} catch (err) {
await this.files.remove(storageKey);
@@ -526,6 +532,7 @@ export class NotesService {
if (!row) throw new NotFoundException('پیوست پیدا نشد');
await this.files.remove(row.storageKey);
await this.prisma.noteAttachment.delete({ where: { id: row.id } });
await this.storage.addLocal(organizationId, -row.sizeBytes);
}
async openAttachmentDownload(