import { INestApplication } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { Test, TestingModule } from "@nestjs/testing"; import request from "supertest"; import { App } from "supertest/types"; import { AppController } from "./../src/app.controller.js"; import { AppService } from "./../src/app.service.js"; import { SyncModule } from "./../src/sync/sync.module.js"; import { configureTestEnv, TEST_S3_ENDPOINT, TEST_SYNC_TOKEN, waitForTestS3, } from "./test-env.js"; // Presigning is offline, so this host never has to accept a connection — the // assertions are about which host ends up in the signed URL. const PUBLIC_ENDPOINT = "https://storage.example.com"; // Only needs to be present for the server to consider itself cloud-mode; no // token is verified against it in these assertions. const CLOUD_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nnot-a-real-key\n-----END PUBLIC KEY-----"; interface PresignResponse { url: string; } interface PresignBatchResponse { items: Array<{ key: string; url: string }>; } interface ReadyResponse { status: string; s3: boolean; storageEndpoint: string; } async function bootstrap(publicEndpoint: string | undefined) { configureTestEnv(); if (publicEndpoint) { process.env.S3_PUBLIC_ENDPOINT = publicEndpoint; } else { delete process.env.S3_PUBLIC_ENDPOINT; } await waitForTestS3(); const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [ConfigModule.forRoot({ isGlobal: true }), SyncModule], controllers: [AppController], providers: [AppService], }).compile(); const app = moduleFixture.createNestApplication>(); await app.listen(0); return app; } // A self-hosted server usually reaches its storage over a private address the // desktop client has no route to. Signing client URLs against that address // handed every client a URL it could not open, so uploads failed at connect // while /health and /readyz stayed green. describe("presigned URL host", () => { describe("with S3_PUBLIC_ENDPOINT set", () => { let app: INestApplication; beforeAll(async () => { app = await bootstrap(PUBLIC_ENDPOINT); }); afterAll(async () => { delete process.env.S3_PUBLIC_ENDPOINT; await app.close(); }); it("signs single upload URLs against the public endpoint", async () => { const response = await request(app.getHttpServer()) .post("/v1/objects/presign-upload") .set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`) .send({ key: "endpoint/single.txt" }) .expect(200); const { url } = response.body as PresignResponse; expect(url.startsWith(PUBLIC_ENDPOINT)).toBe(true); expect(url).not.toContain(TEST_S3_ENDPOINT); }); it("signs batch upload URLs against the public endpoint", async () => { const response = await request(app.getHttpServer()) .post("/v1/objects/presign-upload-batch") .set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`) .send({ items: [{ key: "endpoint/a.txt" }, { key: "endpoint/b.txt" }] }) .expect(200); const { items } = response.body as PresignBatchResponse; expect(items).toHaveLength(2); for (const item of items) { expect(item.url.startsWith(PUBLIC_ENDPOINT)).toBe(true); } }); it("signs download URLs against the public endpoint", async () => { const response = await request(app.getHttpServer()) .post("/v1/objects/presign-download") .set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`) .send({ key: "endpoint/single.txt" }) .expect(200); const { url } = response.body as PresignResponse; expect(url.startsWith(PUBLIC_ENDPOINT)).toBe(true); }); // The server's own S3 calls must keep using the private endpoint, or // pointing clients at a public address would break the server itself. it("still reaches storage over the private endpoint", async () => { const response = await request(app.getHttpServer()) .post("/v1/objects/stat") .set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`) .send({ key: "endpoint/does-not-exist" }) .expect(200); expect(response.body).toEqual({ exists: false }); }); it("reports the client-facing endpoint from /readyz", async () => { const response = await request(app.getHttpServer()) .get("/readyz") .expect(200); const body = response.body as ReadyResponse; expect(body.s3).toBe(true); expect(body.storageEndpoint).toBe(PUBLIC_ENDPOINT); }); }); // /readyz has no auth, so a managed deployment must not publish its storage // host to anyone who can reach the probe. describe("in cloud mode", () => { let app: INestApplication; const previousKey = process.env.SYNC_JWT_PUBLIC_KEY; beforeAll(async () => { process.env.SYNC_JWT_PUBLIC_KEY = CLOUD_PUBLIC_KEY; app = await bootstrap(PUBLIC_ENDPOINT); }); afterAll(async () => { if (previousKey === undefined) { delete process.env.SYNC_JWT_PUBLIC_KEY; } else { process.env.SYNC_JWT_PUBLIC_KEY = previousKey; } delete process.env.S3_PUBLIC_ENDPOINT; await app.close(); }); it("withholds the storage endpoint from /readyz", async () => { const response = await request(app.getHttpServer()) .get("/readyz") .expect(200); const body = response.body as ReadyResponse; expect(body.s3).toBe(true); expect(body.storageEndpoint).toBeUndefined(); expect(JSON.stringify(body)).not.toContain("storage.example.com"); }); }); describe("without S3_PUBLIC_ENDPOINT", () => { let app: INestApplication; beforeAll(async () => { app = await bootstrap(undefined); }); afterAll(async () => { await app.close(); }); it("falls back to S3_ENDPOINT", async () => { const response = await request(app.getHttpServer()) .post("/v1/objects/presign-upload") .set("Authorization", `Bearer ${TEST_SYNC_TOKEN}`) .send({ key: "endpoint/fallback.txt" }) .expect(200); const { url } = response.body as PresignResponse; expect(url.startsWith(TEST_S3_ENDPOINT)).toBe(true); }); it("reports the fallback endpoint from /readyz", async () => { const response = await request(app.getHttpServer()) .get("/readyz") .expect(200); expect((response.body as ReadyResponse).storageEndpoint).toBe( TEST_S3_ENDPOINT, ); }); }); });