1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-07-30 10:39:43 +02:00

S3 uploads support (#273)

* finish s3 uploads implementation

* remove unnecessary file

* fix docs

* update DEVELOPMENT.md

* update doc

* update default uploads path
This commit is contained in:
Nikita Melnikov 2022-10-08 14:26:11 +04:00 committed by GitHub
parent 55b4b3ee61
commit 8c794304b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 1373 additions and 248 deletions

View file

@ -0,0 +1,10 @@
import appConfig from '../utils/appConfig.js';
import S3UploadsDriver from './s3.js';
import LocalUploadsDriver from './local.js';
/**
* Initialize the uploads driver based on the configuration
*/
export const uploadsDriver = appConfig.uploads.driver === 'local'
? new LocalUploadsDriver(appConfig.uploads)
: new S3UploadsDriver(appConfig.uploads);

View file

@ -0,0 +1,72 @@
import { UploadsDriver } from './types.js';
import multer from 'multer';
import mkdirp from 'mkdirp';
import { random16 } from '../utils/crypto.js';
import mime from 'mime';
import appConfig, { LocalUploadsConfig } from '../utils/appConfig.js';
import fs from 'fs';
import fileType from 'file-type';
import { FileData } from '../models/file.js';
/**
* Uploads driver for local storage
*/
export default class LocalUploadsDriver implements UploadsDriver {
/**
* Configuration for local uploads
*/
private readonly config: LocalUploadsConfig;
/**
* Create a new instance of LocalUploadsDriver
*
* @param config - configuration for local uploads
*/
constructor(config: LocalUploadsConfig) {
this.config = config;
}
/**
* Creates multer storage engine for local uploads
*/
public createStorageEngine(): multer.StorageEngine {
return multer.diskStorage({
destination: (req, file, cb) => {
const dir: string = this.config.local.path;
mkdirp(dir);
cb(null, dir);
},
filename: async (req, file, cb) => {
const filename = await random16();
cb(null, `${filename}.${mime.getExtension(file.mimetype)}`);
},
});
}
/**
* Saves passed file to the local storage
*
* @param data - file data to save
* @param mimetype - file mimetype
* @param possibleExtension
*/
public async save(data: Buffer, mimetype?: string, possibleExtension?: string): Promise<FileData> {
const filename = await random16();
const type = await fileType.fromBuffer(data);
const ext = type ? type.ext : possibleExtension;
const fullName = `${filename}.${ext}`;
fs.writeFileSync(`${appConfig.uploads}/${fullName}`, data);
return {
name: fullName,
filename: fullName,
size: data.length,
mimetype,
};
}
}

88
src/backend/uploads/s3.ts Normal file
View file

@ -0,0 +1,88 @@
import { UploadsDriver } from './types.js';
import multerS3 from 'multer-s3';
import { random16 } from '../utils/crypto.js';
import path from 'path';
import mime from 'mime';
import multer from 'multer';
import { S3UploadsConfig } from '../utils/appConfig.js';
import { FileData } from '../models/file.js';
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import fileType from 'file-type';
/**
* Uploads driver for S3 storage
*/
export default class S3UploadsDriver implements UploadsDriver {
/**
* Configuration for S3 uploads
*/
private readonly config: S3UploadsConfig;
/**
* S3 client for uploads
*/
private readonly s3Client: S3Client;
/**
* Create a new instance of S3UploadsDriver
*
* @param config - configuration for s3 uploads
*/
constructor(config: S3UploadsConfig) {
this.config = config;
this.s3Client = new S3Client({
region: this.config.s3.region,
credentials: {
accessKeyId: this.config.s3.accessKeyId,
secretAccessKey: this.config.s3.secretAccessKey,
},
});
}
/**
* Creates multer storage engine for S3
*/
public createStorageEngine(): multer.StorageEngine {
const config = this.config;
return multerS3({
s3: this.s3Client,
bucket: config.s3.bucket,
key: async function (req, file, cb) {
const filename = await random16();
cb(null, path.posix.join(config.s3.keyPrefix, `${filename}.${mime.getExtension(file.mimetype)}`));
},
});
}
/**
* Saves passed file to the storage
*
* @param data - file data to save
* @param mimetype - file mimetype
* @param possibleExtension - possible file extension
*/
public async save(data: Buffer, mimetype?: string, possibleExtension?: string): Promise<FileData> {
const filename = await random16();
const type = await fileType.fromBuffer(data);
const ext = type ? type.ext : possibleExtension;
const fullName = `${filename}.${ext}`;
const fileKey = path.posix.join(this.config.s3.keyPrefix, fullName);
await this.s3Client.send(new PutObjectCommand({
Bucket: this.config.s3.bucket,
Key: fileKey,
Body: data,
ContentType: mimetype,
}));
return {
name: fileKey,
filename: fileKey,
size: data.length,
mimetype,
};
}
}

View file

@ -0,0 +1,21 @@
import multer from 'multer';
import { FileData } from '../models/file.js';
/**
* Represents common uploads driver functionality
*/
export interface UploadsDriver {
/**
* Returns multer storage instance
*/
createStorageEngine(): multer.StorageEngine
/**
* Saves passed file
*
* @param data - file data to save
* @param mimetype - file mimetype
* @param possibleExtension - possible file extension
*/
save(data: Buffer, mimetype?: string, possibleExtension?: string): Promise<FileData>;
}