mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-03 10:30:12 +08:00
1b859a37c5
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Safer, consistent file downloads with automatic attachment headers and filenames. - Smarter MIME detection for uploads (avatars, workspace blobs, Copilot files/transcripts). - Sensible default buffer limit when reading uploads. - **Bug Fixes** - Prevents risky content from rendering inline by forcing downloads and adding no‑sniff protection. - More accurate content types when original metadata is missing or incorrect. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
import { Readable } from 'node:stream';
|
|
|
|
import { BlobQuotaExceeded, StorageQuotaExceeded } from '../error';
|
|
import { OneKB } from './unit';
|
|
|
|
export type CheckExceededResult =
|
|
| {
|
|
storageQuotaExceeded: boolean;
|
|
blobQuotaExceeded: boolean;
|
|
}
|
|
| undefined;
|
|
|
|
export async function readBuffer(
|
|
readable: Readable,
|
|
checkExceeded: (recvSize: number) => CheckExceededResult
|
|
): Promise<Buffer> {
|
|
return new Promise<Buffer>((resolve, reject) => {
|
|
const chunks: Uint8Array[] = [];
|
|
let totalSize = 0;
|
|
let result: CheckExceededResult;
|
|
|
|
readable.on('data', chunk => {
|
|
totalSize += chunk.length;
|
|
|
|
// check size after receive each chunk to avoid unnecessary memory usage
|
|
result = checkExceeded(totalSize);
|
|
if (result?.blobQuotaExceeded) {
|
|
reject(new BlobQuotaExceeded());
|
|
} else if (result?.storageQuotaExceeded) {
|
|
reject(new StorageQuotaExceeded());
|
|
}
|
|
|
|
if (checkExceeded(totalSize)) {
|
|
reject(new BlobQuotaExceeded());
|
|
readable.destroy(new BlobQuotaExceeded());
|
|
return;
|
|
}
|
|
chunks.push(chunk);
|
|
});
|
|
|
|
readable.on('error', reject);
|
|
readable.on('end', () => {
|
|
const buffer = Buffer.concat(chunks, totalSize);
|
|
|
|
if (checkExceeded(buffer.length)) {
|
|
reject(new BlobQuotaExceeded());
|
|
} else {
|
|
resolve(buffer);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function readBufferWithLimit(
|
|
readable: Readable,
|
|
limit: number = 500 * OneKB
|
|
): Promise<Buffer> {
|
|
return readBuffer(readable, size =>
|
|
size > limit
|
|
? { blobQuotaExceeded: true, storageQuotaExceeded: false }
|
|
: undefined
|
|
);
|
|
}
|
|
|
|
export async function readableToBuffer(readable: Readable) {
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of readable) {
|
|
chunks.push(chunk);
|
|
}
|
|
return Buffer.concat(chunks);
|
|
}
|