( req: IncomingMessage, )
| 11 | } from './artifact-download.ts'; |
| 12 | |
| 13 | export async function receiveUpload( |
| 14 | req: IncomingMessage, |
| 15 | ): Promise<{ artifactPath: string; tempDir: string }> { |
| 16 | const artifactType = req.headers['x-artifact-type'] as string | undefined; |
| 17 | const rawFilename = req.headers['x-artifact-filename'] as string | undefined; |
| 18 | if (!artifactType || !rawFilename) { |
| 19 | throw new AppError( |
| 20 | 'INVALID_ARGS', |
| 21 | 'Missing required headers: x-artifact-type and x-artifact-filename', |
| 22 | ); |
| 23 | } |
| 24 | if (artifactType !== 'file' && artifactType !== 'app-bundle') { |
| 25 | throw new AppError( |
| 26 | 'INVALID_ARGS', |
| 27 | `Invalid x-artifact-type: ${artifactType}. Must be "file" or "app-bundle".`, |
| 28 | ); |
| 29 | } |
| 30 | |
| 31 | validateArtifactContentLength(req.headers['content-length']); |
| 32 | const artifactFilename = sanitizeArtifactFilename(rawFilename); |
| 33 | const tempDir = createArtifactTempDir('upload'); |
| 34 | try { |
| 35 | if (artifactType === 'file') { |
| 36 | const artifactPath = path.join(tempDir, artifactFilename); |
| 37 | await streamReadableToFile(req, artifactPath); |
| 38 | return { artifactPath, tempDir }; |
| 39 | } |
| 40 | |
| 41 | const archivePath = path.join(tempDir, 'artifact.tar'); |
| 42 | await streamReadableToFile(req, archivePath); |
| 43 | const artifactPath = await extractTarInstallableArtifact({ |
| 44 | archivePath, |
| 45 | tempDir, |
| 46 | platform: 'ios', |
| 47 | expectedRootName: artifactFilename, |
| 48 | }); |
| 49 | fs.rmSync(archivePath, { force: true }); |
| 50 | return { artifactPath, tempDir }; |
| 51 | } catch (error) { |
| 52 | fs.rmSync(tempDir, { recursive: true, force: true }); |
| 53 | throw error; |
| 54 | } |
| 55 | } |
no test coverage detected
searching dependent graphs…