import { randomUUID } from "node:crypto"; import { mkdtemp, rm } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import type { AppMetadata } from "../../../packages/core/src/index"; import { inspectAdpArchive, storeAdpAssets } from "./manifest"; import type { AppConfig } from "./config"; import type { Store } from "./store"; import type { StorageAdapter } from "./storage"; export async function processUpload(uploadId: string, store: Store, storage: StorageAdapter, appConfig: AppConfig) { const upload = await store.getUpload(uploadId); if (!upload) throw new Error("UPLOAD_NOT_FOUND"); await store.updateUpload(uploadId, { status: "processing", errorCode: undefined, errorMessage: undefined }); const workDir = await mkdtemp(join(tmpdir(), "altdock-upload-")); const archivePath = join(workDir, "package.zip"); try { await storage.downloadToFile(upload.objectKey, archivePath); const inspection = await inspectAdpArchive(archivePath, appConfig.MAX_ARCHIVE_ENTRIES, appConfig.MAX_EXPANDED_BYTES); const source = await store.getSource(upload.sourceId); if (!source) throw new Error("SOURCE_NOT_FOUND"); const existingApp = upload.appId ? await store.getApp(upload.appId) : null; const metadata: Partial = existingApp || { name: source.name, developerName: "AltDock Developer", localizedDescription: `Release ${inspection.summary.shortVersionString} (${inspection.summary.bundleVersion})`, subtitle: "", tintColor: source.tintColor, category: "other", screenshots: [], appPermissions: { entitlements: [], privacy: {} }, }; const app = await store.upsertApp(upload.sourceId, { bundleIdentifier: inspection.summary.bundleId, marketplaceID: inspection.summary.appleItemId, metadata, }); await store.updateUpload(uploadId, { appId: app.id }); const releaseId = randomUUID(); const release = await store.createRelease({ id: releaseId, appId: app.id, uploadId, version: inspection.summary.shortVersionString, buildVersion: inspection.summary.bundleVersion, appleItemId: inspection.summary.appleItemId, date: new Date().toISOString(), localizedDescription: metadata.localizedDescription || "", minOSVersion: inspection.summary.minimumSystemVersions.ios, sizeBytes: 0, manifest: inspection.summary, }); const result = await storeAdpAssets(archivePath, release.id, inspection, storage); await store.addReleaseAssets(result.assets); const completed = await store.updateRelease(release.id, { sizeBytes: result.totalBytes, status: "ready" }); await store.updateUpload(uploadId, { status: "completed", receivedSize: upload.receivedSize || upload.expectedSize }); return completed; } catch (error) { const message = error instanceof Error ? error.message : "ADP_PROCESSING_FAILED"; console.error(`[altdock] upload ${uploadId} failed: ${message}`); await store.updateUpload(uploadId, { status: "failed", errorCode: message.split(":")[0], errorMessage: message }); throw error; } finally { await rm(workDir, { recursive: true, force: true }); } }