@dynamicu/chromedebug-mcp 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +344 -0
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/chrome-extension/README.md +41 -0
- package/chrome-extension/background.js +3917 -0
- package/chrome-extension/chrome-session-manager.js +706 -0
- package/chrome-extension/content.css +181 -0
- package/chrome-extension/content.js +3022 -0
- package/chrome-extension/data-buffer.js +435 -0
- package/chrome-extension/dom-tracker.js +411 -0
- package/chrome-extension/extension-config.js +78 -0
- package/chrome-extension/firebase-client.js +278 -0
- package/chrome-extension/firebase-config.js +32 -0
- package/chrome-extension/firebase-config.module.js +22 -0
- package/chrome-extension/firebase-config.module.template.js +27 -0
- package/chrome-extension/firebase-config.template.js +36 -0
- package/chrome-extension/frame-capture.js +407 -0
- package/chrome-extension/icon128.png +1 -0
- package/chrome-extension/icon16.png +1 -0
- package/chrome-extension/icon48.png +1 -0
- package/chrome-extension/license-helper.js +181 -0
- package/chrome-extension/logger.js +23 -0
- package/chrome-extension/manifest.json +73 -0
- package/chrome-extension/network-tracker.js +510 -0
- package/chrome-extension/offscreen.html +10 -0
- package/chrome-extension/options.html +203 -0
- package/chrome-extension/options.js +282 -0
- package/chrome-extension/pako.min.js +2 -0
- package/chrome-extension/performance-monitor.js +533 -0
- package/chrome-extension/pii-redactor.js +405 -0
- package/chrome-extension/popup.html +532 -0
- package/chrome-extension/popup.js +2446 -0
- package/chrome-extension/upload-manager.js +323 -0
- package/chrome-extension/web-vitals.iife.js +1 -0
- package/config/api-keys.json +11 -0
- package/config/chrome-pilot-config.json +45 -0
- package/package.json +126 -0
- package/scripts/cleanup-processes.js +109 -0
- package/scripts/config-manager.js +280 -0
- package/scripts/generate-extension-config.js +53 -0
- package/scripts/setup-security.js +64 -0
- package/src/capture/architecture.js +426 -0
- package/src/capture/error-handling-tests.md +38 -0
- package/src/capture/error-handling-types.ts +360 -0
- package/src/capture/index.js +508 -0
- package/src/capture/interfaces.js +625 -0
- package/src/capture/memory-manager.js +713 -0
- package/src/capture/types.js +342 -0
- package/src/chrome-controller.js +2658 -0
- package/src/cli.js +19 -0
- package/src/config-loader.js +303 -0
- package/src/database.js +2178 -0
- package/src/firebase-license-manager.js +462 -0
- package/src/firebase-privacy-guard.js +397 -0
- package/src/http-server.js +1516 -0
- package/src/index-direct.js +157 -0
- package/src/index-modular.js +219 -0
- package/src/index-monolithic-backup.js +2230 -0
- package/src/index.js +305 -0
- package/src/legacy/chrome-controller-old.js +1406 -0
- package/src/legacy/index-express.js +625 -0
- package/src/legacy/index-old.js +977 -0
- package/src/legacy/routes.js +260 -0
- package/src/legacy/shared-storage.js +101 -0
- package/src/logger.js +10 -0
- package/src/mcp/handlers/chrome-tool-handler.js +306 -0
- package/src/mcp/handlers/element-tool-handler.js +51 -0
- package/src/mcp/handlers/frame-tool-handler.js +957 -0
- package/src/mcp/handlers/request-handler.js +104 -0
- package/src/mcp/handlers/workflow-tool-handler.js +636 -0
- package/src/mcp/server.js +68 -0
- package/src/mcp/tools/index.js +701 -0
- package/src/middleware/auth.js +371 -0
- package/src/middleware/security.js +267 -0
- package/src/port-discovery.js +258 -0
- package/src/routes/admin.js +182 -0
- package/src/services/browser-daemon.js +494 -0
- package/src/services/chrome-service.js +375 -0
- package/src/services/failover-manager.js +412 -0
- package/src/services/git-safety-service.js +675 -0
- package/src/services/heartbeat-manager.js +200 -0
- package/src/services/http-client.js +195 -0
- package/src/services/process-manager.js +318 -0
- package/src/services/process-tracker.js +574 -0
- package/src/services/profile-manager.js +449 -0
- package/src/services/project-manager.js +415 -0
- package/src/services/session-manager.js +497 -0
- package/src/services/session-registry.js +491 -0
- package/src/services/unified-session-manager.js +678 -0
- package/src/shared-storage-old.js +267 -0
- package/src/standalone-server.js +53 -0
- package/src/utils/extension-path.js +145 -0
- package/src/utils.js +187 -0
- package/src/validation/log-transformer.js +125 -0
- package/src/validation/schemas.js +391 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase License Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles license validation and management with Firebase
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Server-side license validation via Cloud Functions
|
|
8
|
+
* - Offline caching (24 hour default)
|
|
9
|
+
* - Grace period (7 days default)
|
|
10
|
+
* - Privacy guard integration (NO recording data to Firebase)
|
|
11
|
+
* - Instance tracking for anti-sharing
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import admin from 'firebase-admin';
|
|
15
|
+
import { FirebasePrivacyGuard } from './firebase-privacy-guard.js';
|
|
16
|
+
import crypto from 'crypto';
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* License cache for offline operation
|
|
26
|
+
*/
|
|
27
|
+
class LicenseCache {
|
|
28
|
+
constructor(cacheDurationHours = 24) {
|
|
29
|
+
this.cachePath = path.join(__dirname, '../data/license-cache.json');
|
|
30
|
+
this.cacheDurationMs = cacheDurationHours * 60 * 60 * 1000;
|
|
31
|
+
this.cache = this.loadCache();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
loadCache() {
|
|
35
|
+
try {
|
|
36
|
+
if (fs.existsSync(this.cachePath)) {
|
|
37
|
+
const data = fs.readFileSync(this.cachePath, 'utf8');
|
|
38
|
+
return JSON.parse(data);
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Error loading license cache:', error);
|
|
42
|
+
}
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
saveCache() {
|
|
47
|
+
try {
|
|
48
|
+
const dir = path.dirname(this.cachePath);
|
|
49
|
+
if (!fs.existsSync(dir)) {
|
|
50
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
fs.writeFileSync(this.cachePath, JSON.stringify(this.cache, null, 2));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Error saving license cache:', error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get(licenseKey) {
|
|
59
|
+
const entry = this.cache[licenseKey];
|
|
60
|
+
if (!entry) return null;
|
|
61
|
+
|
|
62
|
+
const age = Date.now() - entry.timestamp;
|
|
63
|
+
if (age > this.cacheDurationMs) {
|
|
64
|
+
// Cache expired
|
|
65
|
+
delete this.cache[licenseKey];
|
|
66
|
+
this.saveCache();
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return entry.data;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
set(licenseKey, data) {
|
|
74
|
+
this.cache[licenseKey] = {
|
|
75
|
+
data,
|
|
76
|
+
timestamp: Date.now()
|
|
77
|
+
};
|
|
78
|
+
this.saveCache();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
clear() {
|
|
82
|
+
this.cache = {};
|
|
83
|
+
this.saveCache();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
isExpired(licenseKey) {
|
|
87
|
+
const entry = this.cache[licenseKey];
|
|
88
|
+
if (!entry) return true;
|
|
89
|
+
|
|
90
|
+
const age = Date.now() - entry.timestamp;
|
|
91
|
+
return age > this.cacheDurationMs;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Firebase License Manager
|
|
97
|
+
*/
|
|
98
|
+
export class FirebaseLicenseManager {
|
|
99
|
+
constructor(config = {}) {
|
|
100
|
+
// Initialize Firebase Admin if not already initialized
|
|
101
|
+
if (!admin.apps.length) {
|
|
102
|
+
const firebaseConfig = {
|
|
103
|
+
projectId: process.env.FIREBASE_PROJECT_ID,
|
|
104
|
+
credential: admin.credential.cert({
|
|
105
|
+
projectId: process.env.FIREBASE_PROJECT_ID,
|
|
106
|
+
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
|
|
107
|
+
clientEmail: process.env.FIREBASE_CLIENT_EMAIL
|
|
108
|
+
})
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
admin.initializeApp(firebaseConfig);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.db = admin.firestore();
|
|
115
|
+
this.cache = new LicenseCache(config.cacheDurationHours || 24);
|
|
116
|
+
this.gracePeriodDays = config.gracePeriodDays || 7;
|
|
117
|
+
this.instanceId = this.getOrCreateInstanceId();
|
|
118
|
+
|
|
119
|
+
// Cloud Functions base URL
|
|
120
|
+
this.functionsUrl = process.env.FIREBASE_FUNCTIONS_URL ||
|
|
121
|
+
`https://us-central1-${process.env.FIREBASE_PROJECT_ID}.cloudfunctions.net`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get or create unique instance ID for this installation
|
|
126
|
+
*/
|
|
127
|
+
getOrCreateInstanceId() {
|
|
128
|
+
const instancePath = path.join(__dirname, '../data/instance-id.txt');
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
if (fs.existsSync(instancePath)) {
|
|
132
|
+
return fs.readFileSync(instancePath, 'utf8').trim();
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Error reading instance ID:', error);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Create new instance ID
|
|
139
|
+
const instanceId = `inst_${crypto.randomBytes(16).toString('hex')}`;
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const dir = path.dirname(instancePath);
|
|
143
|
+
if (!fs.existsSync(dir)) {
|
|
144
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
145
|
+
}
|
|
146
|
+
fs.writeFileSync(instancePath, instanceId);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Error saving instance ID:', error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return instanceId;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Validate license key
|
|
156
|
+
*
|
|
157
|
+
* @param {string} licenseKey - License key to validate
|
|
158
|
+
* @param {boolean} forceRefresh - Force refresh from server
|
|
159
|
+
* @returns {Promise<Object>} Validation result
|
|
160
|
+
*/
|
|
161
|
+
async validateLicense(licenseKey, forceRefresh = false) {
|
|
162
|
+
// Sanitize input to prevent any data leakage
|
|
163
|
+
const sanitizedInput = FirebasePrivacyGuard.sanitize({
|
|
164
|
+
licenseKey,
|
|
165
|
+
instanceId: this.instanceId
|
|
166
|
+
}, 'validateLicense');
|
|
167
|
+
|
|
168
|
+
// Check cache first (unless forced refresh)
|
|
169
|
+
if (!forceRefresh) {
|
|
170
|
+
const cached = this.cache.get(licenseKey);
|
|
171
|
+
if (cached) {
|
|
172
|
+
console.log('[License] Using cached validation result');
|
|
173
|
+
return {
|
|
174
|
+
...cached,
|
|
175
|
+
fromCache: true
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
// Call Cloud Function for server-side validation
|
|
182
|
+
const response = await fetch(`${this.functionsUrl}/validateLicense`, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: {
|
|
185
|
+
'Content-Type': 'application/json'
|
|
186
|
+
},
|
|
187
|
+
body: JSON.stringify({
|
|
188
|
+
licenseKey: sanitizedInput.licenseKey,
|
|
189
|
+
instanceId: sanitizedInput.instanceId,
|
|
190
|
+
platform: process.platform,
|
|
191
|
+
deviceName: require('os').hostname()
|
|
192
|
+
})
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const result = await response.json();
|
|
196
|
+
|
|
197
|
+
// Sanitize response before storing
|
|
198
|
+
const sanitizedResult = FirebasePrivacyGuard.sanitize(result, 'validateLicenseResponse');
|
|
199
|
+
|
|
200
|
+
if (result.valid) {
|
|
201
|
+
// Cache valid result
|
|
202
|
+
this.cache.set(licenseKey, sanitizedResult);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
...sanitizedResult,
|
|
207
|
+
fromCache: false
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('[License] Validation error:', error);
|
|
211
|
+
|
|
212
|
+
// Check if we have a cached result (even if expired)
|
|
213
|
+
const cached = this.cache.get(licenseKey);
|
|
214
|
+
if (cached) {
|
|
215
|
+
// Check grace period
|
|
216
|
+
const cacheAge = Date.now() - (this.cache.cache[licenseKey]?.timestamp || 0);
|
|
217
|
+
const gracePeriodMs = this.gracePeriodDays * 24 * 60 * 60 * 1000;
|
|
218
|
+
|
|
219
|
+
if (cacheAge <= gracePeriodMs) {
|
|
220
|
+
console.log('[License] Using grace period validation');
|
|
221
|
+
return {
|
|
222
|
+
...cached,
|
|
223
|
+
fromCache: true,
|
|
224
|
+
gracePeriod: true,
|
|
225
|
+
gracePeriodEndsAt: new Date(
|
|
226
|
+
(this.cache.cache[licenseKey]?.timestamp || 0) + gracePeriodMs
|
|
227
|
+
).toISOString()
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
valid: false,
|
|
234
|
+
error: 'License validation failed and no cached result available',
|
|
235
|
+
networkError: true
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Check usage limit for user
|
|
242
|
+
*
|
|
243
|
+
* @param {string} userId - User ID
|
|
244
|
+
* @returns {Promise<Object>} Usage status
|
|
245
|
+
*/
|
|
246
|
+
async checkUsageLimit(userId) {
|
|
247
|
+
// Sanitize input
|
|
248
|
+
const sanitizedInput = FirebasePrivacyGuard.sanitize({ userId }, 'checkUsageLimit');
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const response = await fetch(`${this.functionsUrl}/checkUsageLimit`, {
|
|
252
|
+
method: 'POST',
|
|
253
|
+
headers: {
|
|
254
|
+
'Content-Type': 'application/json'
|
|
255
|
+
},
|
|
256
|
+
body: JSON.stringify(sanitizedInput)
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const result = await response.json();
|
|
260
|
+
return FirebasePrivacyGuard.sanitize(result, 'checkUsageLimitResponse');
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error('[License] Usage check error:', error);
|
|
263
|
+
return {
|
|
264
|
+
withinLimit: false,
|
|
265
|
+
error: 'Usage check failed',
|
|
266
|
+
networkError: true
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Increment usage counter
|
|
273
|
+
*
|
|
274
|
+
* @param {string} userId - User ID
|
|
275
|
+
* @param {number} count - Count to increment
|
|
276
|
+
* @returns {Promise<Object>} Result
|
|
277
|
+
*/
|
|
278
|
+
async incrementUsage(userId, count = 1) {
|
|
279
|
+
// Sanitize input
|
|
280
|
+
const sanitizedInput = FirebasePrivacyGuard.sanitize({
|
|
281
|
+
userId,
|
|
282
|
+
count
|
|
283
|
+
}, 'incrementUsage');
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
const response = await fetch(`${this.functionsUrl}/incrementUsage`, {
|
|
287
|
+
method: 'POST',
|
|
288
|
+
headers: {
|
|
289
|
+
'Content-Type': 'application/json'
|
|
290
|
+
},
|
|
291
|
+
body: JSON.stringify(sanitizedInput)
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const result = await response.json();
|
|
295
|
+
return FirebasePrivacyGuard.sanitize(result, 'incrementUsageResponse');
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error('[License] Usage increment error:', error);
|
|
298
|
+
return {
|
|
299
|
+
success: false,
|
|
300
|
+
error: 'Usage increment failed',
|
|
301
|
+
networkError: true
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get user license information
|
|
308
|
+
*
|
|
309
|
+
* @param {string} userId - User ID
|
|
310
|
+
* @returns {Promise<Object>} License info
|
|
311
|
+
*/
|
|
312
|
+
async getUserLicense(userId) {
|
|
313
|
+
// Sanitize input
|
|
314
|
+
const sanitizedUserId = FirebasePrivacyGuard.sanitize({ userId }, 'getUserLicense').userId;
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const licenseQuery = await this.db
|
|
318
|
+
.collection('licenses')
|
|
319
|
+
.where('userId', '==', sanitizedUserId)
|
|
320
|
+
.where('status', '==', 'active')
|
|
321
|
+
.limit(1)
|
|
322
|
+
.get();
|
|
323
|
+
|
|
324
|
+
if (licenseQuery.empty) {
|
|
325
|
+
return { error: 'No active license found' };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const licenseDoc = licenseQuery.docs[0];
|
|
329
|
+
const licenseData = licenseDoc.data();
|
|
330
|
+
|
|
331
|
+
// Sanitize response
|
|
332
|
+
return FirebasePrivacyGuard.sanitize({
|
|
333
|
+
id: licenseDoc.id,
|
|
334
|
+
...licenseData
|
|
335
|
+
}, 'getUserLicenseResponse');
|
|
336
|
+
} catch (error) {
|
|
337
|
+
console.error('[License] Get user license error:', error);
|
|
338
|
+
return {
|
|
339
|
+
error: 'Failed to retrieve license',
|
|
340
|
+
networkError: true
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* List user's active instances
|
|
347
|
+
*
|
|
348
|
+
* @param {string} licenseKey - License key
|
|
349
|
+
* @returns {Promise<Array>} Active instances
|
|
350
|
+
*/
|
|
351
|
+
async listInstances(licenseKey) {
|
|
352
|
+
// Sanitize input
|
|
353
|
+
const sanitizedKey = FirebasePrivacyGuard.sanitize({ licenseKey }, 'listInstances').licenseKey;
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
const instancesQuery = await this.db
|
|
357
|
+
.collection('instances')
|
|
358
|
+
.where('licenseKey', '==', sanitizedKey)
|
|
359
|
+
.where('active', '==', true)
|
|
360
|
+
.get();
|
|
361
|
+
|
|
362
|
+
const instances = instancesQuery.docs.map(doc => {
|
|
363
|
+
const data = doc.data();
|
|
364
|
+
return FirebasePrivacyGuard.sanitize({
|
|
365
|
+
id: doc.id,
|
|
366
|
+
...data,
|
|
367
|
+
createdAt: data.createdAt?.toDate().toISOString(),
|
|
368
|
+
lastSeen: data.lastSeen?.toDate().toISOString()
|
|
369
|
+
}, 'listInstancesResponse');
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
return instances;
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error('[License] List instances error:', error);
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Deactivate an instance
|
|
381
|
+
*
|
|
382
|
+
* @param {string} instanceId - Instance ID to deactivate
|
|
383
|
+
* @returns {Promise<Object>} Result
|
|
384
|
+
*/
|
|
385
|
+
async deactivateInstance(instanceId) {
|
|
386
|
+
// Sanitize input
|
|
387
|
+
const sanitizedId = FirebasePrivacyGuard.sanitize({ instanceId }, 'deactivateInstance').instanceId;
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
await this.db.collection('instances').doc(sanitizedId).update({
|
|
391
|
+
active: false,
|
|
392
|
+
deactivatedAt: admin.firestore.FieldValue.serverTimestamp()
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
return { success: true };
|
|
396
|
+
} catch (error) {
|
|
397
|
+
console.error('[License] Deactivate instance error:', error);
|
|
398
|
+
return {
|
|
399
|
+
success: false,
|
|
400
|
+
error: 'Failed to deactivate instance'
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Clear license cache
|
|
407
|
+
*/
|
|
408
|
+
clearCache() {
|
|
409
|
+
this.cache.clear();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Get cache status
|
|
414
|
+
*/
|
|
415
|
+
getCacheStatus() {
|
|
416
|
+
return {
|
|
417
|
+
cacheSize: Object.keys(this.cache.cache).length,
|
|
418
|
+
instanceId: this.instanceId,
|
|
419
|
+
gracePeriodDays: this.gracePeriodDays
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Privacy check - ensure NO recording data can be sent
|
|
425
|
+
*
|
|
426
|
+
* This method validates that any data being sent to Firebase
|
|
427
|
+
* is free of recording/sensitive data
|
|
428
|
+
*/
|
|
429
|
+
static validateDataPrivacy(data, context = 'unknown') {
|
|
430
|
+
return FirebasePrivacyGuard.assertSafe(data, context);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Export singleton instance
|
|
435
|
+
let licenseManagerInstance = null;
|
|
436
|
+
|
|
437
|
+
export function getLicenseManager(config = {}) {
|
|
438
|
+
// Check if Firebase is configured
|
|
439
|
+
if (!process.env.FIREBASE_PROJECT_ID) {
|
|
440
|
+
console.warn('[License] Firebase not configured - licensing disabled');
|
|
441
|
+
console.warn('[License] Set FIREBASE_PROJECT_ID to enable licensing');
|
|
442
|
+
|
|
443
|
+
// Return stub that allows all operations (development mode)
|
|
444
|
+
return {
|
|
445
|
+
isConfigured: false,
|
|
446
|
+
validateLicense: async () => ({ valid: true, tier: 'dev', dev: true }),
|
|
447
|
+
checkUsageLimit: async () => ({ allowed: true, unlimited: true, dev: true }),
|
|
448
|
+
incrementUsage: async () => ({ success: true, dev: true }),
|
|
449
|
+
activateLicense: async () => ({ success: true, dev: true }),
|
|
450
|
+
deactivateLicense: async () => ({ success: true, dev: true }),
|
|
451
|
+
deactivateInstance: async () => ({ success: true, dev: true }),
|
|
452
|
+
clearCache: () => {},
|
|
453
|
+
getCacheStatus: () => ({ dev: true, licensing: 'disabled' }),
|
|
454
|
+
validateDataPrivacy: () => true
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (!licenseManagerInstance) {
|
|
459
|
+
licenseManagerInstance = new FirebaseLicenseManager(config);
|
|
460
|
+
}
|
|
461
|
+
return licenseManagerInstance;
|
|
462
|
+
}
|