UNPKG

@intuitionrobotics/file-upload

Version:

File Uploader - Express & Typescript based backend framework

147 lines 6.11 kB
import { __stringify, BadImplementationException, generateHex, Minute, Module, Queue } from "@intuitionrobotics/ts-common"; import { BaseHttpModule_Class, BaseHttpRequest, HttpMethod } from "@intuitionrobotics/thunderstorm"; import {} from "../../shared/types.js"; const RequestKey_UploadUrl = 'get-upload-url'; const RequestKey_UploadFile = 'upload-file'; export var FileStatus; (function (FileStatus) { FileStatus["ObtainingUrl"] = "ObtainingUrl"; FileStatus["UrlObtained"] = "UrlObtained"; FileStatus["UploadingFile"] = "UploadingFile"; // I can assume that in between I upload and I get // the push I'm processing the file in the be FileStatus["PostProcessing"] = "PostProcessing"; FileStatus["Completed"] = "Completed"; FileStatus["Error"] = "Error"; })(FileStatus || (FileStatus = {})); export class BaseUploaderModule_Class extends Module { files = {}; uploadQueue = new Queue("File Uploader").setParallelCount(2); httpModule; constructor(httpModule, moduleName) { super(moduleName); this.httpModule = httpModule; } init() { if (this.config.uploadQueueParallelCount) this.uploadQueue.setParallelCount(this.config.uploadQueueParallelCount); } getFileInfo(id, key) { return this.files[id] && this.files[id][key]; } getFullFileInfo(id) { return this.files[id]; } setFileInfo(id, key, value) { if (!this.files[id]) throw new BadImplementationException(`Trying to set ${key} for non existent file with id: ${id}`); this.files[id][key] = value; this.files[id].statusCallBack(id, this.files[id].status); } uploadImpl(files, _bucketName, _pathPrefix) { const body = files.map(fileData => { const fileInfo = { name: fileData.name, mimeType: fileData.mimeType, feId: generateHex(32) }; if (fileData.key) fileInfo.key = fileData.key; if (fileData.public) fileInfo.public = fileData.public; this.files[fileInfo.feId] = { file: fileData.file, fileName: fileData.file.name, status: FileStatus.ObtainingUrl, name: fileData.name, statusCallBack: fileData.onFileStatusChanged }; return fileInfo; }); if (_bucketName) this.getUrlEndpointWithBucketName({ files: body, pathPrefix: _pathPrefix, bucketName: _bucketName }); else this.getUrlEndpoint(body); return body; } getUrlEndpoint(body) { this .httpModule .createRequest(HttpMethod.POST, RequestKey_UploadUrl) .setRelativeUrl('/v1/upload/get-url') .setJsonBody(body) .setOnError((request, resError) => { body.forEach(f => { this.setFileInfo(f.feId, "messageStatus", __stringify(resError?.debugMessage)); this.setFileInfo(f.feId, "status", FileStatus.Error); }); }) .execute(async (response) => { body.forEach(f => this.setFileInfo(f.feId, "status", FileStatus.UrlObtained)); if (!response) return; // Not a relevant await but still... await this.uploadFiles(response); }); } getUrlEndpointWithBucketName(body) { this .httpModule .createRequest(HttpMethod.POST, RequestKey_UploadUrl) .setRelativeUrl('/v1/upload/get-url-with-bucket-name') .setJsonBody(body) .setOnError((request, resError) => { body.files.forEach(f => { this.setFileInfo(f.feId, "messageStatus", __stringify(resError?.debugMessage)); this.setFileInfo(f.feId, "status", FileStatus.Error); }); }) .execute(async (response) => { body.files.forEach(f => this.setFileInfo(f.feId, "status", FileStatus.UrlObtained)); if (!response) return; // Not a relevant await but still... await this.uploadFiles(response); }); } uploadFiles = async (response) => { // Subscribe await this.subscribeToPush(response); response.forEach(r => { const feId = r.tempDoc.feId; this.uploadQueue.addItem(async () => { await this.uploadFile(r); delete this.files[feId].file; this.setFileInfo(feId, "progress", undefined); //TODO: Probably need to set a timer here in case we dont get a push back (contingency) }, () => { this.setFileInfo(feId, "status", FileStatus.PostProcessing); }, error => { this.setFileInfo(feId, "status", FileStatus.Error); this.setFileInfo(feId, "messageStatus", __stringify(error)); }); }); }; uploadFile = async (response) => { const feId = response.tempDoc.feId; this.setFileInfo(feId, "status", FileStatus.UploadingFile); this.setFileInfo(feId, "tempDoc", response.tempDoc); const fileInfo = this.files[feId]; if (!fileInfo) throw new BadImplementationException(`Missing file with id ${feId} and name: ${response.tempDoc.name}`); const request = this .httpModule .createRequest(HttpMethod.PUT, RequestKey_UploadFile) .setUrl(response.secureUrl) // Don't change this because it replaces the default headers which we dont need .setDefaultHeaders({ 'Content-Type': response.tempDoc.mimeType }) .setTimeout(20 * Minute) .setBody(fileInfo.file) .setOnProgressListener((ev) => { this.setFileInfo(feId, "progress", ev.loaded / ev.total); }); this.setFileInfo(feId, "request", request); await request.executeSync(); }; } //# sourceMappingURL=BaseUploaderModule.js.map