Skip to content

Commit

Permalink
feat: origin decorator, min/max date
Browse files Browse the repository at this point in the history
  • Loading branch information
rubiin committed Oct 19, 2023
1 parent e0a1bd5 commit ac379dd
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 17 deletions.
42 changes: 41 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,45 @@
"SSSZ",
"travelerdev",
"UNAUTHORISED"
]
],
// Enable the ESlint flat config support
"eslint.experimental.useFlatConfig": true,

// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,

// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "never"
},

// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],

// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
]

}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ultimate-nest",
"version": "1.40.0",
"version": "1.42.0",
"description": "NestJS + MikroORM blog example with batteries included",
"author": {
"email": "roobin.bhandari@gmail.com",
Expand Down
1 change: 1 addition & 0 deletions src/common/@types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ declare global {

JWT_ACCESS_EXPIRY: string
JWT_REFRESH_EXPIRY: string
JWT_ALGORITHM?: string
JWT_SECRET: string
MAGIC_LINK_EXPIRY: string

Expand Down
13 changes: 9 additions & 4 deletions src/common/@types/interfaces/validator.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ export interface FileValidator {

export type MinMaxLengthOptions = Pick<StringFieldOptions, "each" | "minLength" | "maxLength">;

export type DateFieldOptions = BaseValidator & BaseArrayValidator;

export type EnumFieldOptions = DateFieldOptions;
export type EmailFieldOptions = DateFieldOptions;
export type UUIDFieldOptions = DateFieldOptions;
export type DateFieldOptions = BaseValidator & BaseArrayValidator & {
greaterThan?: boolean;
lessThan?: boolean;
date?: Date
};

export type EnumFieldOptions = BaseValidator & BaseArrayValidator;
export type EmailFieldOptions = EnumFieldOptions;
export type UUIDFieldOptions = EnumFieldOptions;
1 change: 1 addition & 0 deletions src/common/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from "./auth.decorator";
export * from "./controller.decorator";
export * from "./custom-cache.decorator";
export * from "./nocache.decorator";
export * from "./origin.decorator";
export * from "./public.decorator";
export * from "./swagger-api.decorator";
export * from "./user.decorator";
Expand Down
12 changes: 12 additions & 0 deletions src/common/decorators/origin.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ExecutionContext } from "@nestjs/common";
import { createParamDecorator } from "@nestjs/common";
import type { Request } from "express";

/*
The `Origin` decorator is used to get the origin of the request.
*/
export const Origin = createParamDecorator(
(_, context: ExecutionContext): string | undefined => {
return context.switchToHttp().getRequest<Request>().headers?.origin;
},
);
10 changes: 9 additions & 1 deletion src/common/decorators/validation/is-date-field.validator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { applyDecorators } from "@nestjs/common";
import { ArrayNotEmpty, IsArray, IsDateString, IsNotEmpty, IsOptional } from "class-validator";
import { ArrayNotEmpty, IsArray, IsDateString, IsNotEmpty, IsOptional, MaxDate, MinDate } from "class-validator";
import type { DateFieldOptions } from "@common/@types";
import { validationI18nMessage } from "@lib/i18n";

Expand All @@ -15,6 +15,8 @@ export function IsDateField(options_?: DateFieldOptions) {
required: true,
arrayMinSize: 0,
arrayMaxSize: Number.MAX_SAFE_INTEGER,
lessThan: false,
greaterThan: false,
...options_,
} satisfies DateFieldOptions;

Expand Down Expand Up @@ -60,5 +62,11 @@ export function IsDateField(options_?: DateFieldOptions) {
);
}

if (options.greaterThan)
decoratorsToApply.push(MinDate(options.date!));

if (options.lessThan)
decoratorsToApply.push(MaxDate(options.date!));

return applyDecorators(...decoratorsToApply);
}
20 changes: 12 additions & 8 deletions src/common/helpers/app.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,18 @@ export const AppUtils = {
.setLicense("MIT", "https://opensource.org/licenses/MIT")
.setDescription(SWAGGER_DESCRIPTION)
.setVersion(SWAGGER_API_CURRENT_VERSION)
.addBearerAuth({
type: "http",
scheme: "Bearer",
bearerFormat: "JWT",
name: "JWT",
description: "Enter JWT token",
in: "header",
})
.addBearerAuth(
{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
'accessToken'
)
.addBearerAuth(
{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
'refreshToken'
)
.addApiKey(
{ type: 'apiKey', in: 'header', name: 'x-api-key' },
'apiKey'
)
.build();

const document = SwaggerModule.createDocument(app, options, {});
Expand Down
1 change: 1 addition & 0 deletions src/common/middlewares/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

export * from "./cache.middleware";
export * from "./ip.middleware";
export * from "./maintainance.middleware";
export * from "./request-id.middleware";
29 changes: 29 additions & 0 deletions src/common/middlewares/maintainance.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type {
NestMiddleware,
} from "@nestjs/common";
import {
Injectable,
ServiceUnavailableException,
} from "@nestjs/common";
import type { NextFunction, Request, Response } from "express";

@Injectable()
export class SettingMaintenanceMiddleware implements NestMiddleware {
async use(
_request: Request,
_response: Response,
next: NextFunction,
): Promise<void> {
const maintenance: boolean = true
// TODO: get maintenance status from database

if (maintenance) {
throw new ServiceUnavailableException({
statusCode: 503,
message: "Service is under maintenance",
});
}

next();
}
}
2 changes: 2 additions & 0 deletions src/common/swagger/swagger.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export const swaggerOptions = {
docExpansion: "list",
filter: true,
showRequestDuration: true,
tryItOutEnabled: true,
displayOperationId: true,
persistAuthorization: true,
plugins: [CaseInsensitiveFilterPlugin],
operationsSorter: (
Expand Down
2 changes: 2 additions & 0 deletions src/lib/config/configs/jwt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { JWT_EXPIRY_REGEX } from "@common/constant";

export const jwtConfigValidationSchema = {
JWT_SECRET: Joi.string().required().min(8),
JWT_ALGORITHM: Joi.string().optional(),
JWT_REFRESH_EXPIRY: Joi.string().regex(JWT_EXPIRY_REGEX).required(),
JWT_ACCESS_EXPIRY: Joi.string().regex(JWT_EXPIRY_REGEX).required(),
MAGIC_LINK_EXPIRY: Joi.string().regex(JWT_EXPIRY_REGEX).required(),
};

export const jwt = registerAs("jwt", () => ({
secret: process.env.JWT_SECRET,
algorithm: process.env?.JWT_ALGORITHM ?? "HS256",
accessExpiry: /^\d+$/.test(process.env.JWT_ACCESS_EXPIRY)
? +process.env.JWT_ACCESS_EXPIRY
: process.env.JWT_ACCESS_EXPIRY,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/jwt.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { JwtModule } from "@nestjs/jwt";
secret: configService.get("jwt.secret", { infer: true }),
signOptions: {
expiresIn: configService.get("jwt.accessExpiry", { infer: true }),
algorithm: "HS256",
algorithm: configService.get("jwt.algorithm", { infer: true })
},
}),
}),
Expand Down
4 changes: 4 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ async function bootstrap() {
await app.listen(port);

const appUrl = `http://localhost:${port}/${globalPrefix}`;

logger.log(`==========================================================`);
logger.log(`🚀 Application is running on: ${chalk.green(appUrl)}`);

logger.log(`==========================================================`);
logger.log(
`🚦 Accepting request only from: ${chalk.green(
`${configService.get("app.allowedOrigins", { infer: true }).toString()}`,
Expand All @@ -109,6 +112,7 @@ async function bootstrap() {

if (!HelperService.isProd()) {
const swaggerUrl = `http://localhost:${port}/doc`;
logger.log(`==========================================================`);
logger.log(`📑 Swagger is running on: ${chalk.green(swaggerUrl)}`);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/auth/strategies/magic-login.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class MagicLoginStrategy extends PassportStrategy(Strategy, "magicLogin")
jwtOptions: {
expiresIn: config.get("jwt.magicLinkExpiry", { infer: true }),
},
algorithms: ["HS256"],
algorithms: [config.get("jwt.algorithm", { infer: true })],
// The authentication callback URL
callbackUrl: "auth/magiclogin/callback",
sendMagicLink: async (destination: string, href: string) => {
Expand Down

0 comments on commit ac379dd

Please sign in to comment.