fix: support editing upstream entries in wizard
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
<div class="upstream-preview-name" id="upstream-preview-name"></div>
|
||||
<div class="upstream-preview-url" id="upstream-preview-url"></div>
|
||||
<span class="upstream-auth-badge" id="upstream-preview-badge"></span>
|
||||
<button class="upstream-preview-change" onclick="openUpstreamDropdown()" data-i18n="wizard.step2.change">Change</button>
|
||||
<button class="upstream-preview-change" onclick="beginEditSelectedUpstream(event)" data-i18n="wizard.step2.change">Change</button>
|
||||
</div>
|
||||
|
||||
<!-- Register new upstream trigger row -->
|
||||
|
||||
@@ -458,6 +458,9 @@ var TRANSLATIONS = {
|
||||
'wizard.step2.upstream_placeholder': 'Select an upstream…',
|
||||
'wizard.step2.search_placeholder': 'Search by name or URL…',
|
||||
'wizard.step2.change': 'Change',
|
||||
'wizard.step2.auth_none': 'No auth',
|
||||
'wizard.step2.auth_bearer': 'Bearer token',
|
||||
'wizard.step2.auth_apikey': 'API key',
|
||||
'wizard.step2.register_new': 'Register new upstream',
|
||||
'wizard.step2.name': 'Name',
|
||||
'wizard.step2.base_url': 'Base URL',
|
||||
@@ -1237,6 +1240,9 @@ var TRANSLATIONS = {
|
||||
'wizard.step2.upstream_placeholder': 'Выберите upstream…',
|
||||
'wizard.step2.search_placeholder': 'Поиск по имени или URL…',
|
||||
'wizard.step2.change': 'Изменить',
|
||||
'wizard.step2.auth_none': 'Без авторизации',
|
||||
'wizard.step2.auth_bearer': 'Bearer token',
|
||||
'wizard.step2.auth_apikey': 'API key',
|
||||
'wizard.step2.register_new': 'Зарегистрировать новый upstream',
|
||||
'wizard.step2.name': 'Имя',
|
||||
'wizard.step2.base_url': 'Base URL',
|
||||
|
||||
+84
-13
@@ -288,15 +288,42 @@ function selectGqlType(btn) {
|
||||
/* ── Upstream selector (Step 3) ── */
|
||||
|
||||
var upstreams = [
|
||||
{ id: 'acme-api', name: 'acme-api', url: 'https://api.acme.com', auth: 'bearer', authLabel: 'Bearer token' },
|
||||
{ id: 'stripe', name: 'stripe-payments', url: 'https://api.stripe.com', auth: 'apikey', authLabel: 'API key' },
|
||||
{ id: 'internal-crm', name: 'internal-crm', url: 'https://crm.internal.acme.io', auth: 'none', authLabel: 'No auth' },
|
||||
{ id: 'sendgrid', name: 'sendgrid-mail', url: 'https://api.sendgrid.com', auth: 'apikey', authLabel: 'API key' },
|
||||
{ id: 'twilio', name: 'twilio-sms', url: 'https://api.twilio.com', auth: 'bearer', authLabel: 'Bearer token' },
|
||||
{ id: 'acme-api', name: 'acme-api', url: 'https://api.acme.com', authHeaders: '{\n \"Authorization\": \"Bearer ${secrets.ACME_API_TOKEN}\"\n}' },
|
||||
{ id: 'stripe', name: 'stripe-payments', url: 'https://api.stripe.com', authHeaders: '{\n \"Authorization\": \"Bearer ${secrets.STRIPE_API_KEY}\"\n}' },
|
||||
{ id: 'internal-crm', name: 'internal-crm', url: 'https://crm.internal.acme.io', authHeaders: '{\n}' },
|
||||
{ id: 'sendgrid', name: 'sendgrid-mail', url: 'https://api.sendgrid.com', authHeaders: '{\n \"Authorization\": \"Bearer ${secrets.SENDGRID_API_KEY}\"\n}' },
|
||||
{ id: 'twilio', name: 'twilio-sms', url: 'https://api.twilio.com', authHeaders: '{\n \"Authorization\": \"Bearer ${secrets.TWILIO_API_KEY}\"\n}' },
|
||||
];
|
||||
|
||||
var selectedUpstreamId = null;
|
||||
var dropdownOpen = false;
|
||||
var editingUpstreamId = null;
|
||||
|
||||
function inferUpstreamAuth(headersText) {
|
||||
var parsed = parseStructuredText(headersText || '{}');
|
||||
var keys = parsed && typeof parsed === 'object' ? Object.keys(parsed) : [];
|
||||
if (!keys.length) {
|
||||
return { kind: 'none', label: tKey('wizard.step2.auth_none') };
|
||||
}
|
||||
|
||||
var hasAuthorization = keys.some(function(key) {
|
||||
return String(key).toLowerCase() === 'authorization';
|
||||
});
|
||||
|
||||
if (hasAuthorization) {
|
||||
return { kind: 'bearer', label: tKey('wizard.step2.auth_bearer') };
|
||||
}
|
||||
|
||||
return { kind: 'apikey', label: tKey('wizard.step2.auth_apikey') };
|
||||
}
|
||||
|
||||
function upstreamMeta(upstream) {
|
||||
var auth = inferUpstreamAuth(upstream && upstream.authHeaders);
|
||||
return {
|
||||
kind: auth.kind,
|
||||
label: auth.label,
|
||||
};
|
||||
}
|
||||
|
||||
function renderDropdownList(filter) {
|
||||
var list = document.getElementById('upstream-dropdown-list');
|
||||
@@ -315,6 +342,7 @@ function renderDropdownList(filter) {
|
||||
list.innerHTML = '';
|
||||
filtered.forEach(function(u) {
|
||||
var sel = u.id === selectedUpstreamId;
|
||||
var meta = upstreamMeta(u);
|
||||
var node = tmpl.content.cloneNode(true);
|
||||
var item = node.querySelector('.upstream-dropdown-item');
|
||||
if (sel) item.classList.add('selected');
|
||||
@@ -322,8 +350,8 @@ function renderDropdownList(filter) {
|
||||
node.querySelector('.upstream-dropdown-item-name').textContent = u.name;
|
||||
node.querySelector('.upstream-dropdown-item-url').textContent = u.url;
|
||||
var badge = node.querySelector('.upstream-auth-badge');
|
||||
badge.textContent = u.authLabel;
|
||||
badge.className = 'upstream-auth-badge auth-' + u.auth;
|
||||
badge.textContent = meta.label;
|
||||
badge.className = 'upstream-auth-badge auth-' + meta.kind;
|
||||
list.appendChild(node);
|
||||
});
|
||||
}
|
||||
@@ -364,6 +392,7 @@ function filterUpstreams(val) {
|
||||
|
||||
function pickUpstream(id) {
|
||||
selectedUpstreamId = id;
|
||||
editingUpstreamId = null;
|
||||
closeUpstreamDropdown();
|
||||
|
||||
var u = upstreams.find(function(x) { return x.id === id; });
|
||||
@@ -392,10 +421,11 @@ function pickUpstream(id) {
|
||||
var pUrl = document.getElementById('upstream-preview-url');
|
||||
var pBadge = document.getElementById('upstream-preview-badge');
|
||||
if (preview) {
|
||||
var meta = upstreamMeta(u);
|
||||
pName.textContent = u.name;
|
||||
pUrl.textContent = u.url;
|
||||
pBadge.textContent = u.authLabel;
|
||||
pBadge.className = 'upstream-auth-badge auth-' + u.auth;
|
||||
pBadge.textContent = meta.label;
|
||||
pBadge.className = 'upstream-auth-badge auth-' + meta.kind;
|
||||
preview.style.display = 'flex';
|
||||
}
|
||||
|
||||
@@ -414,6 +444,7 @@ function pickUpstream(id) {
|
||||
|
||||
function startNewUpstream() {
|
||||
closeUpstreamDropdown();
|
||||
editingUpstreamId = null;
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
var isOpen = form && form.style.display !== 'none';
|
||||
@@ -436,11 +467,35 @@ function startNewUpstream() {
|
||||
if (trigger) trigger.classList.add('active');
|
||||
if (form) {
|
||||
form.style.display = '';
|
||||
setValue('new-upstream-name', '');
|
||||
setValue('new-upstream-url', '');
|
||||
setValue('new-upstream-auth-headers', '{\n}');
|
||||
var first = form.querySelector('input');
|
||||
if (first) setTimeout(function() { first.focus(); }, 50);
|
||||
}
|
||||
}
|
||||
|
||||
function beginEditSelectedUpstream(e) {
|
||||
if (e) e.stopPropagation();
|
||||
var upstream = upstreams.find(function(item) { return item.id === selectedUpstreamId; });
|
||||
if (!upstream) return;
|
||||
|
||||
closeUpstreamDropdown();
|
||||
editingUpstreamId = upstream.id;
|
||||
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
var preview = document.getElementById('upstream-preview');
|
||||
if (trigger) trigger.classList.add('active');
|
||||
if (preview) preview.style.display = 'none';
|
||||
if (form) {
|
||||
form.style.display = '';
|
||||
setValue('new-upstream-name', upstream.name);
|
||||
setValue('new-upstream-url', upstream.url);
|
||||
setValue('new-upstream-auth-headers', upstream.authHeaders || '{\n}');
|
||||
}
|
||||
}
|
||||
|
||||
function saveNewUpstream(e) {
|
||||
e.stopPropagation();
|
||||
var nameEl = document.getElementById('new-upstream-name');
|
||||
@@ -450,18 +505,33 @@ function saveNewUpstream(e) {
|
||||
if (!name) { if (nameEl) nameEl.focus(); return; }
|
||||
if (!url) { if (urlEl) urlEl.focus(); return; }
|
||||
|
||||
// Add to list
|
||||
var newId = 'custom-' + Date.now();
|
||||
upstreams.push({ id: newId, name: name, url: url, auth: 'bearer', authLabel: 'Bearer token' });
|
||||
var authHeadersEl = document.getElementById('new-upstream-auth-headers');
|
||||
var authHeaders = authHeadersEl ? authHeadersEl.value : '{\n}';
|
||||
var nextRecord = {
|
||||
id: editingUpstreamId || ('custom-' + Date.now()),
|
||||
name: name,
|
||||
url: url,
|
||||
authHeaders: authHeaders
|
||||
};
|
||||
|
||||
if (editingUpstreamId) {
|
||||
upstreams = upstreams.map(function(item) {
|
||||
return item.id === editingUpstreamId ? nextRecord : item;
|
||||
});
|
||||
} else {
|
||||
upstreams.push(nextRecord);
|
||||
}
|
||||
|
||||
// Reset form
|
||||
if (nameEl) nameEl.value = '';
|
||||
if (urlEl) urlEl.value = '';
|
||||
if (authHeadersEl) authHeadersEl.value = '{\n}';
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.style.display = 'none';
|
||||
editingUpstreamId = null;
|
||||
|
||||
// Select the new upstream
|
||||
pickUpstream(newId);
|
||||
pickUpstream(nextRecord.id);
|
||||
}
|
||||
|
||||
function cancelNewUpstream(e) {
|
||||
@@ -470,6 +540,7 @@ function cancelNewUpstream(e) {
|
||||
if (form) form.style.display = 'none';
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
if (trigger) trigger.classList.remove('active');
|
||||
editingUpstreamId = null;
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
|
||||
Reference in New Issue
Block a user