feat: enhance configuration and rate limiting
- Added new configuration options: trustProxy, uploadConcurrency, batchMaxItems, batchMaxSizeBytes, and maxRequestBodyBytes to AppConfig. - Implemented utility functions for parsing environment variables and masking sensitive data. - Updated rate limiting logic to use configurable window size and maximum requests per window. - Introduced a middleware for rate limiting on specific routes. - Refactored file handling routes to support streaming downloads instead of redirects. - Improved error handling and response formatting in file routes. - Added support for oversized request rejection based on Content-Length header. - Updated Swagger documentation to reflect changes in API behavior and responses. - Enhanced tests to cover new features and ensure proper functionality.
This commit is contained in:
+37
-18
@@ -13,7 +13,7 @@ beforeAll(async () => {
|
||||
} catch {
|
||||
// Fallback 1x1px JPEG
|
||||
realPhotoBuffer = Buffer.from(
|
||||
'ffd8ffe000104a46494600010101006000600000ffdb004300080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432ffc0000b080001000101011100ffc4001f0000010501010110000000000000000000000102030405060708ffda000c03010002110311003f00a0ffd9',
|
||||
'ffd8ffe000104a46494600010101006000600000ffdb004300080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432ffc0b000080100010101011100ffc4001f0000010501010110000000000000000000000102030405060708ffda000c03010002110311003f00a0ffd9',
|
||||
'hex',
|
||||
);
|
||||
}
|
||||
@@ -22,8 +22,6 @@ beforeAll(async () => {
|
||||
// Mock db
|
||||
type UploadResponseBody = {
|
||||
public_id: string;
|
||||
telegram_file_id: string;
|
||||
telegram_file_unique_id: string;
|
||||
file_name: string;
|
||||
file_type: string;
|
||||
download_url: string;
|
||||
@@ -79,19 +77,17 @@ const mockForwardToStorage = mock(() =>
|
||||
}),
|
||||
);
|
||||
|
||||
const mockGetFile = mock(() =>
|
||||
Promise.resolve({
|
||||
file_id: 'tg-file-id-123',
|
||||
file_size: 1000,
|
||||
mime_type: 'image/jpeg',
|
||||
}),
|
||||
);
|
||||
|
||||
mock.module('../src/utils/telegram', () => ({
|
||||
forwardToStorage: mockForwardToStorage,
|
||||
getBot: () => ({
|
||||
telegram: {
|
||||
getFile: mockGetFile,
|
||||
getFile: mock(() =>
|
||||
Promise.resolve({
|
||||
file_id: 'tg-file-id-123',
|
||||
file_size: 1000,
|
||||
mime_type: 'image/jpeg',
|
||||
}),
|
||||
),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
@@ -106,7 +102,6 @@ describe('Upload Route Handler', () => {
|
||||
mockWhere.mockClear();
|
||||
mockLimit.mockClear();
|
||||
mockForwardToStorage.mockClear();
|
||||
mockGetFile.mockClear();
|
||||
mockSelectResult = [];
|
||||
const uploadRoute = await import('../src/routes/upload');
|
||||
handleUpload = uploadRoute.handleUpload;
|
||||
@@ -144,10 +139,15 @@ describe('Upload Route Handler', () => {
|
||||
const body = await uploadResponseJson(res);
|
||||
|
||||
expect(body.public_id).toContain('mocked-nanoid-id');
|
||||
expect(body.telegram_file_id).toBe('tg-file-id-123');
|
||||
expect(body.telegram_file_unique_id).toBe('tg-unique-id-abc');
|
||||
expect(body.file_name).toBe('test.png');
|
||||
expect(body.file_type).toBe('photo');
|
||||
expect(body.download_url).toContain('/f/');
|
||||
// No internal Telegram IDs in public response
|
||||
expect(body).not.toHaveProperty('telegram_file_id');
|
||||
expect(body).not.toHaveProperty('telegram_file_unique_id');
|
||||
expect(body).not.toHaveProperty('storage_chat_id');
|
||||
expect(body).not.toHaveProperty('storage_message_id');
|
||||
expect(body).not.toHaveProperty('uploader_id');
|
||||
});
|
||||
|
||||
it('should reject JSON upload without file key', async () => {
|
||||
@@ -182,6 +182,7 @@ describe('Upload Route Handler', () => {
|
||||
const body = await uploadResponseJson(res);
|
||||
expect(body.public_id).toContain('mocked-nanoid-id');
|
||||
expect(body.file_name).toBe('test_multi.png');
|
||||
expect(body).not.toHaveProperty('telegram_file_id');
|
||||
});
|
||||
|
||||
it('should deduplicate multipart upload if hash exists', async () => {
|
||||
@@ -216,10 +217,9 @@ describe('Upload Route Handler', () => {
|
||||
const body = await uploadResponseJson(res);
|
||||
|
||||
expect(body.public_id).toBe('existing-id-123');
|
||||
expect(body.telegram_file_id).toBe('existing-tg-id');
|
||||
expect(body.telegram_file_unique_id).toBe('existing-tg-unique');
|
||||
expect(body.file_name).toBe('existing_name.txt');
|
||||
expect(body.download_url).toContain('/f/existing-id-123');
|
||||
expect(body).not.toHaveProperty('telegram_file_id');
|
||||
|
||||
// DB query happened
|
||||
expect(mockSelect).toHaveBeenCalled();
|
||||
@@ -263,9 +263,9 @@ describe('Upload Route Handler', () => {
|
||||
const body = await uploadResponseJson(res);
|
||||
|
||||
expect(body.public_id).toBe('existing-json-id');
|
||||
expect(body.telegram_file_id).toBe('existing-tg-json-id');
|
||||
expect(body.file_name).toBe('existing_json.txt');
|
||||
expect(body.download_url).toContain('/f/existing-json-id');
|
||||
expect(body).not.toHaveProperty('telegram_file_id');
|
||||
|
||||
// DB query happened
|
||||
expect(mockSelect).toHaveBeenCalled();
|
||||
@@ -275,6 +275,25 @@ describe('Upload Route Handler', () => {
|
||||
expect(mockInsert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reject oversized request by Content-Length header', async () => {
|
||||
const req = new Request('http://localhost:3000/api/upload', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
'content-length': String(3 * 1024 * 1024 * 1024),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
file: Buffer.from('hello').toString('base64'),
|
||||
fileName: 'test.txt',
|
||||
}),
|
||||
});
|
||||
|
||||
const res = await handleUpload(req);
|
||||
expect(res.status).toBe(413);
|
||||
const body = await uploadResponseJson(res);
|
||||
expect(body.error).toContain('too large');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user