File size: 4,289 Bytes
7e49c4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { initializeApp, cert, getApps } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import logger from './logger.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

let serviceAccount = null;

// 1. Try environment variable
if (process.env.FIREBASE_SERVICE_ACCOUNT) {
  try {
    serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
    logger.info('Firebase credentials loaded from FIREBASE_SERVICE_ACCOUNT environment variable.');
  } catch (err) {
    logger.error('Failed to parse FIREBASE_SERVICE_ACCOUNT environment variable:', {
      error: err.message || err,
    });
  }
}

// 2. Try local firebase-key.json
const keyPath = path.resolve(__dirname, '../../firebase-key.json');
if (!serviceAccount && fs.existsSync(keyPath)) {
  try {
    const rawKey = fs.readFileSync(keyPath, 'utf8');
    serviceAccount = JSON.parse(rawKey);
    logger.info('Firebase credentials loaded from firebase-key.json.');
  } catch (err) {
    logger.error('Failed to load firebase-key.json:', { error: err.message || err });
  }
}

let db;

if (serviceAccount && process.env.NODE_ENV !== 'test') {
  try {
    if (getApps().length === 0) {
      initializeApp({
        credential: cert(serviceAccount),
      });
    }
    db = getFirestore();
    logger.info('EcoGuide Connected to Firebase Firestore');
  } catch (err) {
    logger.error('Firebase initialization failed:', { error: err.message || err });
  }
}

if (!db) {
  // Safe mock db for testing or local development fallback
  logger.info('EcoGuide Running with IN-MEMORY / MOCK Firestore');

  // Minimal Firestore mock interface
  class MockDoc {
    constructor(data, id) {
      this.id = id;
      this._data = data;
      this.exists = !!data;
    }
    data() {
      return this._data || null;
    }
  }

  class MockQuerySnapshot {
    constructor(docs) {
      this.docs = docs;
      this.empty = docs.length === 0;
    }
  }

  class MockCollection {
    constructor(name, parentDoc = null) {
      this.name = name;
      this.parentDoc = parentDoc;
    }

    doc(id) {
      const self = this;
      return {
        id,
        get: async () => {
          // Access the globally mocked store
          const path = self.getPath(id);
          const data = mockStore[path];
          return new MockDoc(data, id);
        },
        set: async (data) => {
          const path = self.getPath(id);
          mockStore[path] = { ...data };
        },
        update: async (data) => {
          const path = self.getPath(id);
          mockStore[path] = { ...(mockStore[path] || {}), ...data };
        },
        delete: async () => {
          const path = self.getPath(id);
          delete mockStore[path];
        },
        collection: (subName) => {
          return new MockCollection(subName, { path: self.getPath(id) });
        },
      };
    }

    getPath(docId) {
      if (this.parentDoc) {
        return `${this.parentDoc.path}/${this.name}/${docId}`;
      }
      return `${this.name}/${docId}`;
    }

    async get() {
      const prefix = this.parentDoc ? `${this.parentDoc.path}/${this.name}/` : `${this.name}/`;
      const matchedDocs = [];
      for (const [key, val] of Object.entries(mockStore)) {
        if (key.startsWith(prefix)) {
          const parts = key.slice(prefix.length).split('/');
          if (parts.length === 1) {
            matchedDocs.push(new MockDoc(val, parts[0]));
          }
        }
      }
      return new MockQuerySnapshot(matchedDocs);
    }

    where(field, op, value) {
      const self = this;
      return {
        get: async () => {
          const snapshot = await self.get();
          const filtered = snapshot.docs.filter((doc) => {
            const val = doc.data()?.[field];
            if (op === '==') return val === value;
            return false;
          });
          return new MockQuerySnapshot(filtered);
        },
      };
    }
  }

  // Global in-memory storage for the mock Firestore
  const mockStore = {};

  db = {
    collection: (name) => new MockCollection(name),
    _mockStore: mockStore, // Expose for testing
  };
}

export default db;