@capawesome/cli
Version:
The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.
137 lines (136 loc) • 5.48 kB
JavaScript
import { MAX_CONCURRENT_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 AppBundleFilesServiceImpl {
httpClient;
constructor(httpClient) {
this.httpClient = httpClient;
}
async create(dto) {
const sizeInBytes = dto.buffer.byteLength;
const useMultipartUpload = sizeInBytes >= 50 * 1024 * 1024; // 50 MB
const formData = new FormData();
formData.append('checksum', dto.checksum);
if (!useMultipartUpload) {
formData.append('file', dto.buffer, { filename: dto.name });
}
if (dto.href) {
formData.append('href', dto.href);
}
formData.append('mimeType', dto.mimeType);
formData.append('name', dto.name);
if (dto.signature) {
formData.append('signature', dto.signature);
}
formData.append('sizeInBytes', sizeInBytes.toString());
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/bundles/${dto.appBundleId}/files`, formData, {
headers: {
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
...formData.getHeaders(),
},
});
if (useMultipartUpload) {
await this.upload({
appBundleFileId: response.data.id,
appBundleId: dto.appBundleId,
appId: dto.appId,
buffer: dto.buffer,
name: dto.name,
checksum: dto.checksum,
});
}
return response.data;
}
async completeUpload(dto) {
return this.httpClient
.post(`/v1/apps/${dto.appId}/bundles/${dto.appBundleId}/files/${dto.appBundleFileId}/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}/bundles/${dto.appBundleId}/files/${dto.appBundleFileId}/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}/bundles/${dto.appBundleId}/files/${dto.appBundleFileId}/upload?action=mpu-uploadpart&uploadId=${dto.uploadId}`, formData, {
headers: {
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
...formData.getHeaders(),
},
})
.then((response) => response.data);
}
async createUploadParts(dto) {
const uploadedParts = [];
const partSize = 10 * 1024 * 1024; // 10 MB. 5 MB is the minimum part size except for the last part.
const totalParts = Math.ceil(dto.buffer.byteLength / partSize);
let partNumber = 0;
const uploadNextPart = async () => {
if (partNumber >= totalParts) {
return;
}
partNumber++;
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({
appBundleFileId: dto.appBundleFileId,
appBundleId: dto.appBundleId,
appId: dto.appId,
buffer: partBuffer,
name: dto.name,
partNumber,
uploadId: dto.uploadId,
});
uploadedParts.push(uploadedPart);
await uploadNextPart();
};
const uploadPartPromises = Array.from({ length: MAX_CONCURRENT_UPLOADS });
for (let i = 0; i < MAX_CONCURRENT_UPLOADS; i++) {
uploadPartPromises[i] = uploadNextPart();
}
await Promise.all(uploadPartPromises);
return uploadedParts;
}
async upload(dto) {
// 1. Create a multipart upload
const { uploadId } = await this.createUpload({
appBundleFileId: dto.appBundleFileId,
appBundleId: dto.appBundleId,
appId: dto.appId,
});
// 2. Upload the file in parts
const parts = await this.createUploadParts({
appBundleFileId: dto.appBundleFileId,
appBundleId: dto.appBundleId,
appId: dto.appId,
buffer: dto.buffer,
name: dto.name,
uploadId,
});
// 3. Complete the upload
await this.completeUpload({
appBundleFileId: dto.appBundleFileId,
appBundleId: dto.appBundleId,
appId: dto.appId,
parts,
uploadId,
});
}
}
const appBundleFilesService = new AppBundleFilesServiceImpl(httpClient);
export default appBundleFilesService;