mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
🔍 feat: Fetch Google Service Key and Consolidate Key Loading Logic (#8179)
This commit is contained in:
parent
738d04fac4
commit
59d00e99f3
5 changed files with 89 additions and 34 deletions
|
|
@ -1,5 +1,5 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { loadServiceKey } = require('@librechat/api');
|
||||
const { EModelEndpoint } = require('librechat-data-provider');
|
||||
const { isUserProvided } = require('~/server/utils');
|
||||
const { config } = require('./EndpointService');
|
||||
|
|
@ -18,15 +18,7 @@ async function loadAsyncEndpoints(req) {
|
|||
path.join(__dirname, '../../..', 'data', 'auth.json');
|
||||
|
||||
try {
|
||||
if (process.env.GOOGLE_SERVICE_KEY_FILE_PATH) {
|
||||
const absolutePath = path.isAbsolute(serviceKeyPath)
|
||||
? serviceKeyPath
|
||||
: path.resolve(serviceKeyPath);
|
||||
const fileContent = fs.readFileSync(absolutePath, 'utf8');
|
||||
serviceKey = JSON.parse(fileContent);
|
||||
} else {
|
||||
serviceKey = require('~/data/auth.json');
|
||||
}
|
||||
serviceKey = await loadServiceKey(serviceKeyPath);
|
||||
} catch {
|
||||
if (i === 0) {
|
||||
i++;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { getGoogleConfig, isEnabled } = require('@librechat/api');
|
||||
const { EModelEndpoint, AuthKeys } = require('librechat-data-provider');
|
||||
const { getGoogleConfig, isEnabled, loadServiceKey } = require('@librechat/api');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { GoogleClient } = require('~/app');
|
||||
|
||||
|
|
@ -19,17 +18,12 @@ const initializeClient = async ({ req, res, endpointOption, overrideModel, optio
|
|||
let serviceKey = {};
|
||||
|
||||
try {
|
||||
if (process.env.GOOGLE_SERVICE_KEY_FILE_PATH) {
|
||||
const serviceKeyPath =
|
||||
process.env.GOOGLE_SERVICE_KEY_FILE_PATH ||
|
||||
path.join(__dirname, '../../../../..', 'data', 'auth.json');
|
||||
const absolutePath = path.isAbsolute(serviceKeyPath)
|
||||
? serviceKeyPath
|
||||
: path.resolve(serviceKeyPath);
|
||||
const fileContent = fs.readFileSync(absolutePath, 'utf8');
|
||||
serviceKey = JSON.parse(fileContent);
|
||||
} else {
|
||||
serviceKey = require('~/data/auth.json');
|
||||
const serviceKeyPath =
|
||||
process.env.GOOGLE_SERVICE_KEY_FILE_PATH ||
|
||||
path.join(__dirname, '../../../..', 'data', 'auth.json');
|
||||
serviceKey = await loadServiceKey(serviceKeyPath);
|
||||
if (!serviceKey) {
|
||||
serviceKey = {};
|
||||
}
|
||||
} catch (_e) {
|
||||
// Do nothing
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import type {
|
|||
OCRImage,
|
||||
} from '~/types';
|
||||
import { logAxiosError, createAxiosInstance } from '~/utils/axios';
|
||||
import { loadServiceKey } from '~/utils/key';
|
||||
|
||||
const axios = createAxiosInstance();
|
||||
const DEFAULT_MISTRAL_BASE_URL = 'https://api.mistral.ai/v1';
|
||||
|
|
@ -443,27 +444,24 @@ async function loadGoogleAuthConfig(): Promise<{
|
|||
const serviceKeyPath =
|
||||
process.env.GOOGLE_SERVICE_KEY_FILE_PATH ||
|
||||
path.join(__dirname, '..', '..', '..', 'api', 'data', 'auth.json');
|
||||
const absolutePath = path.isAbsolute(serviceKeyPath)
|
||||
? serviceKeyPath
|
||||
: path.resolve(serviceKeyPath);
|
||||
|
||||
let serviceKey: GoogleServiceAccount;
|
||||
try {
|
||||
const authJsonContent = fs.readFileSync(absolutePath, 'utf8');
|
||||
serviceKey = JSON.parse(authJsonContent) as GoogleServiceAccount;
|
||||
} catch {
|
||||
throw new Error(`Google service account not found at ${absolutePath}`);
|
||||
const serviceKey = await loadServiceKey(serviceKeyPath);
|
||||
|
||||
if (!serviceKey) {
|
||||
throw new Error(
|
||||
`Google service account not found or could not be loaded from ${serviceKeyPath}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!serviceKey.client_email || !serviceKey.private_key || !serviceKey.project_id) {
|
||||
throw new Error('Invalid Google service account configuration');
|
||||
}
|
||||
|
||||
const jwt = await createJWT(serviceKey);
|
||||
const jwt = await createJWT(serviceKey as GoogleServiceAccount);
|
||||
const accessToken = await exchangeJWTForAccessToken(jwt);
|
||||
|
||||
return {
|
||||
serviceAccount: serviceKey,
|
||||
serviceAccount: serviceKey as GoogleServiceAccount,
|
||||
accessToken,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export * from './env';
|
|||
export * from './events';
|
||||
export * from './files';
|
||||
export * from './generators';
|
||||
export * from './key';
|
||||
export * from './llm';
|
||||
export * from './math';
|
||||
export * from './openid';
|
||||
|
|
|
|||
70
packages/api/src/utils/key.ts
Normal file
70
packages/api/src/utils/key.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import axios from 'axios';
|
||||
import { logger } from '@librechat/data-schemas';
|
||||
|
||||
export interface GoogleServiceKey {
|
||||
type?: string;
|
||||
project_id?: string;
|
||||
private_key_id?: string;
|
||||
private_key?: string;
|
||||
client_email?: string;
|
||||
client_id?: string;
|
||||
auth_uri?: string;
|
||||
token_uri?: string;
|
||||
auth_provider_x509_cert_url?: string;
|
||||
client_x509_cert_url?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Google service key from file path or URL
|
||||
* @param keyPath - The path or URL to the service key file
|
||||
* @returns The parsed service key object or null if failed
|
||||
*/
|
||||
export async function loadServiceKey(keyPath: string): Promise<GoogleServiceKey | null> {
|
||||
if (!keyPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let serviceKey: unknown;
|
||||
|
||||
// Check if it's a URL
|
||||
if (/^https?:\/\//.test(keyPath)) {
|
||||
try {
|
||||
const response = await axios.get(keyPath);
|
||||
serviceKey = response.data;
|
||||
} catch (error) {
|
||||
logger.error(`Failed to fetch the service key from URL: ${keyPath}`, error);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// It's a file path
|
||||
try {
|
||||
const absolutePath = path.isAbsolute(keyPath) ? keyPath : path.resolve(keyPath);
|
||||
const fileContent = fs.readFileSync(absolutePath, 'utf8');
|
||||
serviceKey = JSON.parse(fileContent);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to load service key from file: ${keyPath}`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// If the response is a string (e.g., from a URL that returns JSON as text), parse it
|
||||
if (typeof serviceKey === 'string') {
|
||||
try {
|
||||
serviceKey = JSON.parse(serviceKey);
|
||||
} catch (parseError) {
|
||||
logger.error(`Failed to parse service key JSON from ${keyPath}`, parseError);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the service key has required fields
|
||||
if (!serviceKey || typeof serviceKey !== 'object') {
|
||||
logger.error(`Invalid service key format from ${keyPath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return serviceKey as GoogleServiceKey;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue