fix: SUG-6 API key modal, SUG-4 project activity filter, WooCommerce health auth
- SUG-6: Replace auto-closing inline banner with persistent modal on Connect page — key stays visible until user clicks "Done" - SUG-4: Filter health_metric_recorded/health_check from project detail activity - WooCommerce: Accept consumer_key/consumer_secret credentials (preferred) with username/app_password fallback; fix health check to use WC endpoint and creds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -918,12 +918,15 @@ async def get_project_detail(project_id: str) -> dict | None:
|
||||
|
||||
# Get recent activity for this project
|
||||
audit_logger = get_audit_logger()
|
||||
recent_entries = audit_logger.get_recent_entries(limit=20)
|
||||
recent_entries = audit_logger.get_recent_entries(limit=60)
|
||||
project_activity = [
|
||||
e
|
||||
for e in recent_entries
|
||||
if e.get("metadata", {}).get("project_id") == project_id
|
||||
or e.get("metadata", {}).get("site") == site_id
|
||||
if (
|
||||
e.get("metadata", {}).get("project_id") == project_id
|
||||
or e.get("metadata", {}).get("site") == site_id
|
||||
)
|
||||
and e.get("event_type", "") not in ("health_metric_recorded", "health_check")
|
||||
][:5]
|
||||
|
||||
# Get cached health status
|
||||
|
||||
@@ -563,13 +563,26 @@ class HealthMonitor:
|
||||
try:
|
||||
import aiohttp
|
||||
|
||||
auth = aiohttp.BasicAuth(
|
||||
config.username or "",
|
||||
config.app_password or "",
|
||||
)
|
||||
if plugin_type == "woocommerce":
|
||||
# WooCommerce uses consumer_key/consumer_secret
|
||||
ck = getattr(config, "consumer_key", None) or ""
|
||||
cs = getattr(config, "consumer_secret", None) or ""
|
||||
# consumer_key/consumer_secret may be in model_extra
|
||||
if not ck and hasattr(config, "model_extra"):
|
||||
ck = (config.model_extra or {}).get("consumer_key", "")
|
||||
cs = (config.model_extra or {}).get("consumer_secret", "")
|
||||
auth = aiohttp.BasicAuth(ck, cs)
|
||||
auth_check_url = f"{config.url}/wp-json/wc/v3/system_status"
|
||||
else:
|
||||
auth = aiohttp.BasicAuth(
|
||||
config.username or "",
|
||||
config.app_password or "",
|
||||
)
|
||||
auth_check_url = f"{config.url}/wp-json/wp/v2/users/me"
|
||||
|
||||
async with aiohttp.ClientSession(auth=auth) as session:
|
||||
async with session.get(
|
||||
f"{config.url}/wp-json/wp/v2/users/me",
|
||||
auth_check_url,
|
||||
timeout=aiohttp.ClientTimeout(total=10),
|
||||
ssl=False,
|
||||
) as resp:
|
||||
|
||||
@@ -28,14 +28,35 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- New key display (JS-created) -->
|
||||
<div id="new-key-display" class="hidden bg-yellow-50 dark:bg-yellow-500/20 border border-yellow-200 dark:border-yellow-500/50 text-yellow-800 dark:text-yellow-300 px-4 py-3 rounded-lg mb-4">
|
||||
<p class="font-medium mb-1">{{ t.your_api_key }}</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<code id="new-key-value" class="bg-gray-100 dark:bg-gray-900 px-3 py-1 rounded text-sm flex-1 overflow-x-auto text-gray-800 dark:text-gray-200"></code>
|
||||
<button onclick="copyNewKey()" class="text-sm text-yellow-700 dark:text-yellow-300 hover:text-yellow-600 dark:hover:text-yellow-200">{{ t['copy'] }}</button>
|
||||
<!-- New Key Modal (stays open until user clicks Done) -->
|
||||
<div id="new-key-display" class="fixed inset-0 bg-black/50 hidden items-center justify-center z-50">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 w-full max-w-lg mx-4">
|
||||
<div class="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<svg class="w-5 h-5 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
{{ t.your_api_key }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div class="bg-yellow-50 dark:bg-yellow-500/10 border border-yellow-200 dark:border-yellow-500/30 rounded-lg p-4">
|
||||
<p class="text-sm text-yellow-700 dark:text-yellow-400">{{ t.key_shown_once }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-500 dark:text-gray-400 mb-2">API Key</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<code id="new-key-value" class="flex-1 bg-gray-100 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg px-4 py-2 text-sm font-mono text-gray-800 dark:text-gray-200 overflow-x-auto"></code>
|
||||
<button onclick="copyNewKey()" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg text-white text-sm transition-colors">{{ t['copy'] }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-6 border-t border-gray-200 dark:border-gray-700 flex justify-end">
|
||||
<button onclick="closeNewKeyModal()" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg text-white text-sm transition-colors">
|
||||
{% if lang == 'fa' %}تأیید{% else %}Done{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-yellow-600 dark:text-yellow-400 mt-1">{{ t.key_shown_once }}</p>
|
||||
</div>
|
||||
|
||||
{% if api_keys %}
|
||||
@@ -132,8 +153,8 @@ async function createKey() {
|
||||
if (resp.ok && data.key) {
|
||||
document.getElementById('new-key-value').textContent = data.key.key;
|
||||
document.getElementById('new-key-display').classList.remove('hidden');
|
||||
// Reload to show in table
|
||||
setTimeout(() => location.reload(), 500);
|
||||
document.getElementById('new-key-display').classList.add('flex');
|
||||
// Modal stays open — user clicks "Done" to dismiss
|
||||
} else {
|
||||
alert(data.error || 'Failed to create key');
|
||||
}
|
||||
@@ -180,6 +201,12 @@ function copyNewKey() {
|
||||
navigator.clipboard.writeText(key);
|
||||
}
|
||||
|
||||
function closeNewKeyModal() {
|
||||
document.getElementById('new-key-display').classList.add('hidden');
|
||||
document.getElementById('new-key-display').classList.remove('flex');
|
||||
location.reload();
|
||||
}
|
||||
|
||||
function copyConfig() {
|
||||
const text = document.getElementById('config-output').textContent;
|
||||
navigator.clipboard.writeText(text);
|
||||
|
||||
Reference in New Issue
Block a user