Use infisical for secrets
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"schemaVersion": 2,
|
"schemaVersion": 2,
|
||||||
"dockerfilePath": "./src/Managing.Web3Proxy/Dockerfile-web3proxy",
|
"dockerfilePath": "./src/Managing.Web3Proxy/Dockerfile-web3proxy"
|
||||||
"preDeployFunction": "function(captainAppObj, dockerUpdateObject) {\n try {\n console.log('preDeployFunction executing...');\n \n // Ensure TaskTemplate exists\n if (!dockerUpdateObject.TaskTemplate) {\n dockerUpdateObject.TaskTemplate = {};\n }\n \n // Ensure ContainerSpec exists\n if (!dockerUpdateObject.TaskTemplate.ContainerSpec) {\n dockerUpdateObject.TaskTemplate.ContainerSpec = {};\n }\n \n // Initialize Secrets array if it doesn't exist\n if (!dockerUpdateObject.TaskTemplate.ContainerSpec.Secrets) {\n dockerUpdateObject.TaskTemplate.ContainerSpec.Secrets = [];\n }\n \n // Add secrets configuration\n dockerUpdateObject.TaskTemplate.ContainerSpec.Secrets = [\n {\n File: {\n Name: 'PRIVY_APP_ID',\n UID: '0',\n GID: '0',\n Mode: 292\n },\n SecretID: '0r43i7pryk5d2fu0q9vyf46km',\n SecretName: 'privy_app_id'\n },\n {\n File: {\n Name: 'PRIVY_APP_SECRET',\n UID: '0',\n GID: '0',\n Mode: 292\n },\n SecretID: '0z6mjh58drzcrhk2kfbzt1f14',\n SecretName: 'privy_app_secret'\n },\n {\n File: {\n Name: 'PRIVY_AUTHORIZATION_KEY',\n UID: '0',\n GID: '0',\n Mode: 292\n },\n SecretID: 'ieg5bklu1t0otxrp69j4pekvj',\n SecretName: 'privy_auth_key'\n }\n ];\n \n console.log('Secrets configured:', dockerUpdateObject.TaskTemplate.ContainerSpec.Secrets.length, 'secrets');\n \n return Promise.resolve();\n } catch (error) {\n console.error('Error in preDeployFunction:', error);\n throw error;\n }\n}"
|
|
||||||
}
|
}
|
||||||
|
|||||||
1968
src/Managing.Web3Proxy/package-lock.json
generated
1968
src/Managing.Web3Proxy/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,7 @@
|
|||||||
"@fastify/swagger-ui": "^5.0.1",
|
"@fastify/swagger-ui": "^5.0.1",
|
||||||
"@fastify/type-provider-typebox": "^5.0.0",
|
"@fastify/type-provider-typebox": "^5.0.0",
|
||||||
"@fastify/under-pressure": "^9.0.1",
|
"@fastify/under-pressure": "^9.0.1",
|
||||||
|
"@infisical/sdk": "^4.0.6",
|
||||||
"@privy-io/server-auth": "^1.18.12",
|
"@privy-io/server-auth": "^1.18.12",
|
||||||
"@sentry/node": "^8.55.0",
|
"@sentry/node": "^8.55.0",
|
||||||
"@sinclair/typebox": "^0.34.11",
|
"@sinclair/typebox": "^0.34.11",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import fp from 'fastify-plugin'
|
import fp from 'fastify-plugin'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import {InfisicalSDK} from '@infisical/sdk'
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
@@ -11,101 +12,91 @@ declare module 'fastify' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Docker secrets are mounted at /run/secrets/ by default in CapRover
|
/**
|
||||||
const readSecretFile = (secretName: string, logger?: any): string | undefined => {
|
* Loads Privy secrets from Infisical in production, or from env vars/files in non-production
|
||||||
const secretPath = `/run/secrets/${secretName}`
|
*/
|
||||||
logger?.debug({ secretPath, exists: fs.existsSync(secretPath) }, `Checking secret file: ${secretName}`)
|
|
||||||
|
|
||||||
if (fs.existsSync(secretPath)) {
|
|
||||||
try {
|
|
||||||
const content = fs.readFileSync(secretPath, 'utf8').trim()
|
|
||||||
logger?.info({ secretName, path: secretPath, length: content.length }, `Successfully read secret: ${secretName}`)
|
|
||||||
return content
|
|
||||||
} catch (error) {
|
|
||||||
logger?.error({ error, secretPath }, `Failed to read secret file ${secretPath}`)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger?.warn({ secretPath }, `Secret file does not exist: ${secretPath}`)
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export default fp(async function (fastify) {
|
export default fp(async function (fastify) {
|
||||||
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')
|
||||||
|
|
||||||
// Debug: List all files in /run/secrets/ directory
|
let appId: string = ''
|
||||||
const secretsDir = '/run/secrets'
|
let appSecret: string = ''
|
||||||
let availableFiles: string[] = []
|
let authKey: string = ''
|
||||||
if (fs.existsSync(secretsDir)) {
|
|
||||||
try {
|
|
||||||
availableFiles = fs.readdirSync(secretsDir)
|
|
||||||
fastify.log.info({ files: availableFiles, dir: secretsDir, count: availableFiles.length }, 'Files found in /run/secrets/')
|
|
||||||
|
|
||||||
// Also log full paths and their sizes
|
|
||||||
availableFiles.forEach(file => {
|
|
||||||
const fullPath = `${secretsDir}/${file}`
|
|
||||||
try {
|
|
||||||
const stats = fs.statSync(fullPath)
|
|
||||||
fastify.log.debug({ file, path: fullPath, size: stats.size, isFile: stats.isFile() }, `Secret file details: ${file}`)
|
|
||||||
} catch (err) {
|
|
||||||
fastify.log.warn({ file, path: fullPath, error: err }, `Could not stat secret file: ${file}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
fastify.log.warn({ error }, 'Could not list /run/secrets/ directory')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fastify.log.error({ dir: secretsDir }, '/run/secrets/ directory does not exist - Docker secrets may not be mounted')
|
|
||||||
}
|
|
||||||
|
|
||||||
let appId: string
|
|
||||||
let appSecret: string
|
|
||||||
let authKey: string
|
|
||||||
|
|
||||||
if (isProd) {
|
if (isProd) {
|
||||||
// In production, read from Docker secrets (mounted files)
|
// In production, use Infisical SDK
|
||||||
// Try exact names first
|
try {
|
||||||
appId = readSecretFile('PRIVY_APP_ID', fastify.log) || process.env.PRIVY_APP_ID || ''
|
const infisicalSiteUrl = process.env.INFISICAL_SITE_URL || 'https://app.infisical.com'
|
||||||
appSecret = readSecretFile('PRIVY_APP_SECRET', fastify.log) || process.env.PRIVY_APP_SECRET || ''
|
const infisicalClientId = process.env.INFISICAL_CLIENT_ID
|
||||||
authKey = readSecretFile('PRIVY_AUTHORIZATION_KEY', fastify.log) || process.env.PRIVY_AUTHORIZATION_KEY || ''
|
const infisicalClientSecret = process.env.INFISICAL_CLIENT_SECRET
|
||||||
|
const infisicalProjectId = process.env.INFISICAL_PROJECT_ID
|
||||||
|
const infisicalEnvironment = process.env.INFISICAL_ENVIRONMENT || 'prod'
|
||||||
|
|
||||||
// If not found, try alternative names (maybe they're mounted with SecretName instead of File.Name)
|
if (!infisicalClientId || !infisicalClientSecret || !infisicalProjectId) {
|
||||||
if (!appId && availableFiles.length > 0) {
|
throw new Error('Infisical credentials missing: INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, and INFISICAL_PROJECT_ID are required in production')
|
||||||
const appIdFile = availableFiles.find(f => f.toLowerCase().includes('app_id') || f.toLowerCase().includes('appid'))
|
|
||||||
if (appIdFile) {
|
|
||||||
fastify.log.info({ foundFile: appIdFile, trying: 'PRIVY_APP_ID' }, 'Trying alternative file name for PRIVY_APP_ID')
|
|
||||||
appId = readSecretFile(appIdFile, fastify.log) || ''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appSecret && availableFiles.length > 0) {
|
fastify.log.info({
|
||||||
const appSecretFile = availableFiles.find(f => f.toLowerCase().includes('app_secret') || f.toLowerCase().includes('appsecret'))
|
siteUrl: infisicalSiteUrl,
|
||||||
if (appSecretFile) {
|
projectId: infisicalProjectId,
|
||||||
fastify.log.info({ foundFile: appSecretFile, trying: 'PRIVY_APP_SECRET' }, 'Trying alternative file name for PRIVY_APP_SECRET')
|
environment: infisicalEnvironment
|
||||||
appSecret = readSecretFile(appSecretFile, fastify.log) || ''
|
}, 'Initializing Infisical SDK')
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!authKey && availableFiles.length > 0) {
|
const client = new InfisicalSDK({
|
||||||
const authKeyFile = availableFiles.find(f => f.toLowerCase().includes('auth_key') || f.toLowerCase().includes('authkey') || f.toLowerCase().includes('authorization'))
|
siteUrl: infisicalSiteUrl
|
||||||
if (authKeyFile) {
|
})
|
||||||
fastify.log.info({ foundFile: authKeyFile, trying: 'PRIVY_AUTHORIZATION_KEY' }, 'Trying alternative file name for PRIVY_AUTHORIZATION_KEY')
|
|
||||||
authKey = readSecretFile(authKeyFile, fastify.log) || ''
|
// Authenticate with Infisical
|
||||||
}
|
await client.auth().universalAuth.login({
|
||||||
}
|
clientId: infisicalClientId,
|
||||||
|
clientSecret: infisicalClientSecret
|
||||||
|
})
|
||||||
|
|
||||||
|
fastify.log.info('Authenticated with Infisical successfully')
|
||||||
|
|
||||||
|
// Fetch all secrets for the project
|
||||||
|
const allSecrets = await client.secrets().listSecrets({
|
||||||
|
environment: infisicalEnvironment,
|
||||||
|
projectId: infisicalProjectId,
|
||||||
|
viewSecretValue: true
|
||||||
|
})
|
||||||
|
|
||||||
|
fastify.log.info({
|
||||||
|
secretCount: allSecrets.secrets?.length || 0
|
||||||
|
}, 'Fetched secrets from Infisical')
|
||||||
|
|
||||||
|
// Extract Privy secrets
|
||||||
|
const privyAppIdSecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_APP_ID')
|
||||||
|
const privyAppSecretSecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_APP_SECRET')
|
||||||
|
const privyAuthKeySecret = allSecrets.secrets?.find(s => s.secretKey === 'PRIVY_AUTHORIZATION_KEY')
|
||||||
|
|
||||||
|
appId = privyAppIdSecret?.secretValue || process.env.PRIVY_APP_ID || ''
|
||||||
|
appSecret = privyAppSecretSecret?.secretValue || process.env.PRIVY_APP_SECRET || ''
|
||||||
|
authKey = privyAuthKeySecret?.secretValue || process.env.PRIVY_AUTHORIZATION_KEY || ''
|
||||||
|
|
||||||
fastify.log.info({
|
fastify.log.info({
|
||||||
appId: !!appId,
|
appId: !!appId,
|
||||||
appSecret: !!appSecret,
|
appSecret: !!appSecret,
|
||||||
authKey: !!authKey,
|
authKey: !!authKey,
|
||||||
appIdLength: appId.length,
|
appIdFound: !!privyAppIdSecret,
|
||||||
appSecretLength: appSecret.length,
|
appSecretFound: !!privyAppSecretSecret,
|
||||||
authKeyLength: authKey.length,
|
authKeyFound: !!privyAuthKeySecret
|
||||||
availableSecretFiles: availableFiles
|
}, 'Privy secrets loaded from Infisical')
|
||||||
}, 'Privy secrets loaded from Docker secrets')
|
|
||||||
|
} catch (error) {
|
||||||
|
fastify.log.error({
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
stack: error instanceof Error ? error.stack : undefined
|
||||||
|
}, 'Failed to load secrets from Infisical')
|
||||||
|
|
||||||
|
// Fallback to environment variables
|
||||||
|
appId = process.env.PRIVY_APP_ID || ''
|
||||||
|
appSecret = process.env.PRIVY_APP_SECRET || ''
|
||||||
|
authKey = process.env.PRIVY_AUTHORIZATION_KEY || ''
|
||||||
|
|
||||||
|
fastify.log.warn('Falling back to environment variables for Privy secrets')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// In non-production, use env vars or file paths
|
// In non-production, use env vars or file paths
|
||||||
const readMaybeFile = (envKey: string, fileKey: string): string | undefined => {
|
const readMaybeFile = (envKey: string, fileKey: string): string | undefined => {
|
||||||
@@ -127,9 +118,7 @@ export default fp(async function (fastify) {
|
|||||||
appSecret: !!appSecret,
|
appSecret: !!appSecret,
|
||||||
authKey: !!authKey,
|
authKey: !!authKey,
|
||||||
isProd,
|
isProd,
|
||||||
nodeEnv: process.env.NODE_ENV,
|
nodeEnv: process.env.NODE_ENV
|
||||||
availableSecretFiles: availableFiles,
|
|
||||||
secretsDirExists: fs.existsSync(secretsDir)
|
|
||||||
}, '⚠️ WARNING: Privy secrets not fully resolved at plugin load - app will continue but Privy operations will fail')
|
}, '⚠️ WARNING: Privy secrets not fully resolved at plugin load - app will continue but Privy operations will fail')
|
||||||
|
|
||||||
// Still decorate with empty strings so the app doesn't crash
|
// Still decorate with empty strings so the app doesn't crash
|
||||||
@@ -142,11 +131,15 @@ export default fp(async function (fastify) {
|
|||||||
|
|
||||||
fastify.log.error({
|
fastify.log.error({
|
||||||
message: 'Please check:',
|
message: 'Please check:',
|
||||||
checks: [
|
checks: isProd ? [
|
||||||
'1. Docker secrets are created: docker secret ls',
|
'1. INFISICAL_CLIENT_ID environment variable is set',
|
||||||
'2. preDeployFunction in captain-definition is executing',
|
'2. INFISICAL_CLIENT_SECRET environment variable is set',
|
||||||
'3. Secret IDs in captain-definition match actual secret IDs',
|
'3. INFISICAL_PROJECT_ID environment variable is set',
|
||||||
'4. Container has access to /run/secrets/ directory'
|
'4. Secrets exist in Infisical with keys: PRIVY_APP_ID, PRIVY_APP_SECRET, PRIVY_AUTHORIZATION_KEY',
|
||||||
|
'5. Machine Identity has access to the project and environment'
|
||||||
|
] : [
|
||||||
|
'1. Environment variables PRIVY_APP_ID, PRIVY_APP_SECRET, PRIVY_AUTHORIZATION_KEY are set',
|
||||||
|
'2. Or file paths are set via PRIVY_APP_ID_FILE, PRIVY_APP_SECRET_FILE, PRIVY_AUTHORIZATION_KEY_FILE'
|
||||||
]
|
]
|
||||||
}, 'Debugging steps for missing secrets')
|
}, 'Debugging steps for missing secrets')
|
||||||
|
|
||||||
|
|||||||
@@ -51,53 +51,20 @@ declare module 'fastify' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a secret from Docker secrets mount point (production only)
|
* Returns an initialized PrivyClient instance with configuration from secrets
|
||||||
*/
|
|
||||||
const readDockerSecret = (secretName: string): string | undefined => {
|
|
||||||
if (process.env.NODE_ENV !== 'production') return undefined
|
|
||||||
|
|
||||||
const secretPath = `/run/secrets/${secretName}`
|
|
||||||
if (fs.existsSync(secretPath)) {
|
|
||||||
try {
|
|
||||||
return fs.readFileSync(secretPath, 'utf8').trim()
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to read Docker secret ${secretPath}:`, error)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an initialized PrivyClient instance with configuration from environment variables
|
|
||||||
* @returns The configured PrivyClient instance
|
* @returns The configured PrivyClient instance
|
||||||
*/
|
*/
|
||||||
export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
||||||
const isProd = process.env.NODE_ENV === 'production'
|
|
||||||
|
|
||||||
// Try multiple sources in order of preference:
|
// Try multiple sources in order of preference:
|
||||||
// 1. Fastify decorator (populated by privy-secrets plugin)
|
// 1. Fastify decorator (populated by privy-secrets plugin - Infisical in prod, env in dev)
|
||||||
// 2. Fastify config (from env plugin)
|
// 2. Fastify config (from env plugin)
|
||||||
// 3. Docker secrets (production only)
|
// 3. Environment variables (fallback)
|
||||||
// 4. Environment variables
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
let appId = decorated?.appId || fastify?.config?.PRIVY_APP_ID
|
const appId = decorated?.appId || fastify?.config?.PRIVY_APP_ID || process.env.PRIVY_APP_ID
|
||||||
let appSecret = decorated?.appSecret || fastify?.config?.PRIVY_APP_SECRET
|
const appSecret = decorated?.appSecret || fastify?.config?.PRIVY_APP_SECRET || process.env.PRIVY_APP_SECRET
|
||||||
let authKey = decorated?.authKey || fastify?.config?.PRIVY_AUTHORIZATION_KEY
|
const authKey = decorated?.authKey || fastify?.config?.PRIVY_AUTHORIZATION_KEY || process.env.PRIVY_AUTHORIZATION_KEY
|
||||||
|
|
||||||
// In production, fall back to Docker secrets if not found in decorator/config
|
|
||||||
if (isProd) {
|
|
||||||
if (!appId) appId = readDockerSecret('PRIVY_APP_ID')
|
|
||||||
if (!appSecret) appSecret = readDockerSecret('PRIVY_APP_SECRET')
|
|
||||||
if (!authKey) authKey = readDockerSecret('PRIVY_AUTHORIZATION_KEY')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final fallback to environment variables
|
|
||||||
appId = appId || process.env.PRIVY_APP_ID
|
|
||||||
appSecret = appSecret || process.env.PRIVY_APP_SECRET
|
|
||||||
authKey = authKey || process.env.PRIVY_AUTHORIZATION_KEY
|
|
||||||
|
|
||||||
if (!appId || !appSecret || !authKey) {
|
if (!appId || !appSecret || !authKey) {
|
||||||
console.warn('Missing Privy environment variables:');
|
console.warn('Missing Privy environment variables:');
|
||||||
@@ -108,18 +75,9 @@ export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
|||||||
console.warn('Fastify privySecrets decorated:', !!decorated);
|
console.warn('Fastify privySecrets decorated:', !!decorated);
|
||||||
console.warn('Fastify config available:', !!fastify?.config);
|
console.warn('Fastify config available:', !!fastify?.config);
|
||||||
|
|
||||||
// In production, also log Docker secret file existence
|
|
||||||
if (isProd) {
|
|
||||||
console.warn('Docker secrets check:');
|
|
||||||
console.warn(' /run/secrets/PRIVY_APP_ID exists:', fs.existsSync('/run/secrets/PRIVY_APP_ID'));
|
|
||||||
console.warn(' /run/secrets/PRIVY_APP_SECRET exists:', fs.existsSync('/run/secrets/PRIVY_APP_SECRET'));
|
|
||||||
console.warn(' /run/secrets/PRIVY_AUTHORIZATION_KEY exists:', fs.existsSync('/run/secrets/PRIVY_AUTHORIZATION_KEY'));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Missing required Privy environment variables');
|
throw new Error('Missing required Privy environment variables');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('appId', appId);
|
|
||||||
console.log('Privy client initialized successfully');
|
console.log('Privy client initialized successfully');
|
||||||
|
|
||||||
return new PrivyClient(
|
return new PrivyClient(
|
||||||
@@ -137,16 +95,24 @@ export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
|||||||
* Authentication function for Privy API calls
|
* Authentication function for Privy API calls
|
||||||
* @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
|
||||||
* @returns The authorization signature
|
* @returns The authorization signature
|
||||||
*/
|
*/
|
||||||
export async function getAuthorizationSignature({url, body}: {url: string; body: object}) {
|
export async function getAuthorizationSignature(
|
||||||
|
{url, body}: {url: string; body: object},
|
||||||
|
fastify?: FastifyInstance
|
||||||
|
) {
|
||||||
|
// Get app ID from Fastify decorator or env
|
||||||
|
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 payload = {
|
const payload = {
|
||||||
version: 1,
|
version: 1,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url,
|
url,
|
||||||
body,
|
body,
|
||||||
headers: {
|
headers: {
|
||||||
'privy-app-id': process.env.PRIVY_APP_ID || ''
|
'privy-app-id': appId
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -155,18 +121,16 @@ export async function getAuthorizationSignature({url, body}: {url: string; body:
|
|||||||
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: Docker secrets in production, file/env in non-production
|
// Resolve authorization key: from Fastify decorator (Infisical in prod), file/env in non-production
|
||||||
let resolvedKey: string | undefined
|
let resolvedKey: string | undefined
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
// In production, read from Docker secrets mounted at /run/secrets/
|
// Try Fastify decorator first (loaded from Infisical in production)
|
||||||
const secretPath = '/run/secrets/PRIVY_AUTHORIZATION_KEY'
|
if (decorated?.authKey) {
|
||||||
if (fs.existsSync(secretPath)) {
|
resolvedKey = decorated.authKey
|
||||||
resolvedKey = fs.readFileSync(secretPath, 'utf8').trim()
|
} else if (fastify?.config?.PRIVY_AUTHORIZATION_KEY) {
|
||||||
|
resolvedKey = fastify.config.PRIVY_AUTHORIZATION_KEY
|
||||||
} else {
|
} else {
|
||||||
resolvedKey = process.env.PRIVY_AUTHORIZATION_KEY
|
// Fallback to file or env var
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Non-production: allow file fallback
|
|
||||||
const filePath = process.env.PRIVY_AUTHORIZATION_KEY_FILE
|
const filePath = process.env.PRIVY_AUTHORIZATION_KEY_FILE
|
||||||
const fromFile = filePath && fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').trim() : undefined
|
const fromFile = filePath && fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').trim() : undefined
|
||||||
resolvedKey = fromFile || process.env.PRIVY_AUTHORIZATION_KEY || undefined
|
resolvedKey = fromFile || process.env.PRIVY_AUTHORIZATION_KEY || undefined
|
||||||
@@ -200,24 +164,16 @@ export const makePrivyRequest = async <T>(
|
|||||||
url: string,
|
url: string,
|
||||||
body: object = {},
|
body: object = {},
|
||||||
requiresAuth = true,
|
requiresAuth = true,
|
||||||
method: 'GET' | 'POST' = 'POST'
|
method: 'GET' | 'POST' = 'POST',
|
||||||
|
fastify?: FastifyInstance
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
try {
|
try {
|
||||||
// Resolve secrets: Docker secrets in production, env in non-production
|
// Resolve secrets: from Fastify decorator (Infisical in prod), env in non-production
|
||||||
let appId: string
|
const decorated = (fastify as any)?.privySecrets as { appId: string; appSecret: string } | undefined
|
||||||
let appSecret: string
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
// Try Fastify decorator first (loaded from Infisical in production)
|
||||||
// In production, read from Docker secrets mounted at /run/secrets/
|
const appId = decorated?.appId || fastify?.config?.PRIVY_APP_ID || process.env.PRIVY_APP_ID || ''
|
||||||
const appIdPath = '/run/secrets/PRIVY_APP_ID'
|
const appSecret = decorated?.appSecret || fastify?.config?.PRIVY_APP_SECRET || process.env.PRIVY_APP_SECRET || ''
|
||||||
const appSecretPath = '/run/secrets/PRIVY_APP_SECRET'
|
|
||||||
appId = (fs.existsSync(appIdPath) ? fs.readFileSync(appIdPath, 'utf8').trim() : undefined) || process.env.PRIVY_APP_ID || ''
|
|
||||||
appSecret = (fs.existsSync(appSecretPath) ? fs.readFileSync(appSecretPath, 'utf8').trim() : undefined) || process.env.PRIVY_APP_SECRET || ''
|
|
||||||
} else {
|
|
||||||
// Non-production: use env vars
|
|
||||||
appId = process.env.PRIVY_APP_ID || ''
|
|
||||||
appSecret = process.env.PRIVY_APP_SECRET || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
let headers: Record<string, string> = {
|
let headers: Record<string, string> = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -1023,11 +979,12 @@ export const sendTokenImpl = async (
|
|||||||
export const getWalletBalanceImpl = async (
|
export const getWalletBalanceImpl = async (
|
||||||
address: string,
|
address: string,
|
||||||
assets: Ticker[],
|
assets: Ticker[],
|
||||||
chains: string[]
|
chains: string[],
|
||||||
|
fastify?: FastifyInstance
|
||||||
): Promise<Balance[]> => {
|
): Promise<Balance[]> => {
|
||||||
try {
|
try {
|
||||||
// First, get the user by wallet address using Privy Client
|
// First, get the user by wallet address using Privy Client
|
||||||
const privy = getPrivyClient();
|
const privy = getPrivyClient(fastify);
|
||||||
|
|
||||||
// Get user by wallet address
|
// Get user by wallet address
|
||||||
const user = await privy.getUserByWalletAddress(address);
|
const user = await privy.getUserByWalletAddress(address);
|
||||||
@@ -1085,7 +1042,7 @@ export const getWalletBalanceImpl = async (
|
|||||||
usd: string;
|
usd: string;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
}>(balanceUrl, {}, true, 'GET');
|
}>(balanceUrl, {}, true, 'GET', fastify);
|
||||||
|
|
||||||
// Convert to Balance interface format (matching ManagingApiTypes.ts)
|
// Convert to Balance interface format (matching ManagingApiTypes.ts)
|
||||||
const balances: Balance[] = balanceResponse.balances
|
const balances: Balance[] = balanceResponse.balances
|
||||||
@@ -1211,8 +1168,8 @@ export async function getWalletBalance(
|
|||||||
throw new Error('At least one chain is required for balance retrieval');
|
throw new Error('At least one chain is required for balance retrieval');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the getWalletBalanceImpl function
|
// Call the getWalletBalanceImpl function, passing the Fastify instance from the request
|
||||||
const balances = await getWalletBalanceImpl(address, assets, chains);
|
const balances = await getWalletBalanceImpl(address, assets, chains, this.server);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export const autoConfig = {
|
|||||||
* @see {@link https://github.com/fastify/fastify-env}
|
* @see {@link https://github.com/fastify/fastify-env}
|
||||||
*/
|
*/
|
||||||
export default fp(async (fastify) => {
|
export default fp(async (fastify) => {
|
||||||
// In production, Privy secrets come from Docker secrets (mounted files), not env vars
|
// In production, Privy secrets come from Infisical, not env vars
|
||||||
// Make them optional in the schema to avoid validation errors
|
// Make them optional in the schema to avoid validation errors
|
||||||
const isProd = process.env.NODE_ENV === 'production'
|
const isProd = process.env.NODE_ENV === 'production'
|
||||||
const required = isProd
|
const required = isProd
|
||||||
|
|||||||
Reference in New Issue
Block a user