afteer enabling dealer on frontend db_password fetch from google sectrets resolved , secret fech db connection order enhanced
This commit is contained in:
parent
798688e4c2
commit
2282d29322
@ -1,2 +1,2 @@
|
|||||||
import{a as s}from"./index-BgkDE8Pi.js";import"./radix-vendor-CYvDqP9X.js";import"./charts-vendor-BVfwAPj-.js";import"./utils-vendor-DNMmNUQL.js";import"./ui-vendor-DyksGUTu.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-B_rK4TXr.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion};
|
import{a as s}from"./index-PI_IMErM.js";import"./radix-vendor-CYvDqP9X.js";import"./charts-vendor-BVfwAPj-.js";import"./utils-vendor-DNMmNUQL.js";import"./ui-vendor-DfwWW08H.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-B_rK4TXr.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion};
|
||||||
//# sourceMappingURL=conclusionApi-VENY18zj.js.map
|
//# sourceMappingURL=conclusionApi-D5monZ70.js.map
|
||||||
@ -1 +1 @@
|
|||||||
{"version":3,"file":"conclusionApi-VENY18zj.js","sources":["../../src/services/conclusionApi.ts"],"sourcesContent":["import apiClient from './authApi';\r\n\r\nexport interface ConclusionRemark {\r\n conclusionId: string;\r\n requestId: string;\r\n aiGeneratedRemark: string | null;\r\n aiModelUsed: string | null;\r\n aiConfidenceScore: number | null;\r\n finalRemark: string | null;\r\n editedBy: string | null;\r\n isEdited: boolean;\r\n editCount: number;\r\n approvalSummary: any;\r\n documentSummary: any;\r\n keyDiscussionPoints: string[];\r\n generatedAt: string | null;\r\n finalizedAt: string | null;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\n/**\r\n * Generate AI-powered conclusion remark\r\n */\r\nexport async function generateConclusion(requestId: string): Promise<{\r\n conclusionId: string;\r\n aiGeneratedRemark: string;\r\n keyDiscussionPoints: string[];\r\n confidence: number;\r\n generatedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/generate`);\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Update conclusion remark (edit by initiator)\r\n */\r\nexport async function updateConclusion(requestId: string, finalRemark: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.put(`/conclusions/${requestId}`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Finalize conclusion and close request\r\n */\r\nexport async function finalizeConclusion(requestId: string, finalRemark: string): Promise<{\r\n conclusionId: string;\r\n requestNumber: string;\r\n status: string;\r\n finalRemark: string;\r\n finalizedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/finalize`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Get conclusion for a request\r\n * Returns null if conclusion doesn't exist (404) instead of throwing error\r\n */\r\nexport async function getConclusion(requestId: string): Promise<ConclusionRemark | null> {\r\n try {\r\n const response = await apiClient.get(`/conclusions/${requestId}`);\r\n return response.data.data;\r\n } catch (error: any) {\r\n // Handle 404 gracefully - conclusion doesn't exist yet, which is normal\r\n if (error.response?.status === 404) {\r\n return null;\r\n }\r\n // Re-throw other errors\r\n throw error;\r\n }\r\n}\r\n\r\n"],"names":["generateConclusion","requestId","apiClient","finalizeConclusion","finalRemark","getConclusion","error","_a"],"mappings":"6RAwBA,eAAsBA,EAAmBC,EAMtC,CAED,OADiB,MAAMC,EAAU,KAAK,gBAAgBD,CAAS,WAAW,GAC1D,KAAK,IACvB,CAaA,eAAsBE,EAAmBF,EAAmBG,EAMzD,CAED,OADiB,MAAMF,EAAU,KAAK,gBAAgBD,CAAS,YAAa,CAAE,YAAAG,EAAa,GAC3E,KAAK,IACvB,CAMA,eAAsBC,EAAcJ,EAAqD,OACvF,GAAI,CAEF,OADiB,MAAMC,EAAU,IAAI,gBAAgBD,CAAS,EAAE,GAChD,KAAK,IACvB,OAASK,EAAY,CAEnB,KAAIC,EAAAD,EAAM,WAAN,YAAAC,EAAgB,UAAW,IAC7B,OAAO,KAGT,MAAMD,CACR,CACF"}
|
{"version":3,"file":"conclusionApi-D5monZ70.js","sources":["../../src/services/conclusionApi.ts"],"sourcesContent":["import apiClient from './authApi';\r\n\r\nexport interface ConclusionRemark {\r\n conclusionId: string;\r\n requestId: string;\r\n aiGeneratedRemark: string | null;\r\n aiModelUsed: string | null;\r\n aiConfidenceScore: number | null;\r\n finalRemark: string | null;\r\n editedBy: string | null;\r\n isEdited: boolean;\r\n editCount: number;\r\n approvalSummary: any;\r\n documentSummary: any;\r\n keyDiscussionPoints: string[];\r\n generatedAt: string | null;\r\n finalizedAt: string | null;\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\n/**\r\n * Generate AI-powered conclusion remark\r\n */\r\nexport async function generateConclusion(requestId: string): Promise<{\r\n conclusionId: string;\r\n aiGeneratedRemark: string;\r\n keyDiscussionPoints: string[];\r\n confidence: number;\r\n generatedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/generate`);\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Update conclusion remark (edit by initiator)\r\n */\r\nexport async function updateConclusion(requestId: string, finalRemark: string): Promise<ConclusionRemark> {\r\n const response = await apiClient.put(`/conclusions/${requestId}`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Finalize conclusion and close request\r\n */\r\nexport async function finalizeConclusion(requestId: string, finalRemark: string): Promise<{\r\n conclusionId: string;\r\n requestNumber: string;\r\n status: string;\r\n finalRemark: string;\r\n finalizedAt: string;\r\n}> {\r\n const response = await apiClient.post(`/conclusions/${requestId}/finalize`, { finalRemark });\r\n return response.data.data;\r\n}\r\n\r\n/**\r\n * Get conclusion for a request\r\n * Returns null if conclusion doesn't exist (404) instead of throwing error\r\n */\r\nexport async function getConclusion(requestId: string): Promise<ConclusionRemark | null> {\r\n try {\r\n const response = await apiClient.get(`/conclusions/${requestId}`);\r\n return response.data.data;\r\n } catch (error: any) {\r\n // Handle 404 gracefully - conclusion doesn't exist yet, which is normal\r\n if (error.response?.status === 404) {\r\n return null;\r\n }\r\n // Re-throw other errors\r\n throw error;\r\n }\r\n}\r\n\r\n"],"names":["generateConclusion","requestId","apiClient","finalizeConclusion","finalRemark","getConclusion","error","_a"],"mappings":"6RAwBA,eAAsBA,EAAmBC,EAMtC,CAED,OADiB,MAAMC,EAAU,KAAK,gBAAgBD,CAAS,WAAW,GAC1D,KAAK,IACvB,CAaA,eAAsBE,EAAmBF,EAAmBG,EAMzD,CAED,OADiB,MAAMF,EAAU,KAAK,gBAAgBD,CAAS,YAAa,CAAE,YAAAG,EAAa,GAC3E,KAAK,IACvB,CAMA,eAAsBC,EAAcJ,EAAqD,OACvF,GAAI,CAEF,OADiB,MAAMC,EAAU,IAAI,gBAAgBD,CAAS,EAAE,GAChD,KAAK,IACvB,OAASK,EAAY,CAEnB,KAAIC,EAAAD,EAAM,WAAN,YAAAC,EAAgB,UAAW,IAC7B,OAAO,KAGT,MAAMD,CACR,CACF"}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
67
build/assets/index-PI_IMErM.js
Normal file
67
build/assets/index-PI_IMErM.js
Normal file
File diff suppressed because one or more lines are too long
1
build/assets/index-PI_IMErM.js.map
Normal file
1
build/assets/index-PI_IMErM.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,31 +1,31 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/royal_enfield_logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/royal_enfield_logo.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description"
|
<meta name="description"
|
||||||
content="Royal Enfield Approval & Request Management Portal - Streamlined approval workflows for enterprise operations" />
|
content="Royal Enfield Approval & Request Management Portal - Streamlined approval workflows for enterprise operations" />
|
||||||
<meta name="theme-color" content="#2d4a3e" />
|
<meta name="theme-color" content="#2d4a3e" />
|
||||||
<title>Royal Enfield | Approval Portal</title>
|
<title>Royal Enfield | Approval Portal</title>
|
||||||
|
|
||||||
<!-- Preload critical fonts and icons -->
|
<!-- Preload critical fonts and icons -->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<script type="module" crossorigin src="/assets/index-BgkDE8Pi.js"></script>
|
<script type="module" crossorigin src="/assets/index-PI_IMErM.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/charts-vendor-BVfwAPj-.js">
|
<link rel="modulepreload" crossorigin href="/assets/charts-vendor-BVfwAPj-.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/radix-vendor-CYvDqP9X.js">
|
<link rel="modulepreload" crossorigin href="/assets/radix-vendor-CYvDqP9X.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/utils-vendor-DNMmNUQL.js">
|
<link rel="modulepreload" crossorigin href="/assets/utils-vendor-DNMmNUQL.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-DyksGUTu.js">
|
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-DfwWW08H.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/socket-vendor-TjCxX7sJ.js">
|
<link rel="modulepreload" crossorigin href="/assets/socket-vendor-TjCxX7sJ.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/redux-vendor-tbZCm13o.js">
|
<link rel="modulepreload" crossorigin href="/assets/redux-vendor-tbZCm13o.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/router-vendor-B_rK4TXr.js">
|
<link rel="modulepreload" crossorigin href="/assets/router-vendor-B_rK4TXr.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D5NCgjQR.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-D5NCgjQR.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -4,7 +4,7 @@
|
|||||||
"description": "Royal Enfield Workflow Management System - Backend API (TypeScript)",
|
"description": "Royal Enfield Workflow Management System - Backend API (TypeScript)",
|
||||||
"main": "dist/server.js",
|
"main": "dist/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run build && npm run start:prod && npm run setup",
|
"start": "npm run build && npm run setup && npm run start:prod",
|
||||||
"dev": "npm run setup && nodemon --exec ts-node -r tsconfig-paths/register src/server.ts",
|
"dev": "npm run setup && nodemon --exec ts-node -r tsconfig-paths/register src/server.ts",
|
||||||
"dev:no-setup": "nodemon --exec ts-node -r tsconfig-paths/register src/server.ts",
|
"dev:no-setup": "nodemon --exec ts-node -r tsconfig-paths/register src/server.ts",
|
||||||
"build": "tsc && tsc-alias",
|
"build": "tsc && tsc-alias",
|
||||||
@ -93,4 +93,4 @@
|
|||||||
"node": ">=22.0.0",
|
"node": ">=22.0.0",
|
||||||
"npm": ">=10.0.0"
|
"npm": ">=10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
11
src/app.ts
11
src/app.ts
@ -85,18 +85,17 @@ app.use(cookieParser());
|
|||||||
|
|
||||||
const userService = new UserService();
|
const userService = new UserService();
|
||||||
|
|
||||||
// Initialize database connection
|
// Initializer for database connection (called from server.ts)
|
||||||
const initializeDatabase = async () => {
|
export const initializeAppDatabase = async () => {
|
||||||
try {
|
try {
|
||||||
await sequelize.authenticate();
|
await sequelize.authenticate();
|
||||||
|
console.log('✅ App database connection established');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Database connection failed:', error);
|
console.error('❌ App database connection failed:', error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize database
|
|
||||||
initializeDatabase();
|
|
||||||
|
|
||||||
// Trust proxy - Enable this when behind a reverse proxy (nginx, load balancer, etc.)
|
// Trust proxy - Enable this when behind a reverse proxy (nginx, load balancer, etc.)
|
||||||
// This allows Express to read X-Forwarded-* headers correctly
|
// This allows Express to read X-Forwarded-* headers correctly
|
||||||
// Set to true in production, false in development
|
// Set to true in production, false in development
|
||||||
|
|||||||
@ -132,10 +132,13 @@ export class AuthController {
|
|||||||
|
|
||||||
// Set new access token in cookie if using cookie-based auth
|
// Set new access token in cookie if using cookie-based auth
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isUat = process.env.NODE_ENV === 'uat';
|
||||||
|
const isSecureEnv = isProduction || isUat;
|
||||||
|
|
||||||
const cookieOptions = {
|
const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProduction,
|
secure: isSecureEnv,
|
||||||
sameSite: isProduction ? 'lax' as const : 'lax' as const, // 'lax' is safer and works on same-domain
|
sameSite: isSecureEnv ? 'lax' as const : 'lax' as const, // 'lax' is safer and works on same-domain
|
||||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -206,10 +209,13 @@ export class AuthController {
|
|||||||
|
|
||||||
// Set tokens in httpOnly cookies (production) or return in body (development)
|
// Set tokens in httpOnly cookies (production) or return in body (development)
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isUat = process.env.NODE_ENV === 'uat';
|
||||||
|
const isSecureEnv = isProduction || isUat;
|
||||||
|
|
||||||
const cookieOptions = {
|
const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProduction,
|
secure: isSecureEnv,
|
||||||
sameSite: isProduction ? ('lax' as const) : ('lax' as const),
|
sameSite: isSecureEnv ? ('lax' as const) : ('lax' as const),
|
||||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||||
path: '/',
|
path: '/',
|
||||||
};
|
};
|
||||||
@ -256,10 +262,13 @@ export class AuthController {
|
|||||||
|
|
||||||
// Set new access token in cookie
|
// Set new access token in cookie
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isUat = process.env.NODE_ENV === 'uat';
|
||||||
|
const isSecureEnv = isProduction || isUat;
|
||||||
|
|
||||||
const cookieOptions = {
|
const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProduction,
|
secure: isSecureEnv,
|
||||||
sameSite: isProduction ? ('lax' as const) : ('lax' as const),
|
sameSite: isSecureEnv ? ('lax' as const) : ('lax' as const),
|
||||||
maxAge: 24 * 60 * 60 * 1000,
|
maxAge: 24 * 60 * 60 * 1000,
|
||||||
path: '/',
|
path: '/',
|
||||||
};
|
};
|
||||||
@ -293,13 +302,16 @@ export class AuthController {
|
|||||||
|
|
||||||
// Helper function to clear cookies with all possible option combinations
|
// Helper function to clear cookies with all possible option combinations
|
||||||
const clearCookiesCompletely = () => {
|
const clearCookiesCompletely = () => {
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isUat = process.env.NODE_ENV === 'uat';
|
||||||
|
const isSecureEnv = isProduction || isUat;
|
||||||
const cookieNames = ['accessToken', 'refreshToken'];
|
const cookieNames = ['accessToken', 'refreshToken'];
|
||||||
|
|
||||||
// Get the EXACT options used when setting cookies (from exchangeToken)
|
// Get the EXACT options used when setting cookies (from exchangeToken)
|
||||||
// These MUST match exactly: httpOnly, secure, sameSite, path
|
// These MUST match exactly: httpOnly, secure, sameSite, path
|
||||||
const cookieOptions = {
|
const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProduction,
|
secure: isSecureEnv,
|
||||||
sameSite: 'lax' as const,
|
sameSite: 'lax' as const,
|
||||||
path: '/',
|
path: '/',
|
||||||
};
|
};
|
||||||
@ -469,10 +481,13 @@ export class AuthController {
|
|||||||
|
|
||||||
// Set cookies for web clients
|
// Set cookies for web clients
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isUat = process.env.NODE_ENV === 'uat';
|
||||||
|
const isSecureEnv = isProduction || isUat;
|
||||||
|
|
||||||
const cookieOptions = {
|
const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProduction,
|
secure: isSecureEnv,
|
||||||
sameSite: isProduction ? 'lax' as const : 'lax' as const,
|
sameSite: isSecureEnv ? 'lax' as const : 'lax' as const,
|
||||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -549,10 +564,13 @@ export class AuthController {
|
|||||||
|
|
||||||
// Set cookies with httpOnly flag for security
|
// Set cookies with httpOnly flag for security
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isUat = process.env.NODE_ENV === 'uat';
|
||||||
|
const isSecureEnv = isProduction || isUat;
|
||||||
|
|
||||||
const cookieOptions = {
|
const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProduction,
|
secure: isSecureEnv,
|
||||||
sameSite: isProduction ? 'lax' as const : 'lax' as const, // 'lax' for same-domain
|
sameSite: isSecureEnv ? 'lax' as const : 'lax' as const, // 'lax' for same-domain
|
||||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours for access token
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours for access token
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,25 +10,42 @@
|
|||||||
* 5. Configs are auto-seeded by configSeed.service.ts on server start (30 configs)
|
* 5. Configs are auto-seeded by configSeed.service.ts on server start (30 configs)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
// 1. Initialize dotenv FIRST before any other internal imports
|
||||||
|
// This ensures that modules imported later (like GoogleSecretManagerService)
|
||||||
|
// can see the environment variables in their constructor/initialization
|
||||||
|
const envPath = path.resolve(process.cwd(), '.env');
|
||||||
|
if (fs.existsSync(envPath)) {
|
||||||
|
dotenv.config({ path: envPath });
|
||||||
|
} else {
|
||||||
|
const parentEnvPath = path.resolve(process.cwd(), '..', '.env');
|
||||||
|
if (fs.existsSync(parentEnvPath)) {
|
||||||
|
dotenv.config({ path: parentEnvPath });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Now import internal modules
|
||||||
import { Client } from 'pg';
|
import { Client } from 'pg';
|
||||||
import { QueryTypes } from 'sequelize';
|
import { QueryTypes } from 'sequelize';
|
||||||
import { initializeGoogleSecretManager } from '../services/googleSecretManager.service';
|
import { initializeGoogleSecretManager } from '../services/googleSecretManager.service';
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import dotenv from 'dotenv';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
|
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
// DB constants moved inside functions to ensure secrets are loaded first
|
|
||||||
const isSSL = (process.env.DB_SSL || '').trim() === 'true';
|
|
||||||
async function checkAndCreateDatabase(): Promise<boolean> {
|
async function checkAndCreateDatabase(): Promise<boolean> {
|
||||||
|
// DB constants moved inside functions to ensure secrets are loaded first
|
||||||
|
const isSSL = (process.env.DB_SSL || '').trim() === 'true';
|
||||||
const DB_HOST = process.env.DB_HOST || 'localhost';
|
const DB_HOST = process.env.DB_HOST || 'localhost';
|
||||||
const DB_PORT = parseInt(process.env.DB_PORT || '5432');
|
const DB_PORT = parseInt(process.env.DB_PORT || '5432');
|
||||||
const DB_USER = process.env.DB_USER || 'postgres';
|
const DB_USER = process.env.DB_USER || 'postgres';
|
||||||
const DB_PASSWORD = process.env.DB_PASSWORD || '';
|
const DB_PASSWORD = typeof process.env.DB_PASSWORD === 'string' ? process.env.DB_PASSWORD : '';
|
||||||
const DB_NAME = process.env.DB_NAME || 'royal_enfield_workflow';
|
const DB_NAME = process.env.DB_NAME || 're_workflow_db';
|
||||||
|
|
||||||
|
console.log(`[Setup Debug] DB_HOST: ${DB_HOST}, DB_USER: ${DB_USER}, SSL: ${isSSL}, HasPassword: ${!!DB_PASSWORD}`);
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
host: DB_HOST,
|
host: DB_HOST,
|
||||||
|
|||||||
@ -4,13 +4,10 @@ import path from 'path';
|
|||||||
|
|
||||||
// Load environment variables from .env file FIRST
|
// Load environment variables from .env file FIRST
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../.env') });
|
dotenv.config({ path: path.resolve(__dirname, '../.env') });
|
||||||
import { initializeGoogleSecretManager } from './services/googleSecretManager.service';
|
// Stop queue metrics collection on shutdown
|
||||||
import { seedDefaultActivityTypes } from './services/activityTypeSeed.service';
|
// Note: This is imported statically but doesn't trigger database/queue activity until called
|
||||||
import { stopQueueMetrics } from './utils/queueMetrics';
|
import { stopQueueMetrics } from './utils/queueMetrics';
|
||||||
|
|
||||||
// Dynamic imports will be used inside startServer to ensure secrets are loaded first
|
|
||||||
import { emailService } from './services/email.service';
|
|
||||||
|
|
||||||
const PORT: number = parseInt(process.env.PORT || '5000', 10);
|
const PORT: number = parseInt(process.env.PORT || '5000', 10);
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
@ -18,12 +15,16 @@ const startServer = async (): Promise<void> => {
|
|||||||
try {
|
try {
|
||||||
// Initialize Google Secret Manager before starting server
|
// Initialize Google Secret Manager before starting server
|
||||||
// This will merge secrets from GCS into process.env if enabled
|
// This will merge secrets from GCS into process.env if enabled
|
||||||
|
const { initializeGoogleSecretManager } = require('./services/googleSecretManager.service');
|
||||||
console.log('🔐 Initializing secrets...');
|
console.log('🔐 Initializing secrets...');
|
||||||
await initializeGoogleSecretManager();
|
await initializeGoogleSecretManager();
|
||||||
|
|
||||||
// Dynamically import everything else after secrets are loaded
|
const { default: app, initializeAppDatabase } = require('./app');
|
||||||
const app = require('./app').default;
|
|
||||||
const { initSocket } = require('./realtime/socket');
|
const { initSocket } = require('./realtime/socket');
|
||||||
|
|
||||||
|
// Initialize database connection explicitly after secrets are loaded
|
||||||
|
await initializeAppDatabase();
|
||||||
|
|
||||||
require('./queues/tatWorker'); // Initialize TAT worker
|
require('./queues/tatWorker'); // Initialize TAT worker
|
||||||
const { logTatConfig } = require('./config/tat.config');
|
const { logTatConfig } = require('./config/tat.config');
|
||||||
const { logSystemConfig } = require('./config/system.config');
|
const { logSystemConfig } = require('./config/system.config');
|
||||||
@ -34,22 +35,12 @@ const startServer = async (): Promise<void> => {
|
|||||||
const { initializeQueueMetrics } = require('./utils/queueMetrics');
|
const { initializeQueueMetrics } = require('./utils/queueMetrics');
|
||||||
const { emailService } = require('./services/email.service');
|
const { emailService } = require('./services/email.service');
|
||||||
|
|
||||||
// Re-initialize email service after secrets are loaded (in case SMTP credentials were loaded)
|
// Initialize email service after secrets are loaded
|
||||||
// This ensures the email service uses production SMTP if credentials are available
|
|
||||||
try {
|
try {
|
||||||
await emailService.initialize();
|
await emailService.initialize();
|
||||||
console.log('📧 Email service re-initialized after secrets loaded');
|
console.log('📧 Email service initialized');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('⚠️ Email service re-initialization warning (will use test account if SMTP not configured):', error);
|
console.warn('⚠️ Email service initialization warning (will use test account if SMTP not configured):', error);
|
||||||
}
|
|
||||||
|
|
||||||
// Re-initialize email service after secrets are loaded (in case SMTP credentials were loaded)
|
|
||||||
// This ensures the email service uses production SMTP if credentials are available
|
|
||||||
try {
|
|
||||||
await emailService.initialize();
|
|
||||||
console.log('📧 Email service re-initialized after secrets loaded');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Email service re-initialization warning (will use test account if SMTP not configured):', error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
@ -63,6 +54,7 @@ const startServer = async (): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Seed default activity types if table is empty
|
// Seed default activity types if table is empty
|
||||||
|
const { seedDefaultActivityTypes } = require('./services/activityTypeSeed.service');
|
||||||
try {
|
try {
|
||||||
await seedDefaultActivityTypes();
|
await seedDefaultActivityTypes();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -264,10 +264,6 @@ export class EmailService {
|
|||||||
// Singleton instance
|
// Singleton instance
|
||||||
export const emailService = new EmailService();
|
export const emailService = new EmailService();
|
||||||
|
|
||||||
// Initialize on import (will use test account if SMTP not configured)
|
// Note: initialize() is now called explicitly in server.ts
|
||||||
// Note: If secrets are loaded later, the service will re-initialize automatically
|
// to ensure it happens after secrets are loaded.
|
||||||
// when sendEmail is called (if SMTP credentials become available)
|
|
||||||
emailService.initialize().catch(error => {
|
|
||||||
logger.error('Failed to initialize email service:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,7 @@ class GoogleSecretManagerService {
|
|||||||
private secretPrefix: string;
|
private secretPrefix: string;
|
||||||
private secretMap: Record<string, string> = {};
|
private secretMap: Record<string, string> = {};
|
||||||
private isInitialized: boolean = false;
|
private isInitialized: boolean = false;
|
||||||
|
private isLoaded: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.projectId = process.env.GCP_PROJECT_ID || '';
|
this.projectId = process.env.GCP_PROJECT_ID || '';
|
||||||
@ -206,6 +207,15 @@ class GoogleSecretManagerService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isLoaded && !secretNames) {
|
||||||
|
logger.debug('[Secret Manager] ℹ️ Secrets already loaded in this process, skipping.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.projectId) {
|
||||||
|
this.projectId = process.env.GCP_PROJECT_ID || '';
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.projectId) {
|
if (!this.projectId) {
|
||||||
logger.warn('[Secret Manager] GCP_PROJECT_ID not set, skipping Google Secret Manager');
|
logger.warn('[Secret Manager] GCP_PROJECT_ID not set, skipping Google Secret Manager');
|
||||||
return;
|
return;
|
||||||
@ -237,33 +247,40 @@ class GoogleSecretManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load each secret
|
// Load each secret
|
||||||
for (const secretName of secretsToLoad) {
|
const uatSecrets = ['OKTA_CLIENT_ID', 'OKTA_CLIENT_SECRET', 'OKTA_API_TOKEN', 'OKTA_DOMAIN', 'DB_PASSWORD'];
|
||||||
const fullSecretName = this.secretPrefix
|
const isUat = process.env.NODE_ENV === 'uat';
|
||||||
? `${this.secretPrefix}-${secretName}`
|
|
||||||
: secretName;
|
|
||||||
|
|
||||||
// Log OKTA and EMAIL secret attempts in detail
|
for (const secretName of secretsToLoad) {
|
||||||
const isOktaSecret = /^OKTA_/i.test(secretName);
|
// Handle UAT-specific secret names if in UAT environment
|
||||||
const isEmailSecret = /^EMAIL_|^SMTP_/i.test(secretName);
|
let secretNameToFetch = secretName;
|
||||||
if (isOktaSecret || isEmailSecret) {
|
if (isUat && uatSecrets.includes(secretName)) {
|
||||||
logger.info(`[Secret Manager] Attempting to load: ${secretName} (full name: ${fullSecretName})`);
|
secretNameToFetch = `${secretName}_UAT`;
|
||||||
|
logger.info(`[Secret Manager] UAT mode: Fetching source ${secretNameToFetch} for ${secretName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretValue = await this.getSecret(secretName);
|
const fullSecretName = this.secretPrefix
|
||||||
|
? `${this.secretPrefix}-${secretNameToFetch}`
|
||||||
|
: secretNameToFetch;
|
||||||
|
|
||||||
|
const rawValue = await this.getSecret(secretNameToFetch);
|
||||||
|
const secretValue = rawValue ? rawValue.trim() : null;
|
||||||
|
|
||||||
if (secretValue !== null) {
|
if (secretValue !== null) {
|
||||||
const envVarName = this.getEnvVarName(secretName);
|
const envVarName = this.getEnvVarName(secretName);
|
||||||
loadedSecrets[envVarName] = secretValue;
|
loadedSecrets[envVarName] = secretValue;
|
||||||
loadedCount++;
|
loadedCount++;
|
||||||
if (isOktaSecret || isEmailSecret) {
|
|
||||||
logger.info(`[Secret Manager] ✅ Successfully loaded: ${secretName} -> ${envVarName}`);
|
// Print masked value for verification as requested by user
|
||||||
}
|
// Note: Trailing/leading whitespace is now trimmed to prevent auth errors
|
||||||
|
const maskedValue = secretValue.length > 8
|
||||||
|
? `${secretValue.substring(0, 3)}...${secretValue.substring(secretValue.length - 3)}`
|
||||||
|
: '***';
|
||||||
|
|
||||||
|
logger.info(`[Secret Manager] ✅ Loaded: ${secretNameToFetch} -> process.env.${envVarName} (Value: ${maskedValue})`);
|
||||||
} else {
|
} else {
|
||||||
// Track which secrets weren't found for better logging
|
// Track which secrets weren't found for better logging
|
||||||
notFoundSecrets.push(fullSecretName);
|
notFoundSecrets.push(fullSecretName);
|
||||||
if (isOktaSecret || isEmailSecret) {
|
logger.warn(`[Secret Manager] ❌ Not found: ${secretNameToFetch} (searched as: ${fullSecretName})`);
|
||||||
logger.warn(`[Secret Manager] ❌ Not found: ${secretName} (searched as: ${fullSecretName})`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,18 +288,18 @@ class GoogleSecretManagerService {
|
|||||||
// Log when overriding existing env vars vs setting new ones
|
// Log when overriding existing env vars vs setting new ones
|
||||||
for (const [envVar, value] of Object.entries(loadedSecrets)) {
|
for (const [envVar, value] of Object.entries(loadedSecrets)) {
|
||||||
const existingValue = process.env[envVar];
|
const existingValue = process.env[envVar];
|
||||||
const isOverriding = existingValue !== undefined;
|
const isAlreadySet = existingValue !== undefined && existingValue !== '';
|
||||||
|
|
||||||
|
if (isAlreadySet) {
|
||||||
|
logger.debug(`[Secret Manager] ℹ️ Skipping ${envVar}: Already set in local environment`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
process.env[envVar] = value;
|
process.env[envVar] = value;
|
||||||
|
logger.debug(`[Secret Manager] ✨ Set new env var: ${envVar} (from Secret Manager)`);
|
||||||
// Log override behavior for debugging
|
|
||||||
if (isOverriding) {
|
|
||||||
logger.debug(`[Secret Manager] 🔄 Overrode existing env var: ${envVar} (was: ${existingValue ? 'set' : 'undefined'}, now: from Secret Manager)`);
|
|
||||||
} else {
|
|
||||||
logger.debug(`[Secret Manager] ✨ Set new env var: ${envVar} (from Secret Manager)`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isLoaded = true;
|
||||||
logger.info(`[Secret Manager] ✅ Successfully loaded ${loadedCount}/${secretsToLoad.length} secrets`);
|
logger.info(`[Secret Manager] ✅ Successfully loaded ${loadedCount}/${secretsToLoad.length} secrets`);
|
||||||
|
|
||||||
if (loadedCount > 0) {
|
if (loadedCount > 0) {
|
||||||
@ -345,9 +362,10 @@ class GoogleSecretManagerService {
|
|||||||
'SESSION_SECRET',
|
'SESSION_SECRET',
|
||||||
|
|
||||||
// Okta/SSO
|
// Okta/SSO
|
||||||
//'OKTA_CLIENT_ID',
|
'OKTA_CLIENT_ID',
|
||||||
//'OKTA_CLIENT_SECRET',
|
'OKTA_CLIENT_SECRET',
|
||||||
//'OKTA_API_TOKEN',
|
'OKTA_API_TOKEN',
|
||||||
|
'OKTA_DOMAIN',
|
||||||
|
|
||||||
// Email
|
// Email
|
||||||
'SMTP_HOST',
|
'SMTP_HOST',
|
||||||
|
|||||||
@ -26,28 +26,28 @@ const maskSensitiveData = (value: any): any => {
|
|||||||
// Mask patterns like "API_KEY = abc123" or "password: secret"
|
// Mask patterns like "API_KEY = abc123" or "password: secret"
|
||||||
let masked = value.replace(SENSITIVE_PATTERN, (match, key, val) => {
|
let masked = value.replace(SENSITIVE_PATTERN, (match, key, val) => {
|
||||||
if (val && val.length > 0) {
|
if (val && val.length > 0) {
|
||||||
const maskedVal = val.length > 4
|
const maskedVal = val.length > 4
|
||||||
? val.substring(0, 2) + '***' + val.substring(val.length - 2)
|
? val.substring(0, 2) + '***' + val.substring(val.length - 2)
|
||||||
: '***';
|
: '***';
|
||||||
return `${key}=${maskedVal}`;
|
return `${key}=${maskedVal}`;
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mask standalone tokens/keys (long alphanumeric strings that look like secrets)
|
// Mask standalone tokens/keys (long alphanumeric strings that look like secrets)
|
||||||
// e.g., "sk-abc123xyz789..." or "ghp_xxxx..."
|
// e.g., "sk-abc123xyz789..." or "ghp_xxxx..."
|
||||||
masked = masked.replace(
|
masked = masked.replace(
|
||||||
/\b(sk-|ghp_|gho_|github_pat_|xox[baprs]-|Bearer\s+)([A-Za-z0-9_-]{20,})/gi,
|
/\b(sk-|ghp_|gho_|github_pat_|xox[baprs]-|Bearer\s+)([A-Za-z0-9_-]{20,})/gi,
|
||||||
(match, prefix, token) => `${prefix}${'*'.repeat(8)}...`
|
(match, prefix, token) => `${prefix}${'*'.repeat(8)}...`
|
||||||
);
|
);
|
||||||
|
|
||||||
return masked;
|
return masked;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return value.map(maskSensitiveData);
|
return value.map(maskSensitiveData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value && typeof value === 'object') {
|
if (value && typeof value === 'object') {
|
||||||
const masked: any = {};
|
const masked: any = {};
|
||||||
for (const [k, v] of Object.entries(value)) {
|
for (const [k, v] of Object.entries(value)) {
|
||||||
@ -61,7 +61,7 @@ const maskSensitiveData = (value: any): any => {
|
|||||||
}
|
}
|
||||||
return masked;
|
return masked;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ const transports: winston.transport[] = [
|
|||||||
if (process.env.LOKI_HOST) {
|
if (process.env.LOKI_HOST) {
|
||||||
try {
|
try {
|
||||||
const LokiTransport = require('winston-loki');
|
const LokiTransport = require('winston-loki');
|
||||||
|
|
||||||
const lokiTransportOptions: any = {
|
const lokiTransportOptions: any = {
|
||||||
host: process.env.LOKI_HOST,
|
host: process.env.LOKI_HOST,
|
||||||
labels: appMeta,
|
labels: appMeta,
|
||||||
@ -124,22 +124,21 @@ if (process.env.LOKI_HOST) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ CONSOLE TRANSPORT (Development) ============
|
// ============ CONSOLE TRANSPORT ============
|
||||||
if (!isProduction) {
|
// Enabled for all environments to ensure visibility in terminal/container logs
|
||||||
transports.push(
|
transports.push(
|
||||||
new winston.transports.Console({
|
new winston.transports.Console({
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
winston.format.colorize(),
|
winston.format.colorize({ all: !isProduction }),
|
||||||
winston.format.printf(({ level, message, timestamp, ...meta }) => {
|
winston.format.printf(({ level, message, timestamp, ...meta }) => {
|
||||||
const metaStr = Object.keys(meta).length && !meta.service
|
const metaStr = Object.keys(meta).length && !meta.service
|
||||||
? ` ${JSON.stringify(meta)}`
|
? ` ${JSON.stringify(meta)}`
|
||||||
: '';
|
: '';
|
||||||
return `${timestamp} [${level}]: ${message}${metaStr}`;
|
return `${timestamp} [${level}]: ${message}${metaStr}`;
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// ============ ERROR SANITIZER ============
|
// ============ ERROR SANITIZER ============
|
||||||
/**
|
/**
|
||||||
@ -183,25 +182,25 @@ const sanitizeFormat = winston.format((info) => {
|
|||||||
if (info.error && typeof info.error === 'object') {
|
if (info.error && typeof info.error === 'object') {
|
||||||
info.error = sanitizeError(info.error);
|
info.error = sanitizeError(info.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If message is an error object, sanitize it
|
// If message is an error object, sanitize it
|
||||||
if (info.message && typeof info.message === 'object' && (info.message as any).stack) {
|
if (info.message && typeof info.message === 'object' && (info.message as any).stack) {
|
||||||
info.error = sanitizeError(info.message);
|
info.error = sanitizeError(info.message);
|
||||||
info.message = (info.message as Error).message;
|
info.message = (info.message as Error).message;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mask sensitive data in message
|
// Mask sensitive data in message
|
||||||
if (typeof info.message === 'string') {
|
if (typeof info.message === 'string') {
|
||||||
info.message = maskSensitiveData(info.message);
|
info.message = maskSensitiveData(info.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mask sensitive data in all metadata
|
// Mask sensitive data in all metadata
|
||||||
for (const key of Object.keys(info)) {
|
for (const key of Object.keys(info)) {
|
||||||
if (key !== 'level' && key !== 'timestamp' && key !== 'service') {
|
if (key !== 'level' && key !== 'timestamp' && key !== 'service') {
|
||||||
info[key] = maskSensitiveData(info[key]);
|
info[key] = maskSensitiveData(info[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -254,7 +253,7 @@ export const logWithContext = (
|
|||||||
if (sanitizedContext.error) {
|
if (sanitizedContext.error) {
|
||||||
sanitizedContext.error = sanitizeError(sanitizedContext.error);
|
sanitizedContext.error = sanitizeError(sanitizedContext.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(level, message, sanitizedContext);
|
logger.log(level, message, sanitizedContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -343,13 +342,13 @@ export const logAuthEvent = (
|
|||||||
} = {}
|
} = {}
|
||||||
) => {
|
) => {
|
||||||
const level = event === 'auth_failure' ? 'warn' : 'info';
|
const level = event === 'auth_failure' ? 'warn' : 'info';
|
||||||
|
|
||||||
// Sanitize error if present
|
// Sanitize error if present
|
||||||
const sanitizedDetails = { ...details };
|
const sanitizedDetails = { ...details };
|
||||||
if (sanitizedDetails.error) {
|
if (sanitizedDetails.error) {
|
||||||
sanitizedDetails.error = sanitizeError(sanitizedDetails.error);
|
sanitizedDetails.error = sanitizeError(sanitizedDetails.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(level, `Auth ${event}${userId ? `: ${userId}` : ''}`, {
|
logger.log(level, `Auth ${event}${userId ? `: ${userId}` : ''}`, {
|
||||||
authEvent: event,
|
authEvent: event,
|
||||||
userId,
|
userId,
|
||||||
@ -394,13 +393,13 @@ export const logNotificationEvent = (
|
|||||||
} = {}
|
} = {}
|
||||||
) => {
|
) => {
|
||||||
const level = event === 'failed' ? 'error' : 'info';
|
const level = event === 'failed' ? 'error' : 'info';
|
||||||
|
|
||||||
// Sanitize error if present
|
// Sanitize error if present
|
||||||
const sanitizedDetails = { ...details };
|
const sanitizedDetails = { ...details };
|
||||||
if (sanitizedDetails.error) {
|
if (sanitizedDetails.error) {
|
||||||
sanitizedDetails.error = sanitizeError(sanitizedDetails.error);
|
sanitizedDetails.error = sanitizeError(sanitizedDetails.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(level, `Notification ${event}`, {
|
logger.log(level, `Notification ${event}`, {
|
||||||
notificationEvent: event,
|
notificationEvent: event,
|
||||||
...sanitizedDetails,
|
...sanitizedDetails,
|
||||||
@ -422,13 +421,13 @@ export const logAIEvent = (
|
|||||||
} = {}
|
} = {}
|
||||||
) => {
|
) => {
|
||||||
const level = event === 'error' ? 'error' : 'info';
|
const level = event === 'error' ? 'error' : 'info';
|
||||||
|
|
||||||
// Sanitize error if present
|
// Sanitize error if present
|
||||||
const sanitizedDetails = { ...details };
|
const sanitizedDetails = { ...details };
|
||||||
if (sanitizedDetails.error) {
|
if (sanitizedDetails.error) {
|
||||||
sanitizedDetails.error = sanitizeError(sanitizedDetails.error);
|
sanitizedDetails.error = sanitizeError(sanitizedDetails.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(level, `AI ${event}`, {
|
logger.log(level, `AI ${event}`, {
|
||||||
aiEvent: event,
|
aiEvent: event,
|
||||||
...sanitizedDetails,
|
...sanitizedDetails,
|
||||||
|
|||||||
@ -2,7 +2,9 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2021",
|
"target": "ES2021",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"lib": ["ES2021"],
|
"lib": [
|
||||||
|
"ES2021"
|
||||||
|
],
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
@ -21,50 +23,57 @@
|
|||||||
"noUnusedParameters": false,
|
"noUnusedParameters": false,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"types": ["node", "jest"],
|
"types": [
|
||||||
"typeRoots": ["./node_modules/@types", "./src/types"],
|
"node",
|
||||||
|
"jest"
|
||||||
|
],
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types",
|
||||||
|
"./src/types"
|
||||||
|
],
|
||||||
"baseUrl": "./src",
|
"baseUrl": "./src",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"],
|
"@/*": [
|
||||||
"@controllers/*": ["./controllers/*"],
|
"./*"
|
||||||
"@middlewares/*": ["./middlewares/*"],
|
],
|
||||||
"@services/*": ["./services/*"],
|
"@controllers/*": [
|
||||||
"@models/*": ["./models/*"],
|
"./controllers/*"
|
||||||
"@routes/*": ["./routes/*"],
|
],
|
||||||
"@validators/*": ["./validators/*"],
|
"@middlewares/*": [
|
||||||
"@utils/*": ["./utils/*"],
|
"./middlewares/*"
|
||||||
"@types/*": ["./types/*"],
|
],
|
||||||
"@config/*": ["./config/*"]
|
"@services/*": [
|
||||||
|
"./services/*"
|
||||||
|
],
|
||||||
|
"@models/*": [
|
||||||
|
"./models/*"
|
||||||
|
],
|
||||||
|
"@routes/*": [
|
||||||
|
"./routes/*"
|
||||||
|
],
|
||||||
|
"@validators/*": [
|
||||||
|
"./validators/*"
|
||||||
|
],
|
||||||
|
"@utils/*": [
|
||||||
|
"./utils/*"
|
||||||
|
],
|
||||||
|
"@types/*": [
|
||||||
|
"./types/*"
|
||||||
|
],
|
||||||
|
"@config/*": [
|
||||||
|
"./config/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/app.ts",
|
"src/**/*"
|
||||||
"src/server.ts",
|
|
||||||
"src/routes/index.ts",
|
|
||||||
"src/routes/auth.routes.ts",
|
|
||||||
"src/controllers/auth.controller.ts",
|
|
||||||
"src/services/auth.service.ts",
|
|
||||||
"src/middlewares/auth.middleware.ts",
|
|
||||||
"src/middlewares/cors.middleware.ts",
|
|
||||||
"src/middlewares/validate.middleware.ts",
|
|
||||||
"src/middlewares/errorHandler.middleware.ts",
|
|
||||||
"src/utils/logger.ts",
|
|
||||||
"src/utils/responseHandler.ts",
|
|
||||||
"src/config/**/*",
|
|
||||||
"src/types/**/*",
|
|
||||||
"src/validators/auth.validator.ts",
|
|
||||||
"src/models/**/*"
|
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"dist",
|
"dist",
|
||||||
"tests",
|
"tests",
|
||||||
"**/*.test.ts",
|
"**/*.test.ts",
|
||||||
"**/*.spec.ts",
|
"**/*.spec.ts",
|
||||||
"src/routes/workflow.routes.ts",
|
"src/services/enhancedTemplate.service.ts"
|
||||||
"src/controllers/workflow.controller.ts",
|
|
||||||
"src/controllers/approval.controller.ts",
|
|
||||||
"src/services/workflow.service.ts",
|
|
||||||
"src/services/approval.service.ts"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user