7.3 KiB
7.3 KiB
Telegram Bot Uploader Backend Design
Date: 2026-05-17
Status: Approved
Stack: Bun, Telegraf, PostgreSQL, Drizzle ORM, Winston, nanoid
Overview
Production-ready backend untuk Telegram file uploader dengan dual upload methods (bot + HTTP API), persistent storage di private Telegram channel, dan redirect-based download untuk minimal bandwidth usage.
Architecture
Components
-
HTTP Server (Bun.serve)
- Handle REST endpoints
- Rate limiting (in-memory, per IP)
- Request/response logging
-
Telegram Bot (Telegraf)
- Listen file uploads from users
- Forward files to private storage channel
- Extract and persist metadata
-
Database Layer (Drizzle + PostgreSQL)
- Persist file metadata
- Indexed queries by public_id, telegram_file_id, uploader_id
Data Flow
Upload via Telegram Bot
- User sends file to bot (document/photo/video/audio/voice/animation)
- Bot validates file size against Telegram limits
- Bot forwards file to private storage channel
- Bot extracts
telegram_file_id,telegram_file_unique_id,storage_message_id - Bot generates
public_idusing nanoid - Bot saves metadata to PostgreSQL
- Bot replies with download link:
https://tele.asepharyana.tech/f/{public_id}
Upload via HTTP API
- Client POSTs to
/api/uploadwith file (multipart or base64) - Server validates file size
- Server forwards file to private storage channel
- Server extracts file IDs and message ID
- Server generates
public_id - Server saves metadata to PostgreSQL
- Server returns full metadata JSON
Download (Redirect)
- User accesses
GET /f/:public_id - Server checks rate limit (30 req/min per IP, in-memory)
- Server queries DB by
public_id - Server calls Telegram Bot API
getFile() - Server redirects to Telegram CDN URL
- Browser downloads from Telegram (VPS bandwidth minimal)
File Info
- User accesses
GET /file/:public_id/info - Server queries DB by
public_id - Server returns full metadata (no rate limit)
Database Schema
Table: files
CREATE TABLE files (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
public_id VARCHAR(21) UNIQUE NOT NULL,
telegram_file_id VARCHAR NOT NULL,
telegram_file_unique_id VARCHAR NOT NULL,
storage_chat_id BIGINT NOT NULL,
storage_message_id BIGINT NOT NULL,
file_name VARCHAR NOT NULL,
mime_type VARCHAR NOT NULL,
size_bytes BIGINT NOT NULL,
file_type VARCHAR NOT NULL,
uploader_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_files_public_id ON files(public_id);
CREATE INDEX idx_files_telegram_file_id ON files(telegram_file_id);
CREATE INDEX idx_files_uploader_id ON files(uploader_id);
CREATE INDEX idx_files_created_at ON files(created_at DESC);
API Endpoints
POST /api/upload
Upload file via HTTP API.
Request (Multipart):
Content-Type: multipart/form-data
file: <binary>
fileName: optional_filename.ext
Request (Base64):
{
"file": "base64encodedstring",
"fileName": "filename.ext"
}
Response (200):
{
"public_id": "abc123xyz",
"file_name": "document.pdf",
"mime_type": "application/pdf",
"size_bytes": 1024000,
"file_type": "document",
"created_at": "2026-05-17T23:42:19Z",
"download_url": "https://tele.asepharyana.tech/f/abc123xyz"
}
Response (400):
{
"error": "File size exceeds limit"
}
GET /f/:public_id
Redirect to Telegram CDN for download.
Response (302):
Location: https://api.telegram.org/file/bot.../...
Response (404):
{
"error": "File not found"
}
Response (429):
{
"error": "Rate limit exceeded"
}
GET /file/:public_id/info
Get file metadata.
Response (200):
{
"public_id": "abc123xyz",
"file_name": "document.pdf",
"mime_type": "application/pdf",
"size_bytes": 1024000,
"file_type": "document",
"uploader_id": 123456789,
"created_at": "2026-05-17T23:42:19Z"
}
Response (404):
{
"error": "File not found"
}
GET /health
Health check endpoint.
Response (200):
{
"status": "ok"
}
Project Structure
src/
index.js — entry point, start server & bot
bot.js — Telegraf bot setup & handlers
env.js — environment validation
db/
index.js — Drizzle setup & connection
schema.js — Drizzle schema definition
routes/
files.js — GET /f/:public_id, GET /file/:public_id/info
upload.js — POST /api/upload (multipart & base64)
health.js — GET /health
utils/
file.js — file type validation, size check
telegram.js — Telegram API helpers, forward file
logger.js — Winston logger setup
rateLimit.js — in-memory rate limiter
package.json
.env.example
schema.sql
Configuration
Environment Variables
BOT_TOKEN=<telegram_bot_token>
STORAGE_CHANNEL_ID=<private_channel_id>
BASE_URL=https://tele.asepharyana.tech
DATABASE_URL=postgresql://user:password@localhost:5432/telegram_uploader
PORT=3000
NODE_ENV=production
LOG_LEVEL=info
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=30
Error Handling
- Winston logger with levels: error, warn, info, debug
- Structured logging (JSON format) for production
- Try-catch in all async operations
- Graceful error responses:
- 400 Bad Request — invalid input
- 404 Not Found — file not found
- 429 Too Many Requests — rate limit exceeded
- 500 Internal Server Error — server error
- Telegram API errors — log and retry logic for transient failures
Rate Limiting
- In-memory rate limiter per IP address
- Window-based: reset every minute
- Limit: 30 requests/minute per IP (configurable)
- Response: 429 Too Many Requests if exceeded
- Applies to:
GET /f/:public_idonly
Graceful Shutdown
- Stop accepting new requests
- Wait for pending requests to complete
- Close database connection pool
- Stop Telegram bot polling
- Exit process
File Type Support
Accepts all Telegram file types:
- document
- photo
- video
- audio
- voice
- animation
File size limits per Telegram API:
- Documents: 2GB
- Photos: 10MB
- Videos: 2GB
- Audio: 200MB
- Voice: 200MB
- Animation: 2GB
Security Considerations
- No authentication on
/api/upload(public endpoint) - Rate limiting on download endpoint to prevent abuse
- File size validation against Telegram limits
- Input validation on file names
- SQL injection prevention via Drizzle ORM
- CORS headers if needed for cross-origin requests
Deployment Notes
- Single instance: in-memory rate limiter sufficient
- Multiple instances: consider Redis-based rate limiter (future enhancement)
- Database: PostgreSQL 12+
- Telegram Bot: must be admin in private storage channel
- URL stability: backend URL is permanent, Telegram CDN URLs may change
Testing Strategy
- Unit tests for utility functions (file validation, rate limiter)
- Integration tests for database operations
- E2E tests for upload and download flows
- Manual testing with actual Telegram bot
Future Enhancements
- Redis-based rate limiter for multi-instance deployments
- File expiration/cleanup policies
- Download analytics
- API key authentication for
/api/upload - Webhook support for upload notifications