fix(api): bound storage listings, ranges and search paging (#2344)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-02 17:23:12 +02:00
1 parent e86e24a2db
commit c79c0ee138
8 files changed
+479 -130

No files matched your search

+30 -11
View File
@@ -11,7 +11,12 @@ import type {
DesktopPlatform,
} from '@fluxer/schema/src/domains/download/DownloadSchemas';
import {Config} from '../Config';
import type {IStorageService} from '../infrastructure/IStorageService';
import {
type IStorageService,
StorageObjectListingOverflowError,
StorageObjectRangeNotSatisfiableError,
} from '../infrastructure/IStorageService';
import {Logger} from '../Logger';
import {isJsonRecord, parseJsonUnknown} from '../utils/JsonBoundaryUtils';
import {
parseDesktopArtifactScope,
@@ -45,10 +50,14 @@ function isStorageNotFoundError(error: unknown): boolean {
}
function isUnsatisfiableRangeError(error: unknown): boolean {
if (error instanceof StorageObjectRangeNotSatisfiableError) {
return true;
}
return (
error instanceof S3ServiceException && (error.name === 'InvalidRange' || error.$metadata?.httpStatusCode === 416)
);
}
const MAX_DESKTOP_OBJECTS_PER_PREFIX = 10_000;
const DESKTOP_BUCKET_PREFIX = 'desktop';
const DESKTOP_TEST_BUCKET_PREFIX = 'desktop-test';
const DOWNLOAD_KEY_ALLOWED_PREFIXES = [`${DESKTOP_BUCKET_PREFIX}/`, `${DESKTOP_TEST_BUCKET_PREFIX}/`];
@@ -349,11 +358,8 @@ export class DownloadService {
}
const prefix = `${basePrefix}/`;
try {
const objects = await this.storageService.listObjects({
bucket: Config.s3.buckets.downloads,
prefix,
});
if (!objects || objects.length === 0) {
const objects = await this.listDesktopArtifacts(prefix);
if (objects.length === 0) {
return {versions: [], hasMore: false};
}
const versionMap = new Map<
@@ -791,6 +797,22 @@ export class DownloadService {
return this.findLatestFilenameForRequestedArch(params);
}
private async listDesktopArtifacts(prefix: string): Promise<ReadonlyArray<{key: string; lastModified?: Date}>> {
try {
return await this.storageService.listObjects({
bucket: Config.s3.buckets.downloads,
prefix,
maxObjects: MAX_DESKTOP_OBJECTS_PER_PREFIX,
});
} catch (error) {
if (error instanceof StorageObjectListingOverflowError) {
Logger.warn({prefix, maxObjects: error.maxObjects}, 'Desktop artifact prefix outgrew its listing cap');
return [];
}
throw error;
}
}
private isFilenameCompatibleWithRequestedArch(params: ManifestFilenameResolutionParams): boolean {
const parsed = this.parseVersionFromFilename(params.filename, params.channel, params.plat, params.arch);
if (!parsed) {
@@ -815,11 +837,8 @@ export class DownloadService {
return null;
}
const prefix = `${basePrefix}/`;
const objects = await this.storageService.listObjects({
bucket: Config.s3.buckets.downloads,
prefix,
});
if (!objects || objects.length === 0) {
const objects = await this.listDesktopArtifacts(prefix);
if (objects.length === 0) {
return null;
}
let latestFilename: string | null = null;