71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
import { getApps, initializeApp, cert, type App } from 'firebase-admin/app';
|
|
import { getAuth, type Auth } from 'firebase-admin/auth';
|
|
import { getFirestore, type Firestore } from 'firebase-admin/firestore';
|
|
import { getServerFirebaseProjectId } from './firebase-config';
|
|
|
|
/**
|
|
* Server-only singleton initialization for Firebase Admin.
|
|
*
|
|
* Requirements:
|
|
* - Singleton reuse across Next.js API route invocations.
|
|
* - Server-only execution (never imported or bundled in client-side code).
|
|
* - Fails closed if project configuration is missing.
|
|
* - Requires explicit FIREBASE_PROJECT_ID with no fallback to production.
|
|
* - Supports Google Application Default Credentials (ADC), explicit service account
|
|
* via FIREBASE_SERVICE_ACCOUNT or GOOGLE_APPLICATION_CREDENTIALS, and Firebase emulator.
|
|
*/
|
|
|
|
let _adminApp: App | null = null;
|
|
|
|
export function resetFirebaseAdminAppForTesting(): void {
|
|
_adminApp = null;
|
|
}
|
|
|
|
export function getFirebaseAdminApp(): App {
|
|
if (_adminApp) {
|
|
return _adminApp;
|
|
}
|
|
|
|
const existingApps = getApps();
|
|
if (existingApps.length > 0 && existingApps[0]) {
|
|
_adminApp = existingApps[0];
|
|
return _adminApp;
|
|
}
|
|
|
|
const projectId = getServerFirebaseProjectId();
|
|
if (process.env.NEXT_PUBLIC_FIREBASE_EMULATORS === 'true' || projectId.startsWith('demo-')) {
|
|
process.env.FIREBASE_AUTH_EMULATOR_HOST = process.env.FIREBASE_AUTH_EMULATOR_HOST || '127.0.0.1:9099';
|
|
process.env.FIRESTORE_EMULATOR_HOST = process.env.FIRESTORE_EMULATOR_HOST || '127.0.0.1:8088';
|
|
}
|
|
const serviceAccountJson = process.env.FIREBASE_SERVICE_ACCOUNT;
|
|
|
|
let credential;
|
|
if (serviceAccountJson) {
|
|
try {
|
|
credential = cert(JSON.parse(serviceAccountJson));
|
|
} catch (err) {
|
|
console.error('[FIREBASE_ADMIN] Failed to parse FIREBASE_SERVICE_ACCOUNT credential:', err);
|
|
throw new Error('Invalid Firebase service account configuration.');
|
|
}
|
|
}
|
|
|
|
try {
|
|
_adminApp = initializeApp({
|
|
projectId,
|
|
...(credential ? { credential } : {}),
|
|
});
|
|
return _adminApp;
|
|
} catch (err: any) {
|
|
console.error('[FIREBASE_ADMIN] Failed to initialize Firebase Admin SDK:', err?.message || err);
|
|
throw new Error('Firebase Admin SDK initialization failed.');
|
|
}
|
|
}
|
|
|
|
export function getAdminAuth(): Auth {
|
|
return getAuth(getFirebaseAdminApp());
|
|
}
|
|
|
|
export function getAdminFirestore(): Firestore {
|
|
return getFirestore(getFirebaseAdminApp());
|
|
}
|