Files
amania-jailbreak 93825ebc64 初回コミット: AltStore PAL向けADPホスティングSaaS
- Fastify API・Node Worker・PostgreSQL・S3互換ストレージ構成
- ADP ZIPのマルチパートアップロードとManifest検証(パストラバーサル・ZIP爆弾等を拒否)
- manifest.json/signatureは再シリアライズせず元バイト列を保持
- Cloudflare風の運用向け管理ダッシュボード(shadcn/Radix・日本語UI・4ルート)
  概要/Sources/アプリ/リリース + ルートランディングページ
- 汎用OIDC SSO・Workspace単位の認可・demo mode
- 匿名配布: /sources/{slug}/source.json, /artifacts/{releaseId}/manifest.json
- Docker Compose対応
2026-08-05 10:09:58 +09:00

67 lines
3.2 KiB
TypeScript

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<AppMetadata> = 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 });
}
}