Commit Graph
203 Commits
Author SHA1 Message Date
Claude 996b7d06ef chore: add more bot tokens for better rate limit distribution
Deploy FileDrop / deploy (push) Successful in 41s
Previous: 4 bots → now: 6 bots
Each bot handles fewer concurrent uploads, reducing 429 rate limits
and avoiding thundering-herd sleep patterns.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:02:00 +07:00
Claude 3f541121f5 fix: yield microtask after Promise.race in backpressure check
Deploy FileDrop / deploy (push) Successful in 48s
Adds a setTimeout(0) microtask yield after Promise.race to ensure
the .finally() handler that removes promises from the inFlight
set has executed before the next backpressure check.

Also ensure parts array is sorted by partNumber after concurrent
uploads complete, since promises resolve in arbitrary order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:44:14 +07:00
Claude 1a38b32fbc feat: concurrent chunk uploads within single file
Deploy FileDrop / deploy (push) Successful in 41s
Previously uploadFileInTelegramChunks awaited each chunk's upload
before reading the next, making all chunks sequential within a file.
Now chunks are uploaded concurrently using a managed Set of in-flight
promises with backpressure limiting (2x uploadConcurrency).

This means a single 1GB Docker layer split into 48MB chunks will
have up to 32 chunks uploading simultaneously, not one at a time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 12:28:05 +07:00
Claude b034c3131d chore: trigger deploy for TELEGRAM_CHUNK_SIZE_BYTES=48MB
Deploy FileDrop / deploy (push) Successful in 52s
2026-07-29 12:23:44 +07:00
Claude d8b8a1381d chore: increase Telegram chunk size to 48MB (max ~49MB)
Deploy FileDrop / deploy (push) Successful in 44s
Tested actual Telegram Bot API limit:
- 49MB 
- 50MB  (413 Request Entity Too Large)
Set TELEGRAM_CHUNK_SIZE_BYTES=50331648 (48MB) for safety

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 11:57:46 +07:00
Claude b0c5327bb3 fix: add content_type column to multipart_uploads migration
Deploy FileDrop / deploy (push) Successful in 50s
The createMultipartUpload function inserts content_type but the
database column was missing, causing 500 errors on every Gitea
Docker registry push (which uses multipart uploads for blob storage).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:09:24 +07:00
Claude e3e7430ce0 fix: zero-downtime deploy with --wait flag + fallback
Deploy FileDrop / deploy (push) Canceled after 0s
2026-07-29 09:57:47 +07:00
Claude d8b1878a75 fix: add ETag and headers to 304 Not Modified responses
Deploy FileDrop / deploy (push) Failing after 14m18s
AWS SDK requires ETag header in 304 responses. Without it, the SDK
throws UnknownError despite receiving a valid 304 status code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:23:16 +07:00
Claude e50e297e79 fix: add ETag and headers to 304 Not Modified responses
Deploy FileDrop / deploy (push) Failing after 15s
AWS SDK requires ETag header in 304 responses. Without it, the SDK
throws UnknownError despite receiving a valid 304 status code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:19:57 +07:00
Claude 888af45a7f fix: normalizeUri SigV4 trailing slash + cleanup debug logs
Deploy FileDrop / deploy (push) Successful in 35s
2026-07-29 09:16:07 +07:00
Claude cd9852a5ec fix: preserve trailing slashes in normalizeUri for SigV4
Deploy FileDrop / deploy (push) Successful in 36s
The empty-segment skip in normalizeUri (introduced in round 1 fix)
was stripping trailing slashes from canonical URIs, e.g. /bucket/
became /bucket. The AWS SDK signs with the trailing slash intact, so
signatures never matched for any S3 operation with a body.

The fix: only skip '.' segments (dot-segment removal per RFC 3986),
preserve all other segments including empty ones from trailing
slashes and double slashes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:14:18 +07:00
Claude c18b9d265b fix: preserve trailing slash in SigV4 canonical URI
Deploy FileDrop / deploy (push) Successful in 35s
AWS SDK includes trailing slash in the canonical URI for bucket
operations (e.g. PUT /bucket-name/). My earlier 'fix' that stripped
trailing slashes broke SigV4 signature verification. The trailing
slash is intentional per AWS SigV4 — only dot-segments are removed,
not trailing slashes.

Re-verified with @smithy/signature-v4: path /bucket-name/ produces
the client signature, while /bucket-name does not match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:08:17 +07:00
Claude 66c4247e64 debug: add SigV4 mismatch logging with expected vs received signature
Deploy FileDrop / deploy (push) Successful in 38s
2026-07-29 09:03:51 +07:00
Claude 9bc2f22589 fix: strip trailing slash in SigV4 canonical URI
Deploy FileDrop / deploy (push) Successful in 36s
AWS SigV4 canonical URI must not have trailing slash (except root '/').
Bun can receive paths with trailing slash from SDK, causing signature
mismatch for all bucket operations (CreateBucket, HeadBucket, etc.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:56:08 +07:00
Claude f9484738db debug: add SigV4 canonical request logging for troubleshooting
Deploy FileDrop / deploy (push) Successful in 36s
2026-07-29 08:54:10 +07:00
Claude 2882247ad3 fix: add catch-all S3 route for path-style requests (/{bucket}/{key})
Deploy FileDrop / deploy (push) Successful in 36s
Bun's '/' route only matches root path '/'. S3 SDK clients using
forcePathStyle:true send ALL requests to /{bucket}/{key} which never
matched any route → 404. Added '/*' catch-all that checks for S3
auth headers before dispatching.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:50:02 +07:00
Claude d8da2044b2 fix: S3→Telegram upload pipeline — OOM, queue limits, shutdown drain, timeouts
Deploy FileDrop / deploy (push) Successful in 46s
CRITICAL:
- Content-MD5 no longer loads entire file via arrayBuffer() — MD5 computed
  incrementally in streamBodyToTemp alongside SHA-256 (fixes OOM for GB files)

HIGH:
- Add 120s timeout to Telegraf API calls via Promise.race in executeWithBotRetry
  (prevents queue slot exhaustion from hung Telegram connections)
- Add queue size limit (1000 pending max) — reject new tasks when full
- Add graceful shutdown drain — waitForQueue with 30s timeout before exit
- Fix temp file leak when findFileByBucketAndKey throws (wrap in try-catch)
- Fix createReadStream fd leak — destroy stream on forwardToStorage error
- writer.end() wrapped in silent try-catch to prevent error swallowing
- writer.end() result ignored, writerFailed flag prevents double-end

MEDIUM:
- Remove 'retry after' from isTransientError patterns to stop double-retry
  layering (was causing up to 96 bot attempts per chunk)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:40:50 +07:00
Claude de7d276245 fix: round 2 S3 audit — CRITICAL SigV4 payload hash bug, timeouts, Content-MD5/Length validation
Deploy FileDrop / deploy (push) Successful in 43s
CRITICAL:
- SigV4 canonical request used sha256Hex('') instead of x-amz-content-sha256
  header value — every PUT/POST with body would fail 403. Now uses the
  signed header value for canonical request, verifyBodyHash after streaming
  for integrity.

HIGH:
- Add 30s AbortSignal.timeout to all Telegram CDN fetches in object-stream.ts
  (previously could hang indefinitely, exhausting connection pool)

MEDIUM:
- Content-MD5 validation: compute and compare when header is present
- Content-Length validation: reject if actual body size != header
- max-keys=0 clamping: enforce minimum of 1 per S3 spec

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:22:45 +07:00
Claude e1e228430f chore: add pre-commit hook with husky — wajib lint pass sebelum commit
Deploy FileDrop / deploy (push) Failing after 10s
- Install husky v9, init .husky/pre-commit
- Hook runs 'bun run lint' and rejects commit on failure
- 'bun install' auto-activates hooks via prepare script

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:15:06 +07:00
Claude af160e0f33 fix: audit S3 protocol — 15+ security & correctness fixes
Deploy FileDrop / deploy (push) Successful in 43s
HIGH severity fixes:
- H1: Bot token leak via 302 redirect — always proxy S3 GETs
- H2: PUT TOCTOU race — add unique partial index (bucket_id, s3_key) WHERE NOT deleted
- H3: GET/HEAD ignore conditional headers (If-Match, If-None-Match, etc.)
- H4: Body payload hash not verified — add verifyBodyHash() post-stream check
- H5: Header-based auth has no expiry check — add 15-min clock skew window
- H7: Multipart abort does not delete parts — DELETE before UPDATE status
- H8: CompleteMultipartUpload skips part number & etag verification
- H9: XML regex fails on keys containing < — use non-greedy [\s\S]*?
- H10: Path-style vs virtual-hosted key decode mismatch

MEDIUM severity fixes:
- M1: Add Date header fallback for x-amz-date
- M2/M3: Validate service/termination in credential scope
- M4: Temp file leak when forwardToStorage throws in handleUploadPart
- M5: Multipart key consistency check (s3Key matches URL)
- M7: Use stored content-type from multipart initiate
- M9: Copy conditional headers skip when fileHash is null
- M11: Add 1000-key limit on DeleteObjects
- M13: Stricter bucket name validation (no .., no IP format)
- M14: NaN partNumber bypasses validation

LOW fixes:
- normalizeUri: dot-segment removal per RFC 3986
- localeCompare -> byte-order comparison in canonical query string
- Validate host in signed headers
- Server: AmazonS3 header on all responses
- x-amz-id-2 separate from x-amz-request-id
- IPv6 handling in stripPort
- Quiet element whitespace tolerance in XML parser
- content-type: application/xml on empty 2xx responses
- Duplicate interfaces/s3/ -> re-exports from utils/s3/

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:02:28 +07:00
Claude f5d56f52d4 chore: fix lint errors — noBannedTypes, import ordering, formatting
Deploy FileDrop / deploy (push) Successful in 42s
- Replace unsafe 'Function' type in test with ITelegramService interface
- Biome auto-fix formatting and import sorting across 8 files

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 07:30:31 +07:00
Claude ea87397801 refactor: remove upload and web-api routes, migrate to new controller structure
Deploy FileDrop / deploy (push) Failing after 12s
- Deleted `upload.ts` and `web-api.ts` routes, consolidating logic into dedicated controllers.
- Updated import paths in tests to reflect new controller structure.
- Refactored Telegram API utilities to utilize a bot pool for improved bot management and error handling.
- Enhanced environment variable tests to ensure additional bot tokens are correctly populated.
- Adjusted S3 bucket configuration tests to align with new controller imports.
- Updated Telegram queue implementation to reflect new infrastructure organization.
2026-07-29 07:28:30 +07:00
Claude 73adb5f58e fix: make S3 resilient for Docker registry — no rate limit, retry on transient Telegram errors
Deploy FileDrop / deploy (push) Failing after 15s
- Remove rate limiting from all S3 endpoints (used by Docker registry
  for concurrent blob pushes — 429 would abort the entire push).
- Add retry with exponential backoff in botPool.forwardToStorage for
  transient Telegram errors (network timeouts, 5xx, socket issues).
- Add retry with exponential backoff in botPool.getFileInfo per bot.
- Introduce isTransientError() pattern matcher covering ~20 transient
  error signatures.
- Fix temp file leak in handlePutObject when storeFileFromTemp throws.
- Fix pre-existing missing botPool namespace on getFileInfo call in
  handleGetMultipartObject.
- Fix route handler return type in PUT handler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 07:25:47 +07:00
Claude 1422318f0a fix: enhance test setup and environment configuration for improved reliability 2026-07-28 22:44:08 +07:00
Claude 667921b100 chore: fix lint errors — duplicate import, unused imports, formatting
Deploy FileDrop / deploy (push) Successful in 45s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 20:09:42 +07:00
Claude 002492626b fix: secure SigV4, temp leaks, OOM risk, duplicate migration, and cache issues
Deploy FileDrop / deploy (push) Failing after 15s
Security fixes:
- SigV4 signature comparison now uses crypto.timingSafeEqual (timing attack fix)
  - AccessKey, region, and HMAC signature all timing-safe
- Presigned URL expiry capped at 7 days (AWS spec compliance)
- Removed duplicate migration import (dead code)

Memory & leak fixes:
- Temp file leak in createZip(): cleanup temp file on error in both utils/ and shared/utils/
- OOM risk in web-api/v1 upload: stream File to temp instead of arrayBuffer()
- Removed duplicate migration import at startup

Performance fixes:
- Removed file.arrayBuffer() -> Bun.write() pattern in web-api-controller (stream + hash)

Test improvements:
- All fixes verified: 74/75 tests pass (1 pre-existing env config test)
- S3 auth tests: 7/7 pass after timing-safe fix
2026-07-28 19:33:05 +07:00
Claude 82c7f81ffa fix: streaming uploads, timeouts, and rate limiting for Docker registry safety
Critical fixes for S3 Docker registry backend:
- Stream PutObject body to temp file instead of req.arrayBuffer()
  - O(1) memory usage regardless of file size
  - SHA-256 hash computed while streaming
- Stream UploadPart body similarly
  - Also fixes: size check after streaming, not before
- Add 30s timeout to Telegram CDN chunk fetches (object-stream.ts)
  - Prevents hanging on stalled CDN connections
- Add rate limiting to S3 API routes (100 req/60s window)
  - Prevents resource exhaustion from concurrent layer pushes
- Add comprehensive test suite (10 tests):
  - Streaming verification (no arrayBuffer in PUT path)
  - Multi-MB body streaming safety
  - Empty body edge case
  - Concurrent upload isolation
  - Timeout signal presence
  - Rate limit route coverage

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:47:16 +07:00
Claude da7d7c2396 chore: clean up leftover directories from agent portability
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:39:27 +07:00
Claude a9a1938ffe Merge branch 'worktree-ddd-clean-architecture-restructure'
# Conflicts:
#	src/infrastructure/cache/index.ts
#	src/interfaces/http/middleware/auth.ts
#	src/interfaces/http/middleware/rate-limit.ts
#	src/shared/logger/index.ts
2026-07-28 18:38:49 +07:00
Claude ee10cb494e fix: resolve code review issues - import paths and structure cleanup
- Fix infrastructure imports: chunked-storage uses new path for shared/utils and interfaces/s3
- Fix health-controller: imports from infrastructure/persistence/drizzle instead of old db/
- Fix routes/index.ts: imports from new interfaces/s3 and middleware paths
- Marked Telegram-specific types in shared/utils/file.ts as future extraction

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:21:57 +07:00
Claude 4568644922 fix: correct import paths and add missing protocol files for DDD structure
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:19:45 +07:00
Claude 234ca7b14c feat: rewire entry point to new architecture
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:12:48 +07:00
Claude e40dfd8084 feat: create HTTP middleware layer
Create auth.ts and rate-limit.ts middleware files in the interfaces layer
as part of the DDD/clean architecture restructure. Also add a config
re-export at src/interfaces/config/index.ts so the middleware can access
configuration through the interfaces layer boundary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:06:38 +07:00
Claude ea48e8fc03 feat: create application use cases (bucket, s3-object, multipart)
- manage-bucket.ts: extract bucket CRUD logic with validation
  (createListBuckets, createGetBucket, createCreateBucket,
   createDeleteBucket, createBucketExists)
- s3-object.ts: extract S3 object operations
  (createGetObject, createHeadObject, createPutObject,
   createCopyObject, createDeleteObject, createDeleteObjects,
   createListObjects, createFindObject)
- multipart-upload.ts: extract S3 multipart upload logic
  (createInitiateMultipartUpload, createUploadPart,
   createCompleteMultipartUpload, createAbortMultipartUpload,
   createListMultipartUploads, createListParts)

All use cases follow the existing factory function pattern with
dependency injection via repository/telegram service interfaces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:04:01 +07:00
Claude e2de8c4245 feat: create application use cases (upload, get-file, auth)
Create upload-file.ts use case with factory pattern supporting dedup, file type detection, size validation, and chunked/single storage strategies.
Create get-file.ts use case supporting redirect, chunked, and archive-entry retrieval strategies.
Create authenticate.ts use case with login, logout, and me operations.
All use cases use dependency injection and return typed DTOs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:59:49 +07:00
Claude 5e29589f1a feat: create application DTOs
Add data-transfer-object interfaces for the application layer:
upload, file, bucket, S3, and auth domains.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:56:49 +07:00
Claude b15219335d feat: create infrastructure cache layer
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:49:17 +07:00
Claude ee3167fbfb feat: create domain port interfaces with JSDoc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:48:24 +07:00
Claude e7657453d5 feat: create shared utilities layer with JSDoc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:43:18 +07:00
Claude 5b5d4b0ba8 feat: create shared errors and logger layer
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:42:06 +07:00
Claude af8949de02 docs: add DDD/clean architecture restructure implementation plan
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:31:11 +07:00
Claude d8f02b79b1 docs: add DDD/clean architecture restructure design spec
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 17:29:20 +07:00
asepharyana 44e887c7cd fix: keep public upload API unauthenticated
Deploy FileDrop / deploy (push) Successful in 37s
2026-07-08 01:59:41 +07:00
asepharyana e8226bba64 ci: migrate deployment to gitea actions
Deploy FileDrop / deploy (push) Successful in 3m21s
2026-07-07 20:39:41 +07:00
asepharyana 340c12d671 feat: implement authentication routes with login, logout, and user info retrieval
feat: add S3 bucket versioning support and related XML response handling

refactor: rename temporary file paths from 'teleuploader' to 'filedrop' for consistency

fix: update Swagger documentation to reflect new API name and descriptions

test: add unit tests for authentication routes and utilities

test: implement end-to-end tests for S3 bucket configuration and versioning

chore: update environment variable defaults for new service name
2026-07-07 19:58:15 +07:00
asepharyana 144ebe6dd3 feat: implement chunked storage for Telegram file uploads and retrieval 2026-07-07 08:08:22 +07:00
asepharyana fbfff3d9ec fix: normalize web object sizes 2026-07-07 06:27:18 +07:00
asepharyana 1ef73fefa9 fix: restore multipart part order validation (without strict ETag check) 2026-07-07 06:11:49 +07:00
asepharyana bc806313b1 fix: use safe LIKE prefix matching instead of non-character range 2026-07-07 06:04:17 +07:00
asepharyana d1c8cc9d23 fix: ensure S3_DEFAULT_REGION and S3_VHOST_DOMAINS env vars are properly set in docker-compose 2026-07-07 06:02:16 +07:00