diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 00000000..c8489dfa --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,4 @@ +{ + "*": "prettier --ignore-unknown --write", + "src/**/*.ts": "eslint --fix" +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..fd5fabf2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist +node_modules +coverage +build \ No newline at end of file diff --git a/package.json b/package.json index dfcbf62c..fe8fb1a9 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,7 @@ "test": "echo \"Error: no test specified\" && exit 1", "start": "npx tsc && NODE_ENV=production node dist/server.js", "dev": "NODE_ENV=development ts-node-dev --respawn ./src/server.ts", - "prettify": "prettier --write .", - "lint:fix": "eslint src/**/*.ts", - "precommit": "npm run prettify && npm run lint:fix" + "precommit": "lint-staged --concurrent false" }, "type": "commonjs", "repository": { @@ -47,6 +45,7 @@ "eslint-config-airbnb": "^19.0.0", "eslint-config-prettier": "^8.3.0", "husky": "^4.3.8", + "lint-staged": "^15.2.2", "nodemon": "^3.0.2", "prettier": "^2.4.1", "ts-node": "^10.9.2", diff --git a/src/constants/index.ts b/src/constants/index.ts index 88c5a274..720bae8e 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -23,9 +23,11 @@ export const HTTP_CODES = { }; export const HTTP_TEXTS = { UNAUTHORIZED: "You're unauthorized to access this resource.", + S3_ERROR: "Something went wrong while handing the file", INTERNAL_ERROR: "Internal server error, please try again later.", SOMETHING_WENT_WRONG: "Something went wrong while processing your request, please try again.", + CS_ERROR: "Contentstack API error", NO_CS_USER: "No user found with the credentials", SUCCESS_LOGIN: "Login Successful.", TOKEN_ERROR: "Error occurred during token generation.", @@ -35,6 +37,7 @@ export const HTTP_TEXTS = { NO_PROJECT: "resource not found with the given ID(s).", MIGRATION_CREATED: "Project's migration created successfully.", MIGRATION_UPDATED: "Project's migration updated successfully.", + AFFIX_UPDATED: "Project's Affix updated successfully", CMS_UPDATED: "Project's migration cms updated successfully", FILE_FORMAT_UPDATED: "Project's migration file format updated successfully", DESTINATION_STACK_UPDATED: @@ -68,6 +71,7 @@ export const VALIDATION_ERRORS = { STRING_REQUIRED: "Provided $ should be a string.", INVALID_REGION: "Provided region doesn't exists.", FIELD_REQUIRED: "Field '$' is required.", + INVALID_AFFIX: "Invalid affix format", }; export const POPULATE_CONTENT_MAPPER = "content_mapper"; export const POPULATE_FIELD_MAPPING = "fieldMapping"; @@ -76,3 +80,4 @@ export const CONTENT_TYPE_POPULATE_FIELDS = export const PROJECT_UNSELECTED_FIELDS = "-content_mapper -legacy_cms -destination_stack_id -execution_log"; export const EXCLUDE_CONTENT_MAPPER = "-content_mapper -execution_log"; +export const AFFIX_REGEX = /^[a-zA-Z][a-zA-Z0-9]{1,4}$/; diff --git a/src/controllers/projects.controller.ts b/src/controllers/projects.controller.ts index 3d1e8888..9831e42e 100644 --- a/src/controllers/projects.controller.ts +++ b/src/controllers/projects.controller.ts @@ -31,6 +31,11 @@ const updateLegacyCMS = async (req: Request, res: Response) => { res.status(resp.status).json(resp.data); }; +const updateAffix = async (req: Request, res: Response) => { + const resp = await projectService.updateAffix(req); + res.status(resp.status).json(resp.data); +}; + const updateFileFormat = async (req: Request, res: Response) => { const resp = await projectService.updateFileFormat(req); res.status(resp.status).json(resp.data); @@ -53,6 +58,7 @@ export const projectController = { createProject, updateProject, updateLegacyCMS, + updateAffix, updateFileFormat, updateDestinationStack, deleteProject, diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts index 24dec902..07bfd36a 100644 --- a/src/controllers/user.controller.ts +++ b/src/controllers/user.controller.ts @@ -1,10 +1,9 @@ import { Request, Response } from "express"; import { userService } from "../services/user.service"; -import { HTTP_CODES } from "../constants"; const getUserProfile = async (req: Request, res: Response) => { - const user = await userService.getUserProfile(req); - res.status(HTTP_CODES.OK).json(user); + const resp = await userService.getUserProfile(req); + res.status(resp?.status).json(resp?.data); }; export const userController = { diff --git a/src/database.ts b/src/database.ts index c4efd3aa..17731577 100644 --- a/src/database.ts +++ b/src/database.ts @@ -4,7 +4,6 @@ import { config } from "./config"; import logger from "./utils/logger"; import ProjectModel from "./models/project"; import AuthenticationModel from "./models/authentication"; -import AuditLogModel from "./models/auditLog"; import ContentTypesMapperModel from "./models/contentTypesMapper"; import FieldMapperModel from "./models/FieldMapper"; @@ -19,7 +18,6 @@ const connectToDatabase = async () => { // Create the collection's if it doesn't exist await ProjectModel.init(); await AuthenticationModel.init(); - await AuditLogModel.init(); await ContentTypesMapperModel.init(); await FieldMapperModel.init(); } catch (error) { diff --git a/src/models/auditLog.ts b/src/models/auditLog.ts deleted file mode 100644 index fa59aca3..00000000 --- a/src/models/auditLog.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { MODULES, MODULES_ACTIONS } from "../constants"; -import { Schema, model, Document } from "mongoose"; - -interface Action { - date: Date; - user_id: string; - user_first_name: string; - user_last_name: string; - module: string; - action: string; -} - -interface AuditLogDocument extends Document { - project_id: Schema.Types.ObjectId; - actions: Action[]; -} - -const auditLogSchema = new Schema({ - project_id: { - type: Schema.Types.ObjectId, - ref: "Project", - }, - actions: { - type: [ - { - date: { type: Date, required: true }, - user_id: { type: String, required: true }, - user_first_name: { type: String, required: true }, - user_last_name: { type: String, required: true }, - module: { type: String, required: true, enum: MODULES }, - action: { - type: String, - required: true, - enum: MODULES_ACTIONS, - }, - }, - ], - required: true, - }, -}); - -const AuditLogModel = model("AuditLog", auditLogSchema); - -export default AuditLogModel; diff --git a/src/models/project.ts b/src/models/project.ts index 037ab4a2..08079f80 100644 --- a/src/models/project.ts +++ b/src/models/project.ts @@ -3,6 +3,7 @@ import { CS_REGIONS } from "../constants"; interface LegacyCMS { cms: string; + affix: string; file_format: string; import_data: string; } @@ -44,6 +45,7 @@ const projectSchema = new Schema( destination_stack_id: { type: String }, legacy_cms: { cms: { type: String }, + affix: { type: String }, file_format: { type: String }, import_data: { type: String }, }, diff --git a/src/models/types.ts b/src/models/types.ts index e5270b81..cf6ccb6e 100644 --- a/src/models/types.ts +++ b/src/models/types.ts @@ -3,15 +3,6 @@ export interface User { password: string; } -export interface UserProfile { - user: { - email: string; - first_name: string; - last_name: string; - orgs: [{ org_id: string; org_name: string }]; - }; -} - export interface AppTokenPayload { region: string; user_id: string; diff --git a/src/routes/projects.routes.ts b/src/routes/projects.routes.ts index fced1779..cc24d945 100644 --- a/src/routes/projects.routes.ts +++ b/src/routes/projects.routes.ts @@ -25,6 +25,13 @@ router.put( asyncRouter(projectController.updateLegacyCMS) ); +// Update project's Affix +router.put( + "/:projectId/affix", + validator("affix"), + asyncRouter(projectController.updateAffix) +); + // Update project's file format router.put( "/:projectId/file-format", diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 0c1e9c5d..d2ca2065 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,6 +1,6 @@ import { Request } from "express"; import { config } from "../config"; -import { safePromise } from "../utils/index"; +import { safePromise, getLogMessage } from "../utils"; import https from "../utils/https.utils"; import { LoginServiceType, AppTokenPayload } from "../models/types"; import { HTTP_CODES, HTTP_TEXTS } from "../constants"; @@ -8,77 +8,95 @@ import { generateToken } from "../utils/jwt.utils"; import { BadRequestError, InternalServerError, + ExceptionFunction, } from "../utils/custom-errors.utils"; import AuthenticationModel from "../models/authentication"; +import logger from "../utils/logger"; const login = async (req: Request): Promise => { - const userData = req?.body; - - const [err, res] = await safePromise( - https({ - method: "POST", - url: `${config.CS_API[ - userData?.region as keyof typeof config.CS_API - ]!}/user-session`, - headers: { - "Content-Type": "application/json", - }, - data: { - user: { - email: userData?.email, - password: userData?.password, - ...(userData?.tfa_token && { tfa_token: userData?.tfa_token }), + const srcFun = "Login"; + + try { + const userData = req?.body; + + const [err, res] = await safePromise( + https({ + method: "POST", + url: `${config.CS_API[ + userData?.region as keyof typeof config.CS_API + ]!}/user-session`, + headers: { + "Content-Type": "application/json", }, - }, - }) - ); + data: { + user: { + email: userData?.email, + password: userData?.password, + ...(userData?.tfa_token && { tfa_token: userData?.tfa_token }), + }, + }, + }) + ); - if (err) - return { - data: err?.response?.data, - status: err?.response?.status, + if (err) { + logger.error( + getLogMessage(srcFun, HTTP_TEXTS.CS_ERROR, {}, err?.response?.data) + ); + + return { + data: err?.response?.data, + status: err?.response?.status, + }; + } + + if (res?.status === HTTP_CODES.SUPPORT_DOC) + return { + data: res?.data, + status: res?.status, + }; + + if (!res?.data?.user) throw new BadRequestError(HTTP_TEXTS.NO_CS_USER); + + const appTokenPayload: AppTokenPayload = { + region: userData?.region, + user_id: res?.data?.user.uid, }; - if (res?.status === HTTP_CODES.SUPPORT_DOC) + // Saving auth info in the DB + await AuthenticationModel.findOneAndUpdate( + appTokenPayload, + { + authtoken: res?.data.user?.authtoken, + }, + { + upsert: true, + } + ); + + // JWT token generation + const app_token = generateToken(appTokenPayload); + return { - data: res?.data, - status: res?.status, + data: { + message: HTTP_TEXTS.SUCCESS_LOGIN, + app_token, + }, + status: HTTP_CODES.OK, }; - - if (!res?.data?.user) throw new BadRequestError(HTTP_TEXTS.NO_CS_USER); - - const appTokenPayload: AppTokenPayload = { - region: userData?.region, - user_id: res?.data?.user.uid, - }; - - // Saving auth info in the DB - await AuthenticationModel.findOneAndUpdate( - appTokenPayload, - { - authtoken: res?.data.user?.authtoken, - }, - { - upsert: true, - } - ); - - // JWT token generation - const app_token = generateToken(appTokenPayload); - - return { - data: { - message: HTTP_TEXTS.SUCCESS_LOGIN, - app_token, - }, - status: HTTP_CODES.OK, - }; + } catch (error: any) { + logger.error(getLogMessage(srcFun, "Error while logging in", {}, error)); + throw new ExceptionFunction( + error?.message || HTTP_TEXTS.INTERNAL_ERROR, + error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR + ); + } }; const requestSms = async (req: Request): Promise => { - const userData = req?.body; + const srcFun = "requestSms"; try { + const userData = req?.body; const [err, res] = await safePromise( https({ method: "POST", @@ -94,17 +112,24 @@ const requestSms = async (req: Request): Promise => { }) ); - if (err) + if (err) { + logger.error( + getLogMessage(srcFun, HTTP_TEXTS.CS_ERROR, {}, err?.response?.data) + ); + return { - data: err.response.data, - status: err.response.status, + data: err?.response?.data, + status: err?.response?.status, }; + } return { data: res.data, status: res.status, }; - } catch (error) { + } catch (error: any) { + logger.error(getLogMessage(srcFun, "Error while in requestSms", {}, error)); + throw new InternalServerError(HTTP_TEXTS.INTERNAL_ERROR); } }; diff --git a/src/services/org.service.ts b/src/services/org.service.ts index 7c4e8149..62bdc881 100644 --- a/src/services/org.service.ts +++ b/src/services/org.service.ts @@ -1,121 +1,200 @@ import { Request } from "express"; import { config } from "../config"; -import { safePromise } from "../utils/index"; +import { safePromise, getLogMessage } from "../utils/index"; import https from "../utils/https.utils"; import { LoginServiceType } from "../models/types"; import getAuthtoken from "../utils/auth.utils"; +import logger from "../utils/logger"; +import { HTTP_TEXTS, HTTP_CODES } from "../constants"; +import { ExceptionFunction } from "../utils/custom-errors.utils"; const getAllStacks = async (req: Request): Promise => { + const srcFun = "getAllStacks"; const orgId = req?.params?.orgId; const { token_payload } = req.body; - const authtoken = await getAuthtoken( - token_payload?.region, - token_payload?.user_id - ); - - const [err, res] = await safePromise( - https({ - method: "GET", - url: `${config.CS_API[ - token_payload?.region as keyof typeof config.CS_API - ]!}/stacks`, - headers: { - organization_uid: orgId, - authtoken, - }, - }) - ); + try { + const authtoken = await getAuthtoken( + token_payload?.region, + token_payload?.user_id + ); + + const [err, res] = await safePromise( + https({ + method: "GET", + url: `${config.CS_API[ + token_payload?.region as keyof typeof config.CS_API + ]!}/stacks`, + headers: { + organization_uid: orgId, + authtoken, + }, + }) + ); + + if (err) { + logger.error( + getLogMessage( + srcFun, + HTTP_TEXTS.CS_ERROR, + token_payload, + err.response.data + ) + ); + + return { + data: err.response.data, + status: err.response.status, + }; + } - if (err) return { - data: err.response.data, - status: err.response.status, + data: { + stacks: + res.data.stacks?.map((stack: any) => ({ + name: stack.name, + api_key: stack.api_key, + })) || [], + }, + status: res.status, }; - - return { - data: { - stacks: - res.data.stacks?.map((stack: any) => ({ - name: stack.name, - api_key: stack.api_key, - })) || [], - }, - status: res.status, - }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while getting all stacks", + token_payload, + error + ) + ); + + throw new ExceptionFunction( + error?.message || HTTP_TEXTS.INTERNAL_ERROR, + error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR + ); + } }; const createStack = async (req: Request): Promise => { + const srcFun = "createStack"; const orgId = req?.params?.orgId; const { token_payload, name, description, master_locale } = req.body; - const authtoken = await getAuthtoken( - token_payload?.region, - token_payload?.user_id - ); - - const [err, res] = await safePromise( - https({ - method: "POST", - url: `${config.CS_API[ - token_payload?.region as keyof typeof config.CS_API - ]!}/stacks`, - headers: { - organization_uid: orgId, - authtoken, - }, - data: { - stack: { - name, - description, - master_locale, + try { + const authtoken = await getAuthtoken( + token_payload?.region, + token_payload?.user_id + ); + + const [err, res] = await safePromise( + https({ + method: "POST", + url: `${config.CS_API[ + token_payload?.region as keyof typeof config.CS_API + ]!}/stacks`, + headers: { + organization_uid: orgId, + authtoken, }, - }, - }) - ); + data: { + stack: { + name, + description, + master_locale, + }, + }, + }) + ); + + if (err) { + logger.error( + getLogMessage( + srcFun, + HTTP_TEXTS.CS_ERROR, + token_payload, + err.response.data + ) + ); + + return { + data: err.response.data, + status: err.response.status, + }; + } - if (err) return { - data: err.response.data, - status: err.response.status, + data: res.data, + status: res.status, }; - - return { - data: res.data, - status: res.status, - }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while creating a stack", + token_payload, + error + ) + ); + + throw new ExceptionFunction( + error?.message || HTTP_TEXTS.INTERNAL_ERROR, + error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR + ); + } }; const getLocales = async (req: Request): Promise => { + const srcFun = "getLocales"; const { token_payload } = req.body; - const authtoken = await getAuthtoken( - token_payload?.region, - token_payload?.user_id - ); - - const [err, res] = await safePromise( - https({ - method: "GET", - url: `${config.CS_API[ - token_payload?.region as keyof typeof config.CS_API - ]!}/locales?include_all=true`, - headers: { - authtoken, - }, - }) - ); + try { + const authtoken = await getAuthtoken( + token_payload?.region, + token_payload?.user_id + ); + + const [err, res] = await safePromise( + https({ + method: "GET", + url: `${config.CS_API[ + token_payload?.region as keyof typeof config.CS_API + ]!}/locales?include_all=true`, + headers: { + authtoken, + }, + }) + ); + + if (err) { + logger.error( + getLogMessage( + srcFun, + HTTP_TEXTS.CS_ERROR, + token_payload, + err.response.data + ) + ); + + return { + data: err.response.data, + status: err.response.status, + }; + } - if (err) return { - data: err.response.data, - status: err.response.status, + data: res.data, + status: res.status, }; - - return { - data: res.data, - status: res.status, - }; + } catch (error: any) { + logger.error( + getLogMessage(srcFun, "Error while getting locales", token_payload, error) + ); + + throw new ExceptionFunction( + error?.message || HTTP_TEXTS.INTERNAL_ERROR, + error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR + ); + } }; export const orgService = { diff --git a/src/services/projects.service.ts b/src/services/projects.service.ts index 525ab13d..659f342a 100644 --- a/src/services/projects.service.ts +++ b/src/services/projects.service.ts @@ -177,6 +177,33 @@ const updateLegacyCMS = async (req: Request) => { }; }; +const updateAffix = async (req: Request) => { + const { orgId, projectId } = req.params; + const { token_payload, affix } = req.body; + + const project = await getProjectUtil( + projectId, + { + _id: projectId, + org_id: orgId, + region: token_payload?.region, + owner: token_payload?.user_id, + }, + EXCLUDE_CONTENT_MAPPER + ); + + project.legacy_cms.affix = affix; + + await project.save(); + + return { + status: HTTP_CODES.OK, + data: { + message: HTTP_TEXTS.AFFIX_UPDATED, + }, + }; +}; + const updateFileFormat = async (req: Request) => { const { orgId, projectId } = req.params; const { token_payload, file_format } = req.body; @@ -276,6 +303,7 @@ export const projectService = { createProject, updateProject, updateLegacyCMS, + updateAffix, updateFileFormat, updateDestinationStack, deleteProject, diff --git a/src/services/projects.uploads.service.ts b/src/services/projects.uploads.service.ts index afce0f20..0c7dc43e 100644 --- a/src/services/projects.uploads.service.ts +++ b/src/services/projects.uploads.service.ts @@ -2,88 +2,142 @@ import { Request } from "express"; import { EXCLUDE_CONTENT_MAPPER, HTTP_TEXTS, HTTP_CODES } from "../constants"; import { initialize, preSignedUrls, finalize } from "../utils/s3-uploads.utils"; import getProjectUtil from "../utils/get-project.utils"; +import { getLogMessage } from "../utils/index"; +import { S3Error } from "../utils/custom-errors.utils"; +import logger from "../utils/logger"; const initializeUpload = async (req: Request) => { + const srcFun = "initializeUpload"; const orgId = req?.params?.orgId; const projectId = req?.params?.projectId; const fileName = req.body.file_name; - const { user_id = "", region = "" } = req.body.token_payload; - - // Find the project based on both orgId and projectId, region, owner - await getProjectUtil( - projectId, - { - _id: projectId, - org_id: orgId, - region: region, - owner: user_id, - }, - EXCLUDE_CONTENT_MAPPER - ); - - const result = await initialize(region, orgId, user_id, projectId, fileName); - - return { - status: HTTP_CODES.OK, - data: { - file_id: result.UploadId, - file_key: result.Key, - }, - }; + const { token_payload } = req.body; + const { user_id = "", region = "" } = token_payload; + + try { + // Find the project based on both orgId and projectId, region, owner + await getProjectUtil( + projectId, + { + _id: projectId, + org_id: orgId, + region: region, + owner: user_id, + }, + EXCLUDE_CONTENT_MAPPER + ); + + const result = await initialize( + region, + orgId, + user_id, + projectId, + fileName + ); + + return { + status: HTTP_CODES.OK, + data: { + file_id: result.UploadId, + file_key: result.Key, + }, + }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while initializing upload", + token_payload, + error + ) + ); + + throw new S3Error(); + } }; const getPreSignedUrls = async (req: Request) => { + const srcFun = "getPreSignedUrls"; const orgId = req?.params?.orgId; const projectId = req?.params?.projectId; const { file_key, file_id, parts } = req.body; - const { user_id = "", region = "" } = req.body.token_payload; - - // Find the project based on both orgId and projectId, region, owner - await getProjectUtil( - projectId, - { - _id: projectId, - org_id: orgId, - region: region, - owner: user_id, - }, - EXCLUDE_CONTENT_MAPPER - ); - - const result = await preSignedUrls(file_key, file_id, parts); - - return { - status: HTTP_CODES.OK, - data: { parts: result }, - }; + const { token_payload } = req.body; + const { user_id = "", region = "" } = token_payload; + + try { + // Find the project based on both orgId and projectId, region, owner + await getProjectUtil( + projectId, + { + _id: projectId, + org_id: orgId, + region: region, + owner: user_id, + }, + EXCLUDE_CONTENT_MAPPER + ); + + const result = await preSignedUrls(file_key, file_id, parts); + + return { + status: HTTP_CODES.OK, + data: { parts: result }, + }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while getting presined URLs", + token_payload, + error + ) + ); + + throw new S3Error(); + } }; const finalizeUpload = async (req: Request) => { + const srcFun = "finalizeUpload"; const orgId = req?.params?.orgId; const projectId = req?.params?.projectId; const { file_key, file_id, parts } = req.body; - const { user_id = "", region = "" } = req.body.token_payload; - - // Find the project based on both orgId and projectId, region, owner - await getProjectUtil( - projectId, - { - _id: projectId, - org_id: orgId, - region: region, - owner: user_id, - }, - EXCLUDE_CONTENT_MAPPER - ); - - await finalize(file_key, file_id, parts); - - return { - status: HTTP_CODES.OK, - data: { - message: HTTP_TEXTS.UPLOAD_SUCCESS, - }, - }; + const { token_payload } = req.body; + const { user_id = "", region = "" } = token_payload; + + try { + // Find the project based on both orgId and projectId, region, owner + await getProjectUtil( + projectId, + { + _id: projectId, + org_id: orgId, + region: region, + owner: user_id, + }, + EXCLUDE_CONTENT_MAPPER + ); + + await finalize(file_key, file_id, parts); + + return { + status: HTTP_CODES.OK, + data: { + message: HTTP_TEXTS.UPLOAD_SUCCESS, + }, + }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while finalizing upload", + token_payload, + error + ) + ); + + throw new S3Error(); + } }; export const uploadsService = { diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 12a2aa86..c547890f 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -1,46 +1,88 @@ import { Request } from "express"; import { config } from "../config"; import https from "../utils/https.utils"; -import { AppTokenPayload, UserProfile } from "../models/types"; -import { HTTP_TEXTS } from "../constants"; -import { BadRequestError } from "../utils/custom-errors.utils"; +import { AppTokenPayload, LoginServiceType } from "../models/types"; +import { HTTP_CODES, HTTP_TEXTS } from "../constants"; +import { + BadRequestError, + ExceptionFunction, +} from "../utils/custom-errors.utils"; import AuthenticationModel from "../models/authentication"; +import { safePromise, getLogMessage } from "../utils"; +import logger from "../utils/logger"; -const getUserProfile = async (req: Request): Promise => { +const getUserProfile = async (req: Request): Promise => { + const srcFun = "getUserProfile"; const appTokenPayload: AppTokenPayload = req?.body?.token_payload; - const user = await AuthenticationModel.findOne({ - user_id: appTokenPayload?.user_id, - region: appTokenPayload?.region, - }).lean(); - - if (!user?.authtoken) throw new BadRequestError(HTTP_TEXTS.NO_CS_USER); - - const res = await https({ - method: "GET", - url: `${config.CS_API[ - appTokenPayload?.region as keyof typeof config.CS_API - ]!}/user?include_orgs_roles=true`, - headers: { - "Content-Type": "application/json", - authtoken: user?.authtoken, - }, - }); - - if (!res?.data?.user) throw new BadRequestError(HTTP_TEXTS.NO_CS_USER); - - const orgs = (res?.data?.user?.organizations || []) - ?.filter((org: any) => org?.org_roles?.some((item: any) => item.admin)) - ?.map(({ uid, name }: any) => ({ org_id: uid, org_name: name })); - - return { - user: { - email: res?.data?.user?.email, - first_name: res?.data?.user?.first_name, - last_name: res?.data?.user?.last_name, - orgs: orgs, - }, - }; + try { + const user = await AuthenticationModel.findOne({ + user_id: appTokenPayload?.user_id, + region: appTokenPayload?.region, + }).lean(); + + if (!user?.authtoken) throw new BadRequestError(HTTP_TEXTS.NO_CS_USER); + + const [err, res] = await safePromise( + https({ + method: "GET", + url: `${config.CS_API[ + appTokenPayload?.region as keyof typeof config.CS_API + ]!}/user?include_orgs_roles=true`, + headers: { + "Content-Type": "application/json", + authtoken: user?.authtoken, + }, + }) + ); + + if (err) { + logger.error( + getLogMessage( + srcFun, + HTTP_TEXTS.CS_ERROR, + appTokenPayload, + err.response.data + ) + ); + + return { + data: err.response.data, + status: err.response.status, + }; + } + + if (!res?.data?.user) throw new BadRequestError(HTTP_TEXTS.NO_CS_USER); + + const orgs = (res?.data?.user?.organizations || []) + ?.filter((org: any) => org?.org_roles?.some((item: any) => item.admin)) + ?.map(({ uid, name }: any) => ({ org_id: uid, org_name: name })); + + return { + data: { + user: { + email: res?.data?.user?.email, + first_name: res?.data?.user?.first_name, + last_name: res?.data?.user?.last_name, + orgs: orgs, + }, + }, + status: res.status, + }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while getting user profile", + appTokenPayload, + error + ) + ); + throw new ExceptionFunction( + error?.message || HTTP_TEXTS.INTERNAL_ERROR, + error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR + ); + } }; export const userService = { diff --git a/src/utils/custom-errors.utils.ts b/src/utils/custom-errors.utils.ts index 3ccb7f65..b6ff34a6 100644 --- a/src/utils/custom-errors.utils.ts +++ b/src/utils/custom-errors.utils.ts @@ -43,6 +43,12 @@ export class UnauthorizedError extends AppError { } } +export class S3Error extends AppError { + constructor(message: string = HTTP_TEXTS.S3_ERROR) { + super(HTTP_CODES.SERVER_ERROR, message); + } +} + export class ExceptionFunction extends AppError { constructor(message: string, httpStatus: number) { super(httpStatus, message); diff --git a/src/utils/get-project.utils.ts b/src/utils/get-project.utils.ts index 0302c180..cda181f7 100644 --- a/src/utils/get-project.utils.ts +++ b/src/utils/get-project.utils.ts @@ -1,28 +1,32 @@ import ProjectModel from "../models/project"; -import { - BadRequestError, - NotFoundError, - DatabaseError, -} from "../utils/custom-errors.utils"; +import { BadRequestError, NotFoundError } from "../utils/custom-errors.utils"; import { HTTP_TEXTS } from "../constants"; import { MigrationQueryType } from "../models/types"; -import { isValidObjectId } from "../utils"; +import { isValidObjectId, getLogMessage } from "../utils"; +import logger from "../utils/logger"; export default async ( projectId: string, query: MigrationQueryType, projections: string = "" ) => { - try { - if (!isValidObjectId(projectId)) - throw new BadRequestError(HTTP_TEXTS.INVALID_ID.replace("$", "project")); + if (!isValidObjectId(projectId)) + throw new BadRequestError(HTTP_TEXTS.INVALID_ID.replace("$", "project")); + try { const project = await ProjectModel.findOne(query).select(projections); if (!project) throw new NotFoundError(HTTP_TEXTS.NO_PROJECT); return project; } catch (err) { - throw new DatabaseError(HTTP_TEXTS.SOMETHING_WENT_WRONG); + logger.error( + getLogMessage( + "get-project.utils", + `${HTTP_TEXTS.PROJECT_NOT_FOUND} projectId: ${projectId}`, + query + ) + ); + throw err; } }; diff --git a/src/validators/affix.validator.ts b/src/validators/affix.validator.ts new file mode 100644 index 00000000..0174a348 --- /dev/null +++ b/src/validators/affix.validator.ts @@ -0,0 +1,18 @@ +import { checkSchema } from "express-validator"; +import { VALIDATION_ERRORS, AFFIX_REGEX } from "../constants"; + +export default checkSchema({ + affix: { + in: "body", + isString: { + errorMessage: VALIDATION_ERRORS.STRING_REQUIRED.replace("$", "affix"), + bail: true, + }, + trim: true, + matches: { + options: AFFIX_REGEX, + errorMessage: VALIDATION_ERRORS.INVALID_AFFIX, + bail: true, + }, + }, +}); diff --git a/src/validators/index.ts b/src/validators/index.ts index 39951d22..89dc5efa 100644 --- a/src/validators/index.ts +++ b/src/validators/index.ts @@ -8,6 +8,7 @@ import fileFormatValidator from "./file-format.validator"; import destinationStackValidator from "./destination-stack.validator"; import initializeUploadValidator from "./initialize-upload.validator"; import uploadValidator from "./upload.validator"; +import affixValidator from "./affix.validator"; export default (route: string = "") => asyncRouter(async (req: Request, res: Response, next: NextFunction) => { @@ -19,6 +20,7 @@ export default (route: string = "") => destination_stack: destinationStackValidator, initialize_upload: initializeUploadValidator, file_upload: uploadValidator, + affix: affixValidator, }; const validator = appValidators[route as keyof typeof appValidators];