@intuitionrobotics/file-upload
Version:
File Uploader - Express & Typescript based backend framework
102 lines • 4.69 kB
JavaScript
import { auditBy, generateHex, Hour, ImplementationMissingException, Module, ThisShouldNotHappenException } from "@intuitionrobotics/ts-common";
import { FileWrapper, FirebaseModule, StorageWrapper } from "@intuitionrobotics/firebase/backend";
import { fileUploadedKey, UploadResult } from "../../shared/types.js";
import { UploaderTempFileModule } from "./UploaderTempFileModule.js";
import { PushPubSubModule } from "@intuitionrobotics/push-pub-sub/backend";
import {} from "./BucketListener.js";
export const Temp_Path = "files-temp";
export class UploaderModule_Class extends Module {
constructor() {
super("UploaderModule");
}
storage;
postProcessor;
async __onFileUploaded(filePath) {
await this.fileUploaded(filePath);
}
setPostProcessor = (validator) => {
this.postProcessor = validator;
};
getProcessor = (k) => {
const postProcessorElement = this.postProcessor[k];
if (!postProcessorElement)
throw new ImplementationMissingException(`Missing validator for type ${k}`);
return postProcessorElement;
};
init() {
if (!this.postProcessor)
throw new ImplementationMissingException("You must set a postProcessor for the UploaderModule");
this.storage = FirebaseModule.createAdminSession(this.config.uploaderProjectId).getStorage();
}
async getUrl(body, pathPrefix = Temp_Path, _bucketName) {
const bucketName = _bucketName || this.config?.bucketName;
const bucket = await this.storage.getOrCreateBucket(bucketName);
return Promise.all(body.map(async (_file) => {
const key = _file.key || _file.mimeType;
this.getProcessor(key);
const _id = generateHex(32);
const path = `${pathPrefix}/${_id}`;
const instance = {
_id,
feId: _file.feId,
name: _file.name,
mimeType: _file.mimeType,
key,
path,
_audit: auditBy("be-stub"),
bucketName: bucket.bucketName
};
if (_file.public)
instance.public = _file.public;
if (_file.metadata)
instance.metadata = _file.metadata;
const temp = await UploaderTempFileModule.upsert(instance);
const file = await bucket.getFile(temp.path);
const url = await file.getWriteSecuredUrl(_file.mimeType, Hour);
return {
secureUrl: url.securedUrl,
tempDoc: temp
};
}));
}
fileUploaded = async (filePath) => {
if (!filePath)
throw new ThisShouldNotHappenException("Missing file path");
this.logInfo(`Looking for file with path ${filePath}`);
// I use collection and not the module directly since I want to handle failure my way
const tempMeta = await UploaderTempFileModule.collection.queryUnique({ where: { path: filePath } });
if (!tempMeta)
return this.logInfo(`File with path: ${filePath}, not found in temp collection db`);
this.logInfo(`Found temp meta with _id: ${tempMeta._id}`, tempMeta);
const val = this.postProcessor[tempMeta.key];
this.logInfo(`Found a validator ${!!val}`);
if (!val)
return this.notifyFrontend(tempMeta.feId, UploadResult.Failure, `Missing a validator for ${tempMeta.key} for file: ${tempMeta.name}`);
const bucket = await this.storage.getOrCreateBucket(tempMeta.bucketName);
const file = await bucket.getFile(tempMeta.path);
if (tempMeta.public) {
try {
await file.makePublic();
}
catch (e) {
await this.notifyFrontend(tempMeta.feId, UploadResult.Failure, `Failed to make the file public: ${tempMeta.name}`, e);
}
}
try {
await val(file, tempMeta);
}
catch (e) {
//TODO delete the file and the temp doc
return await this.notifyFrontend(tempMeta.feId, UploadResult.Failure, `Post-processing failed for file: ${tempMeta.name}`, e);
}
return this.notifyFrontend(tempMeta.feId, UploadResult.Success, `Successfully parsed and processed file ${tempMeta.name}`);
};
notifyFrontend = async (feId, result, message, cause) => {
if (cause)
this.logWarning(cause);
const data = { message, result, cause };
await PushPubSubModule.pushToKey(fileUploadedKey, { feId }, data);
};
}
export const UploaderModule = new UploaderModule_Class();
//# sourceMappingURL=UploaderModule.js.map