CUSIPstudio / src /firebase.ts
caustino's picture
Upload 91 files
e3b5f50 verified
Raw History Blame Contribute Delete
5.15 kB
import { initializeApp } from 'firebase/app';
import {
getAuth,
signInWithPopup,
GoogleAuthProvider,
onAuthStateChanged,
User,
signOut
} from 'firebase/auth';
export type { User };
import {
getFirestore,
doc,
getDocFromServer,
collection,
addDoc,
setDoc,
getDocs,
onSnapshot
} from 'firebase/firestore';
import firebaseConfig from '../firebase-applet-config.json';
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize Firestore with explicit databaseId from config
export const db = getFirestore(app, firebaseConfig.firestoreDatabaseId);
// Initialize Auth
export const auth = getAuth(app);
// Google Workspace Scopes
export const WORKSPACE_SCOPES = [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/spreadsheets.readonly',
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.send',
'https://www.googleapis.com/auth/calendar',
'https://www.googleapis.com/auth/calendar.events',
'https://www.googleapis.com/auth/calendar.readonly',
'https://www.googleapis.com/auth/documents',
'https://www.googleapis.com/auth/documents.readonly',
'https://www.googleapis.com/auth/chat.messages',
'https://www.googleapis.com/auth/chat.messages.create',
'https://www.googleapis.com/auth/chat.spaces',
'https://www.googleapis.com/auth/chat.spaces.readonly',
'https://www.googleapis.com/auth/meetings.space.created',
'https://www.googleapis.com/auth/meetings.space.readonly'
];
const provider = new GoogleAuthProvider();
WORKSPACE_SCOPES.forEach(scope => provider.addScope(scope));
// In-memory token management (strictly not stored in localStorage)
let isSigningIn = false;
let cachedAccessToken: string | null = null;
// Auth listener
export const initAuth = (
onAuthSuccess?: (user: User, token: string) => void,
onAuthFailure?: () => void
) => {
return onAuthStateChanged(auth, async (user: User | null) => {
if (user) {
if (cachedAccessToken) {
if (onAuthSuccess) onAuthSuccess(user, cachedAccessToken);
} else if (!isSigningIn) {
cachedAccessToken = null;
if (onAuthFailure) onAuthFailure();
}
} else {
cachedAccessToken = null;
if (onAuthFailure) onAuthFailure();
}
});
};
// Sign in with Google Popup
export const googleSignIn = async (): Promise<{ user: User; accessToken: string } | null> => {
try {
isSigningIn = true;
const result = await signInWithPopup(auth, provider);
const credential = GoogleAuthProvider.credentialFromResult(result);
if (!credential?.accessToken) {
throw new Error('Failed to obtain Google Workspace access token.');
}
cachedAccessToken = credential.accessToken;
return { user: result.user, accessToken: cachedAccessToken };
} catch (error: any) {
console.error('Google Sign-in Error:', error);
throw error;
} finally {
isSigningIn = false;
}
};
export const getAccessToken = (): string | null => {
return cachedAccessToken;
};
export const logout = async () => {
await signOut(auth);
cachedAccessToken = null;
};
// Error handling standard required by Firebase skill
export enum OperationType {
CREATE = 'create',
UPDATE = 'update',
DELETE = 'delete',
LIST = 'list',
GET = 'get',
WRITE = 'write',
}
export interface FirestoreErrorInfo {
error: string;
operationType: OperationType;
path: string | null;
authInfo: {
userId?: string | null;
email?: string | null;
emailVerified?: boolean | null;
isAnonymous?: boolean | null;
tenantId?: string | null;
providerInfo?: {
providerId?: string | null;
email?: string | null;
}[];
};
}
export function handleFirestoreError(error: unknown, operationType: OperationType, path: string | null): never {
const errInfo: FirestoreErrorInfo = {
error: error instanceof Error ? error.message : String(error),
authInfo: {
userId: auth.currentUser?.uid,
email: auth.currentUser?.email,
emailVerified: auth.currentUser?.emailVerified,
isAnonymous: auth.currentUser?.isAnonymous,
tenantId: auth.currentUser?.tenantId,
providerInfo: auth.currentUser?.providerData?.map(provider => ({
providerId: provider.providerId,
email: provider.email,
})) || []
},
operationType,
path
};
console.error('Firestore Error: ', JSON.stringify(errInfo));
throw new Error(JSON.stringify(errInfo));
}
// Test connection on app load
export async function testFirestoreConnection() {
try {
await getDocFromServer(doc(db, 'test', 'connection'));
} catch (error: any) {
if (error instanceof Error && error.message.includes('the client is offline')) {
console.warn("Firestore client appears offline. Please check network connection.");
}
}
}
testFirestoreConnection();