@capawesome/cli
Version:
The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.
121 lines (120 loc) • 4.86 kB
JavaScript
import { MAX_CONCURRENT_PART_UPLOADS } from '../config/index.js';
import authorizationService from '../services/authorization-service.js';
import httpClient from '../utils/http-client.js';
import FormData from 'form-data';
class AppBuildSourcesServiceImpl {
httpClient;
constructor(httpClient) {
this.httpClient = httpClient;
}
async createFromFile(dto, onProgress) {
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/build-sources`, { fileSizeInBytes: dto.fileSizeInBytes }, {
headers: {
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
},
});
await this.upload({
appBuildSourceId: response.data.id,
appId: dto.appId,
buffer: dto.buffer,
name: dto.name,
}, onProgress);
return response.data;
}
async createFromUrl(dto) {
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/build-sources`, { fileUrl: dto.fileUrl }, {
headers: {
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
},
});
return response.data;
}
async completeUpload(dto) {
return this.httpClient
.post(`/v1/apps/${dto.appId}/build-sources/${dto.appBuildSourceId}/upload?action=mpu-complete&uploadId=${dto.uploadId}`, {
parts: dto.parts,
}, {
headers: {
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
},
})
.then((response) => response.data);
}
async createUpload(dto) {
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/build-sources/${dto.appBuildSourceId}/upload?action=mpu-create`, {}, {
headers: {
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
},
});
return response.data;
}
async createUploadPart(dto) {
const formData = new FormData();
formData.append('blob', dto.buffer, { filename: dto.name });
formData.append('partNumber', dto.partNumber.toString());
return this.httpClient
.put(`/v1/apps/${dto.appId}/build-sources/${dto.appBuildSourceId}/upload?action=mpu-uploadpart&uploadId=${dto.uploadId}`, formData, {
headers: {
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
...formData.getHeaders(),
},
})
.then((response) => response.data);
}
async createUploadParts(dto, onProgress) {
const uploadedParts = [];
const partSize = 10 * 1024 * 1024; // 10 MB
const totalParts = Math.ceil(dto.buffer.byteLength / partSize);
let partNumber = 0;
const uploadNextPart = async () => {
if (partNumber >= totalParts) {
return;
}
partNumber++;
onProgress?.(partNumber, totalParts);
const start = (partNumber - 1) * partSize;
const end = Math.min(start + partSize, dto.buffer.byteLength);
const partBuffer = dto.buffer.subarray(start, end);
const uploadedPart = await this.createUploadPart({
appBuildSourceId: dto.appBuildSourceId,
appId: dto.appId,
buffer: partBuffer,
name: dto.name,
partNumber,
uploadId: dto.uploadId,
});
uploadedParts.push(uploadedPart);
await uploadNextPart();
};
const uploadPartPromises = Array.from({ length: MAX_CONCURRENT_PART_UPLOADS });
for (let i = 0; i < MAX_CONCURRENT_PART_UPLOADS; i++) {
uploadPartPromises[i] = uploadNextPart();
}
await Promise.all(uploadPartPromises);
return uploadedParts.sort((a, b) => a.partNumber - b.partNumber);
}
async upload(dto, onProgress) {
// 1. Create a multipart upload
const { uploadId } = await this.createUpload({
appBuildSourceId: dto.appBuildSourceId,
appId: dto.appId,
});
// 2. Upload the file in parts
const parts = await this.createUploadParts({
appBuildSourceId: dto.appBuildSourceId,
appId: dto.appId,
buffer: dto.buffer,
name: dto.name,
uploadId,
}, onProgress);
// 3. Complete the upload
await this.completeUpload({
appBuildSourceId: dto.appBuildSourceId,
appId: dto.appId,
parts,
uploadId,
});
}
}
const appBuildSourcesService = new AppBuildSourcesServiceImpl(httpClient);
export default appBuildSourcesService;