gaurv007 commited on
Commit
0473fc1
Β·
verified Β·
1 Parent(s): 306583f

v3.0: Upload actual extension/background.js content

Browse files
Files changed (1) hide show
  1. extension/background.js +241 -1
extension/background.js CHANGED
@@ -1 +1,241 @@
1
- /app/clauseguard/extension/background.js
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * ClauseGuard β€” Background Service Worker v3.0
3
+ * FIXED: API payload now sends {text, source_url} (not {clauses})
4
+ * FIXED: Error handling and retry logic
5
+ */
6
+
7
+ const API_BASE = "https://gaurv007-clauseguard-api.hf.space";
8
+ const FREE_SCANS_PER_MONTH = 10;
9
+ const API_TIMEOUT_MS = 45000;
10
+
11
+ const SITE_ORIGINS = [
12
+ "https://clauseguardweb.netlify.app",
13
+ ];
14
+
15
+ try { chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }); } catch(e) {}
16
+
17
+ // ─── Fetch with timeout ───
18
+ async function fetchWithTimeout(url, options, timeoutMs) {
19
+ const controller = new AbortController();
20
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
21
+ try {
22
+ const resp = await fetch(url, { ...options, signal: controller.signal });
23
+ clearTimeout(timer);
24
+ return resp;
25
+ } catch (err) { clearTimeout(timer); throw err; }
26
+ }
27
+
28
+ // ─── Message Router ───
29
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
30
+ const handle = async () => {
31
+ switch (message.type) {
32
+ case "ANALYZE_TEXT": return await handleAnalyze(message.payload, sender.tab?.id);
33
+ case "GET_AUTH": return await getAuth();
34
+ case "GET_USER": return await getUser();
35
+ case "CHECK_USAGE": return await checkUsage();
36
+ case "OPEN_SIDEPANEL": if (sender.tab?.id) chrome.sidePanel.open({ tabId: sender.tab.id }); return { ok: true };
37
+ case "GET_RESULTS": return await getStoredResults(sender.tab?.id || message.tabId);
38
+ case "SYNC_AUTH": return await syncAuthFromWebsite();
39
+ case "GET_SCAN_HISTORY": return await getScanHistory();
40
+ default: return null;
41
+ }
42
+ };
43
+ handle().then(sendResponse).catch(err => sendResponse({ error: err.message }));
44
+ return true;
45
+ });
46
+
47
+ // ─── External messages from website ───
48
+ chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
49
+ const handle = async () => {
50
+ switch (message.type) {
51
+ case "SET_AUTH": {
52
+ await chrome.storage.sync.set({
53
+ authToken: message.token,
54
+ plan: message.plan || "free",
55
+ email: message.email || "",
56
+ userName: message.name || "",
57
+ userId: message.userId || "",
58
+ authSource: "website",
59
+ lastSyncAt: Date.now(),
60
+ });
61
+ return { success: true };
62
+ }
63
+ case "LOGOUT": {
64
+ await chrome.storage.sync.remove(["authToken", "plan", "email", "userName", "userId", "authSource", "lastSyncAt"]);
65
+ return { success: true };
66
+ }
67
+ case "GET_STATUS": {
68
+ const auth = await getAuth();
69
+ const usage = await checkUsage();
70
+ return { auth, usage };
71
+ }
72
+ case "PING": {
73
+ return { installed: true, version: chrome.runtime.getManifest().version };
74
+ }
75
+ default: return null;
76
+ }
77
+ };
78
+ handle().then(sendResponse).catch(err => sendResponse({ error: err.message }));
79
+ return true;
80
+ });
81
+
82
+ // ─── Core: Analyze ───
83
+ async function handleAnalyze(payload, tabId) {
84
+ const usage = await checkUsage();
85
+ if (!usage.allowed) {
86
+ return { error: "limit_reached", message: "Free scan limit reached.", usage };
87
+ }
88
+
89
+ const { text, url } = payload;
90
+ if (!text || text.trim().length < 100) {
91
+ return { error: "too_short", message: "Not enough text to analyze." };
92
+ }
93
+
94
+ let results;
95
+ try {
96
+ const auth = await getAuth();
97
+ // FIXED: Send {text, source_url} not {clauses}
98
+ const resp = await fetchWithTimeout(`${API_BASE}/api/analyze`, {
99
+ method: "POST",
100
+ headers: {
101
+ "Content-Type": "application/json",
102
+ ...(auth.token ? { Authorization: `Bearer ${auth.token}` } : {}),
103
+ },
104
+ body: JSON.stringify({ text: text.substring(0, 100000), source_url: url }),
105
+ }, API_TIMEOUT_MS);
106
+
107
+ if (resp.status === 429) {
108
+ return { error: "rate_limited", message: "Too many requests. Please wait a moment." };
109
+ }
110
+
111
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
112
+ results = await resp.json();
113
+ results.source = "api";
114
+ } catch (err) {
115
+ console.warn("API unavailable, using local:", err.message);
116
+ results = localAnalyze(text);
117
+ results.source = "local";
118
+ }
119
+
120
+ // Store results
121
+ if (tabId) {
122
+ await chrome.storage.local.set({ [`results_${tabId}`]: results });
123
+ const flagged = results.results?.filter(r => r.categories?.length > 0).length || results.flagged_count || 0;
124
+ chrome.action.setBadgeText({ text: flagged > 0 ? String(flagged) : "", tabId });
125
+ if (flagged > 0) chrome.action.setBadgeBackgroundColor({ color: flagged > 3 ? "#ef4444" : "#f59e0b", tabId });
126
+ }
127
+
128
+ // Save scan to history
129
+ const scanRecord = {
130
+ url: url || "",
131
+ risk_score: results.risk_score,
132
+ grade: results.grade,
133
+ flagged_count: results.flagged_count,
134
+ total_clauses: results.total_clauses,
135
+ source: results.source,
136
+ scanned_at: Date.now(),
137
+ };
138
+
139
+ const { scanHistory = [] } = await chrome.storage.local.get("scanHistory");
140
+ scanHistory.unshift(scanRecord);
141
+ if (scanHistory.length > 50) scanHistory.length = 50;
142
+ await chrome.storage.local.set({ scanHistory });
143
+
144
+ await incrementUsage();
145
+ return results;
146
+ }
147
+
148
+ // ─── Get scan history ───
149
+ async function getScanHistory() {
150
+ const { scanHistory = [] } = await chrome.storage.local.get("scanHistory");
151
+ return { history: scanHistory };
152
+ }
153
+
154
+ // ─── Sync auth from website ───
155
+ async function syncAuthFromWebsite() {
156
+ return await getAuth();
157
+ }
158
+
159
+ // ─── Local fallback ───
160
+ function localAnalyze(text) {
161
+ const clauses = splitIntoClauses(text);
162
+ const patterns = {
163
+ 0: [/arbitrat/i, /binding arbitration/i, /waive.*right.*court/i],
164
+ 1: [/sole discretion/i, /reserves? the right to (modify|change|update)/i, /at any time.*without.*notice/i],
165
+ 2: [/remove.*content.*without/i, /right to remove/i],
166
+ 3: [/exclusive jurisdiction/i, /courts? of.*(california|delaware|new york|ireland)/i, /submit to.*jurisdiction/i],
167
+ 4: [/governed by.*laws? of/i, /shall be governed/i],
168
+ 5: [/not liable/i, /shall not be (liable|responsible)/i, /in no event.*liable/i, /limitation of liability/i, /without warranty/i],
169
+ 6: [/terminat.*at any time/i, /suspend.*account.*without/i, /we may (terminat|suspend)/i],
170
+ 7: [/by (using|accessing).*you agree/i, /continued use.*constitutes/i],
171
+ };
172
+ const names = ["Arbitration","Unilateral change","Content removal","Jurisdiction","Choice of law","Limitation of liability","Unilateral termination","Contract by using"];
173
+ const sevMap = {0:"HIGH",1:"MEDIUM",2:"MEDIUM",3:"MEDIUM",4:"MEDIUM",5:"HIGH",6:"HIGH",7:"LOW"};
174
+
175
+ const results = clauses.map(clause => {
176
+ const categories = [];
177
+ for (const [id, pats] of Object.entries(patterns)) {
178
+ if (pats.some(p => p.test(clause))) categories.push({ name: names[id], severity: sevMap[id] });
179
+ }
180
+ return { text: clause, categories };
181
+ });
182
+
183
+ const flagged = results.filter(r => r.categories.length > 0);
184
+ const sev = { HIGH: 0, MEDIUM: 0, LOW: 0 };
185
+ flagged.forEach(r => r.categories.forEach(c => sev[c.severity]++));
186
+ const risk = Math.min(100, Math.round((sev.HIGH*20 + sev.MEDIUM*10 + sev.LOW*5) / Math.max(1, clauses.length) * 100));
187
+
188
+ return {
189
+ risk_score: risk,
190
+ grade: risk >= 60 ? "F" : risk >= 40 ? "D" : risk >= 20 ? "C" : risk >= 10 ? "B" : "A",
191
+ total_clauses: clauses.length, flagged_count: flagged.length, results,
192
+ };
193
+ }
194
+
195
+ // ─── Utils ───
196
+ function splitIntoClauses(text) {
197
+ return text.replace(/\n{2,}/g, "\n").trim()
198
+ .split(/(?<=[.!?])\s+(?=[A-Z0-9(])|(?:\n)(?=\d+[.)]\s|\([a-z]\)\s)/)
199
+ .map(c => c.trim()).filter(c => c.length > 30);
200
+ }
201
+
202
+ async function getAuth() {
203
+ return new Promise(r => chrome.storage.sync.get(
204
+ ["authToken","plan","email","userName","userId","authSource","lastSyncAt"], d => r({
205
+ token: d.authToken||null, plan: d.plan||"free", email: d.email||null,
206
+ name: d.userName||null, userId: d.userId||null,
207
+ isLoggedIn: !!d.authToken, isPro: d.plan==="pro"||d.plan==="team",
208
+ isGuest: !d.authToken, authSource: d.authSource||null,
209
+ lastSyncAt: d.lastSyncAt||null,
210
+ })));
211
+ }
212
+
213
+ async function getUser() {
214
+ const auth = await getAuth();
215
+ const usage = await checkUsage();
216
+ return { ...auth, ...usage };
217
+ }
218
+
219
+ async function checkUsage() {
220
+ return new Promise(r => chrome.storage.sync.get(["plan","scansThisMonth","monthResetAt"], d => {
221
+ const now = new Date(), reset = d.monthResetAt ? new Date(d.monthResetAt) : null;
222
+ if (!reset || now.getMonth() !== reset.getMonth() || now.getFullYear() !== reset.getFullYear()) {
223
+ chrome.storage.sync.set({ scansThisMonth: 0, monthResetAt: now.toISOString() });
224
+ r({ allowed: true, used: 0, limit: FREE_SCANS_PER_MONTH, plan: d.plan||"free" });
225
+ return;
226
+ }
227
+ const used = d.scansThisMonth||0, plan = d.plan||"free";
228
+ r({ allowed: plan!=="free"||used<FREE_SCANS_PER_MONTH, used, limit: plan==="free"?FREE_SCANS_PER_MONTH:Infinity, plan });
229
+ }));
230
+ }
231
+
232
+ async function incrementUsage() {
233
+ return new Promise(r => chrome.storage.sync.get(["scansThisMonth"], d =>
234
+ chrome.storage.sync.set({ scansThisMonth: (d.scansThisMonth||0)+1 }, r)));
235
+ }
236
+
237
+ async function getStoredResults(tabId) {
238
+ return new Promise(r => chrome.storage.local.get([`results_${tabId}`], d => r(d[`results_${tabId}`]||null)));
239
+ }
240
+
241
+ chrome.tabs.onRemoved.addListener(tabId => chrome.storage.local.remove([`results_${tabId}`]));