Files
TeleUploader/docker-compose.yml
T
Claude 1484d5265d
Deploy FileDrop / deploy (push) Failing after 19s
refactor: merge BOT_TOKEN + ADDITIONAL_BOT_TOKENS into single BOT_TOKENS env + speed audit
BOT_TOKENS env:
- Single BOT_TOKENS env var (comma-separated) replaces BOT_TOKEN + ADDITIONAL_BOT_TOKENS
- Backward compat: falls back to BOT_TOKEN + ADDITIONAL_BOT_TOKENS if BOT_TOKENS unset
- Config exposes botTokens: string[] instead of botToken + additionalBotTokens
- Updated env.ts, bot-pool.ts, docker-compose.yml, .env.example, CLAUDE.md, all tests

Speed audit (S3 -> Telegram upload flow):
- Hoisted 5 dynamic await import('../../../db/index') to top-level static imports
  in s3-controller.ts (3x) and web-api-controller.ts (2x)
  -> saves module resolution + async overhead on every upload
- Removed stale UPLOAD_CONCURRENCY env from docker-compose.yml
  (already removed from env.ts in prior refactor)

Upload flow is already concurrent:
- streamBodyToTemp() uses Bun.file(path).writer() — O(1) memory, safe for multi-GB blobs
- utils/chunked-storage.ts reads chunks serially but uploads concurrently with
  inFlight backpressure at effectiveConcurrency * 2 (= 16 with 8 bots)
- bot-pool.ts: per-bot PQueue(concurrency=1), 8 bots = 8 concurrent uploads per file,
  selectBot() picks least-loaded, 429 detection + inner+outer retry loops
- TELEGRAM_API_TIMEOUT_MS=120s — ample for 48MB chunks
- Infrastructure chunked-storage.ts (DI-based, dead code) has serial upload trap —
  noted for future cleanup
2026-07-29 15:03:02 +07:00

71 lines
2.8 KiB
YAML

services:
app:
build: .
container_name: filedrop-app
restart: always
environment:
- BOT_TOKENS=${BOT_TOKENS}
- STORAGE_CHANNEL_ID=${STORAGE_CHANNEL_ID}
- BASE_URL=${BASE_URL}
- DATABASE_URL=${DATABASE_URL}
- PORT=3000
- NODE_ENV=production
- LOG_LEVEL=info
- TRUST_PROXY=true
- BATCH_MAX_ITEMS=${BATCH_MAX_ITEMS:-20}
- BATCH_MAX_SIZE_BYTES=${BATCH_MAX_SIZE_BYTES:-524288000}
- MAX_REQUEST_BODY_BYTES=${MAX_REQUEST_BODY_BYTES:-2147483648}
- TELEGRAM_CHUNK_SIZE_BYTES=${TELEGRAM_CHUNK_SIZE_BYTES:-20971520}
- COMPRESS_CHUNKED_UPLOADS=${COMPRESS_CHUNKED_UPLOADS:-true}
- CHUNK_COMPRESSION_MIN_SIZE_BYTES=${CHUNK_COMPRESSION_MIN_SIZE_BYTES:-4096}
- RATE_LIMIT_WINDOW_MS=${RATE_LIMIT_WINDOW_MS:-60000}
- RATE_LIMIT_MAX_REQUESTS=${RATE_LIMIT_MAX_REQUESTS:-30}
- ADMIN_API_TOKEN=${ADMIN_API_TOKEN:-}
- SESSION_COOKIE_NAME=${SESSION_COOKIE_NAME:-tu_session}
- SESSION_COOKIE_MAX_AGE_SECONDS=${SESSION_COOKIE_MAX_AGE_SECONDS:-86400}
- S3_ACCESS_KEY=${S3_ACCESS_KEY:-filedrop-admin}
- S3_SECRET_KEY=${S3_SECRET_KEY}
- S3_DEFAULT_REGION=${S3_DEFAULT_REGION:-us-east-1}
- S3_VHOST_DOMAINS=${S3_VHOST_DOMAINS:-upload.asepharyana.my.id,asepharyana.web.id}
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp:size=4g,mode=1777
- /usr/src/app/logs:size=64m,mode=1777
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
healthcheck:
test:
- CMD-SHELL
- "bun -e \"fetch('http://localhost:3000/health').then(r => r.status === 200 ? process.exit(0) : process.exit(1))\""
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
networks:
- app-shared-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.filedrop.rule=Host(`upload.asepharyana.my.id`) || Host(`upload.asepharyana.web.id`) || Host(`asepharyana.web.id`) || HostRegexp(`{subhost:[a-z0-9][a-z0-9.-]+}.asepharyana.web.id`)"
- "traefik.http.routers.filedrop.entrypoints=websecure"
- "traefik.http.routers.filedrop.tls=true"
- "traefik.http.routers.filedrop.tls.certresolver=cloudflare"
- "traefik.http.services.filedrop.loadbalancer.server.port=3000"
- "traefik.http.middlewares.filedrop-rl.ratelimit.average=300"
- "traefik.http.middlewares.filedrop-rl.ratelimit.burst=100"
- "traefik.http.middlewares.filedrop-rl.ratelimit.period=1m"
- "traefik.http.middlewares.filedrop-buf.buffering.maxRequestBodyBytes=2147483648"
- "traefik.http.routers.filedrop.middlewares=filedrop-rl@docker,filedrop-buf@docker"
networks:
app-shared-net:
name: app-shared-net
external: true