358 lines
14 KiB
JavaScript
358 lines
14 KiB
JavaScript
// API Key management functionality
|
|
|
|
// Load API keys
|
|
async function loadApiKeys() {
|
|
if (!config.isConfigured()) {
|
|
showAlert('Please configure your API settings first.', 'warning');
|
|
return;
|
|
}
|
|
|
|
const container = document.getElementById('apiKeysContainer');
|
|
container.innerHTML = '<div class="text-center"><div class="loading-spinner"></div> Loading API keys...</div>';
|
|
|
|
try {
|
|
const response = await apiClient.getApiKeys();
|
|
// Handle structured response - extract api_keys array from response object
|
|
const apiKeys = response.api_keys || response;
|
|
displayApiKeys(apiKeys);
|
|
} catch (error) {
|
|
handleApiError(error, 'loading API keys');
|
|
container.innerHTML = '<div class="alert alert-danger">Failed to load API keys</div>';
|
|
}
|
|
}
|
|
|
|
// Display API keys
|
|
function displayApiKeys(apiKeys) {
|
|
const container = document.getElementById('apiKeysContainer');
|
|
|
|
if (!apiKeys || apiKeys.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-key fa-3x text-muted mb-3"></i>
|
|
<h4>No API keys found</h4>
|
|
<p class="text-muted">Create your first API key to get started!</p>
|
|
<button class="btn btn-primary" onclick="showCreateApiKeyModal()">
|
|
<i class="fas fa-plus me-1"></i>Create API Key
|
|
</button>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
const apiKeysHtml = `
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Key</th>
|
|
<th>Status</th>
|
|
<th>Created</th>
|
|
<th>Last Used</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${apiKeys.map(key => `
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-key me-2 text-primary"></i>
|
|
${escapeHtml(key.name)}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<code class="me-2" id="key-${key.id}">
|
|
${key.key ? key.key.substring(0, 8) + '...' : 'Hidden'}
|
|
</code>
|
|
<button class="btn btn-sm btn-outline-secondary"
|
|
onclick="copyApiKey('${key.key || ''}')"
|
|
title="Copy to clipboard">
|
|
<i class="fas fa-copy"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge ${key.is_active ? 'bg-success' : 'bg-danger'}">
|
|
${key.is_active ? 'Active' : 'Inactive'}
|
|
</span>
|
|
</td>
|
|
<td class="text-muted small">${formatDate(key.created_at)}</td>
|
|
<td class="text-muted small">
|
|
${key.last_used_at ? formatDate(key.last_used_at) : 'Never'}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group" role="group">
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewApiKey('${key.id}')">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger" onclick="deleteApiKey('${key.id}')">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
`;
|
|
|
|
container.innerHTML = apiKeysHtml;
|
|
}
|
|
|
|
// Show create API key modal
|
|
function showCreateApiKeyModal() {
|
|
const modalBody = `
|
|
<form id="createApiKeyForm">
|
|
<div class="mb-3">
|
|
<label for="apiKeyName" class="form-label">API Key Name *</label>
|
|
<input type="text" class="form-control" id="apiKeyName" required
|
|
placeholder="e.g., Mobile App, Integration Service">
|
|
<div class="form-text">Choose a descriptive name to identify this API key</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="apiKeyDescription" class="form-label">Description</label>
|
|
<textarea class="form-control" id="apiKeyDescription" rows="3"
|
|
placeholder="Describe what this API key will be used for..."></textarea>
|
|
</div>
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
<strong>Important:</strong> The API key will only be shown once after creation.
|
|
Make sure to copy and store it securely.
|
|
</div>
|
|
</form>
|
|
`;
|
|
|
|
const modalFooter = `
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="createApiKey()">
|
|
<i class="fas fa-plus me-1"></i>Create API Key
|
|
</button>
|
|
`;
|
|
|
|
const modal = createModal('createApiKeyModal', 'Create New API Key', modalBody, modalFooter);
|
|
|
|
// Handle form submission
|
|
document.getElementById('createApiKeyForm').addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
createApiKey();
|
|
});
|
|
|
|
modal.show();
|
|
|
|
// Focus on name field
|
|
setTimeout(() => document.getElementById('apiKeyName').focus(), 100);
|
|
}
|
|
|
|
// Create API key
|
|
async function createApiKey() {
|
|
const name = document.getElementById('apiKeyName').value.trim();
|
|
const description = document.getElementById('apiKeyDescription').value.trim();
|
|
|
|
if (!name) {
|
|
showAlert('API key name is required', 'danger');
|
|
return;
|
|
}
|
|
|
|
const createButton = document.querySelector('#createApiKeyModal .btn-primary');
|
|
setLoadingState(createButton);
|
|
|
|
try {
|
|
const result = await apiClient.createApiKey({
|
|
name,
|
|
description
|
|
});
|
|
|
|
// Close the create modal
|
|
bootstrap.Modal.getInstance(document.getElementById('createApiKeyModal')).hide();
|
|
removeModal('createApiKeyModal');
|
|
|
|
// Show the new API key
|
|
showNewApiKeyModal(result);
|
|
|
|
// Refresh the list
|
|
loadApiKeys();
|
|
|
|
} catch (error) {
|
|
handleApiError(error, 'creating API key');
|
|
} finally {
|
|
setLoadingState(createButton, false);
|
|
}
|
|
}
|
|
|
|
// Show new API key modal
|
|
function showNewApiKeyModal(apiKeyData) {
|
|
const modalBody = `
|
|
<div class="alert alert-success">
|
|
<i class="fas fa-check-circle me-2"></i>
|
|
<strong>API Key Created Successfully!</strong>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label class="form-label"><strong>API Key Name:</strong></label>
|
|
<p>${escapeHtml(apiKeyData.name)}</p>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label class="form-label"><strong>API Key:</strong></label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="newApiKeyValue"
|
|
value="${apiKeyData.key}" readonly>
|
|
<button class="btn btn-outline-secondary" onclick="copyApiKey('${apiKeyData.key}')">
|
|
<i class="fas fa-copy"></i> Copy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
|
<strong>Important:</strong> This is the only time you'll see this API key.
|
|
Make sure to copy and store it securely before closing this dialog.
|
|
</div>
|
|
`;
|
|
|
|
const modalFooter = `
|
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="copyApiKey('${apiKeyData.key}')">
|
|
<i class="fas fa-copy me-1"></i>Copy & Close
|
|
</button>
|
|
`;
|
|
|
|
const modal = createModal('newApiKeyModal', 'New API Key Created', modalBody, modalFooter);
|
|
modal.show();
|
|
|
|
// Auto-select the API key text
|
|
setTimeout(() => {
|
|
const input = document.getElementById('newApiKeyValue');
|
|
input.select();
|
|
input.focus();
|
|
}, 100);
|
|
}
|
|
|
|
// Copy API key to clipboard
|
|
async function copyApiKey(key) {
|
|
if (!key) {
|
|
showAlert('No API key to copy', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await copyToClipboard(key);
|
|
} catch (error) {
|
|
// Fallback for older browsers
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = key;
|
|
document.body.appendChild(textArea);
|
|
textArea.select();
|
|
document.execCommand('copy');
|
|
document.body.removeChild(textArea);
|
|
showAlert('API key copied to clipboard!', 'success');
|
|
}
|
|
}
|
|
|
|
// View API key details
|
|
async function viewApiKey(keyId) {
|
|
try {
|
|
const response = await apiClient.getApiKeys();
|
|
// Handle structured response - extract api_keys array from response object
|
|
const apiKeys = response.api_keys || response;
|
|
const apiKey = apiKeys.find(k => k.id === keyId);
|
|
|
|
if (!apiKey) {
|
|
showAlert('API key not found', 'danger');
|
|
return;
|
|
}
|
|
|
|
const modalBody = `
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h5><i class="fas fa-key me-2"></i>${escapeHtml(apiKey.name)}</h5>
|
|
<p class="text-muted">${escapeHtml(apiKey.description || 'No description')}</p>
|
|
<hr>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>Key Information</h6>
|
|
<p><strong>Created:</strong> ${formatDate(apiKey.created_at)}</p>
|
|
<p><strong>Last Used:</strong> ${apiKey.last_used_at ? formatDate(apiKey.last_used_at) : 'Never'}</p>
|
|
<p><strong>ID:</strong> <code>${apiKey.id}</code></p>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Status & Security</h6>
|
|
<p><strong>Status:</strong>
|
|
<span class="badge ${apiKey.is_active ? 'bg-success' : 'bg-danger'}">
|
|
${apiKey.is_active ? 'Active' : 'Inactive'}
|
|
</span>
|
|
</p>
|
|
<p><strong>Key Preview:</strong> <code>${apiKey.key ? apiKey.key.substring(0, 8) + '...' : 'Hidden'}</code></p>
|
|
</div>
|
|
</div>
|
|
|
|
${apiKey.last_used_at ? `
|
|
<div class="mt-3">
|
|
<h6>Usage Statistics</h6>
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-chart-line me-2"></i>
|
|
This API key was last used on ${formatDate(apiKey.last_used_at)}
|
|
</div>
|
|
</div>
|
|
` : `
|
|
<div class="mt-3">
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
|
This API key has never been used
|
|
</div>
|
|
</div>
|
|
`}
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const modalFooter = `
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
<button type="button" class="btn btn-danger" onclick="deleteApiKey('${keyId}')">
|
|
<i class="fas fa-trash me-1"></i>Delete Key
|
|
</button>
|
|
`;
|
|
|
|
const modal = createModal('viewApiKeyModal', 'API Key Details', modalBody, modalFooter);
|
|
modal.show();
|
|
|
|
} catch (error) {
|
|
handleApiError(error, 'loading API key details');
|
|
}
|
|
}
|
|
|
|
// Delete API key
|
|
function deleteApiKey(keyId) {
|
|
confirmAction('Are you sure you want to delete this API key? This action cannot be undone and will immediately revoke access for any applications using this key.', async () => {
|
|
try {
|
|
await apiClient.deleteApiKey(keyId);
|
|
showAlert('API key deleted successfully!', 'success');
|
|
loadApiKeys();
|
|
|
|
// Close any open modals
|
|
const modals = ['viewApiKeyModal', 'newApiKeyModal'];
|
|
modals.forEach(modalId => {
|
|
const modalElement = document.getElementById(modalId);
|
|
if (modalElement) {
|
|
bootstrap.Modal.getInstance(modalElement)?.hide();
|
|
removeModal(modalId);
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
handleApiError(error, 'deleting API key');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Generate API key usage report
|
|
function generateUsageReport() {
|
|
showAlert('Usage report functionality would be implemented here', 'info');
|
|
}
|
|
|
|
// Bulk operations for API keys
|
|
function bulkDeleteApiKeys() {
|
|
showAlert('Bulk operations functionality would be implemented here', 'info');
|
|
}
|