secret credentials mpped and template preview modified
This commit is contained in:
parent
f69814ce98
commit
4cf7288857
@ -1,16 +1,20 @@
|
||||
import { SSOConfig, SSOUserData } from '../types/auth.types';
|
||||
|
||||
// Use getter functions to read from process.env dynamically
|
||||
// This ensures values are read after secrets are loaded from Google Secret Manager
|
||||
const ssoConfig: SSOConfig = {
|
||||
jwtSecret: process.env.JWT_SECRET || '',
|
||||
jwtExpiry: process.env.JWT_EXPIRY || '24h',
|
||||
refreshTokenExpiry: process.env.REFRESH_TOKEN_EXPIRY || '7d',
|
||||
sessionSecret: process.env.SESSION_SECRET || '',
|
||||
get jwtSecret() { return process.env.JWT_SECRET || ''; },
|
||||
get jwtExpiry() { return process.env.JWT_EXPIRY || '24h'; },
|
||||
get refreshTokenExpiry() { return process.env.REFRESH_TOKEN_EXPIRY || '7d'; },
|
||||
get sessionSecret() { return process.env.SESSION_SECRET || ''; },
|
||||
// Use only FRONTEND_URL from environment - no fallbacks
|
||||
allowedOrigins: process.env.FRONTEND_URL?.split(',').map(s => s.trim()).filter(Boolean) || [],
|
||||
get allowedOrigins() {
|
||||
return process.env.FRONTEND_URL?.split(',').map(s => s.trim()).filter(Boolean) || [];
|
||||
},
|
||||
// Okta/Auth0 configuration for token exchange
|
||||
oktaDomain: process.env.OKTA_DOMAIN || 'https://dev-830839.oktapreview.com',
|
||||
oktaClientId: process.env.OKTA_CLIENT_ID || '',
|
||||
oktaClientSecret: process.env.OKTA_CLIENT_SECRET || '',
|
||||
get oktaDomain() { return process.env.OKTA_DOMAIN || 'https://dev-830839.oktapreview.com'; },
|
||||
get oktaClientId() { return process.env.OKTA_CLIENT_ID || ''; },
|
||||
get oktaClientSecret() { return process.env.OKTA_CLIENT_SECRET || ''; },
|
||||
};
|
||||
|
||||
export { ssoConfig };
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { ApprovalConfirmationData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getNextStepsSection, wrapRichText, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getNextStepsSection, wrapRichText, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getApprovalConfirmationEmail(data: ApprovalConfirmationData): string {
|
||||
@ -31,7 +31,7 @@ export function getApprovalConfirmationEmail(data: ApprovalConfirmationData): st
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Request Approved',
|
||||
...HeaderStyles.success
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { ApprovalRequestData } from './types';
|
||||
import { getEmailFooter, getPrioritySection, getEmailHeader, HeaderStyles, getResponsiveStyles, wrapRichText } from './helpers';
|
||||
import { getEmailFooter, getPrioritySection, getEmailHeader, HeaderStyles, getResponsiveStyles, wrapRichText, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getApprovalRequestEmail(data: ApprovalRequestData): string {
|
||||
@ -22,7 +22,7 @@ export function getApprovalRequestEmail(data: ApprovalRequestData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
<!-- Header -->
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Approval Request',
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { ApproverSkippedData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getApproverSkippedEmail(data: ApproverSkippedData): string {
|
||||
@ -22,7 +22,7 @@ export function getApproverSkippedEmail(data: ApproverSkippedData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Approval Level Skipped',
|
||||
...HeaderStyles.infoSecondary
|
||||
|
||||
@ -144,6 +144,15 @@ export function wrapRichText(htmlContent: string): string {
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get inline styles for email container table
|
||||
* This ensures width is preserved when emails are forwarded
|
||||
* Email clients often strip CSS classes, so inline styles are critical
|
||||
*/
|
||||
export function getEmailContainerStyles(): string {
|
||||
return 'width: 95%; max-width: 1200px; min-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all email styles (responsive + rich text)
|
||||
* Desktop-first design (optimized for browser) with mobile responsive breakpoints
|
||||
@ -175,8 +184,22 @@ export function getResponsiveStyles(): string {
|
||||
|
||||
/* Desktop-first base styles */
|
||||
.email-container {
|
||||
width: 95%;
|
||||
max-width: 1200px;
|
||||
width: 95% !important;
|
||||
max-width: 1200px !important;
|
||||
min-width: 600px !important; /* Prevent shrinking below 600px when forwarded */
|
||||
}
|
||||
|
||||
/* Force full width for forwarded emails - use inline styles in templates */
|
||||
table.email-container {
|
||||
width: 95% !important;
|
||||
max-width: 1200px !important;
|
||||
min-width: 600px !important;
|
||||
}
|
||||
|
||||
/* Wrapper table to force full width even when forwarded */
|
||||
.email-wrapper {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.email-content {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { MultiApproverRequestData } from './types';
|
||||
import { getEmailFooter, getPrioritySection, getApprovalChain, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getPrioritySection, getApprovalChain, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getMultiApproverRequestEmail(data: MultiApproverRequestData): string {
|
||||
@ -22,7 +22,7 @@ export function getMultiApproverRequestEmail(data: MultiApproverRequestData): st
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
<!-- Header -->
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Multi-Level Approval Request',
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { ParticipantAddedData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getPermissionsContent, getRoleDescription, wrapRichText, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getPermissionsContent, getRoleDescription, wrapRichText, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getParticipantAddedEmail(data: ParticipantAddedData): string {
|
||||
@ -22,7 +22,7 @@ export function getParticipantAddedEmail(data: ParticipantAddedData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: `You've Been Added as ${data.participantRole}`,
|
||||
...HeaderStyles.info
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { RejectionNotificationData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getRejectionNotificationEmail(data: RejectionNotificationData): string {
|
||||
@ -22,7 +22,7 @@ export function getRejectionNotificationEmail(data: RejectionNotificationData):
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Request Rejected',
|
||||
...HeaderStyles.error
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { RequestClosedData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getConclusionSection, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getConclusionSection, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getRequestClosedEmail(data: RequestClosedData): string {
|
||||
@ -22,7 +22,7 @@ export function getRequestClosedEmail(data: RequestClosedData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Request Closed',
|
||||
...HeaderStyles.complete
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { RequestCreatedData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getResponsiveStyles, wrapRichText } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getResponsiveStyles, wrapRichText, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getRequestCreatedEmail(data: RequestCreatedData): string {
|
||||
@ -22,7 +22,7 @@ export function getRequestCreatedEmail(data: RequestCreatedData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
<!-- Header -->
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Request Created Successfully',
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { TATBreachedData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getTATBreachedEmail(data: TATBreachedData): string {
|
||||
@ -22,7 +22,7 @@ export function getTATBreachedEmail(data: TATBreachedData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'TAT Breached',
|
||||
subtitle: 'Immediate Action Required',
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { TATReminderData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
/**
|
||||
@ -52,7 +52,7 @@ export function getTATReminderEmail(data: TATReminderData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'TAT Reminder',
|
||||
subtitle: `${data.thresholdPercentage}% Elapsed - ${urgencyStyle.title}`,
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { WorkflowPausedData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, wrapRichText, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getWorkflowPausedEmail(data: WorkflowPausedData): string {
|
||||
@ -22,7 +22,7 @@ export function getWorkflowPausedEmail(data: WorkflowPausedData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Workflow Paused',
|
||||
...HeaderStyles.neutral
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { WorkflowResumedData } from './types';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getActionRequiredSection, getResponsiveStyles } from './helpers';
|
||||
import { getEmailFooter, getEmailHeader, HeaderStyles, getActionRequiredSection, getResponsiveStyles, getEmailContainerStyles } from './helpers';
|
||||
import { getBrandedHeader } from './branding.config';
|
||||
|
||||
export function getWorkflowResumedEmail(data: WorkflowResumedData): string {
|
||||
@ -22,7 +22,7 @@ export function getWorkflowResumedEmail(data: WorkflowResumedData): string {
|
||||
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td style="padding: 40px 0;">
|
||||
<table role="presentation" class="email-container" style="margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" cellpadding="0" cellspacing="0">
|
||||
<table role="presentation" class="email-container" style="${getEmailContainerStyles()}" cellpadding="0" cellspacing="0">
|
||||
${getEmailHeader(getBrandedHeader({
|
||||
title: 'Workflow Resumed',
|
||||
...HeaderStyles.success
|
||||
|
||||
@ -139,15 +139,24 @@ class GoogleSecretManagerService {
|
||||
logger.debug(`[Secret Manager] Secret ${fullSecretName} exists but payload is empty`);
|
||||
return null;
|
||||
} catch (error: any) {
|
||||
const isOktaSecret = /OKTA_/i.test(secretName);
|
||||
const logLevel = isOktaSecret ? logger.info.bind(logger) : logger.debug.bind(logger);
|
||||
|
||||
// Handle "not found" errors (code 5 = NOT_FOUND)
|
||||
if (error.code === 5 || error.code === 'NOT_FOUND' || error.message?.includes('not found')) {
|
||||
logger.debug(`[Secret Manager] Secret not found: ${fullSecretName} (project: ${this.projectId})`);
|
||||
logLevel(`[Secret Manager] Secret not found: ${fullSecretName} (project: ${this.projectId})`);
|
||||
if (isOktaSecret) {
|
||||
logger.info(`[Secret Manager] Searched path: projects/${this.projectId}/secrets/${fullSecretName}/versions/latest`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Handle permission errors (code 7 = PERMISSION_DENIED)
|
||||
if (error.code === 7 || error.code === 'PERMISSION_DENIED' || error.message?.includes('Permission denied')) {
|
||||
logger.warn(`[Secret Manager] ❌ Permission denied for secret '${fullSecretName}'`);
|
||||
if (isOktaSecret) {
|
||||
logger.warn(`[Secret Manager] This is an OKTA secret - check service account permissions`);
|
||||
}
|
||||
logger.warn(`[Secret Manager] Service account needs 'Secret Manager Secret Accessor' role`);
|
||||
logger.warn(`[Secret Manager] To grant access, run:`);
|
||||
logger.warn(`[Secret Manager] gcloud secrets add-iam-policy-binding ${fullSecretName} \\`);
|
||||
@ -157,11 +166,12 @@ class GoogleSecretManagerService {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Log full error details for debugging
|
||||
logger.warn(`[Secret Manager] Failed to fetch secret '${fullSecretName}' (project: ${this.projectId})`);
|
||||
logger.warn(`[Secret Manager] Error code: ${error.code || 'unknown'}, Message: ${error.message || 'no message'}`);
|
||||
// Log full error details for debugging (info level for OKTA secrets)
|
||||
const errorLogLevel = isOktaSecret ? logger.warn.bind(logger) : logger.warn.bind(logger);
|
||||
errorLogLevel(`[Secret Manager] Failed to fetch secret '${fullSecretName}' (project: ${this.projectId})`);
|
||||
errorLogLevel(`[Secret Manager] Error code: ${error.code || 'unknown'}, Message: ${error.message || 'no message'}`);
|
||||
if (error.details) {
|
||||
logger.warn(`[Secret Manager] Error details: ${JSON.stringify(error.details)}`);
|
||||
errorLogLevel(`[Secret Manager] Error details: ${JSON.stringify(error.details)}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -216,26 +226,61 @@ class GoogleSecretManagerService {
|
||||
const notFoundSecrets: string[] = [];
|
||||
let loadedCount = 0;
|
||||
|
||||
// Log OKTA and EMAIL secrets specifically if they're in the list
|
||||
const oktaSecrets = secretsToLoad.filter(name => /^OKTA_/i.test(name));
|
||||
const emailSecrets = secretsToLoad.filter(name => /^EMAIL_|^SMTP_/i.test(name));
|
||||
if (oktaSecrets.length > 0) {
|
||||
logger.info(`[Secret Manager] 🔍 Attempting to load OKTA secrets: ${oktaSecrets.join(', ')}`);
|
||||
}
|
||||
if (emailSecrets.length > 0) {
|
||||
logger.info(`[Secret Manager] 📧 Attempting to load EMAIL secrets: ${emailSecrets.join(', ')}`);
|
||||
}
|
||||
|
||||
// Load each secret
|
||||
for (const secretName of secretsToLoad) {
|
||||
const fullSecretName = this.secretPrefix
|
||||
? `${this.secretPrefix}-${secretName}`
|
||||
: secretName;
|
||||
|
||||
// Log OKTA and EMAIL secret attempts in detail
|
||||
const isOktaSecret = /^OKTA_/i.test(secretName);
|
||||
const isEmailSecret = /^EMAIL_|^SMTP_/i.test(secretName);
|
||||
if (isOktaSecret || isEmailSecret) {
|
||||
logger.info(`[Secret Manager] Attempting to load: ${secretName} (full name: ${fullSecretName})`);
|
||||
}
|
||||
|
||||
const secretValue = await this.getSecret(secretName);
|
||||
|
||||
if (secretValue !== null) {
|
||||
const envVarName = this.getEnvVarName(secretName);
|
||||
loadedSecrets[envVarName] = secretValue;
|
||||
loadedCount++;
|
||||
if (isOktaSecret || isEmailSecret) {
|
||||
logger.info(`[Secret Manager] ✅ Successfully loaded: ${secretName} -> ${envVarName}`);
|
||||
}
|
||||
} else {
|
||||
// Track which secrets weren't found for better logging
|
||||
const fullSecretName = this.secretPrefix
|
||||
? `${this.secretPrefix}-${secretName}`
|
||||
: secretName;
|
||||
notFoundSecrets.push(fullSecretName);
|
||||
if (isOktaSecret || isEmailSecret) {
|
||||
logger.warn(`[Secret Manager] ❌ Not found: ${secretName} (searched as: ${fullSecretName})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge secrets into process.env (only override if secret exists)
|
||||
// Log when overriding existing env vars vs setting new ones
|
||||
for (const [envVar, value] of Object.entries(loadedSecrets)) {
|
||||
const existingValue = process.env[envVar];
|
||||
const isOverriding = existingValue !== undefined;
|
||||
|
||||
process.env[envVar] = value;
|
||||
|
||||
// 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)`);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`[Secret Manager] ✅ Successfully loaded ${loadedCount}/${secretsToLoad.length} secrets`);
|
||||
@ -251,6 +296,33 @@ class GoogleSecretManagerService {
|
||||
}
|
||||
}
|
||||
|
||||
// Log summary of not found secrets, especially OKTA and EMAIL ones
|
||||
if (notFoundSecrets.length > 0) {
|
||||
const notFoundOkta = notFoundSecrets.filter(name => /OKTA_/i.test(name));
|
||||
const notFoundEmail = notFoundSecrets.filter(name => /EMAIL_|SMTP_/i.test(name));
|
||||
|
||||
if (notFoundOkta.length > 0) {
|
||||
logger.warn(`[Secret Manager] ⚠️ OKTA secrets not found (${notFoundOkta.length}): ${notFoundOkta.join(', ')}`);
|
||||
logger.info(`[Secret Manager] 💡 To create OKTA secrets, use:`);
|
||||
notFoundOkta.forEach(secretName => {
|
||||
logger.info(`[Secret Manager] gcloud secrets create ${secretName} --data-file=- --project=${this.projectId}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (notFoundEmail.length > 0) {
|
||||
logger.warn(`[Secret Manager] ⚠️ EMAIL secrets not found (${notFoundEmail.length}): ${notFoundEmail.join(', ')}`);
|
||||
logger.info(`[Secret Manager] 💡 To create EMAIL secrets, use:`);
|
||||
notFoundEmail.forEach(secretName => {
|
||||
logger.info(`[Secret Manager] gcloud secrets create ${secretName} --data-file=- --project=${this.projectId}`);
|
||||
});
|
||||
}
|
||||
|
||||
const otherNotFound = notFoundSecrets.filter(name => !/OKTA_|EMAIL_|SMTP_/i.test(name));
|
||||
if (otherNotFound.length > 0) {
|
||||
logger.debug(`[Secret Manager] Other secrets not found (${otherNotFound.length}): ${otherNotFound.slice(0, 10).join(', ')}${otherNotFound.length > 10 ? '...' : ''}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.isInitialized = true;
|
||||
} catch (error: any) {
|
||||
logger.error('[Secret Manager] Failed to load secrets:', error);
|
||||
@ -265,10 +337,6 @@ class GoogleSecretManagerService {
|
||||
private getDefaultSecretNames(): string[] {
|
||||
return [
|
||||
// Database
|
||||
'DB_HOST',
|
||||
'DB_PORT',
|
||||
'DB_NAME',
|
||||
'DB_USER',
|
||||
'DB_PASSWORD',
|
||||
|
||||
// JWT & Session
|
||||
@ -277,7 +345,6 @@ class GoogleSecretManagerService {
|
||||
'SESSION_SECRET',
|
||||
|
||||
// Okta/SSO
|
||||
'OKTA_DOMAIN',
|
||||
'OKTA_CLIENT_ID',
|
||||
'OKTA_CLIENT_SECRET',
|
||||
'OKTA_API_TOKEN',
|
||||
@ -287,15 +354,7 @@ class GoogleSecretManagerService {
|
||||
'SMTP_PORT',
|
||||
'SMTP_USER',
|
||||
'SMTP_PASSWORD',
|
||||
|
||||
// VAPID (Web Push)
|
||||
'VAPID_PUBLIC_KEY',
|
||||
'VAPID_PRIVATE_KEY',
|
||||
|
||||
// Loki
|
||||
'LOKI_HOST',
|
||||
'LOKI_USER',
|
||||
'LOKI_PASSWORD',
|
||||
'EMAIL_FROM',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user