diff --git a/.app.pid b/.app.pid index e43e440..4c5a421 100644 --- a/.app.pid +++ b/.app.pid @@ -1 +1 @@ -96509 +97312 diff --git a/server.ts b/server.ts index 4814267..3591b2b 100644 --- a/server.ts +++ b/server.ts @@ -815,6 +815,42 @@ app.post("/api/jobs/import", (req, res) => { } }); +// API 14: Local Disk Storage Persistence Engine (No Cloud / Firebase Required) +const DATA_FILE = path.join(process.cwd(), ".data.json"); +import fs from "fs"; + +app.get("/api/local/data", (req, res) => { + try { + const key = String(req.query.key || ""); + if (!fs.existsSync(DATA_FILE)) { + return res.json({}); + } + const raw = fs.readFileSync(DATA_FILE, "utf-8"); + const store = JSON.parse(raw || "{}"); + res.json(store[key] || {}); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +app.post("/api/local/data", (req, res) => { + try { + const { key, items, profile } = req.body; + let store: any = {}; + if (fs.existsSync(DATA_FILE)) { + try { store = JSON.parse(fs.readFileSync(DATA_FILE, "utf-8") || "{}"); } catch (e) {} + } + if (key) { + if (items !== undefined) store[key] = { items }; + if (profile !== undefined) store[key] = { profile }; + } + fs.writeFileSync(DATA_FILE, JSON.stringify(store, null, 2)); + res.json({ success: true }); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + // API 10: Google Drive - List Job Application Artifacts app.get("/api/workspace/drive/files", async (req, res) => { try { diff --git a/src/lib/firebase.ts b/src/lib/firebase.ts index 849e907..de60785 100644 --- a/src/lib/firebase.ts +++ b/src/lib/firebase.ts @@ -1,185 +1,132 @@ -import { initializeApp, getApps, getApp } from 'firebase/app'; -import { - getFirestore, - collection, - doc, - getDocs, - getDoc, - setDoc, - updateDoc, - deleteDoc, - addDoc, - query, - orderBy -} from 'firebase/firestore'; -import firebaseConfig from '../../firebase-applet-config.json'; import { JobApplication, UserProfile, NotificationLog, CalendarInterviewEvent } from '../types'; -const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp(); -export const db = getFirestore(app); +export const isFirebaseConfigured = false; -const APPS_COLLECTION = 'applications'; -const PROFILE_DOC = 'user_profile'; -const NOTIFICATIONS_COLLECTION = 'notifications'; -const CALENDAR_COLLECTION = 'calendar_events'; +// 100% Pure Local Disk & LocalStorage Persistence System -export const isFirebaseConfigured = true; - -// Firestore Helper Functions with LocalStorage Sync Fallback +const STORAGE_KEYS = { + APPLICATIONS: 'ai_job_portal_applications', + USER_PROFILE: 'ai_job_portal_profile', + NOTIFICATIONS: 'ai_job_portal_notifications', + CALENDAR: 'ai_job_portal_calendar' +}; export async function fetchApplications(): Promise { try { - const q = query(collection(db, APPS_COLLECTION)); - const snapshot = await getDocs(q); - if (snapshot.empty) return []; - return snapshot.docs.map(docSnap => ({ id: docSnap.id, ...docSnap.data() } as JobApplication)); - } catch (err) { - console.warn('Firestore fetch failed, using local storage cache:', err); - const local = localStorage.getItem('ai_job_portal_applications'); - return local ? JSON.parse(local) : []; + const res = await fetch('/api/local/data?key=' + STORAGE_KEYS.APPLICATIONS); + if (res.ok) { + const data = await res.json(); + if (Array.isArray(data.items)) return data.items; + } + } catch (e) { + console.warn('Local API read failed, reading from browser localStorage'); } + const local = localStorage.getItem(STORAGE_KEYS.APPLICATIONS); + return local ? JSON.parse(local) : []; } export async function saveApplication(appData: Omit & { id?: string }): Promise { const id = appData.id || `app_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`; const record: JobApplication = { ...appData, id }; - try { - await setDoc(doc(db, APPS_COLLECTION, id), record, { merge: true }); - } catch (err) { - console.warn('Firestore save failed, caching locally:', err); - } - - // Update local storage backup - const localApps = localStorage.getItem('ai_job_portal_applications'); - const list: JobApplication[] = localApps ? JSON.parse(localApps) : []; + const list = await fetchApplications(); const idx = list.findIndex(a => a.id === id); if (idx >= 0) list[idx] = record; else list.unshift(record); - localStorage.setItem('ai_job_portal_applications', JSON.stringify(list)); + + localStorage.setItem(STORAGE_KEYS.APPLICATIONS, JSON.stringify(list)); + + try { + await fetch('/api/local/data', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ key: STORAGE_KEYS.APPLICATIONS, items: list }) + }); + } catch (e) { + console.warn('Failed to sync to local disk file'); + } return record; } -export async function updateApplicationStatus(id: string, newStatus: JobApplication['status'], note?: string): Promise { - const timestamp = new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); - const timelineItem = { date: timestamp, action: `Status changed to ${newStatus}`, details: note }; - - try { - const docRef = doc(db, APPS_COLLECTION, id); - const snap = await getDoc(docRef); - if (snap.exists()) { - const existing = snap.data() as JobApplication; - const timeline = existing.timeline || []; - timeline.unshift(timelineItem); - await updateDoc(docRef, { status: newStatus, timeline }); - } - } catch (err) { - console.warn('Firestore update failed:', err); - } - - // Local storage update - const localApps = localStorage.getItem('ai_job_portal_applications'); - if (localApps) { - const list: JobApplication[] = JSON.parse(localApps); - const idx = list.findIndex(a => a.id === id); - if (idx >= 0) { - list[idx].status = newStatus; - if (!list[idx].timeline) list[idx].timeline = []; - list[idx].timeline!.unshift(timelineItem); - localStorage.setItem('ai_job_portal_applications', JSON.stringify(list)); - } +export async function updateApplicationStatus(id: string, status: JobApplication['status'], note?: string): Promise { + const list = await fetchApplications(); + const target = list.find(a => a.id === id); + if (target) { + target.status = status; + if (note) target.notes = note; + if (!target.timeline) target.timeline = []; + target.timeline.unshift({ + date: new Date().toLocaleDateString() + ' ' + new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + action: `Status updated to ${status}` + }); + await saveApplication(target); } } export async function deleteApplicationRecord(id: string): Promise { - try { - await deleteDoc(doc(db, APPS_COLLECTION, id)); - } catch (err) { - console.warn('Firestore delete failed:', err); - } + const list = await fetchApplications(); + const updated = list.filter(a => a.id !== id); + localStorage.setItem(STORAGE_KEYS.APPLICATIONS, JSON.stringify(updated)); - const localApps = localStorage.getItem('ai_job_portal_applications'); - if (localApps) { - const list: JobApplication[] = JSON.parse(localApps); - const filtered = list.filter(a => a.id !== id); - localStorage.setItem('ai_job_portal_applications', JSON.stringify(filtered)); - } + try { + await fetch('/api/local/data', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ key: STORAGE_KEYS.APPLICATIONS, items: updated }) + }); + } catch (e) {} } export async function fetchUserProfile(): Promise { try { - const snap = await getDoc(doc(db, 'profiles', PROFILE_DOC)); - if (snap.exists()) return snap.data() as UserProfile; - } catch (err) { - console.warn('Firestore fetch profile failed:', err); - } - const local = localStorage.getItem('ai_job_portal_user_profile'); + const res = await fetch('/api/local/data?key=' + STORAGE_KEYS.USER_PROFILE); + if (res.ok) { + const data = await res.json(); + if (data.profile) return data.profile; + } + } catch (e) {} + + const local = localStorage.getItem(STORAGE_KEYS.USER_PROFILE); return local ? JSON.parse(local) : null; } export async function saveUserProfile(profile: UserProfile): Promise { + localStorage.setItem(STORAGE_KEYS.USER_PROFILE, JSON.stringify(profile)); + try { - await setDoc(doc(db, 'profiles', PROFILE_DOC), profile, { merge: true }); - } catch (err) { - console.warn('Firestore save profile failed:', err); - } - localStorage.setItem('ai_job_portal_user_profile', JSON.stringify(profile)); + await fetch('/api/local/data', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ key: STORAGE_KEYS.USER_PROFILE, profile }) + }); + } catch (e) {} } export async function fetchNotifications(): Promise { - try { - const q = query(collection(db, NOTIFICATIONS_COLLECTION)); - const snap = await getDocs(q); - return snap.docs.map(d => ({ id: d.id, ...d.data() } as NotificationLog)); - } catch (err) { - const local = localStorage.getItem('ai_job_portal_notifications'); - return local ? JSON.parse(local) : []; - } + const local = localStorage.getItem(STORAGE_KEYS.NOTIFICATIONS); + return local ? JSON.parse(local) : []; } -export async function saveNotificationLog(notif: Omit): Promise { +export async function saveNotificationLog(notifData: Omit): Promise { const id = `notif_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`; - const record: NotificationLog = { ...notif, id }; - try { - await setDoc(doc(db, NOTIFICATIONS_COLLECTION, id), record); - } catch (err) { - console.warn('Firestore save notification failed:', err); - } - - const local = localStorage.getItem('ai_job_portal_notifications'); - const list: NotificationLog[] = local ? JSON.parse(local) : []; + const record: NotificationLog = { ...notifData, id }; + const list = await fetchNotifications(); list.unshift(record); - localStorage.setItem('ai_job_portal_notifications', JSON.stringify(list)); - + localStorage.setItem(STORAGE_KEYS.NOTIFICATIONS, JSON.stringify(list)); return record; } export async function fetchCalendarEvents(): Promise { - try { - const snap = await getDocs(collection(db, CALENDAR_COLLECTION)); - return snap.docs.map(d => ({ id: d.id, ...d.data() } as CalendarInterviewEvent)); - } catch (err) { - const local = localStorage.getItem('ai_job_portal_calendar'); - return local ? JSON.parse(local) : []; - } + const local = localStorage.getItem(STORAGE_KEYS.CALENDAR); + return local ? JSON.parse(local) : []; } -export async function saveCalendarEvent(event: Omit & { id?: string }): Promise { - const id = event.id || `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`; - const record: CalendarInterviewEvent = { ...event, id }; - try { - await setDoc(doc(db, CALENDAR_COLLECTION, id), record, { merge: true }); - } catch (err) { - console.warn('Firestore save calendar event failed:', err); - } - - const local = localStorage.getItem('ai_job_portal_calendar'); - const list: CalendarInterviewEvent[] = local ? JSON.parse(local) : []; - const idx = list.findIndex(e => e.id === id); - if (idx >= 0) list[idx] = record; - else list.push(record); - localStorage.setItem('ai_job_portal_calendar', JSON.stringify(list)); - +export async function saveCalendarEvent(eventData: Omit & { id?: string }): Promise { + const id = eventData.id || `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`; + const record: CalendarInterviewEvent = { ...eventData, id }; + const list = await fetchCalendarEvents(); + list.push(record); + localStorage.setItem(STORAGE_KEYS.CALENDAR, JSON.stringify(list)); return record; }