-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpopup.js
More file actions
223 lines (182 loc) · 6.29 KB
/
popup.js
File metadata and controls
223 lines (182 loc) · 6.29 KB
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// CloudFlare configuration is loaded from config.js
// CONFIG is defined in config.js and loaded via manifest.json
const STATUS_BASE_CLASS = 'status';
function setStatus(message, state) {
const statusElement = document.getElementById('status');
statusElement.textContent = message;
statusElement.className = `${STATUS_BASE_CLASS} ${state ? state : ''}`.trim();
}
function setButtonsDisabled(isDisabled) {
['purgeBtn', 'allowAccessBtn', 'blockAccessBtn'].forEach((id) => {
const btn = document.getElementById(id);
if (btn) {
btn.disabled = isDisabled;
}
});
}
function assertConfigField(field, message) {
if (!CONFIG[field]) {
throw new Error(message);
}
}
// Get current tab URL
async function getCurrentTabUrl() {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
return tab?.url;
}
// Display the current URL
async function displayCurrentUrl() {
const url = await getCurrentTabUrl();
document.getElementById('currentUrl').textContent = url || 'Unavailable';
return url;
}
async function cfRequest(path, options = {}) {
assertConfigField('zoneId', 'Zone ID is not configured');
assertConfigField('apiToken', 'API token is not configured');
const { method = 'GET', body } = options;
const response = await fetch(`https://api.cloudflare.com/client/v4${path}`, {
method,
headers: {
'Authorization': `Bearer ${CONFIG.apiToken}`,
'Content-Type': 'application/json'
},
body: body ? JSON.stringify(body) : undefined
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new Error(data.errors?.[0]?.message || 'CloudFlare API error');
}
return data;
}
async function purgeCache() {
const url = await displayCurrentUrl();
if (!url) {
setStatus('Could not determine current tab URL.', 'error');
return;
}
try {
setButtonsDisabled(true);
setStatus('Purging cache...', 'loading');
const response = await fetch(CONFIG.apiEndpoint, {
method: 'POST',
headers: {
'Authorization': `Bearer ${CONFIG.apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ files: [url] })
});
const data = await response.json();
if (!response.ok || !data.success) {
throw new Error(data.errors?.[0]?.message || 'Unknown error occurred');
}
setStatus('Cache purged successfully!', 'success');
} catch (error) {
setStatus(`Error: ${error.message}`, 'error');
} finally {
setButtonsDisabled(false);
}
}
async function detectPublicIp() {
const endpoint = CONFIG.ipLookupUrl || 'https://api.ipify.org?format=json';
const response = await fetch(endpoint);
if (!response.ok) {
throw new Error('Failed to detect public IP address');
}
let ipValue;
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
const data = await response.json();
ipValue = data.ip || data.ipAddress || data.address;
} else {
ipValue = (await response.text()).trim();
}
if (!ipValue) {
throw new Error('Public IP response was empty');
}
const ipv4Pattern = /^(25[0-5]|2[0-4]\d|[01]?\d?\d)(\.(25[0-5]|2[0-4]\d|[01]?\d?\d)){3}$/;
const ipv6Pattern = /^([a-f0-9:]+:+)+[a-f0-9]+$/i;
if (!ipv4Pattern.test(ipValue) && !ipv6Pattern.test(ipValue)) {
throw new Error('Unexpected IP address format');
}
return ipValue;
}
async function findAllowRule() {
const rulesetsData = await cfRequest(`/zones/${CONFIG.zoneId}/rulesets`);
const customRuleset = rulesetsData.result?.find(
(ruleset) => ruleset.kind === 'zone' && ruleset.phase === 'http_request_firewall_custom'
);
if (!customRuleset) {
throw new Error('Custom firewall ruleset not found');
}
const rulesetDetails = await cfRequest(`/zones/${CONFIG.zoneId}/rulesets/${customRuleset.id}`);
const description = CONFIG.allowRuleDescription || 'Allow my own Access';
const allowRule = rulesetDetails.result?.rules?.find((rule) => rule.description === description);
if (!allowRule) {
throw new Error(`Rule "${description}" not found`);
}
return { rulesetId: customRuleset.id, rule: allowRule, description };
}
async function updateAllowRule(ipAddress) {
const { rulesetId, rule, description } = await findAllowRule();
const expression = `(ip.src eq ${ipAddress})`;
await cfRequest(`/zones/${CONFIG.zoneId}/rulesets/${rulesetId}/rules/${rule.id}`, {
method: 'PATCH',
body: {
description,
expression,
action: 'skip',
action_parameters: { ruleset: 'current' },
enabled: true,
logging: { enabled: true }
}
});
}
async function disableAllowRule() {
const { rulesetId, rule, description } = await findAllowRule();
const expression = rule.expression || '(ip.src eq 0.0.0.0)';
const actionParameters = rule.action_parameters || { ruleset: 'current' };
await cfRequest(`/zones/${CONFIG.zoneId}/rulesets/${rulesetId}/rules/${rule.id}`, {
method: 'PATCH',
body: {
description,
expression,
action: 'skip',
action_parameters: actionParameters,
enabled: false,
logging: { enabled: true }
}
});
}
async function allowMyAccess() {
try {
setButtonsDisabled(true);
setStatus('Detecting your public IP...', 'loading');
const ipAddress = await detectPublicIp();
setStatus(`Updating CloudFlare rule for ${ipAddress}...`, 'loading');
await updateAllowRule(ipAddress);
setStatus(`Rule updated. Access allowed for ${ipAddress}`, 'success');
} catch (error) {
setStatus(`Error: ${error.message}`, 'error');
} finally {
setButtonsDisabled(false);
}
}
async function blockMyAccess() {
try {
setButtonsDisabled(true);
setStatus('Disabling CloudFlare allow rule...', 'loading');
await disableAllowRule();
setStatus('Rule disabled. Your IP is no longer auto-allowed.', 'success');
} catch (error) {
setStatus(`Error: ${error.message}`, 'error');
} finally {
setButtonsDisabled(false);
}
}
// Initialize popup
document.addEventListener('DOMContentLoaded', async () => {
await displayCurrentUrl();
document.getElementById('purgeBtn').addEventListener('click', purgeCache);
document.getElementById('allowAccessBtn').addEventListener('click', allowMyAccess);
document.getElementById('blockAccessBtn').addEventListener('click', blockMyAccess);
});