fix: production bugs + comprehensive production e2e tests

Fixes:
- RowList bug: postgres.js returns array directly, not {rows}. Fix in
  buckets.ts, files-ext.ts, multipart.ts (3 files, 8 functions)
- S3 ListBuckets routing: GET / was intercepted by handleHome route
- Presigned URL detection: isS3Request() only checked Authorization header
- FK constraint on bucket delete: cascade-delete files & multipart rows first
- docker-compose.yml: pass S3_ACCESS_KEY / S3_SECRET_KEY to container
- Dockerfile: copy home.html to runner stage for handleHome

Tests:
- test/production-e2e.test.ts: 29 tests (11 Web API + 18 S3 SigV4)
  All pass against https://upload.asepharyana.my.id
- Creates and cleans up real buckets/objects on production
This commit is contained in:
asepharyana
2026-07-07 00:49:45 +07:00
parent b731ddd7e1
commit 57f2740df9
7 changed files with 452 additions and 31 deletions
+37 -2
View File
@@ -52,7 +52,42 @@ const server = serve({
GET: handleSwaggerJson,
},
'/': {
GET: handleHome,
GET: (req: Request) => {
const headers = Object.fromEntries(req.headers);
const url = new URL(req.url);
if (isS3Request(headers) || url.searchParams.has('X-Amz-Signature')) {
return handleS3Request(req);
}
return handleHome();
},
PUT: (req: Request) => {
const headers = Object.fromEntries(req.headers);
if (isS3Request(headers)) {
return handleS3Request(req);
}
return new Response('Not Allowed', { status: 405 });
},
HEAD: (req: Request) => {
const headers = Object.fromEntries(req.headers);
if (isS3Request(headers)) {
return handleS3Request(req);
}
return new Response('Not Allowed', { status: 405 });
},
DELETE: (req: Request) => {
const headers = Object.fromEntries(req.headers);
if (isS3Request(headers)) {
return handleS3Request(req);
}
return new Response('Not Allowed', { status: 405 });
},
POST: (req: Request) => {
const headers = Object.fromEntries(req.headers);
if (isS3Request(headers)) {
return handleS3Request(req);
}
return new Response('Not Allowed', { status: 405 });
},
},
'/api/v1/*': {
GET: handleWebApiV1,
@@ -63,7 +98,7 @@ const server = serve({
},
fetch: async (req: Request) => {
const headers = Object.fromEntries(req.headers);
if (isS3Request(headers)) {
if (isS3Request(headers) || new URL(req.url).searchParams.has('X-Amz-Signature')) {
return handleS3Request(req);
}
return new Response('Not Found', { status: 404 });