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:
MythEclipse
2026-05-29 03:33:39 +07:00
parent be813b1c0e
commit 5425f6d33d
16 changed files with 466 additions and 201 deletions
+15 -27
View File
@@ -25,7 +25,6 @@ const fileInfoProperties = {
mime_type: { type: 'string', example: 'application/pdf' },
size_bytes: { type: 'integer', example: 1048576 },
file_type: { type: 'string', example: 'document' },
uploader_id: { type: 'integer', example: 0 },
created_at: {
type: 'string',
format: 'date-time',
@@ -35,10 +34,6 @@ const fileInfoProperties = {
const uploadProperties = {
...fileInfoProperties,
telegram_file_id: { type: 'string', example: 'BQACAgQAAxkBA...' },
telegram_file_unique_id: { type: 'string', example: 'AgAD8w...' },
storage_chat_id: { type: 'integer', example: -1001234567890 },
storage_message_id: { type: 'integer', example: 42 },
download_url: {
type: 'string',
example: `${config.baseUrl}/f/xYz123`,
@@ -56,7 +51,7 @@ export const handleSwaggerJson = async (): Promise<Response> => {
info: {
title: 'TeleUploader API',
version: '1.0.0',
description: 'Telegram-backed file uploader API with redirect-based downloads.',
description: 'Telegram-backed file uploader API with stream-based downloads.',
},
servers: [
{
@@ -95,7 +90,7 @@ export const handleSwaggerJson = async (): Promise<Response> => {
'/api/upload': {
post: {
summary: 'Upload File',
description: 'Uploads a file to Telegram storage via multipart/form-data or JSON base64.',
description: 'Uploads a file to Telegram storage via multipart/form-data or JSON base64. Rate-limited by IP.',
requestBody: {
required: true,
content: {
@@ -144,6 +139,14 @@ export const handleSwaggerJson = async (): Promise<Response> => {
description: 'Bad request.',
content: jsonContent(errorSchema('No file provided')),
},
'413': {
description: 'Request body too large.',
content: jsonContent(errorSchema('Request body too large')),
},
'429': {
description: 'Rate limit exceeded.',
content: jsonContent(errorSchema('Rate limit exceeded')),
},
'500': {
description: 'Internal server error.',
content: jsonContent(errorSchema('Upload failed')),
@@ -153,21 +156,12 @@ export const handleSwaggerJson = async (): Promise<Response> => {
},
'/f/{public_id}': {
get: {
summary: 'Redirect to Telegram File URL',
description:
'Gets a fresh Telegram download URL and redirects with 302. Rate-limited by IP.',
summary: 'Download File',
description: 'Proxies file from Telegram storage as a streamed download. Rate-limited by IP.',
parameters: [publicIdParameter],
responses: {
'302': {
description: 'Redirect to Telegram CDN URL.',
headers: {
Location: {
schema: {
type: 'string',
example: 'https://api.telegram.org/file/botTOKEN/documents/file_0.pdf',
},
},
},
'200': {
description: 'File binary stream.',
},
'404': {
description: 'File not found.',
@@ -212,12 +206,7 @@ export const handleSwaggerJson = async (): Promise<Response> => {
},
};
return Response.json(spec, {
status: 200,
headers: {
'access-control-allow-origin': '*',
},
});
return Response.json(spec, { status: 200 });
};
export const handleSwaggerHtml = async (): Promise<Response> => {
@@ -256,7 +245,6 @@ export const handleSwaggerHtml = async (): Promise<Response> => {
status: 200,
headers: {
'content-type': 'text/html; charset=utf-8',
'access-control-allow-origin': '*',
'x-content-type-options': 'nosniff',
},
});