feat(nestjs): migrate to NestJS

This commit is contained in:
Ahmed Bouhuolia
2025-04-07 11:51:24 +02:00
parent f068218a16
commit 55fcc908ef
3779 changed files with 631 additions and 195332 deletions

View File

@@ -0,0 +1,70 @@
import {
CallHandler,
ExecutionContext,
Inject,
mixin,
NestInterceptor,
Optional,
Type,
} from '@nestjs/common';
import * as multer from 'multer';
import { Observable } from 'rxjs';
import { MULTER_MODULE_OPTIONS } from '../constants/files.constants';
import { transformException } from '../constants/multer.utils';
import { MulterModuleOptions } from '@nestjs/platform-express';
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
type MulterInstance = any;
/**
* @param {string} fieldName
* @param {Function|MulterOptions} localOptions - Function that receives controller instance or MulterOptions object
*/
export function FileInterceptor(
fieldName: string,
localOptions?: ((instance: any) => MulterOptions) | MulterOptions,
): Type<NestInterceptor> {
class MixinInterceptor implements NestInterceptor {
protected multer: MulterInstance;
constructor(
@Optional()
@Inject(MULTER_MODULE_OPTIONS)
options: (() => MulterModuleOptions | MulterModuleOptions) = () => ({}),
) {
const resolvedOptions = typeof localOptions === 'function'
? localOptions(this)
: localOptions;
this.multer = (multer as any)({
...(typeof options === 'function' ? options() : options),
...resolvedOptions,
});
}
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(),
(err: any) => {
if (err) {
const error = transformException(err);
return reject(error);
}
resolve();
},
),
);
return next.handle();
}
}
const Interceptor = mixin(MixinInterceptor);
return Interceptor;
}

View File

@@ -0,0 +1,78 @@
import {
type ExecutionContext,
Injectable,
type NestInterceptor,
type CallHandler,
Optional,
} from '@nestjs/common';
import { type Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export function camelToSnake<T = any>(value: T) {
if (value === null || value === undefined) {
return value;
}
if (Array.isArray(value)) {
return value.map(camelToSnake);
}
if (typeof value === 'object' && !(value instanceof Date)) {
return Object.fromEntries(
Object.entries(value).map(([key, value]) => [
key
.split(/(?=[A-Z])/)
.join('_')
.toLowerCase(),
camelToSnake(value),
]),
);
}
return value;
}
export function snakeToCamel<T = any>(value: T) {
if (value === null || value === undefined) {
return value;
}
if (Array.isArray(value)) {
return value.map(snakeToCamel);
}
const impl = (str: string) => {
const converted = str.replace(/([-_]\w)/g, (group) =>
group[1].toUpperCase(),
);
return converted[0].toLowerCase() + converted.slice(1);
};
if (typeof value === 'object' && !(value instanceof Date)) {
return Object.fromEntries(
Object.entries(value).map(([key, value]) => [
impl(key),
snakeToCamel(value),
]),
);
}
return value;
}
export const DEFAULT_STRATEGY = {
in: snakeToCamel,
out: camelToSnake,
};
@Injectable()
export class SerializeInterceptor implements NestInterceptor<any, any> {
constructor(@Optional() readonly strategy = DEFAULT_STRATEGY) {}
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> {
const request = context.switchToHttp().getRequest();
request.body = this.strategy.in(request.body);
// handle returns stream..
return next.handle().pipe(map(this.strategy.out));
}
}