- 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対応
103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
import type {
|
|
AppRecord,
|
|
ReleaseRecord,
|
|
SourceRecord,
|
|
} from "./types";
|
|
|
|
export interface AltStoreSourceDocument {
|
|
name: string;
|
|
subtitle?: string;
|
|
description?: string;
|
|
iconURL?: string;
|
|
headerURL?: string;
|
|
website?: string;
|
|
tintColor?: string;
|
|
featuredApps: string[];
|
|
apps: Array<{
|
|
name: string;
|
|
bundleIdentifier: string;
|
|
marketplaceID: string;
|
|
developerName: string;
|
|
subtitle?: string;
|
|
localizedDescription: string;
|
|
iconURL?: string;
|
|
tintColor?: string;
|
|
category: string;
|
|
screenshots: Array<{ imageURL: string; width?: number; height?: number }>;
|
|
versions: Array<{
|
|
version: string;
|
|
buildVersion: string;
|
|
date: string;
|
|
localizedDescription: string;
|
|
downloadURL: string;
|
|
size: number;
|
|
minOSVersion?: string;
|
|
}>;
|
|
appPermissions: {
|
|
entitlements: string[];
|
|
privacy: Record<string, string>;
|
|
};
|
|
}>;
|
|
news: [];
|
|
}
|
|
|
|
export function buildSourceDocument(
|
|
source: SourceRecord,
|
|
apps: AppRecord[],
|
|
releases: ReleaseRecord[],
|
|
publicBaseUrl: string,
|
|
): AltStoreSourceDocument {
|
|
const sourceApps = apps
|
|
.map((app) => {
|
|
const appReleases = releases
|
|
.filter(
|
|
(release) =>
|
|
release.appId === app.id && release.status === "published",
|
|
)
|
|
.sort((a, b) => {
|
|
const dateOrder = b.date.localeCompare(a.date);
|
|
if (dateOrder !== 0) return dateOrder;
|
|
return b.buildVersion.localeCompare(a.buildVersion, undefined, {
|
|
numeric: true,
|
|
});
|
|
});
|
|
|
|
return {
|
|
name: app.name,
|
|
bundleIdentifier: app.bundleIdentifier,
|
|
marketplaceID: app.marketplaceID,
|
|
developerName: app.developerName,
|
|
subtitle: app.subtitle || undefined,
|
|
localizedDescription: app.localizedDescription,
|
|
iconURL: app.iconURL,
|
|
tintColor: app.tintColor,
|
|
category: app.category,
|
|
screenshots: app.screenshots,
|
|
appPermissions: app.appPermissions,
|
|
versions: appReleases.map((release) => ({
|
|
version: release.version,
|
|
buildVersion: release.buildVersion,
|
|
date: release.date,
|
|
localizedDescription: release.localizedDescription,
|
|
downloadURL: `${publicBaseUrl.replace(/\/$/, "")}/artifacts/${release.id}/manifest.json`,
|
|
size: release.sizeBytes,
|
|
minOSVersion: release.minOSVersion,
|
|
})),
|
|
};
|
|
})
|
|
.filter((app) => app.versions.length > 0);
|
|
|
|
return {
|
|
name: source.name,
|
|
subtitle: source.subtitle || undefined,
|
|
description: source.description || undefined,
|
|
iconURL: source.iconURL || sourceApps[0]?.iconURL,
|
|
headerURL: source.headerURL,
|
|
website: source.website,
|
|
tintColor: source.tintColor,
|
|
featuredApps: sourceApps.slice(0, 5).map((app) => app.bundleIdentifier),
|
|
apps: sourceApps,
|
|
news: [],
|
|
};
|
|
}
|