Fix web3health check + cache secret keys
This commit is contained in:
@@ -13,19 +13,28 @@ declare module 'fastify' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads Privy secrets from Infisical in production, or from env vars/files in non-production
|
* Loads Privy secrets ONCE at application startup from Infisical (production) or env vars/files (non-production).
|
||||||
|
* Secrets are cached in memory via Fastify decorator and never reloaded during runtime.
|
||||||
|
* This ensures secrets are only fetched once from Infisical at startup, not on every Privy client creation.
|
||||||
*/
|
*/
|
||||||
export default fp(async function (fastify) {
|
export default fp(async function (fastify) {
|
||||||
|
// Guard: Fastify-plugin ensures this runs only once, but add explicit check as safety
|
||||||
|
if ((fastify as any).privySecrets) {
|
||||||
|
fastify.log.warn('privySecrets already decorated - skipping reload (this should not happen)')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const isProd = process.env.NODE_ENV === 'production'
|
const isProd = process.env.NODE_ENV === 'production'
|
||||||
|
|
||||||
fastify.log.info({ isProd, nodeEnv: process.env.NODE_ENV }, 'Loading Privy secrets')
|
fastify.log.info({ isProd, nodeEnv: process.env.NODE_ENV }, 'Loading Privy secrets at startup (one-time only)')
|
||||||
|
|
||||||
let appId: string = ''
|
let appId: string = ''
|
||||||
let appSecret: string = ''
|
let appSecret: string = ''
|
||||||
let authKey: string = ''
|
let authKey: string = ''
|
||||||
|
let infisicalClient: InfisicalSDK | null = null
|
||||||
|
|
||||||
if (isProd) {
|
if (isProd) {
|
||||||
// In production, use Infisical SDK
|
// In production, use Infisical SDK - this runs ONCE at startup
|
||||||
try {
|
try {
|
||||||
const infisicalSiteUrl = process.env.INFISICAL_SITE_URL || 'https://app.infisical.com'
|
const infisicalSiteUrl = process.env.INFISICAL_SITE_URL || 'https://app.infisical.com'
|
||||||
const infisicalClientId = process.env.INFISICAL_CLIENT_ID
|
const infisicalClientId = process.env.INFISICAL_CLIENT_ID
|
||||||
@@ -41,22 +50,23 @@ export default fp(async function (fastify) {
|
|||||||
siteUrl: infisicalSiteUrl,
|
siteUrl: infisicalSiteUrl,
|
||||||
projectId: infisicalProjectId,
|
projectId: infisicalProjectId,
|
||||||
environment: infisicalEnvironment
|
environment: infisicalEnvironment
|
||||||
}, 'Initializing Infisical SDK')
|
}, 'Initializing Infisical SDK (startup only)')
|
||||||
|
|
||||||
const client = new InfisicalSDK({
|
// Create Infisical client - used once at startup
|
||||||
|
infisicalClient = new InfisicalSDK({
|
||||||
siteUrl: infisicalSiteUrl
|
siteUrl: infisicalSiteUrl
|
||||||
})
|
})
|
||||||
|
|
||||||
// Authenticate with Infisical
|
// Authenticate with Infisical - done once at startup
|
||||||
await client.auth().universalAuth.login({
|
await infisicalClient.auth().universalAuth.login({
|
||||||
clientId: infisicalClientId,
|
clientId: infisicalClientId,
|
||||||
clientSecret: infisicalClientSecret
|
clientSecret: infisicalClientSecret
|
||||||
})
|
})
|
||||||
|
|
||||||
fastify.log.info('Authenticated with Infisical successfully')
|
fastify.log.info('Authenticated with Infisical successfully (startup only)')
|
||||||
|
|
||||||
// Fetch all secrets for the project
|
// Fetch all secrets for the project - done ONCE at startup
|
||||||
const allSecrets = await client.secrets().listSecrets({
|
const allSecrets = await infisicalClient.secrets().listSecrets({
|
||||||
environment: infisicalEnvironment,
|
environment: infisicalEnvironment,
|
||||||
projectId: infisicalProjectId,
|
projectId: infisicalProjectId,
|
||||||
viewSecretValue: true
|
viewSecretValue: true
|
||||||
@@ -64,9 +74,9 @@ export default fp(async function (fastify) {
|
|||||||
|
|
||||||
fastify.log.info({
|
fastify.log.info({
|
||||||
secretCount: allSecrets.secrets?.length || 0
|
secretCount: allSecrets.secrets?.length || 0
|
||||||
}, 'Fetched secrets from Infisical')
|
}, 'Fetched secrets from Infisical (startup only)')
|
||||||
|
|
||||||
// Extract Privy secrets
|
// Extract Privy secrets and store in memory
|
||||||
const privyAppIdSecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_APP_ID')
|
const privyAppIdSecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_APP_ID')
|
||||||
const privyAppSecretSecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_APP_SECRET')
|
const privyAppSecretSecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_APP_SECRET')
|
||||||
const privyAuthKeySecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_AUTHORIZATION_KEY')
|
const privyAuthKeySecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_AUTHORIZATION_KEY')
|
||||||
@@ -82,13 +92,18 @@ export default fp(async function (fastify) {
|
|||||||
appIdFound: !!privyAppIdSecret,
|
appIdFound: !!privyAppIdSecret,
|
||||||
appSecretFound: !!privyAppSecretSecret,
|
appSecretFound: !!privyAppSecretSecret,
|
||||||
authKeyFound: !!privyAuthKeySecret
|
authKeyFound: !!privyAuthKeySecret
|
||||||
}, 'Privy secrets loaded from Infisical')
|
}, 'Privy secrets loaded from Infisical and cached in memory (startup only)')
|
||||||
|
|
||||||
|
// Note: Infisical SDK doesn't expose explicit cleanup methods
|
||||||
|
// The client will be garbage collected after secrets are extracted
|
||||||
|
// No need to keep the client instance - secrets are now in memory
|
||||||
|
infisicalClient = null
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
fastify.log.error({
|
fastify.log.error({
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
stack: error instanceof Error ? error.stack : undefined
|
stack: error instanceof Error ? error.stack : undefined
|
||||||
}, 'Failed to load secrets from Infisical')
|
}, 'Failed to load secrets from Infisical at startup')
|
||||||
|
|
||||||
// Fallback to environment variables
|
// Fallback to environment variables
|
||||||
appId = process.env.PRIVY_APP_ID || ''
|
appId = process.env.PRIVY_APP_ID || ''
|
||||||
@@ -96,9 +111,12 @@ export default fp(async function (fastify) {
|
|||||||
authKey = process.env.PRIVY_AUTHORIZATION_KEY || ''
|
authKey = process.env.PRIVY_AUTHORIZATION_KEY || ''
|
||||||
|
|
||||||
fastify.log.warn('Falling back to environment variables for Privy secrets')
|
fastify.log.warn('Falling back to environment variables for Privy secrets')
|
||||||
|
} finally {
|
||||||
|
// Ensure client is cleaned up
|
||||||
|
infisicalClient = null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// In non-production, use env vars or file paths
|
// In non-production, use env vars or file paths - loaded once at startup
|
||||||
const readMaybeFile = (envKey: string, fileKey: string): string | undefined => {
|
const readMaybeFile = (envKey: string, fileKey: string): string | undefined => {
|
||||||
const filePath = process.env[fileKey]
|
const filePath = process.env[fileKey]
|
||||||
if (filePath && fs.existsSync(filePath)) return fs.readFileSync(filePath, 'utf8').trim()
|
if (filePath && fs.existsSync(filePath)) return fs.readFileSync(filePath, 'utf8').trim()
|
||||||
@@ -146,6 +164,8 @@ export default fp(async function (fastify) {
|
|||||||
return // Continue without throwing
|
return // Continue without throwing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decorate Fastify instance with secrets - these are cached in memory for the lifetime of the application
|
||||||
|
// This decoration happens ONCE at startup, and secrets are reused from memory for all Privy client creations
|
||||||
fastify.decorate('privySecrets', {
|
fastify.decorate('privySecrets', {
|
||||||
appId,
|
appId,
|
||||||
appSecret,
|
appSecret,
|
||||||
@@ -155,6 +175,11 @@ export default fp(async function (fastify) {
|
|||||||
fastify.log.info({
|
fastify.log.info({
|
||||||
appId: appId.substring(0, 10) + '...',
|
appId: appId.substring(0, 10) + '...',
|
||||||
appSecret: appSecret.substring(0, 10) + '...',
|
appSecret: appSecret.substring(0, 10) + '...',
|
||||||
authKey: authKey.substring(0, 20) + '...'
|
authKey: authKey.substring(0, 20) + '...',
|
||||||
}, '✅ Privy secrets decorated on Fastify instance successfully')
|
note: 'Secrets cached in memory - Infisical will not be called again during runtime'
|
||||||
}, { name: 'privy-secrets' })
|
}, '✅ Privy secrets loaded at startup and cached in memory')
|
||||||
|
}, {
|
||||||
|
name: 'privy-secrets',
|
||||||
|
// Ensure plugin runs only once per Fastify instance
|
||||||
|
dependencies: []
|
||||||
|
})
|
||||||
|
|||||||
@@ -51,14 +51,18 @@ declare module 'fastify' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an initialized PrivyClient instance with configuration from secrets
|
* Returns an initialized PrivyClient instance with configuration from cached secrets.
|
||||||
|
* Secrets are loaded ONCE at startup from Infisical (production) or env vars (non-production)
|
||||||
|
* and cached in memory via Fastify decorator. This function only reads from cache, never fetches from Infisical.
|
||||||
|
* @param fastify Optional Fastify instance to access cached secrets from decorator
|
||||||
* @returns The configured PrivyClient instance
|
* @returns The configured PrivyClient instance
|
||||||
*/
|
*/
|
||||||
export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
||||||
// Try multiple sources in order of preference:
|
// Read from cached secrets in memory (loaded once at startup):
|
||||||
// 1. Fastify decorator (populated by privy-secrets plugin - Infisical in prod, env in dev)
|
// 1. Fastify decorator (populated by privy-secrets plugin at startup - Infisical in prod, env in dev)
|
||||||
// 2. Fastify config (from env plugin)
|
// 2. Fastify config (from env plugin)
|
||||||
// 3. Environment variables (fallback)
|
// 3. Environment variables (fallback)
|
||||||
|
// Note: In production, secrets come from Infisical and are cached in fastify.privySecrets at startup
|
||||||
|
|
||||||
const decorated = (fastify as any)?.privySecrets as { appId: string; appSecret: string; authKey: string } | undefined
|
const decorated = (fastify as any)?.privySecrets as { appId: string; appSecret: string; authKey: string } | undefined
|
||||||
|
|
||||||
@@ -92,17 +96,18 @@ export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authentication function for Privy API calls
|
* Authentication function for Privy API calls.
|
||||||
|
* Reads cached secrets from memory (loaded once at startup from Infisical in production).
|
||||||
* @param url The URL for the API endpoint
|
* @param url The URL for the API endpoint
|
||||||
* @param body The request body
|
* @param body The request body
|
||||||
* @param fastify Optional Fastify instance to get secrets from decorator
|
* @param fastify Optional Fastify instance to get cached secrets from decorator
|
||||||
* @returns The authorization signature
|
* @returns The authorization signature
|
||||||
*/
|
*/
|
||||||
export async function getAuthorizationSignature(
|
export async function getAuthorizationSignature(
|
||||||
{url, body}: {url: string; body: object},
|
{url, body}: {url: string; body: object},
|
||||||
fastify?: FastifyInstance
|
fastify?: FastifyInstance
|
||||||
) {
|
) {
|
||||||
// Get app ID from Fastify decorator or env
|
// Get app ID from cached secrets (loaded once at startup) or env fallback
|
||||||
const decorated = (fastify as any)?.privySecrets as { appId: string; authKey: string } | undefined
|
const decorated = (fastify as any)?.privySecrets as { appId: string; authKey: string } | undefined
|
||||||
const appId = decorated?.appId || fastify?.config?.PRIVY_APP_ID || process.env.PRIVY_APP_ID || ''
|
const appId = decorated?.appId || fastify?.config?.PRIVY_APP_ID || process.env.PRIVY_APP_ID || ''
|
||||||
|
|
||||||
@@ -121,10 +126,10 @@ export async function getAuthorizationSignature(
|
|||||||
const serializedPayload = canonicalize(payload) as string;
|
const serializedPayload = canonicalize(payload) as string;
|
||||||
const serializedPayloadBuffer = Buffer.from(serializedPayload);
|
const serializedPayloadBuffer = Buffer.from(serializedPayload);
|
||||||
|
|
||||||
// Resolve authorization key: from Fastify decorator (Infisical in prod), file/env in non-production
|
// Resolve authorization key from cached secrets (loaded once at startup from Infisical in production)
|
||||||
let resolvedKey: string | undefined
|
let resolvedKey: string | undefined
|
||||||
|
|
||||||
// Try Fastify decorator first (loaded from Infisical in production)
|
// Try Fastify decorator first (secrets cached in memory from Infisical at startup in production)
|
||||||
if (decorated?.authKey) {
|
if (decorated?.authKey) {
|
||||||
resolvedKey = decorated.authKey
|
resolvedKey = decorated.authKey
|
||||||
} else if (fastify?.config?.PRIVY_AUTHORIZATION_KEY) {
|
} else if (fastify?.config?.PRIVY_AUTHORIZATION_KEY) {
|
||||||
@@ -154,10 +159,12 @@ export async function getAuthorizationSignature(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a request to the Privy API with proper authentication and headers.
|
* Makes a request to the Privy API with proper authentication and headers.
|
||||||
|
* Reads cached secrets from memory (loaded once at startup from Infisical in production).
|
||||||
* @param url - The full URL for the API endpoint.
|
* @param url - The full URL for the API endpoint.
|
||||||
* @param body - The request body (optional for GET requests).
|
* @param body - The request body (optional for GET requests).
|
||||||
* @param requiresAuth - Whether the request requires authentication.
|
* @param requiresAuth - Whether the request requires authentication.
|
||||||
* @param method - The HTTP method (defaults to POST).
|
* @param method - The HTTP method (defaults to POST).
|
||||||
|
* @param fastify - Optional Fastify instance to access cached secrets from decorator
|
||||||
* @returns The response data.
|
* @returns The response data.
|
||||||
*/
|
*/
|
||||||
export const makePrivyRequest = async <T>(
|
export const makePrivyRequest = async <T>(
|
||||||
@@ -168,10 +175,10 @@ export const makePrivyRequest = async <T>(
|
|||||||
fastify?: FastifyInstance
|
fastify?: FastifyInstance
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
try {
|
try {
|
||||||
// Resolve secrets: from Fastify decorator (Infisical in prod), env in non-production
|
// Resolve secrets from cached memory (loaded once at startup from Infisical in production)
|
||||||
const decorated = (fastify as any)?.privySecrets as { appId: string; appSecret: string } | undefined
|
const decorated = (fastify as any)?.privySecrets as { appId: string; appSecret: string } | undefined
|
||||||
|
|
||||||
// Try Fastify decorator first (loaded from Infisical in production)
|
// Try Fastify decorator first (secrets cached in memory from Infisical at startup in production)
|
||||||
const appId = decorated?.appId || fastify?.config?.PRIVY_APP_ID || process.env.PRIVY_APP_ID || ''
|
const appId = decorated?.appId || fastify?.config?.PRIVY_APP_ID || process.env.PRIVY_APP_ID || ''
|
||||||
const appSecret = decorated?.appSecret || fastify?.config?.PRIVY_APP_SECRET || process.env.PRIVY_APP_SECRET || ''
|
const appSecret = decorated?.appSecret || fastify?.config?.PRIVY_APP_SECRET || process.env.PRIVY_APP_SECRET || ''
|
||||||
|
|
||||||
|
|||||||
@@ -34,28 +34,20 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
|||||||
fastify.get('/health', {
|
fastify.get('/health', {
|
||||||
schema: {
|
schema: {
|
||||||
tags: ['Health'],
|
tags: ['Health'],
|
||||||
description: 'Enhanced health check endpoint that verifies API, Privy, and GMX connectivity',
|
description: 'Enhanced health check endpoint that verifies API, Privy, Infisical (production), GMX, and Redis connectivity',
|
||||||
response: {
|
response: {
|
||||||
200: Type.Object({
|
200: Type.Object({
|
||||||
status: Type.String(),
|
status: Type.String(),
|
||||||
timestamp: Type.String(),
|
timestamp: Type.String(),
|
||||||
version: Type.String(),
|
version: Type.String(),
|
||||||
checks: Type.Object({
|
checks: Type.Record(
|
||||||
privy: Type.Object({
|
Type.String(),
|
||||||
status: Type.String(),
|
Type.Object({
|
||||||
message: Type.String()
|
|
||||||
}),
|
|
||||||
gmx: Type.Object({
|
|
||||||
status: Type.String(),
|
|
||||||
message: Type.String(),
|
|
||||||
data: Type.Optional(Type.Any())
|
|
||||||
}),
|
|
||||||
redis: Type.Object({
|
|
||||||
status: Type.String(),
|
status: Type.String(),
|
||||||
message: Type.String(),
|
message: Type.String(),
|
||||||
data: Type.Optional(Type.Any())
|
data: Type.Optional(Type.Any())
|
||||||
})
|
})
|
||||||
})
|
)
|
||||||
}),
|
}),
|
||||||
500: Type.Object({
|
500: Type.Object({
|
||||||
success: Type.Boolean(),
|
success: Type.Boolean(),
|
||||||
@@ -66,12 +58,19 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
|||||||
}, async function (request, reply) {
|
}, async function (request, reply) {
|
||||||
try {
|
try {
|
||||||
console.log('Checking health...')
|
console.log('Checking health...')
|
||||||
const checks = {
|
const isProd = process.env.NODE_ENV === 'production'
|
||||||
|
|
||||||
|
const checks: Record<string, {status: string; message: string; data?: any}> = {
|
||||||
privy: await checkPrivy(fastify),
|
privy: await checkPrivy(fastify),
|
||||||
gmx: await checkGmx(),
|
gmx: await checkGmx(),
|
||||||
redis: await checkRedis()
|
redis: await checkRedis()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In production, add Infisical health check
|
||||||
|
if (isProd) {
|
||||||
|
checks.infisical = await checkInfisical(fastify)
|
||||||
|
}
|
||||||
|
|
||||||
// If any check failed, set status to degraded
|
// If any check failed, set status to degraded
|
||||||
const overallStatus = Object.values(checks).some(check => check.status !== 'healthy')
|
const overallStatus = Object.values(checks).some(check => check.status !== 'healthy')
|
||||||
? 'degraded'
|
? 'degraded'
|
||||||
@@ -91,40 +90,165 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
|||||||
// Helper function to check Privy connectivity and configuration
|
// Helper function to check Privy connectivity and configuration
|
||||||
async function checkPrivy(fastify) {
|
async function checkPrivy(fastify) {
|
||||||
try {
|
try {
|
||||||
// Try to initialize the Privy client - this will validate env vars and connectivity
|
// Check if secrets are loaded via Fastify decorator (from Infisical in prod)
|
||||||
const privy = getPrivyClient(fastify);
|
const privySecrets = (fastify as any)?.privySecrets as { appId: string; appSecret: string; authKey: string } | undefined
|
||||||
|
const isProd = process.env.NODE_ENV === 'production'
|
||||||
|
|
||||||
// Check if the necessary configuration is available
|
if (isProd) {
|
||||||
const hasPrivyAppId = !!process.env.PRIVY_APP_ID;
|
// In production, verify secrets are loaded from Infisical
|
||||||
const hasPrivySecret = !!process.env.PRIVY_APP_SECRET;
|
if (!privySecrets) {
|
||||||
const hasPrivyAuthKey = !!process.env.PRIVY_AUTHORIZATION_KEY;
|
return {
|
||||||
|
status: 'unhealthy',
|
||||||
|
message: 'Privy secrets not loaded from Infisical - privySecrets decorator not found',
|
||||||
|
data: {
|
||||||
|
source: 'infisical',
|
||||||
|
secretsDecorated: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the client status
|
const hasAppId = !!privySecrets.appId
|
||||||
const allConfigPresent = hasPrivyAppId && hasPrivySecret && hasPrivyAuthKey;
|
const hasAppSecret = !!privySecrets.appSecret
|
||||||
|
const hasAuthKey = !!privySecrets.authKey
|
||||||
|
|
||||||
if (!allConfigPresent) {
|
if (!hasAppId || !hasAppSecret || !hasAuthKey) {
|
||||||
return {
|
return {
|
||||||
status: 'degraded',
|
status: 'unhealthy',
|
||||||
message: 'Privy configuration incomplete: ' +
|
message: 'Privy secrets incomplete from Infisical',
|
||||||
(!hasPrivyAppId ? 'PRIVY_APP_ID ' : '') +
|
data: {
|
||||||
(!hasPrivySecret ? 'PRIVY_APP_SECRET ' : '') +
|
source: 'infisical',
|
||||||
(!hasPrivyAuthKey ? 'PRIVY_AUTHORIZATION_KEY ' : '')
|
appId: hasAppId,
|
||||||
};
|
appSecret: hasAppSecret,
|
||||||
|
authKey: hasAuthKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In non-production, check environment variables
|
||||||
|
const hasPrivyAppId = !!process.env.PRIVY_APP_ID
|
||||||
|
const hasPrivySecret = !!process.env.PRIVY_APP_SECRET
|
||||||
|
const hasPrivyAuthKey = !!process.env.PRIVY_AUTHORIZATION_KEY
|
||||||
|
|
||||||
|
if (!hasPrivyAppId || !hasPrivySecret || !hasPrivyAuthKey) {
|
||||||
|
return {
|
||||||
|
status: 'degraded',
|
||||||
|
message: 'Privy configuration incomplete from environment variables',
|
||||||
|
data: {
|
||||||
|
source: 'environment',
|
||||||
|
appId: hasPrivyAppId,
|
||||||
|
appSecret: hasPrivySecret,
|
||||||
|
authKey: hasPrivyAuthKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to initialize the Privy client - this will validate secrets and connectivity
|
||||||
|
const privy = getPrivyClient(fastify);
|
||||||
|
const appSettings = await privy.getAppSettings()
|
||||||
|
|
||||||
// If we got this far, the Privy client was successfully initialized
|
// If we got this far, the Privy client was successfully initialized
|
||||||
return {
|
return {
|
||||||
status: 'healthy',
|
status: 'healthy',
|
||||||
message: 'Privy client successfully initialized'
|
message: 'Privy client successfully initialized',
|
||||||
|
data: {
|
||||||
|
source: isProd ? 'infisical' : 'environment',
|
||||||
|
appId: isProd ? privySecrets?.appId.substring(0, 10) + '...' : process.env.PRIVY_APP_ID?.substring(0, 10) + '...',
|
||||||
|
privyAppSettingsId: appSettings.id
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
status: 'unhealthy',
|
status: 'unhealthy',
|
||||||
message: `Privy client initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
message: `Privy client initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||||
|
data: {
|
||||||
|
errorType: error instanceof Error ? error.constructor.name : 'Unknown'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to check Infisical connectivity and secret loading
|
||||||
|
async function checkInfisical(fastify) {
|
||||||
|
try {
|
||||||
|
const infisicalClientId = process.env.INFISICAL_CLIENT_ID
|
||||||
|
const infisicalClientSecret = process.env.INFISICAL_CLIENT_SECRET
|
||||||
|
const infisicalProjectId = process.env.INFISICAL_PROJECT_ID
|
||||||
|
const infisicalEnvironment = process.env.INFISICAL_ENVIRONMENT || 'prod'
|
||||||
|
const infisicalSiteUrl = process.env.INFISICAL_SITE_URL || 'https://app.infisical.com'
|
||||||
|
|
||||||
|
// Check if required Infisical environment variables are set
|
||||||
|
if (!infisicalClientId || !infisicalClientSecret || !infisicalProjectId) {
|
||||||
|
return {
|
||||||
|
status: 'unhealthy',
|
||||||
|
message: 'Infisical configuration incomplete - missing required environment variables',
|
||||||
|
data: {
|
||||||
|
hasClientId: !!infisicalClientId,
|
||||||
|
hasClientSecret: !!infisicalClientSecret,
|
||||||
|
hasProjectId: !!infisicalProjectId,
|
||||||
|
environment: infisicalEnvironment,
|
||||||
|
siteUrl: infisicalSiteUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if secrets are loaded in Fastify decorator
|
||||||
|
const privySecrets = (fastify as any)?.privySecrets as { appId: string; appSecret: string; authKey: string } | undefined
|
||||||
|
|
||||||
|
if (!privySecrets) {
|
||||||
|
return {
|
||||||
|
status: 'unhealthy',
|
||||||
|
message: 'Infisical secrets not loaded - privySecrets decorator not found',
|
||||||
|
data: {
|
||||||
|
projectId: infisicalProjectId,
|
||||||
|
environment: infisicalEnvironment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all three secrets are present
|
||||||
|
const hasAllSecrets = !!(privySecrets.appId && privySecrets.appSecret && privySecrets.authKey)
|
||||||
|
|
||||||
|
if (!hasAllSecrets) {
|
||||||
|
return {
|
||||||
|
status: 'unhealthy',
|
||||||
|
message: 'Infisical secrets incomplete',
|
||||||
|
data: {
|
||||||
|
projectId: infisicalProjectId,
|
||||||
|
environment: infisicalEnvironment,
|
||||||
|
appId: !!privySecrets.appId,
|
||||||
|
appSecret: !!privySecrets.appSecret,
|
||||||
|
authKey: !!privySecrets.authKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All checks passed
|
||||||
|
return {
|
||||||
|
status: 'healthy',
|
||||||
|
message: 'Infisical secrets successfully loaded',
|
||||||
|
data: {
|
||||||
|
projectId: infisicalProjectId,
|
||||||
|
environment: infisicalEnvironment,
|
||||||
|
siteUrl: infisicalSiteUrl,
|
||||||
|
secretsLoaded: {
|
||||||
|
PRIVY_APP_ID: !!privySecrets.appId,
|
||||||
|
PRIVY_APP_SECRET: !!privySecrets.appSecret,
|
||||||
|
PRIVY_AUTHORIZATION_KEY: !!privySecrets.authKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: 'unhealthy',
|
||||||
|
message: `Infisical check failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||||
|
data: {
|
||||||
|
errorType: error instanceof Error ? error.constructor.name : 'Unknown'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to check GMX connectivity using the GMX SDK
|
// Helper function to check GMX connectivity using the GMX SDK
|
||||||
async function checkGmx() {
|
async function checkGmx() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user