diff --git a/backend/services/admin-service/src/infrastructure/parsers/package-parser.service.ts b/backend/services/admin-service/src/infrastructure/parsers/package-parser.service.ts index e476309..85c8091 100644 --- a/backend/services/admin-service/src/infrastructure/parsers/package-parser.service.ts +++ b/backend/services/admin-service/src/infrastructure/parsers/package-parser.service.ts @@ -1,6 +1,3 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; import { Injectable, Logger, BadRequestException } from '@nestjs/common'; import { IPackageParser, ParsedPackageInfo } from '../../domain/ports/package-parser.interface'; @@ -16,14 +13,47 @@ export class PackageParserService implements IPackageParser { } private async parseApk(buffer: Buffer): Promise { - // adbkit-apkreader v3 底层用 yauzl.open(),只接受文件路径而非 Buffer - // 先写入临时文件,解析后删除 - const tmpFile = path.join(os.tmpdir(), `apk-parse-${Date.now()}.apk`); + // yauzl.open() 不支持 APK V2/V3 签名格式(EOCD 被签名块挤移), + // 改用 yauzl.fromBuffer() + adbkit-apkreader 内部 ManifestParser 直接解析 try { - fs.writeFileSync(tmpFile, buffer); - const ApkReader = await import('adbkit-apkreader').then(m => m.default || m); - const reader = await ApkReader.open(tmpFile); - const manifest = await reader.readManifest(); + const yauzl = await import('yauzl'); + const { ManifestParser } = await import('adbkit-apkreader/lib/apkreader/parser/manifest' as any).then( + (m: any) => ({ ManifestParser: m.default || m }), + ); + const { BinaryXmlParser } = await import('adbkit-apkreader/lib/apkreader/parser/binaryxml' as any).then( + (m: any) => ({ BinaryXmlParser: m.default || m }), + ); + + const zipfile = await new Promise((resolve, reject) => + yauzl.fromBuffer(buffer, { lazyEntries: true }, (err: any, zf: any) => + err ? reject(err) : resolve(zf), + ), + ); + + const manifestBuffer = await new Promise((resolve, reject) => { + zipfile.readEntry(); + zipfile.on('entry', (entry: any) => { + if (entry.fileName === 'AndroidManifest.xml') { + zipfile.openReadStream(entry, (err: any, stream: any) => { + if (err) return reject(err); + const chunks: Buffer[] = []; + stream.on('data', (c: Buffer) => chunks.push(c)); + stream.on('end', () => resolve(Buffer.concat(chunks))); + stream.on('error', reject); + }); + } else { + zipfile.readEntry(); + } + }); + zipfile.on('end', () => reject(new Error('AndroidManifest.xml not found'))); + zipfile.on('error', reject); + }); + + const binaryXml = new BinaryXmlParser(manifestBuffer); + const parsed = binaryXml.parse(); + const manifestParser = new ManifestParser(parsed); + const manifest = manifestParser.parse(); + return { packageName: manifest.package || 'unknown', versionCode: manifest.versionCode || 0, @@ -39,8 +69,6 @@ export class PackageParserService implements IPackageParser { versionName: '0.0.0', platform: 'ANDROID', }; - } finally { - try { fs.unlinkSync(tmpFile); } catch { /* ignore */ } } }