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