gcx/backend/services/admin-service/src/infrastructure/parsers/package-parser.service.ts

76 lines
2.6 KiB
TypeScript

import { Injectable, Logger, BadRequestException } from '@nestjs/common';
interface ParsedPackageInfo {
packageName: string;
versionCode: number;
versionName: string;
minSdkVersion?: string;
platform: 'ANDROID' | 'IOS';
}
@Injectable()
export class PackageParserService {
private readonly logger = new Logger(PackageParserService.name);
async parse(buffer: Buffer, filename: string): Promise<ParsedPackageInfo> {
const ext = filename.split('.').pop()?.toLowerCase();
if (ext === 'apk') return this.parseApk(buffer);
if (ext === 'ipa') return this.parseIpa(buffer);
throw new BadRequestException('Unsupported file format. Only .apk and .ipa are supported.');
}
private async parseApk(buffer: Buffer): Promise<ParsedPackageInfo> {
try {
// Dynamic import to handle missing dependency gracefully
const ApkReader = await import('adbkit-apkreader').then(m => m.default || m);
const reader = await ApkReader.open(buffer);
const manifest = await reader.readManifest();
return {
packageName: manifest.package || 'unknown',
versionCode: manifest.versionCode || 0,
versionName: manifest.versionName || '0.0.0',
minSdkVersion: manifest.usesSdk?.minSdkVersion?.toString(),
platform: 'ANDROID',
};
} catch (err: any) {
this.logger.warn(`APK parse failed, using fallback: ${err.message}`);
return {
packageName: 'unknown',
versionCode: 0,
versionName: '0.0.0',
platform: 'ANDROID',
};
}
}
private async parseIpa(buffer: Buffer): Promise<ParsedPackageInfo> {
try {
const unzipper = await import('unzipper');
const bplistParser = await import('bplist-parser');
const directory = await unzipper.Open.buffer(buffer);
const plistEntry = directory.files.find((f: any) => /Payload\/[^/]+\.app\/Info\.plist$/.test(f.path));
if (!plistEntry) throw new Error('Info.plist not found in IPA');
const plistBuffer = await plistEntry.buffer();
const parsed = bplistParser.parseBuffer(plistBuffer);
const info = parsed[0] || {};
return {
packageName: info.CFBundleIdentifier || 'unknown',
versionCode: parseInt(info.CFBundleVersion || '0', 10),
versionName: info.CFBundleShortVersionString || '0.0.0',
minSdkVersion: info.MinimumOSVersion,
platform: 'IOS',
};
} catch (err: any) {
this.logger.warn(`IPA parse failed, using fallback: ${err.message}`);
return {
packageName: 'unknown',
versionCode: 0,
versionName: '0.0.0',
platform: 'IOS',
};
}
}
}