All files / src/controllers files.controller.ts

29.62% Statements 16/54
0% Branches 0/8
0% Functions 0/9
28% Lines 14/50

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 991x 1x 1x 1x 1x 1x   1x 1x   1x 1x                 1x                         1x                         1x                                   1x                                                                    
import {inject} from '@loopback/core';
import {get, oas, param, post, Request, requestBody, Response, RestBindings} from '@loopback/rest';
import fs from 'fs';
import path from 'path';
import {promisify} from 'util';
import {ASSETS_DIRECTORY, FILE_UPLOAD_SERVICE, STORAGE_DIRECTORY} from '../keys';
import {FileUploadHandler} from '../types';
import {getFilesAndFields} from './components';
import {baseResponse} from './requestSpecs';
 
const readdir = promisify(fs.readdir);
export class FilesController {
  constructor(
    @inject(FILE_UPLOAD_SERVICE) private handler: FileUploadHandler,
    @inject(STORAGE_DIRECTORY) private storageDirectory: string,
    @inject(ASSETS_DIRECTORY) private assetsDirectory: string,
    @inject(RestBindings.Http.RESPONSE) private res: Response,
  ) {
  }
  @post('/files', baseResponse)
  async fileUpload(@requestBody.file() request: Request): Promise<object> {
    return new Promise<object>((resolve, reject) => {
      this.handler(request, this.res, (err: unknown) => {
        if (err) {
          reject(err)
        } else {
          resolve(getFilesAndFields(request));
        }
      });
    });
  }
 
  @get('/files', baseResponse)
  async listFiles() {
    let methodName = "listFiles"
 
    try {
      const files = await readdir(this.storageDirectory);
      return files;
    } catch (error) {
      throw {code: 422, message: error, methodName, className: FilesController.name}
    }
  }
 
  @get('/files/{filename}')
  @oas.response.file()
  downloadFile(
    @param.path.string('filename') fileName: string,
    @inject(RestBindings.Http.RESPONSE) response: Response,
  ) {
    let methodName = "downloadFile"
 
    try {
 
      const file = this.validateFileName(fileName);
      response.download(file, fileName);
      return response;
    } catch (error) {
      throw {code: 422, message: error, methodName, className: FilesController.name}
    }
  }
 
  @get('/assets/{filename}')
  @oas.response.file()
  downloadAssets(
    @param.path.string('filename') fileName: string,
    @inject(RestBindings.Http.RESPONSE) response: Response,
  ) {
    let methodName = "downloadAssets"
    try {
      const file = this.validateAssetsFileName(fileName);
      response.download(file, fileName);
      return response;
    } catch (error) {
      throw {code: 422, message: error, methodName, className: FilesController.name}
    }
  }
 
  private validateAssetsFileName(fileName: string) {
    let methodName = "validateAssetsFileName"
 
    const resolved = path.resolve(this.assetsDirectory, fileName);
    if (resolved.startsWith(this.assetsDirectory)) return resolved;
    // The resolved file is outside sandbox
    throw {code: 406, message: `Invalid file name: ${fileName}`, methodName, className: FilesController.name}
  }
 
  private validateFileName(fileName: string) {
    const resolved = path.resolve(this.storageDirectory, fileName);
    const validFile = fs.existsSync(resolved)
    let methodName = "validateFileName"
    if (validFile)
      if (resolved.startsWith(this.storageDirectory)) return resolved;
    // The resolved file is outside sandbox
    throw {code: 406, message: `Invalid file name: ${fileName}`, methodName, className: FilesController.name}
  }
 
}