mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-07-31 08:27:29 +02:00
fix: properly handle x-amz-meta-updated-at
This commit is contained in:
@@ -329,7 +329,16 @@ export class SyncService implements OnModuleInit {
|
|||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = await getSignedUrl(this.s3Client, command, { expiresIn });
|
const metadataHeaders = new Set(
|
||||||
|
Object.keys(metadata ?? {}).map((name) => `x-amz-meta-${name}`),
|
||||||
|
);
|
||||||
|
const url = await getSignedUrl(this.s3Client, command, {
|
||||||
|
expiresIn,
|
||||||
|
// The AWS presigner otherwise hoists user metadata into the query string.
|
||||||
|
// The client echoes the response metadata as headers, so those headers
|
||||||
|
// must remain in the request and be covered by SignedHeaders.
|
||||||
|
unhoistableHeaders: metadataHeaders,
|
||||||
|
});
|
||||||
|
|
||||||
// Report profile usage after upload presign if key is under profiles/
|
// Report profile usage after upload presign if key is under profiles/
|
||||||
if (ctx.mode === "cloud" && dto.key.startsWith("profiles/")) {
|
if (ctx.mode === "cloud" && dto.key.startsWith("profiles/")) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
interface PresignResponse {
|
interface PresignResponse {
|
||||||
url: string;
|
url: string;
|
||||||
expiresAt: string;
|
expiresAt: string;
|
||||||
|
metadata?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ListResponse {
|
interface ListResponse {
|
||||||
@@ -34,6 +35,7 @@ interface StatResponse {
|
|||||||
exists: boolean;
|
exists: boolean;
|
||||||
size?: number;
|
size?: number;
|
||||||
lastModified?: string;
|
lastModified?: string;
|
||||||
|
metadata?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("SyncController (e2e)", () => {
|
describe("SyncController (e2e)", () => {
|
||||||
@@ -112,6 +114,65 @@ describe("SyncController (e2e)", () => {
|
|||||||
expect(body.url).toContain("test/upload-key.txt");
|
expect(body.url).toContain("test/upload-key.txt");
|
||||||
expect(body.expiresAt).toBeDefined();
|
expect(body.expiresAt).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should sign and persist echoed object metadata", async () => {
|
||||||
|
const testKey = `vpns/metadata-${Date.now()}.json`;
|
||||||
|
const updatedAt = Math.floor(Date.now() / 1000).toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.post("/v1/objects/presign-upload")
|
||||||
|
.set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`)
|
||||||
|
.send({
|
||||||
|
key: testKey,
|
||||||
|
contentType: "application/json",
|
||||||
|
metadata: {
|
||||||
|
"updated-at": updatedAt,
|
||||||
|
ignored: "not-allowed",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const body = response.body as PresignResponse;
|
||||||
|
expect(body.metadata).toEqual({ "updated-at": updatedAt });
|
||||||
|
|
||||||
|
const uploadUrl = new URL(body.url);
|
||||||
|
const signedHeaders =
|
||||||
|
uploadUrl.searchParams.get("X-Amz-SignedHeaders")?.split(";") ?? [];
|
||||||
|
expect(signedHeaders).toContain("x-amz-meta-updated-at");
|
||||||
|
expect(uploadUrl.searchParams.has("x-amz-meta-updated-at")).toBe(false);
|
||||||
|
|
||||||
|
const uploadResult = await fetch(body.url, {
|
||||||
|
method: "PUT",
|
||||||
|
body: "{}",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-amz-meta-updated-at": updatedAt,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!uploadResult.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Metadata upload failed with status ${uploadResult.status}: ${await uploadResult.text()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const statResponse = await request(app.getHttpServer())
|
||||||
|
.post("/v1/objects/stat")
|
||||||
|
.set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`)
|
||||||
|
.send({ key: testKey })
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const statBody = statResponse.body as StatResponse;
|
||||||
|
expect(statBody.exists).toBe(true);
|
||||||
|
expect(statBody.metadata?.["updated-at"]).toBe(updatedAt);
|
||||||
|
} finally {
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.post("/v1/objects/delete")
|
||||||
|
.set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`)
|
||||||
|
.send({ key: testKey })
|
||||||
|
.expect(200);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("POST /v1/objects/presign-download", () => {
|
describe("POST /v1/objects/presign-download", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user