forked from Yara724/api
Added Joi for schema validation, tidied up envs
This commit is contained in:
73
package-lock.json
generated
73
package-lock.json
generated
@@ -23,6 +23,7 @@
|
||||
"class-validator": "^0.15.1",
|
||||
"express": "^5.2.1",
|
||||
"fastest-levenshtein": "^1.0.16",
|
||||
"joi": "^18.2.1",
|
||||
"mongoose": "^8.9.2",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
@@ -1043,6 +1044,54 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hapi/address": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz",
|
||||
"integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^11.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hapi/formula": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz",
|
||||
"integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "11.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz",
|
||||
"integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@hapi/pinpoint": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz",
|
||||
"integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@hapi/tlds": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz",
|
||||
"integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hapi/topo": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz",
|
||||
"integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^11.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
||||
@@ -3114,6 +3163,12 @@
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@swc/cli": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.8.1.tgz",
|
||||
@@ -8754,6 +8809,24 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "18.2.1",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.1.tgz",
|
||||
"integrity": "sha512-2/OKlogiESf2Nh3TFCrRjrr9z1DRHeW0I+KReF67+4J0Ns+8hBtHRmoWAZ2OFU6I5+TWLEe6sVlSdXPjHm5UbQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/address": "^5.1.1",
|
||||
"@hapi/formula": "^3.0.2",
|
||||
"@hapi/hoek": "^11.0.7",
|
||||
"@hapi/pinpoint": "^2.0.1",
|
||||
"@hapi/tlds": "^1.1.1",
|
||||
"@hapi/topo": "^6.0.2",
|
||||
"@standard-schema/spec": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"class-validator": "^0.15.1",
|
||||
"express": "^5.2.1",
|
||||
"fastest-levenshtein": "^1.0.16",
|
||||
"joi": "^18.2.1",
|
||||
"mongoose": "^8.9.2",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { join } from "node:path";
|
||||
import { APP_INTERCEPTOR } from "@nestjs/core";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { APP_INTERCEPTOR, APP_PIPE } from "@nestjs/core";
|
||||
import { Module, ValidationPipe } from "@nestjs/common";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { UnicodeDigitsNormalizeInterceptor } from "./common/interceptors/unicode-digits-normalize.interceptor";
|
||||
import { MongooseModule } from "@nestjs/mongoose";
|
||||
import { ServeStaticModule } from "@nestjs/serve-static";
|
||||
@@ -23,31 +23,77 @@ import { UsersModule } from "./users/users.module";
|
||||
import { applyIranFaTimestampPlugin } from "./helpers/mongoose-fa-timestamps.plugin";
|
||||
import { CronModule } from "./utils/cron/cron.module";
|
||||
import { WorkflowStepManagementModule } from "./workflow-step-management/workflow-step-management.module";
|
||||
import * as Joi from "joi";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
validationSchema: Joi.object({
|
||||
NODE_ENV: Joi.string()
|
||||
.valid("development", "production")
|
||||
.default("development"),
|
||||
PORT: Joi.number().port().default(9001),
|
||||
URL: Joi.string().uri().required(),
|
||||
BASE_URL_DEV: Joi.string()
|
||||
.uri()
|
||||
.when("NODE_ENV", {
|
||||
is: Joi.string().valid("development"),
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional(),
|
||||
}),
|
||||
SWAGGER_USER_DEV: Joi.string().when("NODE_ENV", {
|
||||
is: Joi.string().valid("development"),
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional(),
|
||||
}),
|
||||
SWAGGER_PASSWORD_DEV: Joi.string().when("NODE_ENV", {
|
||||
is: Joi.string().valid("development"),
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional(),
|
||||
}),
|
||||
MONGO_HOST: Joi.string().required(),
|
||||
MONGO_PORT: Joi.number().port().required(),
|
||||
MONGO_USER: Joi.string().required(),
|
||||
MONGO_PASS: Joi.string().required(),
|
||||
MONGO_DB_NAME: Joi.string().required(),
|
||||
JWT_SECRET: Joi.string().required(),
|
||||
SANHUB_BASE_URL: Joi.string().uri(),
|
||||
SANHUB_URL_LOGIN: Joi.string().uri(),
|
||||
SANHUB_USERNAME: Joi.string(),
|
||||
SANHUB_PASSWORD: Joi.string(),
|
||||
AI_URL: Joi.string().uri(),
|
||||
AI_URL_V2: Joi.string().uri(),
|
||||
AI_USERNAME: Joi.string(),
|
||||
AI_PASSWORD: Joi.string(),
|
||||
SMS_PROVIDER: Joi.string()
|
||||
.valid("kavenegar", "parsian")
|
||||
.default("kavenegar"),
|
||||
SMS_API_KEY: Joi.string(),
|
||||
AUTH_SMS_TEMPLATE: Joi.string(),
|
||||
EXP_OTP_TIME: Joi.number(),
|
||||
}),
|
||||
}),
|
||||
CronModule,
|
||||
ServeStaticModule.forRoot({
|
||||
rootPath: join(__dirname, "..", "files"),
|
||||
serveRoot: "/files",
|
||||
}),
|
||||
MongooseModule.forRoot(
|
||||
`mongodb://${process.env.MONGO_URL}:${process.env.MONGO_PORT}/`,
|
||||
{
|
||||
dbName: "yara724",
|
||||
autoIndex: true,
|
||||
user: process.env.MONGO_USER,
|
||||
pass: process.env.MONGO_PASS,
|
||||
authMechanism: "SCRAM-SHA-256",
|
||||
MongooseModule.forRootAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
return {
|
||||
uri: `mongodb://${configService.get<string>("MONGO_USER")}:${configService.get<string>("MONGO_PASS")}@${configService.get<string>("MONGO_HOST")}:${configService.get<string>("MONGO_PORT")}/${configService.get<string>("MONGO_DB_NAME")}?authSource=admin&${configService.get<string>("MONGO_OPTIONS")}`,
|
||||
tls: true,
|
||||
tlsAllowInvalidCertificates: true,
|
||||
autoIndex: configService.get<string>("NODE_ENV") !== "production",
|
||||
connectionFactory: (connection) => {
|
||||
applyIranFaTimestampPlugin(connection);
|
||||
return connection;
|
||||
},
|
||||
};
|
||||
},
|
||||
),
|
||||
}),
|
||||
UsersModule,
|
||||
AuthModule,
|
||||
ClientModule,
|
||||
|
||||
@@ -78,8 +78,7 @@ export class ActorAuthService {
|
||||
res = await this.fieldExpertDbService.findOne({
|
||||
_id: new Types.ObjectId(userId),
|
||||
});
|
||||
else
|
||||
res = await this.fieldExpertDbService.findOne({ email: username });
|
||||
else res = await this.fieldExpertDbService.findOne({ email: username });
|
||||
break;
|
||||
case RoleEnum.REGISTRAR:
|
||||
if (username == null && userId)
|
||||
@@ -139,7 +138,7 @@ export class ActorAuthService {
|
||||
};
|
||||
|
||||
const access_token = this.jwtService.sign(payload, {
|
||||
secret: `${process.env.SECRET}`,
|
||||
secret: `${process.env.JWT_SECRET}`,
|
||||
expiresIn: "1h",
|
||||
});
|
||||
|
||||
@@ -159,9 +158,7 @@ export class ActorAuthService {
|
||||
throw new UnauthorizedException("user not assigned to this role");
|
||||
}
|
||||
if (!(await this.hashService.compare(pass, user.password))) {
|
||||
throw new UnauthorizedException(
|
||||
"password is incorrect or access Denied",
|
||||
);
|
||||
throw new UnauthorizedException("password is incorrect or access Denied");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
@@ -445,13 +442,7 @@ export class ActorAuthService {
|
||||
"state",
|
||||
"address",
|
||||
],
|
||||
field_expert: [
|
||||
"firstName",
|
||||
"lastName",
|
||||
"email",
|
||||
"phone",
|
||||
"mobile",
|
||||
],
|
||||
field_expert: ["firstName", "lastName", "email", "phone", "mobile"],
|
||||
registrar: ["email"],
|
||||
};
|
||||
|
||||
@@ -477,7 +468,11 @@ export class ActorAuthService {
|
||||
}
|
||||
|
||||
// fetch user detail (document or plain object)
|
||||
const document = await this.dynamicDbController(role, currentUser.role === "company" ? currentUser.username : null, userId);
|
||||
const document = await this.dynamicDbController(
|
||||
role,
|
||||
currentUser.role === "company" ? currentUser.username : null,
|
||||
userId,
|
||||
);
|
||||
|
||||
if (!document) throw new NotFoundException("Profile not found");
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ export class UserAuthService {
|
||||
role: "user",
|
||||
};
|
||||
const accToken = this.jwtService.sign(payload, {
|
||||
secret: `${process.env.SECRET}`,
|
||||
secret: `${process.env.JWT_SECRET}`,
|
||||
});
|
||||
await this.userDbService.findOneAndUpdate(
|
||||
{ username: user.username },
|
||||
@@ -162,10 +162,7 @@ export class UserAuthService {
|
||||
process.env.AUTH_SMS_TEMPLATE,
|
||||
);
|
||||
if (!ok) {
|
||||
throw new HttpException(
|
||||
"auth sms send failed",
|
||||
HttpStatus.BAD_GATEWAY,
|
||||
);
|
||||
throw new HttpException("auth sms send failed", HttpStatus.BAD_GATEWAY);
|
||||
}
|
||||
this.logger.log(
|
||||
`Auth OTP SMS accepted by provider phone=${mobile} otp=${otp}`,
|
||||
|
||||
@@ -49,9 +49,9 @@ import { CaptchaModule } from "src/captcha/captcha.module";
|
||||
{ name: ClaimCase.name, schema: ClaimCaseSchema },
|
||||
]),
|
||||
JwtModule.register({
|
||||
signOptions: { expiresIn: "1h" },
|
||||
signOptions: { expiresIn: "1h" }, // TODO: MAKE IT ENV
|
||||
global: true,
|
||||
secret: `${process.env.SECRET}`,
|
||||
secret: `${process.env.JWT_SECRET}`,
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
|
||||
@@ -33,7 +33,7 @@ export class LocalActorAuthGuard implements CanActivate {
|
||||
|
||||
try {
|
||||
const payload = await this.jwtService.verifyAsync(token, {
|
||||
secret: `${process.env.SECRET}`,
|
||||
secret: `${process.env.JWT_SECRET}`,
|
||||
});
|
||||
|
||||
if (
|
||||
|
||||
@@ -31,7 +31,7 @@ export class ClaimAccessGuard implements CanActivate {
|
||||
|
||||
try {
|
||||
const payload = await this.jwtService.verifyAsync(token, {
|
||||
secret: `${process.env.SECRET}`,
|
||||
secret: `${process.env.JWT_SECRET}`,
|
||||
});
|
||||
|
||||
// Allow users to pass through (they will be checked by service methods)
|
||||
@@ -81,7 +81,10 @@ export class ClaimAccessGuard implements CanActivate {
|
||||
|
||||
throw new UnauthorizedException("Invalid role");
|
||||
} catch (error) {
|
||||
if (error instanceof ForbiddenException || error instanceof UnauthorizedException) {
|
||||
if (
|
||||
error instanceof ForbiddenException ||
|
||||
error instanceof UnauthorizedException
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
throw new UnauthorizedException();
|
||||
@@ -124,4 +127,3 @@ export class ClaimAccessGuard implements CanActivate {
|
||||
return type === "Bearer" ? token : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,13 @@ export class GlobalGuard implements CanActivate {
|
||||
|
||||
try {
|
||||
const payload = await this.jwtService.verifyAsync(token, {
|
||||
secret: `${process.env.SECRET}`,
|
||||
secret: `${process.env.JWT_SECRET}`,
|
||||
});
|
||||
|
||||
if (payload.role !== RoleEnum.USER && payload.role !== RoleEnum.FIELD_EXPERT) {
|
||||
if (
|
||||
payload.role !== RoleEnum.USER &&
|
||||
payload.role !== RoleEnum.FIELD_EXPERT
|
||||
) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export class SettingsJwtGuard implements CanActivate {
|
||||
|
||||
try {
|
||||
const payload = await this.jwtService.verifyAsync(token, {
|
||||
secret: `${process.env.SECRET}`,
|
||||
secret: `${process.env.JWT_SECRET}`,
|
||||
});
|
||||
(request as any).user = payload;
|
||||
(request as any).identity = payload;
|
||||
|
||||
@@ -39,9 +39,9 @@ async function bootstrap() {
|
||||
.split(":");
|
||||
const password = rest.join(":");
|
||||
|
||||
const expectedUser = configService.get<string>("SWAGGER_USER", "");
|
||||
const expectedUser = configService.get<string>("SWAGGER_USER_DEV", "");
|
||||
const expectedPassword = configService.get<string>(
|
||||
"SWAGGER_PASSWORD",
|
||||
"SWAGGER_PASSWORD_DEV",
|
||||
"",
|
||||
);
|
||||
|
||||
@@ -59,7 +59,7 @@ async function bootstrap() {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle("yara724-backend")
|
||||
.setVersion("1.0.0")
|
||||
.addServer(process.env.BASE_URL + "/api")
|
||||
.addServer(process.env.BASE_URL_DEV + "/api")
|
||||
.addServer("http://192.168.20.170:9001")
|
||||
.addServer("http://localhost:9001")
|
||||
.addBearerAuth()
|
||||
|
||||
@@ -182,9 +182,9 @@ export class SandHubService {
|
||||
|
||||
try {
|
||||
const response = await firstValueFrom(
|
||||
this.httpService.post(process.env.SANDHUB_URL_LOGIN, {
|
||||
email: process.env.SANDHUB_USERNAME,
|
||||
password: process.env.SANDHUB_PASSWORD,
|
||||
this.httpService.post(process.env.SANHUB_URL_LOGIN, {
|
||||
email: process.env.SANHUB_USERNAME,
|
||||
password: process.env.SANHUB_PASSWORD,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -476,7 +476,7 @@ export class SandHubService {
|
||||
rightTwoDigits: String(userDetail.plate.ir),
|
||||
nationalCode: userDetail.nationalCodeOfInsurer,
|
||||
};
|
||||
const base = process.env.SANDHUB_BASE_URL ?? "";
|
||||
const base = process.env.SANHUB_BASE_URL ?? "";
|
||||
const requestUrl = `${base}/block-inquiry-tejarat`;
|
||||
|
||||
let response: any;
|
||||
@@ -509,7 +509,7 @@ export class SandHubService {
|
||||
*/
|
||||
async getPersonalInquiry(nationalCode: string, birthDate: number | string) {
|
||||
try {
|
||||
const requestUrl = `${process.env.SANDHUB_BASE_URL}/personal-inquiry/tejarat-no`;
|
||||
const requestUrl = `${process.env.SANHUB_BASE_URL}/personal-inquiry/tejarat-no`;
|
||||
|
||||
const gregorianBirthdate = jalaliToGregorianDate(birthDate);
|
||||
if (!gregorianBirthdate) {
|
||||
@@ -549,7 +549,7 @@ export class SandHubService {
|
||||
nationalCode: string,
|
||||
driverLicenseNumber: string,
|
||||
) {
|
||||
const requestUrl = `${process.env.SANDHUB_BASE_URL}/driver-license-check`;
|
||||
const requestUrl = `${process.env.SANHUB_BASE_URL}/driver-license-check`;
|
||||
const requestPayload = {
|
||||
driverLicenseNumber,
|
||||
nationalCode,
|
||||
@@ -588,7 +588,7 @@ export class SandHubService {
|
||||
|
||||
async getCarOwnershipInfo(plate: any, nationalCode: string) {
|
||||
try {
|
||||
const requestUrl = `${process.env.SANDHUB_BASE_URL}/ownership`;
|
||||
const requestUrl = `${process.env.SANHUB_BASE_URL}/ownership`;
|
||||
const requestPayload = {
|
||||
Plk1: String(plate.leftDigits),
|
||||
Plk2: String(plate.centerAlphabet),
|
||||
@@ -624,7 +624,7 @@ export class SandHubService {
|
||||
|
||||
async getShebaValidation(nationalId: string, shebaId: string) {
|
||||
try {
|
||||
const requestUrl = `${process.env.SANDHUB_BASE_URL}/sheba/sheba-tejaratno`;
|
||||
const requestUrl = `${process.env.SANHUB_BASE_URL}/sheba/sheba-tejaratno`;
|
||||
const requestPayload = {
|
||||
AccountOwnerType: "1",
|
||||
NationalId: nationalId,
|
||||
|
||||
Reference in New Issue
Block a user