@usequark/quark-core 2.5.7 → 2.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/storage.js +7 -2
- package/src/storage.test.js +49 -0
package/package.json
CHANGED
package/src/storage.js
CHANGED
|
@@ -187,11 +187,16 @@ export function generateStorageKey(originalFilename, options = {}) {
|
|
|
187
187
|
*
|
|
188
188
|
* Checks `ASSET_CDN_URL` first - if set, prepends it to the key.
|
|
189
189
|
* This is provider-agnostic: works with any CDN (CloudFront, Cloudflare,
|
|
190
|
-
* Bunny, Fastly, etc.)
|
|
190
|
+
* Bunny, Fastly, etc.) provided the CDN is rooted at the bucket, so that a
|
|
191
|
+
* request for `<cdn>/<key>` maps to the object stored at `<key>`. That is the
|
|
192
|
+
* standard arrangement for a CDN in front of a bucket and needs no rewrite
|
|
193
|
+
* rules. If your CDN is not bucket-rooted, set `S3_PUBLIC_URL` instead and use
|
|
194
|
+
* the S3 adapter's `getPublicUrl()`.
|
|
191
195
|
*
|
|
192
196
|
* Falls back to the local API route (`/api/files/<key>`) when no CDN is
|
|
193
197
|
* configured - covers local development and any environment where
|
|
194
|
-
* `STORAGE_PROVIDER=local` is used.
|
|
198
|
+
* `STORAGE_PROVIDER=local` is used. The scaffolded `/api/files/[id]` route
|
|
199
|
+
* accepts a storage key as well as a database id, so this resolves.
|
|
195
200
|
*
|
|
196
201
|
* @param {string} key - Storage key (e.g. "uploads/2026/02/abc-photo.jpg")
|
|
197
202
|
* @returns {string} Full CDN URL or local API route
|
package/src/storage.test.js
CHANGED
|
@@ -351,6 +351,55 @@ test("getAssetUrl - percent-encodes key in fallback URL", () => {
|
|
|
351
351
|
assert.strictEqual(url, "/api/files/uploads%2Ffile%20with%20spaces.jpg");
|
|
352
352
|
});
|
|
353
353
|
|
|
354
|
+
// ─── getAssetUrl / route contract ────────────────────────────────────────────
|
|
355
|
+
//
|
|
356
|
+
// The scaffolded GET /api/files/[id] route used to look up by database id only,
|
|
357
|
+
// so every URL this function produced resolved to a 404. It now also resolves a
|
|
358
|
+
// storage key. These tests pin the two halves of that contract from each side:
|
|
359
|
+
// the shape produced here, and the lookup performed by the route (in
|
|
360
|
+
// apps/web/src/app/api/files/[id]/route.test.js).
|
|
361
|
+
test("getAssetUrl - fallback URL carries the key in a single path segment", () => {
|
|
362
|
+
delete process.env.ASSET_CDN_URL;
|
|
363
|
+
const url = getAssetUrl("uploads/2026/02/abc-photo.jpg");
|
|
364
|
+
|
|
365
|
+
// A key with slashes must stay inside ONE path segment, or the request never
|
|
366
|
+
// reaches the [id] route at all — it 404s as an unrouted path. `URL` keeps the
|
|
367
|
+
// percent-encoding in `pathname`, so it is the encoded slashes that hold the
|
|
368
|
+
// segment together.
|
|
369
|
+
const segments = new URL(url, "http://localhost").pathname.split("/");
|
|
370
|
+
assert.strictEqual(
|
|
371
|
+
segments.length,
|
|
372
|
+
4,
|
|
373
|
+
`key leaked into extra segments: ${url}`,
|
|
374
|
+
);
|
|
375
|
+
assert.strictEqual(segments[3], "uploads%2F2026%2F02%2Fabc-photo.jpg");
|
|
376
|
+
// And it must decode back to exactly the key, which is what the route looks up.
|
|
377
|
+
assert.strictEqual(
|
|
378
|
+
decodeURIComponent(segments[3]),
|
|
379
|
+
"uploads/2026/02/abc-photo.jpg",
|
|
380
|
+
);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test("getAssetUrl - CDN URL leaves the key path unencoded", () => {
|
|
384
|
+
// The CDN branch is a real URL space, not a path segment: encoding the slashes
|
|
385
|
+
// would ask the CDN for a single object literally named "uploads/2026/02/...".
|
|
386
|
+
// A bucket-rooted CDN maps the path straight onto the key.
|
|
387
|
+
process.env.ASSET_CDN_URL = "https://assets.example.com";
|
|
388
|
+
try {
|
|
389
|
+
const url = getAssetUrl("uploads/2026/02/abc-photo.jpg");
|
|
390
|
+
assert.strictEqual(
|
|
391
|
+
url,
|
|
392
|
+
"https://assets.example.com/uploads/2026/02/abc-photo.jpg",
|
|
393
|
+
);
|
|
394
|
+
assert.ok(
|
|
395
|
+
!url.includes("%2F"),
|
|
396
|
+
"CDN URL must not percent-encode the key path",
|
|
397
|
+
);
|
|
398
|
+
} finally {
|
|
399
|
+
delete process.env.ASSET_CDN_URL;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
354
403
|
// ---------------------------------------------------------------------------
|
|
355
404
|
// getSignedUploadUrl
|
|
356
405
|
// ---------------------------------------------------------------------------
|