179 lines
6.8 KiB
JavaScript
179 lines
6.8 KiB
JavaScript
const { onAuthStateChanged, signInWithCustomToken, signOut } = require('firebase/auth');
|
|
const { BrowserWindow } = require('electron');
|
|
const { getFirebaseAuth } = require('./firebaseClient');
|
|
const userRepository = require('../repositories/user');
|
|
const fetch = require('node-fetch');
|
|
|
|
async function getVirtualKeyByEmail(email, idToken) {
|
|
if (!idToken) {
|
|
throw new Error('Firebase ID token is required for virtual key request');
|
|
}
|
|
|
|
const resp = await fetch('https://serverless-api-sf3o.vercel.app/api/virtual_key', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${idToken}`,
|
|
},
|
|
body: JSON.stringify({ email: email.trim().toLowerCase() }),
|
|
redirect: 'follow',
|
|
});
|
|
|
|
const json = await resp.json().catch(() => ({}));
|
|
if (!resp.ok) {
|
|
console.error('[VK] API request failed:', json.message || 'Unknown error');
|
|
throw new Error(json.message || `HTTP ${resp.status}: Virtual key request failed`);
|
|
}
|
|
|
|
const vKey = json?.data?.virtualKey || json?.data?.virtual_key || json?.data?.newVKey?.slug;
|
|
|
|
if (!vKey) throw new Error('virtual key missing in response');
|
|
return vKey;
|
|
}
|
|
|
|
class AuthService {
|
|
constructor() {
|
|
this.currentUserId = 'default_user';
|
|
this.currentUserMode = 'local'; // 'local' or 'firebase'
|
|
this.currentUser = null;
|
|
this.hasApiKey = false; // Add a flag for API key status
|
|
this.isInitialized = false;
|
|
}
|
|
|
|
initialize() {
|
|
if (this.isInitialized) return;
|
|
|
|
const auth = getFirebaseAuth();
|
|
onAuthStateChanged(auth, async (user) => {
|
|
const previousUser = this.currentUser;
|
|
|
|
if (user) {
|
|
// User signed IN
|
|
console.log(`[AuthService] Firebase user signed in:`, user.uid);
|
|
this.currentUser = user;
|
|
this.currentUserId = user.uid;
|
|
this.currentUserMode = 'firebase';
|
|
this.hasApiKey = false; // Optimistically assume no key yet
|
|
|
|
// Broadcast immediately to make UI feel responsive
|
|
this.broadcastUserState();
|
|
|
|
// Start background task to fetch and save virtual key
|
|
(async () => {
|
|
try {
|
|
const idToken = await user.getIdToken(true);
|
|
const virtualKey = await getVirtualKeyByEmail(user.email, idToken);
|
|
await userRepository.saveApiKey(virtualKey, user.uid, 'openai');
|
|
console.log(`[AuthService] BG: Virtual key for ${user.email} has been saved.`);
|
|
// Now update the key status, which will trigger another broadcast
|
|
await this.updateApiKeyStatus();
|
|
} catch (error) {
|
|
console.error('[AuthService] BG: Failed to fetch or save virtual key:', error);
|
|
}
|
|
})();
|
|
|
|
} else {
|
|
// User signed OUT
|
|
console.log(`[AuthService] Firebase user signed out.`);
|
|
if (previousUser) {
|
|
console.log(`[AuthService] Clearing API key for logged-out user: ${previousUser.uid}`);
|
|
await userRepository.saveApiKey(null, previousUser.uid);
|
|
}
|
|
this.currentUser = null;
|
|
this.currentUserId = 'default_user';
|
|
this.currentUserMode = 'local';
|
|
// Update API key status (e.g., if a local key for default_user exists)
|
|
// This will also broadcast the final logged-out state.
|
|
await this.updateApiKeyStatus();
|
|
}
|
|
});
|
|
|
|
// Check for initial API key state
|
|
this.updateApiKeyStatus();
|
|
|
|
this.isInitialized = true;
|
|
console.log('[AuthService] Initialized and attached to Firebase Auth state.');
|
|
}
|
|
|
|
async signInWithCustomToken(token) {
|
|
const auth = getFirebaseAuth();
|
|
try {
|
|
const userCredential = await signInWithCustomToken(auth, token);
|
|
console.log(`[AuthService] Successfully signed in with custom token for user:`, userCredential.user.uid);
|
|
// onAuthStateChanged will handle the state update and broadcast
|
|
} catch (error) {
|
|
console.error('[AuthService] Error signing in with custom token:', error);
|
|
throw error; // Re-throw to be handled by the caller
|
|
}
|
|
}
|
|
|
|
async signOut() {
|
|
const auth = getFirebaseAuth();
|
|
try {
|
|
await signOut(auth);
|
|
console.log('[AuthService] User sign-out initiated successfully.');
|
|
// onAuthStateChanged will handle the state update and broadcast,
|
|
// which will also re-evaluate the API key status.
|
|
} catch (error) {
|
|
console.error('[AuthService] Error signing out:', error);
|
|
}
|
|
}
|
|
|
|
broadcastUserState() {
|
|
const userState = this.getCurrentUser();
|
|
console.log('[AuthService] Broadcasting user state change:', userState);
|
|
BrowserWindow.getAllWindows().forEach(win => {
|
|
if (win && !win.isDestroyed()) {
|
|
win.webContents.send('user-state-changed', userState);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Updates the internal API key status from the repository and broadcasts if changed.
|
|
*/
|
|
async updateApiKeyStatus() {
|
|
try {
|
|
const user = await userRepository.getById(this.currentUserId);
|
|
const newStatus = !!(user && user.api_key);
|
|
if (this.hasApiKey !== newStatus) {
|
|
console.log(`[AuthService] API key status changed to: ${newStatus}`);
|
|
this.hasApiKey = newStatus;
|
|
this.broadcastUserState();
|
|
}
|
|
} catch (error) {
|
|
console.error('[AuthService] Error checking API key status:', error);
|
|
this.hasApiKey = false;
|
|
}
|
|
}
|
|
|
|
getCurrentUserId() {
|
|
return this.currentUserId;
|
|
}
|
|
|
|
getCurrentUser() {
|
|
const isLoggedIn = !!(this.currentUserMode === 'firebase' && this.currentUser);
|
|
|
|
if (isLoggedIn) {
|
|
return {
|
|
uid: this.currentUser.uid,
|
|
email: this.currentUser.email,
|
|
displayName: this.currentUser.displayName,
|
|
mode: 'firebase',
|
|
isLoggedIn: true,
|
|
hasApiKey: this.hasApiKey // Always true for firebase users, but good practice
|
|
};
|
|
}
|
|
return {
|
|
uid: this.currentUserId, // returns 'default_user'
|
|
email: 'contact@pickle.com',
|
|
displayName: 'Default User',
|
|
mode: 'local',
|
|
isLoggedIn: false,
|
|
hasApiKey: this.hasApiKey
|
|
};
|
|
}
|
|
}
|
|
|
|
const authService = new AuthService();
|
|
module.exports = authService;
|