-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b4365e7
commit f38b436
Showing
33 changed files
with
7,022 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { | ||
Controller, | ||
Post, | ||
UploadedFile, | ||
UseInterceptors, | ||
Param, | ||
Get, | ||
Res, | ||
Query, | ||
} from '@nestjs/common'; | ||
import { FastifyFileInterceptor } from '../interceptors/file-upload.interceptor'; | ||
import { MultipartFile } from '../interfaces/file-upload.interface'; | ||
import { FileUploadService } from '../services/file-upload.service'; | ||
import { FastifyReply } from 'fastify'; | ||
|
||
@Controller('files') | ||
export class FileUploadController { | ||
constructor(private readonly filesService: FileUploadService) {} | ||
|
||
@Post('upload-file') | ||
@UseInterceptors(FastifyFileInterceptor('file', {})) | ||
async uploadFile( | ||
@UploadedFile() file: MultipartFile, | ||
@Query('destination') destination: string, | ||
@Query('filename') filename: string, | ||
): Promise<{ | ||
statusCode?: number; | ||
message: string; | ||
file?: { url: string } | undefined; | ||
}> { | ||
try { | ||
const directory = await this.filesService.upload( | ||
file, | ||
destination, | ||
filename, | ||
); | ||
return { | ||
message: 'File uploaded successfully', | ||
file: { url: directory }, | ||
}; | ||
} catch (error) { | ||
console.error(`Error uploading file: ${error.message}`); | ||
return { | ||
statusCode: 500, | ||
message: 'File upload failed', | ||
file: undefined, | ||
}; | ||
} | ||
} | ||
|
||
@Get('download/:destination') | ||
async downloadFile( | ||
@Param('destination') destination: string, | ||
@Res() res: FastifyReply, | ||
): Promise<void> { | ||
try { | ||
const fileStream = await this.filesService.download(destination); | ||
res.headers({ | ||
'Content-Type': 'application/octet-stream', | ||
'Content-Disposition': `attachment; filename=${destination}`, | ||
}); | ||
fileStream.pipe(res.raw); | ||
} catch (error) { | ||
console.log('error: ', error); | ||
console.error(`Error downloading file: ${error.message}`); | ||
res.status(500).send('File download failed'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './file-upload.controller'; | ||
export * from './prometheus.controller'; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
packages/common/src/interceptors/file-upload.interceptor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { | ||
CallHandler, | ||
ExecutionContext, | ||
Inject, | ||
mixin, | ||
NestInterceptor, | ||
Optional, | ||
Type, | ||
} from '@nestjs/common'; | ||
import { Observable } from 'rxjs'; | ||
import FastifyMulter from 'fastify-multer'; | ||
import { Options, Multer } from 'multer'; | ||
|
||
type MulterInstance = any; | ||
export function FastifyFileInterceptor( | ||
fieldName: string, | ||
localOptions: Options, | ||
): Type<NestInterceptor> { | ||
class MixinInterceptor implements NestInterceptor { | ||
protected multer: MulterInstance; | ||
|
||
constructor( | ||
@Optional() | ||
@Inject('MULTER_MODULE_OPTIONS') | ||
options: Multer, | ||
) { | ||
this.multer = (FastifyMulter as any)({ ...options, ...localOptions }); | ||
} | ||
|
||
async intercept( | ||
context: ExecutionContext, | ||
next: CallHandler, | ||
): Promise<Observable<any>> { | ||
const ctx = context.switchToHttp(); | ||
|
||
await new Promise<void>((resolve, reject) => | ||
this.multer.single(fieldName)( | ||
ctx.getRequest(), | ||
ctx.getResponse(), | ||
(error: any) => { | ||
if (error) { | ||
// const error = transformException(err); | ||
console.log(error); | ||
return reject(error); | ||
} | ||
resolve(); | ||
}, | ||
), | ||
); | ||
|
||
return next.handle(); | ||
} | ||
} | ||
const Interceptor = mixin(MixinInterceptor); | ||
return Interceptor as Type<NestInterceptor>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './response-time.interceptor'; | ||
export * from './response-format.interceptor'; | ||
export * from './file-upload.interceptor'; | ||
export * from './utils'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import * as fastifyMutipart from 'fastify-multipart'; | ||
|
||
export enum STORAGE_MODE { | ||
MINIO = 'minio', | ||
LOCAL = 'local', | ||
} | ||
|
||
export interface MultipartFile { | ||
toBuffer: () => Promise<Buffer>; | ||
file: NodeJS.ReadableStream; | ||
filepath: string; | ||
fieldname: string; | ||
filename: string; | ||
encoding: string; | ||
mimetype: string; | ||
fields: fastifyMutipart.MultipartFields; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './file-upload.interface'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { FileUploadController } from '../controllers/file-upload.controller'; | ||
import { FileUploadService } from '../services/file-upload.service'; | ||
|
||
@Module({ | ||
imports: [], | ||
controllers: [FileUploadController], | ||
providers: [FileUploadService], | ||
}) | ||
export class FileUploadModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './temporal.module'; | ||
export * from './file-upload.module'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { FastifyInstance } from 'fastify'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import * as fastify from 'fastify'; | ||
import { InternalServerErrorException, Logger } from '@nestjs/common'; | ||
import { Client } from 'minio'; | ||
import { STORAGE_MODE } from '../interfaces/file-upload.interface'; | ||
|
||
export class FileUploadService { | ||
private readonly storage: any; | ||
private readonly useMinio: boolean; | ||
private readonly fastifyInstance: FastifyInstance; | ||
private logger: Logger; | ||
private useSSL = false; | ||
|
||
constructor() { | ||
this.logger = new Logger('FileUploadService'); | ||
this.useMinio = process.env.STORAGE_MODE?.toLowerCase() === 'minio'; | ||
this.useSSL = !process.env.STORAGE_USE_SSL | ||
? false | ||
: process.env.STORAGE_USE_SSL?.toLocaleLowerCase() === 'true'; | ||
|
||
switch (process.env.STORAGE_MODE?.toLowerCase()) { | ||
case STORAGE_MODE.MINIO: | ||
this.storage = new Client({ | ||
endPoint: process.env.STORAGE_ENDPOINT, | ||
port: parseInt(process.env.STORAGE_PORT), | ||
useSSL: this.useSSL, | ||
accessKey: process.env.STORAGE_ACCESS_KEY, | ||
secretKey: process.env.STORAGE_SECRET_KEY, | ||
}); | ||
break; | ||
default: | ||
this.fastifyInstance = fastify(); | ||
} | ||
} | ||
|
||
async uploadToMinio(filename: string, file: any): Promise<string> { | ||
const metaData = { | ||
'Content-Type': file.mimetype, | ||
}; | ||
return new Promise((resolve, reject) => { | ||
this.storage.putObject( | ||
process.env.MINIO_BUCKETNAME, | ||
filename, | ||
file.buffer, | ||
metaData, | ||
function (err) { | ||
if (err) { | ||
console.log('err: ', err); | ||
reject(err); | ||
} | ||
resolve( | ||
`${this.useSSL ? 'https' : 'http'}://${process.env.STORAGE_ENDPOINT | ||
}:${process.env.STORAGE_PORT}/${process.env.MINIO_BUCKETNAME | ||
}/${filename}`, | ||
); | ||
}, | ||
); | ||
}); | ||
} | ||
|
||
async saveLocalFile( | ||
destination: string, | ||
filename: string, | ||
file: any, | ||
): Promise<string> { | ||
const uploadsDir = path.join(process.cwd(), destination); | ||
const localFilePath = path.join(uploadsDir, filename); | ||
if (!fs.existsSync(uploadsDir)) { | ||
try { | ||
// Create the directory | ||
fs.mkdirSync(uploadsDir, { recursive: true }); | ||
this.logger.log(`Directory created at ${uploadsDir}`); | ||
} catch (err) { | ||
this.logger.error(`Error creating directory: ${err.message}`); | ||
} | ||
} else { | ||
this.logger.log(`Directory already exists at ${uploadsDir}`); | ||
} | ||
fs.writeFileSync(localFilePath, file.buffer); | ||
return destination; | ||
} | ||
|
||
async upload( | ||
file: any, | ||
destination: string, | ||
filename: string, | ||
): Promise<string> { | ||
try { | ||
switch (process.env.STORAGE_MODE?.toLowerCase()) { | ||
case STORAGE_MODE.MINIO: | ||
this.logger.log('using minio'); | ||
return await this.uploadToMinio(filename, file); | ||
default: | ||
this.logger.log('writing to storage'); | ||
return await this.saveLocalFile(destination, filename, file); | ||
} | ||
} catch (error) { | ||
this.logger.error(`Error uploading file: ${error}`); | ||
throw new InternalServerErrorException('File upload failed'); | ||
} | ||
} | ||
|
||
async download(destination: string): Promise<any> { | ||
try { | ||
if (this.useMinio) { | ||
const fileStream = await this.storage.getObject( | ||
process.env.STORAGE_CONTAINER_NAME, | ||
destination, | ||
); | ||
return fileStream; | ||
} else { | ||
const localFilePath = path.join(process.cwd(), 'uploads', destination); // don't use __dirname here that'll point to the dist folder and not the top level folder containing the project (and the uploads folder) | ||
const fileStream = fs.createReadStream(localFilePath); | ||
return fileStream; | ||
} | ||
} catch (error) { | ||
this.logger.error(`Error downloading file: ${error.message}`); | ||
throw new InternalServerErrorException('File download failed'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './temporal.service'; | ||
export * from './file-upload.service'; |
Oops, something went wrong.