mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50: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 path = require('path');
|
||||||
|
const { loadServiceKey } = require('@librechat/api');
|
||||||
const { EModelEndpoint } = require('librechat-data-provider');
|
const { EModelEndpoint } = require('librechat-data-provider');
|
||||||
const { isUserProvided } = require('~/server/utils');
|
const { isUserProvided } = require('~/server/utils');
|
||||||
const { config } = require('./EndpointService');
|
const { config } = require('./EndpointService');
|
||||||
|
|
@ -18,15 +18,7 @@ async function loadAsyncEndpoints(req) {
|
||||||
path.join(__dirname, '../../..', 'data', 'auth.json');
|
path.join(__dirname, '../../..', 'data', 'auth.json');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (process.env.GOOGLE_SERVICE_KEY_FILE_PATH) {
|
serviceKey = await loadServiceKey(serviceKeyPath);
|
||||||
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');
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
i++;
|
i++;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { getGoogleConfig, isEnabled } = require('@librechat/api');
|
|
||||||
const { EModelEndpoint, AuthKeys } = require('librechat-data-provider');
|
const { EModelEndpoint, AuthKeys } = require('librechat-data-provider');
|
||||||
|
const { getGoogleConfig, isEnabled, loadServiceKey } = require('@librechat/api');
|
||||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||||
const { GoogleClient } = require('~/app');
|
const { GoogleClient } = require('~/app');
|
||||||
|
|
||||||
|
|
@ -19,17 +18,12 @@ const initializeClient = async ({ req, res, endpointOption, overrideModel, optio
|
||||||
let serviceKey = {};
|
let serviceKey = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (process.env.GOOGLE_SERVICE_KEY_FILE_PATH) {
|
const serviceKeyPath =
|
||||||
const serviceKeyPath =
|
process.env.GOOGLE_SERVICE_KEY_FILE_PATH ||
|
||||||
process.env.GOOGLE_SERVICE_KEY_FILE_PATH ||
|
path.join(__dirname, '../../../..', 'data', 'auth.json');
|
||||||
path.join(__dirname, '../../../../..', 'data', 'auth.json');
|
serviceKey = await loadServiceKey(serviceKeyPath);
|
||||||
const absolutePath = path.isAbsolute(serviceKeyPath)
|
if (!serviceKey) {
|
||||||
? serviceKeyPath
|
serviceKey = {};
|
||||||
: path.resolve(serviceKeyPath);
|
|
||||||
const fileContent = fs.readFileSync(absolutePath, 'utf8');
|
|
||||||
serviceKey = JSON.parse(fileContent);
|
|
||||||
} else {
|
|
||||||
serviceKey = require('~/data/auth.json');
|
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import type {
|
||||||
OCRImage,
|
OCRImage,
|
||||||
} from '~/types';
|
} from '~/types';
|
||||||
import { logAxiosError, createAxiosInstance } from '~/utils/axios';
|
import { logAxiosError, createAxiosInstance } from '~/utils/axios';
|
||||||
|
import { loadServiceKey } from '~/utils/key';
|
||||||
|
|
||||||
const axios = createAxiosInstance();
|
const axios = createAxiosInstance();
|
||||||
const DEFAULT_MISTRAL_BASE_URL = 'https://api.mistral.ai/v1';
|
const DEFAULT_MISTRAL_BASE_URL = 'https://api.mistral.ai/v1';
|
||||||
|
|
@ -443,27 +444,24 @@ async function loadGoogleAuthConfig(): Promise<{
|
||||||
const serviceKeyPath =
|
const serviceKeyPath =
|
||||||
process.env.GOOGLE_SERVICE_KEY_FILE_PATH ||
|
process.env.GOOGLE_SERVICE_KEY_FILE_PATH ||
|
||||||
path.join(__dirname, '..', '..', '..', 'api', 'data', 'auth.json');
|
path.join(__dirname, '..', '..', '..', 'api', 'data', 'auth.json');
|
||||||
const absolutePath = path.isAbsolute(serviceKeyPath)
|
|
||||||
? serviceKeyPath
|
|
||||||
: path.resolve(serviceKeyPath);
|
|
||||||
|
|
||||||
let serviceKey: GoogleServiceAccount;
|
const serviceKey = await loadServiceKey(serviceKeyPath);
|
||||||
try {
|
|
||||||
const authJsonContent = fs.readFileSync(absolutePath, 'utf8');
|
if (!serviceKey) {
|
||||||
serviceKey = JSON.parse(authJsonContent) as GoogleServiceAccount;
|
throw new Error(
|
||||||
} catch {
|
`Google service account not found or could not be loaded from ${serviceKeyPath}`,
|
||||||
throw new Error(`Google service account not found at ${absolutePath}`);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serviceKey.client_email || !serviceKey.private_key || !serviceKey.project_id) {
|
if (!serviceKey.client_email || !serviceKey.private_key || !serviceKey.project_id) {
|
||||||
throw new Error('Invalid Google service account configuration');
|
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);
|
const accessToken = await exchangeJWTForAccessToken(jwt);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
serviceAccount: serviceKey,
|
serviceAccount: serviceKey as GoogleServiceAccount,
|
||||||
accessToken,
|
accessToken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export * from './env';
|
||||||
export * from './events';
|
export * from './events';
|
||||||
export * from './files';
|
export * from './files';
|
||||||
export * from './generators';
|
export * from './generators';
|
||||||
|
export * from './key';
|
||||||
export * from './llm';
|
export * from './llm';
|
||||||
export * from './math';
|
export * from './math';
|
||||||
export * from './openid';
|
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