From e6cb7952d8c3847f649867b36070e513c1c641e3 Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Tue, 28 Apr 2026 10:39:30 +0200 Subject: [PATCH] refactor: hoist objectKeyFromArtifactUrl into helpers Both src/releases.ts and scripts/sync-releases.ts had their own copy of the same URL-to-S3-key conversion. Moved into src/helpers.ts and imported from both call sites so a future change (e.g. CDN path prefix handling) only needs to land once. --- scripts/sync-releases.ts | 8 ++------ src/helpers.ts | 8 ++++++++ src/releases.ts | 6 +----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/sync-releases.ts b/scripts/sync-releases.ts index 7c1b290..f462f17 100644 --- a/scripts/sync-releases.ts +++ b/scripts/sync-releases.ts @@ -17,7 +17,7 @@ import { import { PrismaClient } from "@prisma/client"; import semver from "semver"; -import { streamToString } from "../src/helpers"; +import { objectKeyFromArtifactUrl, streamToString } from "../src/helpers"; const OTA_ROOT_KEY_FPR = "AF5A36A993D828FEFE7C18C2D1B9856C26A79E95"; @@ -85,10 +85,6 @@ interface ArtifactDisplayInfo { signature: SignatureStatus; } -function s3KeyFromArtifactUrl(artifactUrl: string): string { - return decodeURIComponent(new URL(artifactUrl).pathname.replace(/^\/+/, "")); -} - function shortFpr(fpr: string): string { // Keep the leading 16 hex chars (8 bytes) — enough to be unambiguous in a log // line while staying readable. The full fingerprint is what we actually @@ -243,7 +239,7 @@ async function loadArtifactDisplayInfo( const signature = await verifySignature( clients.s3Client, config.bucketName, - s3KeyFromArtifactUrl(artifact.url), + objectKeyFromArtifactUrl(artifact.url), ); return { artifact, signature }; }), diff --git a/src/helpers.ts b/src/helpers.ts index 453e040..9d7a542 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -56,4 +56,12 @@ export function getDeviceRolloutBucket(deviceId: string): number { const hash = createHash("md5").update(deviceId).digest("hex"); const hashPrefix = hash.substring(0, 8); return parseInt(hashPrefix, 16) % 100; +} + +/** + * Extracts the S3 object key from an artifact URL like + * `https://cdn.example.com/app/0.5.0/jetkvm_app` → `app/0.5.0/jetkvm_app`. + */ +export function objectKeyFromArtifactUrl(artifactUrl: string): string { + return decodeURIComponent(new URL(artifactUrl).pathname.replace(/^\/+/, "")); } \ No newline at end of file diff --git a/src/releases.ts b/src/releases.ts index 1985b6c..7088d55 100644 --- a/src/releases.ts +++ b/src/releases.ts @@ -13,6 +13,7 @@ import { LRUCache } from "lru-cache"; import { getDeviceRolloutBucket, + objectKeyFromArtifactUrl, streamToString, toSemverRange, verifyHash, @@ -388,11 +389,6 @@ function toRelease( return release as Release; } -function objectKeyFromArtifactUrl(artifactUrl: string): string { - const parsed = new URL(artifactUrl); - return decodeURIComponent(parsed.pathname.replace(/^\/+/, "")); -} - async function resolveSigUrlFromArtifactUrl( artifactUrl: string, ): Promise {