mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-07-29 05:53:50 +00:00
feat: major feature updates and cleanup
- Add Redis architecture implementation - Update browser extension functionality - Clean up deprecated files and documentation - Enhance backend handlers for auth, messages, search - Add new configuration options and settings - Update Docker and deployment configurations
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
/* global chrome, browser */
|
||||
|
||||
// Browser compatibility polyfill
|
||||
if (typeof browser === 'undefined' && typeof chrome !== 'undefined') {
|
||||
browser = chrome;
|
||||
}
|
||||
|
||||
const apiBaseUrlInput = document.getElementById('trackeepApiUrl');
|
||||
const apiKeyInput = document.getElementById('trackeepApiKey');
|
||||
const testConnectionBtn = document.getElementById('testConnectionBtn');
|
||||
const generateKeyBtn = document.getElementById('generateKeyBtn');
|
||||
const saveBtn = document.getElementById('saveBtn');
|
||||
const statusMessageEl = document.getElementById('statusMessage');
|
||||
const connectionStatusEl = document.getElementById('connectionStatus');
|
||||
const statusTitleEl = document.getElementById('statusTitle');
|
||||
const statusTextEl = document.getElementById('statusMessage');
|
||||
const installWelcomeEl = document.getElementById('installWelcome');
|
||||
const mainOptionsEl = document.getElementById('mainOptions');
|
||||
|
||||
function showMessage(message, type = 'info', duration = 5000) {
|
||||
statusMessageEl.textContent = message;
|
||||
statusMessageEl.className = `status-message ${type}`;
|
||||
statusMessageEl.style.display = 'flex';
|
||||
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
statusMessageEl.style.display = 'none';
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
||||
function hideMessage() {
|
||||
statusMessageEl.style.display = 'none';
|
||||
}
|
||||
|
||||
function showConnectionStatus(title, message, type = 'info') {
|
||||
connectionStatusEl.style.display = 'block';
|
||||
statusTitleEl.textContent = title;
|
||||
statusTextEl.textContent = message;
|
||||
connectionStatusEl.className = `connection-status ${type}`;
|
||||
}
|
||||
|
||||
function hideConnectionStatus() {
|
||||
connectionStatusEl.style.display = 'none';
|
||||
}
|
||||
|
||||
function setButtonLoading(button, loading = true) {
|
||||
if (loading) {
|
||||
button.disabled = true;
|
||||
const originalContent = button.innerHTML;
|
||||
button.dataset.originalContent = originalContent;
|
||||
button.innerHTML = `
|
||||
<svg class="icon icon-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
||||
</svg>
|
||||
<span>Saving...</span>
|
||||
`;
|
||||
} else {
|
||||
button.disabled = false;
|
||||
if (button.dataset.originalContent) {
|
||||
button.innerHTML = button.dataset.originalContent;
|
||||
delete button.dataset.originalContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function detectAndPrefillApiBaseUrl(callback) {
|
||||
browser.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
const tab = tabs && tabs[0];
|
||||
if (!tab || !tab.url) {
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(tab.url);
|
||||
const isTrackeepDomain = url.hostname.includes('trackeep') || url.hostname === 'localhost';
|
||||
if (isTrackeepDomain && (url.protocol === 'https:' || url.protocol === 'http:')) {
|
||||
const candidate = `${url.origin}/api/v1`;
|
||||
browser.storage.sync.get(['trackeepApiBaseUrl'], (items) => {
|
||||
if (!items.trackeepApiBaseUrl) {
|
||||
apiBaseUrlInput.value = candidate;
|
||||
}
|
||||
if (callback) callback();
|
||||
});
|
||||
} else {
|
||||
// Fallback to localhost if nothing set
|
||||
browser.storage.sync.get(['trackeepApiBaseUrl'], (items) => {
|
||||
if (!items.trackeepApiBaseUrl) {
|
||||
apiBaseUrlInput.value = 'http://localhost:8080/api/v1';
|
||||
}
|
||||
if (callback) callback();
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
browser.storage.sync.get(['trackeepApiBaseUrl', 'trackeepApiKey', 'isFirstInstall'], (items) => {
|
||||
// Handle first-time install
|
||||
if (items.isFirstInstall) {
|
||||
installWelcomeEl.style.display = 'flex';
|
||||
mainOptionsEl.style.display = 'none';
|
||||
} else {
|
||||
installWelcomeEl.style.display = 'none';
|
||||
mainOptionsEl.style.display = 'block';
|
||||
}
|
||||
|
||||
// Load saved settings
|
||||
if (items.trackeepApiBaseUrl) {
|
||||
apiBaseUrlInput.value = items.trackeepApiBaseUrl;
|
||||
}
|
||||
if (items.trackeepApiKey) {
|
||||
apiKeyInput.value = items.trackeepApiKey;
|
||||
}
|
||||
|
||||
// Auto-detect API URL if empty
|
||||
if (!items.trackeepApiBaseUrl) {
|
||||
detectAndPrefillApiBaseUrl();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
const apiBaseUrl = apiBaseUrlInput.value.trim();
|
||||
const apiKey = apiKeyInput.value.trim();
|
||||
|
||||
if (!apiBaseUrl) {
|
||||
showMessage('API base URL is required.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
showMessage('API key is required.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiKey.startsWith('tk_')) {
|
||||
showMessage('API key should start with "tk_"', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
setButtonLoading(saveBtn, true);
|
||||
showMessage('Saving settings...', 'info', 0);
|
||||
|
||||
browser.storage.sync.set({
|
||||
trackeepApiBaseUrl: apiBaseUrl,
|
||||
trackeepApiKey: apiKey,
|
||||
isFirstInstall: false
|
||||
}, () => {
|
||||
setButtonLoading(saveBtn, false);
|
||||
showMessage('Settings saved successfully!', 'success');
|
||||
});
|
||||
}
|
||||
|
||||
async function testConnection() {
|
||||
const apiBaseUrl = apiBaseUrlInput.value.trim();
|
||||
const apiKey = apiKeyInput.value.trim();
|
||||
|
||||
if (!apiBaseUrl || !apiKey) {
|
||||
showConnectionStatus('Connection Failed', 'Please enter both URL and API key', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showConnectionStatus('Testing Connection', 'Connecting to your Trackeep instance...', 'info');
|
||||
|
||||
try {
|
||||
const base = apiBaseUrl.replace(/\/$/, '');
|
||||
const response = await fetch(`${base}/auth/me`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
showConnectionStatus('Connection Successful', `Connected as ${data.username || 'user'}. API key is valid!`, 'success');
|
||||
|
||||
// Hide success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
hideConnectionStatus();
|
||||
}, 3000);
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
showConnectionStatus('Connection Failed', `Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function generateApiKey() {
|
||||
const apiBaseUrl = apiBaseUrlInput.value.trim();
|
||||
|
||||
if (!apiBaseUrl) {
|
||||
showMessage('Please enter API URL first', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showConnectionStatus('Generating API Key', 'Opening Trackeep to generate new API key...', 'info');
|
||||
|
||||
try {
|
||||
const base = apiBaseUrl.replace(/\/$/, '');
|
||||
const response = await fetch(`${base}/auth/generate-key`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.api_key) {
|
||||
apiKeyInput.value = data.api_key;
|
||||
showConnectionStatus('API Key Generated', 'New API key generated and copied to clipboard!', 'success');
|
||||
|
||||
// Copy to clipboard
|
||||
navigator.clipboard.writeText(data.api_key);
|
||||
|
||||
// Hide success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
hideConnectionStatus();
|
||||
}, 3000);
|
||||
} else {
|
||||
throw new Error('No API key in response');
|
||||
}
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
showConnectionStatus('Generation Failed', `Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize everything when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
detectAndPrefillApiBaseUrl(() => {
|
||||
loadSettings();
|
||||
});
|
||||
|
||||
// Event listeners for main options
|
||||
saveBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
saveSettings();
|
||||
});
|
||||
|
||||
testConnectionBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
testConnection();
|
||||
});
|
||||
|
||||
generateKeyBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
generateApiKey();
|
||||
});
|
||||
|
||||
// Event listeners for setup form
|
||||
const testSetupConnectionBtn = document.getElementById('testSetupConnectionBtn');
|
||||
const completeSetupBtn = document.getElementById('completeSetupBtn');
|
||||
const getStartedBtn = document.getElementById('getStartedBtn');
|
||||
|
||||
if (testSetupConnectionBtn) {
|
||||
testSetupConnectionBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
testSetupConnection();
|
||||
});
|
||||
}
|
||||
|
||||
if (completeSetupBtn) {
|
||||
completeSetupBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
completeSetup();
|
||||
});
|
||||
}
|
||||
|
||||
if (getStartedBtn) {
|
||||
getStartedBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
// Hide welcome and show main options with setup form
|
||||
document.getElementById('installWelcome').style.display = 'none';
|
||||
document.getElementById('mainOptions').style.display = 'block';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Test connection from setup form
|
||||
async function testSetupConnection() {
|
||||
const apiBaseUrl = document.getElementById('setupApiUrl').value.trim();
|
||||
const apiKey = document.getElementById('setupApiKey').value.trim();
|
||||
|
||||
if (!apiBaseUrl || !apiKey) {
|
||||
showSetupConnectionStatus('Connection Failed', 'Please enter both URL and API key', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showSetupConnectionStatus('Testing Connection', 'Connecting to your Trackeep instance...', 'info');
|
||||
|
||||
try {
|
||||
const base = apiBaseUrl.replace(/\/$/, '');
|
||||
const response = await fetch(`${base}/auth/me`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
showSetupConnectionStatus('Connection Successful', `Connected as ${data.username || 'user'}. API key is valid!`, 'success');
|
||||
|
||||
// Copy values to main form
|
||||
document.getElementById('trackeepApiUrl').value = apiBaseUrl;
|
||||
document.getElementById('trackeepApiKey').value = apiKey;
|
||||
|
||||
// Hide success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
hideSetupConnectionStatus();
|
||||
}, 3000);
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
showSetupConnectionStatus('Connection Failed', `Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Complete setup
|
||||
function completeSetup() {
|
||||
const apiBaseUrl = document.getElementById('setupApiUrl').value.trim();
|
||||
const apiKey = document.getElementById('setupApiKey').value.trim();
|
||||
|
||||
if (!apiBaseUrl || !apiKey) {
|
||||
showMessage('Please fill in both URL and API key', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Save settings
|
||||
browser.storage.sync.set({
|
||||
trackeepApiBaseUrl: apiBaseUrl,
|
||||
trackeepApiKey: apiKey,
|
||||
isFirstInstall: false
|
||||
}, () => {
|
||||
showMessage('Setup completed successfully!', 'success');
|
||||
|
||||
// Switch to main options view
|
||||
document.getElementById('installWelcome').style.display = 'none';
|
||||
document.getElementById('mainOptions').style.display = 'block';
|
||||
|
||||
// Load settings in main form
|
||||
document.getElementById('trackeepApiUrl').value = apiBaseUrl;
|
||||
document.getElementById('trackeepApiKey').value = apiKey;
|
||||
});
|
||||
}
|
||||
|
||||
// Setup connection status functions
|
||||
function showSetupConnectionStatus(title, message, type = 'info') {
|
||||
const statusEl = document.getElementById('setupConnectionStatus');
|
||||
const titleEl = document.getElementById('setupStatusTitle');
|
||||
const messageEl = document.getElementById('setupStatusMessage');
|
||||
|
||||
statusEl.style.display = 'block';
|
||||
titleEl.textContent = title;
|
||||
messageEl.textContent = message;
|
||||
statusEl.className = `connection-status ${type}`;
|
||||
}
|
||||
|
||||
function hideSetupConnectionStatus() {
|
||||
document.getElementById('setupConnectionStatus').style.display = 'none';
|
||||
}
|
||||
Reference in New Issue
Block a user