Files
Alta-Proxy-Tool/chrome-extension/popup.js
T

183 lines
6.0 KiB
JavaScript

'use strict';
const APT_URL = 'http://127.0.0.1:18247/cookie';
const PAIRING_STORAGE_KEY = 'aptPairingSecret';
const PAIRING_SECRET_PATTERN = /^[A-Za-z0-9_-]{43}$/;
function isSupportedDeploymentUrl(value) {
try {
const url = new URL(value);
const host = url.hostname.toLowerCase();
return url.protocol === 'https:' &&
(host.endsWith('.avasecurity.com') || host.endsWith('.avigilon.com'));
} catch {
return false;
}
}
function createPopupController({
chromeApi,
documentApi,
navigatorApi,
fetchImpl,
confirmCopy
}) {
const tabInfo = documentApi.getElementById('tabInfo');
const pairingInfo = documentApi.getElementById('pairingInfo');
const sendBtn = documentApi.getElementById('sendBtn');
const copyBtn = documentApi.getElementById('copyBtn');
const copyWarning = documentApi.getElementById('copyWarning');
const statusMsg = documentApi.getElementById('statusMsg');
const openOptionsBtn = documentApi.getElementById('openOptionsBtn');
let detectedOrigin = null;
let pairingSecret = null;
let busy = false;
function showStatus(message, type) {
statusMsg.textContent = message;
statusMsg.className = `status-msg ${type}`;
}
function updateActions() {
const disabled = busy || !detectedOrigin || !pairingSecret;
sendBtn.disabled = disabled;
copyBtn.disabled = disabled;
}
function setBusy(value) {
busy = value;
updateActions();
}
async function getVaCookieValue() {
if (!detectedOrigin) throw new Error('NO_DEPLOYMENT');
const cookie = await chromeApi.cookies.get({ url: detectedOrigin, name: 'va' });
if (!cookie || !cookie.value) throw new Error('MISSING_COOKIE');
if (cookie.expirationDate && cookie.expirationDate < Date.now() / 1000) {
throw new Error('EXPIRED_COOKIE');
}
return cookie.value;
}
function showCookieError(error, copyOperation = false) {
if (error && error.message === 'MISSING_COOKIE') {
showStatus('No VA token found. Log in to Alta and try again.', 'error');
} else if (error && error.message === 'EXPIRED_COOKIE') {
showStatus('The VA token has expired. Log in to Alta again.', 'error');
} else if (copyOperation) {
showStatus('Could not copy the VA token. Check clipboard permission.', 'error');
} else {
showStatus('Could not reach Alta Proxy Tool on this computer.', 'error');
}
}
async function sendToApt() {
if (!detectedOrigin || !pairingSecret || busy) return;
setBusy(true);
let cookieValue = null;
try {
showStatus('Sending to Alta Proxy Tool...', 'info');
cookieValue = await getVaCookieValue();
const response = await fetchImpl(APT_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-APT-Pairing': pairingSecret
},
body: JSON.stringify({ deploymentUrl: detectedOrigin, cookieValue })
});
const data = await response.json();
if (!response.ok || !data || data.success !== true) throw new Error('BRIDGE_REJECTED');
showStatus('VA token sent successfully.', 'success');
} catch (error) {
showCookieError(error);
} finally {
cookieValue = null;
setBusy(false);
}
}
async function copyToken() {
if (!detectedOrigin || !pairingSecret || busy) return;
const confirmed = confirmCopy(
'Copy the full VA bearer token? Clipboard history or sync may retain it. Continue only if you will paste it into a trusted destination.'
);
if (!confirmed) {
showStatus('Copy cancelled. The VA token was not read.', 'info');
return;
}
setBusy(true);
let cookieValue = null;
try {
cookieValue = await getVaCookieValue();
await navigatorApi.clipboard.writeText(cookieValue);
showStatus('VA token copied. Clear your clipboard after use.', 'success');
} catch (error) {
showCookieError(error, true);
} finally {
cookieValue = null;
setBusy(false);
}
}
async function init() {
copyWarning.textContent = 'Copying exposes the full bearer token. Clipboard history or sync may retain it. A confirmation is required.';
sendBtn.addEventListener('click', sendToApt);
copyBtn.addEventListener('click', copyToken);
openOptionsBtn.addEventListener('click', () => chromeApi.runtime.openOptionsPage());
const stored = await chromeApi.storage.local.get(PAIRING_STORAGE_KEY);
const candidate = stored && stored[PAIRING_STORAGE_KEY];
if (PAIRING_SECRET_PATTERN.test(typeof candidate === 'string' ? candidate : '')) {
pairingSecret = candidate;
pairingInfo.textContent = 'Paired with Alta Proxy Tool';
pairingInfo.className = 'pairing-info paired';
openOptionsBtn.hidden = true;
} else {
pairingInfo.textContent = 'Not paired. Add the one-time secret from APT.';
pairingInfo.className = 'pairing-info unpaired';
openOptionsBtn.hidden = false;
}
const tabs = await chromeApi.tabs.query({ active: true, currentWindow: true });
const tab = tabs && tabs[0];
if (!tab || !isSupportedDeploymentUrl(tab.url)) {
tabInfo.textContent = 'This tab is not an Alta deployment.';
tabInfo.className = 'tab-info not-detected';
updateActions();
return;
}
const url = new URL(tab.url);
detectedOrigin = url.origin;
tabInfo.textContent = `Detected: ${url.hostname}`;
tabInfo.className = 'tab-info detected';
updateActions();
}
return { copyToken, init, sendToApt };
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
APT_URL,
PAIRING_STORAGE_KEY,
createPopupController,
isSupportedDeploymentUrl
};
} else {
createPopupController({
chromeApi: chrome,
documentApi: document,
navigatorApi: navigator,
fetchImpl: fetch,
confirmCopy: (message) => window.confirm(message)
}).init().catch(() => {
const status = document.getElementById('statusMsg');
status.textContent = 'Extension initialization failed. Open pairing settings and try again.';
status.className = 'status-msg error';
});
}