ui: extract wizard upstream module
This commit is contained in:
@@ -2,14 +2,14 @@
|
||||
|
||||
## Current
|
||||
|
||||
### `feat/frontend-performance-polish`
|
||||
### `feat/frontend-wizard-modularization`
|
||||
|
||||
Status: in_progress
|
||||
|
||||
DoD:
|
||||
- repeated derived views are cached or precomputed instead of recalculated per render path
|
||||
- UI interaction handlers avoid unnecessary full rerenders for small state changes
|
||||
- performance changes keep current page behavior and pass existing smoke checks
|
||||
- `wizard.js` is split by responsibility into smaller files with stable ownership boundaries
|
||||
- extracted modules keep existing wizard behavior and asset build flow
|
||||
- modularization changes pass UI build and targeted wizard smoke checks
|
||||
|
||||
## Next
|
||||
|
||||
@@ -27,6 +27,7 @@ DoD:
|
||||
- `feat/frontend-wizard-modularization`
|
||||
- `feat/frontend-css-state-cleanup`
|
||||
- `feat/frontend-performance-polish`
|
||||
- `feat/frontend-wizard-modularization`
|
||||
- `feat/frontend-observability-and-testability`
|
||||
- `feat/id-display-support`
|
||||
- `feat/postgres-pool-config`
|
||||
|
||||
@@ -0,0 +1,562 @@
|
||||
/* ── Upstream selector (Step 2) ── */
|
||||
|
||||
var upstreams = [
|
||||
{ id: 'open-meteo', name: 'Open Meteo', url: 'https://api.open-meteo.com', staticHeaders: '{}', authProfileId: null, authProfileName: '', authKind: null },
|
||||
{ id: 'countries-graphql', name: 'Countries GraphQL', url: 'https://countries.trevorblades.com', staticHeaders: '{}', authProfileId: null, authProfileName: '', authKind: null },
|
||||
{ id: 'grpcb', name: 'grpcb.in', url: 'https://grpcb.in:443', staticHeaders: '{}', authProfileId: null, authProfileName: '', authKind: null }
|
||||
];
|
||||
|
||||
var dropdownOpen = false;
|
||||
|
||||
function createDivWithClass(className, text) {
|
||||
var element = document.createElement('div');
|
||||
element.className = className;
|
||||
element.textContent = text;
|
||||
return element;
|
||||
}
|
||||
|
||||
function authProfileById(authProfileId) {
|
||||
return wizardAuthProfiles.find(function(item) { return item.id === authProfileId; }) || null;
|
||||
}
|
||||
|
||||
function syncUpstreamAuthMetadata(upstream) {
|
||||
if (!upstream) return upstream;
|
||||
if (!upstream.authProfileId) {
|
||||
upstream.authProfileName = '';
|
||||
upstream.authKind = null;
|
||||
return upstream;
|
||||
}
|
||||
|
||||
var profile = authProfileById(upstream.authProfileId);
|
||||
if (profile) {
|
||||
upstream.authProfileName = profile.name;
|
||||
upstream.authKind = profile.kind;
|
||||
}
|
||||
return upstream;
|
||||
}
|
||||
|
||||
function inferUpstreamAuth(upstream) {
|
||||
syncUpstreamAuthMetadata(upstream);
|
||||
if (!upstream || !upstream.authProfileId || !upstream.authKind) {
|
||||
return { kind: 'none', label: tKey('wizard.step2.auth_none') };
|
||||
}
|
||||
if (upstream.authKind === 'bearer') {
|
||||
return { kind: 'bearer', label: tKey('wizard.step2.auth_bearer') };
|
||||
}
|
||||
if (upstream.authKind === 'basic') {
|
||||
return { kind: 'basic', label: tKey('wizard.step2.auth_basic') };
|
||||
}
|
||||
return { kind: 'apikey', label: tKey('wizard.step2.auth_apikey') };
|
||||
}
|
||||
|
||||
function upstreamMeta(upstream) {
|
||||
var auth = inferUpstreamAuth(upstream);
|
||||
return {
|
||||
kind: auth.kind,
|
||||
label: auth.label,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadWizardAuthResources() {
|
||||
if (!wizardWorkspaceId || !window.CrankApi) {
|
||||
wizardSecrets = [];
|
||||
wizardAuthProfiles = [];
|
||||
renderAuthProfileOptions();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var results = await Promise.all([
|
||||
window.CrankApi.listSecrets(wizardWorkspaceId),
|
||||
window.CrankApi.listAuthProfiles(wizardWorkspaceId),
|
||||
]);
|
||||
wizardSecrets = (results[0] && results[0].items) || [];
|
||||
wizardAuthProfiles = (results[1] && results[1].items) || [];
|
||||
} catch (_error) {
|
||||
wizardSecrets = [];
|
||||
wizardAuthProfiles = [];
|
||||
}
|
||||
|
||||
upstreams.forEach(syncUpstreamAuthMetadata);
|
||||
renderAuthProfileOptions();
|
||||
}
|
||||
|
||||
function renderAuthProfileOptions() {
|
||||
var profileSelect = document.getElementById('new-upstream-auth-profile');
|
||||
var secretSelect = document.getElementById('new-auth-profile-secret-id');
|
||||
var usernameSelect = document.getElementById('new-auth-profile-username-secret');
|
||||
var passwordSelect = document.getElementById('new-auth-profile-password-secret');
|
||||
var profileHint = document.getElementById('new-upstream-auth-profile-hint');
|
||||
|
||||
if (profileSelect) {
|
||||
var previousProfile = profileSelect.value;
|
||||
profileSelect.innerHTML = '';
|
||||
if (!wizardAuthProfiles.length) {
|
||||
var emptyProfile = document.createElement('option');
|
||||
emptyProfile.value = '';
|
||||
emptyProfile.textContent = tKey('wizard.step2.auth_profile_empty');
|
||||
profileSelect.appendChild(emptyProfile);
|
||||
if (profileHint) profileHint.textContent = tKey('wizard.step2.auth_profile_empty');
|
||||
} else {
|
||||
var placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.textContent = tKey('wizard.step2.auth_profile');
|
||||
profileSelect.appendChild(placeholder);
|
||||
wizardAuthProfiles.forEach(function(profile) {
|
||||
var option = document.createElement('option');
|
||||
option.value = profile.id;
|
||||
option.textContent = profile.name + ' · ' + profile.kind;
|
||||
profileSelect.appendChild(option);
|
||||
});
|
||||
if (previousProfile) {
|
||||
profileSelect.value = previousProfile;
|
||||
}
|
||||
if (profileHint) profileHint.textContent = tKey('wizard.step2.auth_profile_hint');
|
||||
}
|
||||
}
|
||||
|
||||
[secretSelect, usernameSelect, passwordSelect].forEach(function(select) {
|
||||
if (!select) return;
|
||||
var previous = select.value;
|
||||
select.innerHTML = '';
|
||||
var placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.textContent = tKey('wizard.step2.secret_value');
|
||||
select.appendChild(placeholder);
|
||||
wizardSecrets.forEach(function(secret) {
|
||||
var option = document.createElement('option');
|
||||
option.value = secret.id;
|
||||
option.textContent = secret.name + ' · ' + secret.kind;
|
||||
select.appendChild(option);
|
||||
});
|
||||
if (previous) {
|
||||
select.value = previous;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateAuthProfileCreateUi() {
|
||||
var kind = textValue('new-auth-profile-kind') || 'bearer';
|
||||
var headerGroup = document.getElementById('auth-profile-header-name-group');
|
||||
var queryGroup = document.getElementById('auth-profile-query-param-group');
|
||||
var basicRow = document.getElementById('auth-profile-basic-secret-row');
|
||||
var singleSecretGroup = document.getElementById('auth-profile-single-secret-group');
|
||||
|
||||
if (headerGroup) {
|
||||
headerGroup.hidden = !(kind === 'bearer' || kind === 'api_key_header');
|
||||
}
|
||||
if (queryGroup) {
|
||||
queryGroup.hidden = kind !== 'api_key_query';
|
||||
}
|
||||
if (basicRow) {
|
||||
basicRow.hidden = kind !== 'basic';
|
||||
}
|
||||
if (singleSecretGroup) {
|
||||
singleSecretGroup.hidden = kind === 'basic';
|
||||
}
|
||||
}
|
||||
|
||||
function updateUpstreamAuthUi() {
|
||||
var mode = textValue('new-upstream-auth-mode') || 'none';
|
||||
var existingGroup = document.getElementById('upstream-auth-existing-group');
|
||||
var createGroup = document.getElementById('upstream-auth-create-group');
|
||||
if (existingGroup) existingGroup.hidden = mode !== 'existing';
|
||||
if (createGroup) createGroup.hidden = mode !== 'create';
|
||||
updateAuthProfileCreateUi();
|
||||
}
|
||||
|
||||
function buildUpstreamTriggerContent(name, url) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
fragment.appendChild(createDivWithClass('upstream-trigger-name', name));
|
||||
fragment.appendChild(createDivWithClass('upstream-trigger-url', url));
|
||||
return fragment;
|
||||
}
|
||||
|
||||
function setUpstreamComboboxPlaceholder(container) {
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
var placeholder = document.createElement('span');
|
||||
placeholder.className = 'upstream-combobox-placeholder';
|
||||
placeholder.textContent = tKey('wizard.step2.upstream_placeholder');
|
||||
container.appendChild(placeholder);
|
||||
}
|
||||
|
||||
function renderDropdownList(filter) {
|
||||
var list = document.getElementById('upstream-dropdown-list');
|
||||
if (!list) return;
|
||||
var q = (filter || '').toLowerCase();
|
||||
var filtered = upstreams.filter(function(u) {
|
||||
return !q || u.name.toLowerCase().includes(q) || u.url.toLowerCase().includes(q);
|
||||
});
|
||||
|
||||
if (filtered.length === 0) {
|
||||
list.innerHTML = '';
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'upstream-dropdown-empty';
|
||||
empty.textContent = tfKey('agents.drawer.ops_no_match', { query: filter || '' });
|
||||
list.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
var tmpl = document.getElementById('tmpl-upstream-item');
|
||||
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');
|
||||
item.addEventListener('click', function() { pickUpstream(u.id); });
|
||||
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 = meta.label;
|
||||
badge.className = 'upstream-auth-badge auth-' + meta.kind;
|
||||
list.appendChild(node);
|
||||
});
|
||||
}
|
||||
|
||||
function openUpstreamDropdown() {
|
||||
var combobox = document.getElementById('upstream-combobox');
|
||||
var dropdown = document.getElementById('upstream-dropdown');
|
||||
var search = document.getElementById('upstream-search');
|
||||
if (!combobox || !dropdown) return;
|
||||
combobox.classList.add('open');
|
||||
dropdown.hidden = false;
|
||||
dropdownOpen = true;
|
||||
renderDropdownList('');
|
||||
if (search) { search.value = ''; search.focus(); }
|
||||
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
}
|
||||
|
||||
function closeUpstreamDropdown() {
|
||||
var combobox = document.getElementById('upstream-combobox');
|
||||
var dropdown = document.getElementById('upstream-dropdown');
|
||||
if (!combobox || !dropdown) return;
|
||||
combobox.classList.remove('open');
|
||||
dropdown.hidden = true;
|
||||
dropdownOpen = false;
|
||||
}
|
||||
|
||||
function toggleUpstreamDropdown(e) {
|
||||
e.stopPropagation();
|
||||
if (dropdownOpen) { closeUpstreamDropdown(); } else { openUpstreamDropdown(); }
|
||||
}
|
||||
|
||||
function filterUpstreams(val) {
|
||||
renderDropdownList(val);
|
||||
}
|
||||
|
||||
function pickUpstream(id) {
|
||||
selectedUpstreamId = id;
|
||||
editingUpstreamId = null;
|
||||
closeUpstreamDropdown();
|
||||
|
||||
var u = upstreams.find(function(x) { return x.id === id; });
|
||||
if (!u) return;
|
||||
|
||||
var val = document.getElementById('upstream-combobox-value');
|
||||
if (val) {
|
||||
var trigTmpl = document.getElementById('tmpl-upstream-trigger-value');
|
||||
if (trigTmpl) {
|
||||
val.innerHTML = '';
|
||||
var trigNode = trigTmpl.content.cloneNode(true);
|
||||
trigNode.querySelector('.upstream-trigger-name').textContent = u.name;
|
||||
trigNode.querySelector('.upstream-trigger-url').textContent = u.url;
|
||||
val.appendChild(trigNode);
|
||||
} else {
|
||||
val.innerHTML = '';
|
||||
val.appendChild(buildUpstreamTriggerContent(u.name, u.url));
|
||||
}
|
||||
}
|
||||
|
||||
var preview = document.getElementById('upstream-preview');
|
||||
var pName = document.getElementById('upstream-preview-name');
|
||||
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 = meta.label;
|
||||
pBadge.className = 'upstream-auth-badge auth-' + meta.kind;
|
||||
preview.hidden = false;
|
||||
}
|
||||
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
if (trigger) trigger.classList.remove('active');
|
||||
|
||||
var reflectName = document.getElementById('grpc-reflect-upstream-name');
|
||||
var reflectUrl = document.getElementById('grpc-reflect-upstream-url');
|
||||
if (reflectName) reflectName.textContent = u.name;
|
||||
if (reflectUrl) reflectUrl.textContent = u.url;
|
||||
}
|
||||
|
||||
function startNewUpstream() {
|
||||
closeUpstreamDropdown();
|
||||
editingUpstreamId = null;
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
var isOpen = form && !form.hidden;
|
||||
|
||||
if (isOpen) {
|
||||
if (form) form.hidden = true;
|
||||
if (trigger) trigger.classList.remove('active');
|
||||
return;
|
||||
}
|
||||
|
||||
var val = document.getElementById('upstream-combobox-value');
|
||||
if (val) setUpstreamComboboxPlaceholder(val);
|
||||
var preview = document.getElementById('upstream-preview');
|
||||
if (preview) preview.hidden = true;
|
||||
selectedUpstreamId = null;
|
||||
|
||||
if (trigger) trigger.classList.add('active');
|
||||
if (form) {
|
||||
form.hidden = false;
|
||||
setValue('new-upstream-name', '');
|
||||
setValue('new-upstream-url', '');
|
||||
setValue('new-upstream-static-headers', '{\n}');
|
||||
setValue('new-upstream-auth-mode', 'none');
|
||||
setValue('new-upstream-auth-profile', '');
|
||||
setValue('new-auth-profile-name', '');
|
||||
setValue('new-auth-profile-kind', 'bearer');
|
||||
setValue('new-auth-profile-header-name', 'Authorization');
|
||||
setValue('new-auth-profile-query-param', 'api_key');
|
||||
setValue('new-auth-profile-secret-id', '');
|
||||
setValue('new-auth-profile-username-secret', '');
|
||||
setValue('new-auth-profile-password-secret', '');
|
||||
updateUpstreamAuthUi();
|
||||
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.hidden = true;
|
||||
if (form) {
|
||||
form.hidden = false;
|
||||
setValue('new-upstream-name', upstream.name);
|
||||
setValue('new-upstream-url', upstream.url);
|
||||
setValue('new-upstream-static-headers', upstream.staticHeaders || '{\n}');
|
||||
setValue('new-upstream-auth-profile', upstream.authProfileId || '');
|
||||
setValue('new-upstream-auth-mode', upstream.authProfileId ? 'existing' : 'none');
|
||||
updateUpstreamAuthUi();
|
||||
}
|
||||
}
|
||||
|
||||
function buildAuthProfileConfig(kind) {
|
||||
if (kind === 'bearer') {
|
||||
var bearerHeader = textValue('new-auth-profile-header-name') || 'Authorization';
|
||||
var bearerSecretId = textValue('new-auth-profile-secret-id');
|
||||
if (!bearerHeader) throw new Error(tKey('wizard.error.header_name_required'));
|
||||
if (!bearerSecretId) throw new Error(tKey('wizard.error.secret_required'));
|
||||
return {
|
||||
bearer: {
|
||||
header_name: bearerHeader,
|
||||
secret_id: bearerSecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (kind === 'basic') {
|
||||
var usernameSecretId = textValue('new-auth-profile-username-secret');
|
||||
var passwordSecretId = textValue('new-auth-profile-password-secret');
|
||||
if (!usernameSecretId || !passwordSecretId) throw new Error(tKey('wizard.error.basic_secrets_required'));
|
||||
return {
|
||||
basic: {
|
||||
username_secret_id: usernameSecretId,
|
||||
password_secret_id: passwordSecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (kind === 'api_key_header') {
|
||||
var headerName = textValue('new-auth-profile-header-name');
|
||||
var headerSecretId = textValue('new-auth-profile-secret-id');
|
||||
if (!headerName) throw new Error(tKey('wizard.error.header_name_required'));
|
||||
if (!headerSecretId) throw new Error(tKey('wizard.error.secret_required'));
|
||||
return {
|
||||
api_key_header: {
|
||||
header_name: headerName,
|
||||
secret_id: headerSecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var queryParam = textValue('new-auth-profile-query-param');
|
||||
var querySecretId = textValue('new-auth-profile-secret-id');
|
||||
if (!queryParam) throw new Error(tKey('wizard.error.query_param_required'));
|
||||
if (!querySecretId) throw new Error(tKey('wizard.error.secret_required'));
|
||||
return {
|
||||
api_key_query: {
|
||||
param_name: queryParam,
|
||||
secret_id: querySecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function createAuthProfileFromForm() {
|
||||
var name = textValue('new-auth-profile-name');
|
||||
var kind = textValue('new-auth-profile-kind') || 'bearer';
|
||||
if (!name) throw new Error(tKey('wizard.error.auth_profile_name'));
|
||||
|
||||
var profile = await window.CrankApi.createAuthProfile(wizardWorkspaceId, {
|
||||
name: name,
|
||||
kind: kind,
|
||||
config: buildAuthProfileConfig(kind),
|
||||
});
|
||||
wizardAuthProfiles.push(profile);
|
||||
renderAuthProfileOptions();
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.success(
|
||||
tKey('wizard.toast.auth_profile_created_body'),
|
||||
tKey('wizard.toast.auth_profile_created_title')
|
||||
);
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
async function saveNewUpstream(e) {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
var nameEl = document.getElementById('new-upstream-name');
|
||||
var urlEl = document.getElementById('new-upstream-url');
|
||||
var name = nameEl ? nameEl.value.trim() : '';
|
||||
var url = urlEl ? urlEl.value.trim() : '';
|
||||
if (!name) { if (nameEl) nameEl.focus(); return; }
|
||||
if (!url) { if (urlEl) urlEl.focus(); return; }
|
||||
|
||||
var staticHeadersEl = document.getElementById('new-upstream-static-headers');
|
||||
var staticHeaders = staticHeadersEl ? staticHeadersEl.value : '{\n}';
|
||||
parseHeaderMap(staticHeaders);
|
||||
|
||||
var authMode = textValue('new-upstream-auth-mode') || 'none';
|
||||
var authProfileId = null;
|
||||
var authProfileName = '';
|
||||
var authKind = null;
|
||||
|
||||
if (authMode === 'existing') {
|
||||
authProfileId = textValue('new-upstream-auth-profile');
|
||||
if (!authProfileId) throw new Error(tKey('wizard.error.auth_profile_required'));
|
||||
var existingProfile = authProfileById(authProfileId);
|
||||
authProfileName = existingProfile ? existingProfile.name : '';
|
||||
authKind = existingProfile ? existingProfile.kind : null;
|
||||
} else if (authMode === 'create') {
|
||||
var createdProfile = await createAuthProfileFromForm();
|
||||
authProfileId = createdProfile.id;
|
||||
authProfileName = createdProfile.name;
|
||||
authKind = createdProfile.kind;
|
||||
setValue('new-upstream-auth-profile', createdProfile.id);
|
||||
setValue('new-upstream-auth-mode', 'existing');
|
||||
}
|
||||
var nextRecord = {
|
||||
id: editingUpstreamId || ('custom-' + Date.now()),
|
||||
name: name,
|
||||
url: url,
|
||||
staticHeaders: staticHeaders,
|
||||
authProfileId: authProfileId,
|
||||
authProfileName: authProfileName,
|
||||
authKind: authKind,
|
||||
};
|
||||
|
||||
if (editingUpstreamId) {
|
||||
upstreams = upstreams.map(function(item) {
|
||||
return item.id === editingUpstreamId ? nextRecord : item;
|
||||
});
|
||||
} else {
|
||||
upstreams.push(nextRecord);
|
||||
}
|
||||
|
||||
if (nameEl) nameEl.value = '';
|
||||
if (urlEl) urlEl.value = '';
|
||||
if (staticHeadersEl) staticHeadersEl.value = '{\n}';
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
editingUpstreamId = null;
|
||||
pickUpstream(nextRecord.id);
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(
|
||||
error.message || tKey('wizard.toast.auth_profile_error_title'),
|
||||
tKey('wizard.toast.auth_profile_error_title')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cancelNewUpstream(e) {
|
||||
e.stopPropagation();
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
if (trigger) trigger.classList.remove('active');
|
||||
editingUpstreamId = null;
|
||||
}
|
||||
|
||||
function openQuickSecretModal(e) {
|
||||
if (e) e.preventDefault();
|
||||
var modal = document.getElementById('quick-secret-modal');
|
||||
if (!modal) return;
|
||||
setValue('quick-secret-name', '');
|
||||
setValue('quick-secret-value', '');
|
||||
setValue('quick-secret-kind', 'token');
|
||||
modal.classList.add('open');
|
||||
setTimeout(function() {
|
||||
var input = document.getElementById('quick-secret-name');
|
||||
if (input) input.focus();
|
||||
}, 30);
|
||||
}
|
||||
|
||||
function closeQuickSecretModal() {
|
||||
var modal = document.getElementById('quick-secret-modal');
|
||||
if (modal) modal.classList.remove('open');
|
||||
}
|
||||
|
||||
async function submitQuickSecret() {
|
||||
var name = textValue('quick-secret-name');
|
||||
var value = textValue('quick-secret-value');
|
||||
var kind = textValue('quick-secret-kind') || 'token';
|
||||
if (!name) throw new Error(tKey('wizard.error.secret_name_required'));
|
||||
if (!value) throw new Error(tKey('wizard.error.secret_value_required'));
|
||||
|
||||
var secret = await window.CrankApi.createSecret(wizardWorkspaceId, {
|
||||
name: name,
|
||||
kind: kind,
|
||||
value: value,
|
||||
});
|
||||
wizardSecrets.push(secret);
|
||||
renderAuthProfileOptions();
|
||||
var primarySelect = document.getElementById('new-auth-profile-secret-id');
|
||||
var usernameSelect = document.getElementById('new-auth-profile-username-secret');
|
||||
var passwordSelect = document.getElementById('new-auth-profile-password-secret');
|
||||
if (primarySelect && primarySelect.offsetParent !== null) {
|
||||
primarySelect.value = secret.id;
|
||||
} else if (usernameSelect && !usernameSelect.value) {
|
||||
usernameSelect.value = secret.id;
|
||||
} else if (passwordSelect) {
|
||||
passwordSelect.value = secret.id;
|
||||
}
|
||||
closeQuickSecretModal();
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.success(
|
||||
tKey('wizard.toast.quick_secret_created_body'),
|
||||
tKey('wizard.toast.quick_secret_created_title')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -223,570 +223,6 @@ function selectGqlType(btn) {
|
||||
if (defaults[type]) editor.value = defaults[type];
|
||||
}
|
||||
|
||||
/* ── Upstream selector (Step 2) ── */
|
||||
|
||||
var upstreams = [
|
||||
{ id: 'open-meteo', name: 'Open Meteo', url: 'https://api.open-meteo.com', staticHeaders: '{}', authProfileId: null, authProfileName: '', authKind: null },
|
||||
{ id: 'countries-graphql', name: 'Countries GraphQL', url: 'https://countries.trevorblades.com', staticHeaders: '{}', authProfileId: null, authProfileName: '', authKind: null },
|
||||
{ id: 'grpcb', name: 'grpcb.in', url: 'https://grpcb.in:443', staticHeaders: '{}', authProfileId: null, authProfileName: '', authKind: null }
|
||||
];
|
||||
|
||||
var dropdownOpen = false;
|
||||
|
||||
function authProfileById(authProfileId) {
|
||||
return wizardAuthProfiles.find(function(item) { return item.id === authProfileId; }) || null;
|
||||
}
|
||||
|
||||
function syncUpstreamAuthMetadata(upstream) {
|
||||
if (!upstream) return upstream;
|
||||
if (!upstream.authProfileId) {
|
||||
upstream.authProfileName = '';
|
||||
upstream.authKind = null;
|
||||
return upstream;
|
||||
}
|
||||
|
||||
var profile = authProfileById(upstream.authProfileId);
|
||||
if (profile) {
|
||||
upstream.authProfileName = profile.name;
|
||||
upstream.authKind = profile.kind;
|
||||
}
|
||||
return upstream;
|
||||
}
|
||||
|
||||
function inferUpstreamAuth(upstream) {
|
||||
syncUpstreamAuthMetadata(upstream);
|
||||
if (!upstream || !upstream.authProfileId || !upstream.authKind) {
|
||||
return { kind: 'none', label: tKey('wizard.step2.auth_none') };
|
||||
}
|
||||
if (upstream.authKind === 'bearer') {
|
||||
return { kind: 'bearer', label: tKey('wizard.step2.auth_bearer') };
|
||||
}
|
||||
if (upstream.authKind === 'basic') {
|
||||
return { kind: 'basic', label: tKey('wizard.step2.auth_basic') };
|
||||
}
|
||||
return { kind: 'apikey', label: tKey('wizard.step2.auth_apikey') };
|
||||
}
|
||||
|
||||
function upstreamMeta(upstream) {
|
||||
var auth = inferUpstreamAuth(upstream);
|
||||
return {
|
||||
kind: auth.kind,
|
||||
label: auth.label,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadWizardAuthResources() {
|
||||
if (!wizardWorkspaceId || !window.CrankApi) {
|
||||
wizardSecrets = [];
|
||||
wizardAuthProfiles = [];
|
||||
renderAuthProfileOptions();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var results = await Promise.all([
|
||||
window.CrankApi.listSecrets(wizardWorkspaceId),
|
||||
window.CrankApi.listAuthProfiles(wizardWorkspaceId),
|
||||
]);
|
||||
wizardSecrets = (results[0] && results[0].items) || [];
|
||||
wizardAuthProfiles = (results[1] && results[1].items) || [];
|
||||
} catch (_error) {
|
||||
wizardSecrets = [];
|
||||
wizardAuthProfiles = [];
|
||||
}
|
||||
|
||||
upstreams.forEach(syncUpstreamAuthMetadata);
|
||||
renderAuthProfileOptions();
|
||||
}
|
||||
|
||||
function renderAuthProfileOptions() {
|
||||
var profileSelect = document.getElementById('new-upstream-auth-profile');
|
||||
var secretSelect = document.getElementById('new-auth-profile-secret-id');
|
||||
var usernameSelect = document.getElementById('new-auth-profile-username-secret');
|
||||
var passwordSelect = document.getElementById('new-auth-profile-password-secret');
|
||||
var profileHint = document.getElementById('new-upstream-auth-profile-hint');
|
||||
|
||||
if (profileSelect) {
|
||||
var previousProfile = profileSelect.value;
|
||||
profileSelect.innerHTML = '';
|
||||
if (!wizardAuthProfiles.length) {
|
||||
var emptyProfile = document.createElement('option');
|
||||
emptyProfile.value = '';
|
||||
emptyProfile.textContent = tKey('wizard.step2.auth_profile_empty');
|
||||
profileSelect.appendChild(emptyProfile);
|
||||
if (profileHint) profileHint.textContent = tKey('wizard.step2.auth_profile_empty');
|
||||
} else {
|
||||
var placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.textContent = tKey('wizard.step2.auth_profile');
|
||||
profileSelect.appendChild(placeholder);
|
||||
wizardAuthProfiles.forEach(function(profile) {
|
||||
var option = document.createElement('option');
|
||||
option.value = profile.id;
|
||||
option.textContent = profile.name + ' · ' + profile.kind;
|
||||
profileSelect.appendChild(option);
|
||||
});
|
||||
if (previousProfile) {
|
||||
profileSelect.value = previousProfile;
|
||||
}
|
||||
if (profileHint) profileHint.textContent = tKey('wizard.step2.auth_profile_hint');
|
||||
}
|
||||
}
|
||||
|
||||
[secretSelect, usernameSelect, passwordSelect].forEach(function(select) {
|
||||
if (!select) return;
|
||||
var previous = select.value;
|
||||
select.innerHTML = '';
|
||||
var placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.textContent = tKey('wizard.step2.secret_value');
|
||||
select.appendChild(placeholder);
|
||||
wizardSecrets.forEach(function(secret) {
|
||||
var option = document.createElement('option');
|
||||
option.value = secret.id;
|
||||
option.textContent = secret.name + ' · ' + secret.kind;
|
||||
select.appendChild(option);
|
||||
});
|
||||
if (previous) {
|
||||
select.value = previous;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateAuthProfileCreateUi() {
|
||||
var kind = textValue('new-auth-profile-kind') || 'bearer';
|
||||
var headerGroup = document.getElementById('auth-profile-header-name-group');
|
||||
var queryGroup = document.getElementById('auth-profile-query-param-group');
|
||||
var basicRow = document.getElementById('auth-profile-basic-secret-row');
|
||||
var singleSecretGroup = document.getElementById('auth-profile-single-secret-group');
|
||||
|
||||
if (headerGroup) {
|
||||
headerGroup.hidden = !(kind === 'bearer' || kind === 'api_key_header');
|
||||
}
|
||||
if (queryGroup) {
|
||||
queryGroup.hidden = kind !== 'api_key_query';
|
||||
}
|
||||
if (basicRow) {
|
||||
basicRow.hidden = kind !== 'basic';
|
||||
}
|
||||
if (singleSecretGroup) {
|
||||
singleSecretGroup.hidden = kind === 'basic';
|
||||
}
|
||||
}
|
||||
|
||||
function updateUpstreamAuthUi() {
|
||||
var mode = textValue('new-upstream-auth-mode') || 'none';
|
||||
var existingGroup = document.getElementById('upstream-auth-existing-group');
|
||||
var createGroup = document.getElementById('upstream-auth-create-group');
|
||||
if (existingGroup) existingGroup.hidden = mode !== 'existing';
|
||||
if (createGroup) createGroup.hidden = mode !== 'create';
|
||||
updateAuthProfileCreateUi();
|
||||
}
|
||||
|
||||
function buildUpstreamTriggerContent(name, url) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
fragment.appendChild(createDivWithClass('upstream-trigger-name', name));
|
||||
fragment.appendChild(createDivWithClass('upstream-trigger-url', url));
|
||||
return fragment;
|
||||
}
|
||||
|
||||
function setUpstreamComboboxPlaceholder(container) {
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
var placeholder = document.createElement('span');
|
||||
placeholder.className = 'upstream-combobox-placeholder';
|
||||
placeholder.textContent = tKey('wizard.step2.upstream_placeholder');
|
||||
container.appendChild(placeholder);
|
||||
}
|
||||
|
||||
function renderDropdownList(filter) {
|
||||
var list = document.getElementById('upstream-dropdown-list');
|
||||
if (!list) return;
|
||||
var q = (filter || '').toLowerCase();
|
||||
var filtered = upstreams.filter(function(u) {
|
||||
return !q || u.name.toLowerCase().includes(q) || u.url.toLowerCase().includes(q);
|
||||
});
|
||||
|
||||
if (filtered.length === 0) {
|
||||
list.innerHTML = '';
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'upstream-dropdown-empty';
|
||||
empty.textContent = tfKey('agents.drawer.ops_no_match', { query: filter || '' });
|
||||
list.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
var tmpl = document.getElementById('tmpl-upstream-item');
|
||||
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');
|
||||
item.addEventListener('click', function() { pickUpstream(u.id); });
|
||||
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 = meta.label;
|
||||
badge.className = 'upstream-auth-badge auth-' + meta.kind;
|
||||
list.appendChild(node);
|
||||
});
|
||||
}
|
||||
|
||||
function openUpstreamDropdown() {
|
||||
var combobox = document.getElementById('upstream-combobox');
|
||||
var dropdown = document.getElementById('upstream-dropdown');
|
||||
var search = document.getElementById('upstream-search');
|
||||
if (!combobox || !dropdown) return;
|
||||
combobox.classList.add('open');
|
||||
dropdown.hidden = false;
|
||||
dropdownOpen = true;
|
||||
renderDropdownList('');
|
||||
if (search) { search.value = ''; search.focus(); }
|
||||
|
||||
// Close new-upstream form if open
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
}
|
||||
|
||||
function closeUpstreamDropdown() {
|
||||
var combobox = document.getElementById('upstream-combobox');
|
||||
var dropdown = document.getElementById('upstream-dropdown');
|
||||
if (!combobox || !dropdown) return;
|
||||
combobox.classList.remove('open');
|
||||
dropdown.hidden = true;
|
||||
dropdownOpen = false;
|
||||
}
|
||||
|
||||
function toggleUpstreamDropdown(e) {
|
||||
e.stopPropagation();
|
||||
if (dropdownOpen) { closeUpstreamDropdown(); } else { openUpstreamDropdown(); }
|
||||
}
|
||||
|
||||
function filterUpstreams(val) {
|
||||
renderDropdownList(val);
|
||||
}
|
||||
|
||||
function pickUpstream(id) {
|
||||
selectedUpstreamId = id;
|
||||
editingUpstreamId = null;
|
||||
closeUpstreamDropdown();
|
||||
|
||||
var u = upstreams.find(function(x) { return x.id === id; });
|
||||
if (!u) return;
|
||||
|
||||
// Update combobox trigger value
|
||||
var val = document.getElementById('upstream-combobox-value');
|
||||
if (val) {
|
||||
var trigTmpl = document.getElementById('tmpl-upstream-trigger-value');
|
||||
if (trigTmpl) {
|
||||
val.innerHTML = '';
|
||||
var trigNode = trigTmpl.content.cloneNode(true);
|
||||
trigNode.querySelector('.upstream-trigger-name').textContent = u.name;
|
||||
trigNode.querySelector('.upstream-trigger-url').textContent = u.url;
|
||||
val.appendChild(trigNode);
|
||||
} else {
|
||||
val.innerHTML = '';
|
||||
val.appendChild(buildUpstreamTriggerContent(u.name, u.url));
|
||||
}
|
||||
}
|
||||
|
||||
// Show preview strip
|
||||
var preview = document.getElementById('upstream-preview');
|
||||
var pName = document.getElementById('upstream-preview-name');
|
||||
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 = meta.label;
|
||||
pBadge.className = 'upstream-auth-badge auth-' + meta.kind;
|
||||
preview.hidden = false;
|
||||
}
|
||||
|
||||
// Hide new-upstream form and reset trigger
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
if (trigger) trigger.classList.remove('active');
|
||||
|
||||
// Update reflection panel upstream info strip
|
||||
var reflectName = document.getElementById('grpc-reflect-upstream-name');
|
||||
var reflectUrl = document.getElementById('grpc-reflect-upstream-url');
|
||||
if (reflectName) reflectName.textContent = u.name;
|
||||
if (reflectUrl) reflectUrl.textContent = u.url;
|
||||
}
|
||||
|
||||
function startNewUpstream() {
|
||||
closeUpstreamDropdown();
|
||||
editingUpstreamId = null;
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
var isOpen = form && !form.hidden;
|
||||
|
||||
if (isOpen) {
|
||||
// toggle off — restore previous state
|
||||
if (form) form.hidden = true;
|
||||
if (trigger) trigger.classList.remove('active');
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear combobox trigger & hide preview
|
||||
var val = document.getElementById('upstream-combobox-value');
|
||||
if (val) setUpstreamComboboxPlaceholder(val);
|
||||
var preview = document.getElementById('upstream-preview');
|
||||
if (preview) preview.hidden = true;
|
||||
selectedUpstreamId = null;
|
||||
|
||||
// Mark trigger active and show form
|
||||
if (trigger) trigger.classList.add('active');
|
||||
if (form) {
|
||||
form.hidden = false;
|
||||
setValue('new-upstream-name', '');
|
||||
setValue('new-upstream-url', '');
|
||||
setValue('new-upstream-static-headers', '{\n}');
|
||||
setValue('new-upstream-auth-mode', 'none');
|
||||
setValue('new-upstream-auth-profile', '');
|
||||
setValue('new-auth-profile-name', '');
|
||||
setValue('new-auth-profile-kind', 'bearer');
|
||||
setValue('new-auth-profile-header-name', 'Authorization');
|
||||
setValue('new-auth-profile-query-param', 'api_key');
|
||||
setValue('new-auth-profile-secret-id', '');
|
||||
setValue('new-auth-profile-username-secret', '');
|
||||
setValue('new-auth-profile-password-secret', '');
|
||||
updateUpstreamAuthUi();
|
||||
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.hidden = true;
|
||||
if (form) {
|
||||
form.hidden = false;
|
||||
setValue('new-upstream-name', upstream.name);
|
||||
setValue('new-upstream-url', upstream.url);
|
||||
setValue('new-upstream-static-headers', upstream.staticHeaders || '{\n}');
|
||||
setValue('new-upstream-auth-profile', upstream.authProfileId || '');
|
||||
setValue('new-upstream-auth-mode', upstream.authProfileId ? 'existing' : 'none');
|
||||
updateUpstreamAuthUi();
|
||||
}
|
||||
}
|
||||
|
||||
function buildAuthProfileConfig(kind) {
|
||||
if (kind === 'bearer') {
|
||||
var bearerHeader = textValue('new-auth-profile-header-name') || 'Authorization';
|
||||
var bearerSecretId = textValue('new-auth-profile-secret-id');
|
||||
if (!bearerHeader) throw new Error(tKey('wizard.error.header_name_required'));
|
||||
if (!bearerSecretId) throw new Error(tKey('wizard.error.secret_required'));
|
||||
return {
|
||||
bearer: {
|
||||
header_name: bearerHeader,
|
||||
secret_id: bearerSecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (kind === 'basic') {
|
||||
var usernameSecretId = textValue('new-auth-profile-username-secret');
|
||||
var passwordSecretId = textValue('new-auth-profile-password-secret');
|
||||
if (!usernameSecretId || !passwordSecretId) throw new Error(tKey('wizard.error.basic_secrets_required'));
|
||||
return {
|
||||
basic: {
|
||||
username_secret_id: usernameSecretId,
|
||||
password_secret_id: passwordSecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (kind === 'api_key_header') {
|
||||
var headerName = textValue('new-auth-profile-header-name');
|
||||
var headerSecretId = textValue('new-auth-profile-secret-id');
|
||||
if (!headerName) throw new Error(tKey('wizard.error.header_name_required'));
|
||||
if (!headerSecretId) throw new Error(tKey('wizard.error.secret_required'));
|
||||
return {
|
||||
api_key_header: {
|
||||
header_name: headerName,
|
||||
secret_id: headerSecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var queryParam = textValue('new-auth-profile-query-param');
|
||||
var querySecretId = textValue('new-auth-profile-secret-id');
|
||||
if (!queryParam) throw new Error(tKey('wizard.error.query_param_required'));
|
||||
if (!querySecretId) throw new Error(tKey('wizard.error.secret_required'));
|
||||
return {
|
||||
api_key_query: {
|
||||
param_name: queryParam,
|
||||
secret_id: querySecretId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function createAuthProfileFromForm() {
|
||||
var name = textValue('new-auth-profile-name');
|
||||
var kind = textValue('new-auth-profile-kind') || 'bearer';
|
||||
if (!name) throw new Error(tKey('wizard.error.auth_profile_name'));
|
||||
|
||||
var profile = await window.CrankApi.createAuthProfile(wizardWorkspaceId, {
|
||||
name: name,
|
||||
kind: kind,
|
||||
config: buildAuthProfileConfig(kind),
|
||||
});
|
||||
wizardAuthProfiles.push(profile);
|
||||
renderAuthProfileOptions();
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.success(
|
||||
tKey('wizard.toast.auth_profile_created_body'),
|
||||
tKey('wizard.toast.auth_profile_created_title')
|
||||
);
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
async function saveNewUpstream(e) {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
var nameEl = document.getElementById('new-upstream-name');
|
||||
var urlEl = document.getElementById('new-upstream-url');
|
||||
var name = nameEl ? nameEl.value.trim() : '';
|
||||
var url = urlEl ? urlEl.value.trim() : '';
|
||||
if (!name) { if (nameEl) nameEl.focus(); return; }
|
||||
if (!url) { if (urlEl) urlEl.focus(); return; }
|
||||
|
||||
var staticHeadersEl = document.getElementById('new-upstream-static-headers');
|
||||
var staticHeaders = staticHeadersEl ? staticHeadersEl.value : '{\n}';
|
||||
parseHeaderMap(staticHeaders);
|
||||
|
||||
var authMode = textValue('new-upstream-auth-mode') || 'none';
|
||||
var authProfileId = null;
|
||||
var authProfileName = '';
|
||||
var authKind = null;
|
||||
|
||||
if (authMode === 'existing') {
|
||||
authProfileId = textValue('new-upstream-auth-profile');
|
||||
if (!authProfileId) throw new Error(tKey('wizard.error.auth_profile_required'));
|
||||
var existingProfile = authProfileById(authProfileId);
|
||||
authProfileName = existingProfile ? existingProfile.name : '';
|
||||
authKind = existingProfile ? existingProfile.kind : null;
|
||||
} else if (authMode === 'create') {
|
||||
var createdProfile = await createAuthProfileFromForm();
|
||||
authProfileId = createdProfile.id;
|
||||
authProfileName = createdProfile.name;
|
||||
authKind = createdProfile.kind;
|
||||
setValue('new-upstream-auth-profile', createdProfile.id);
|
||||
setValue('new-upstream-auth-mode', 'existing');
|
||||
}
|
||||
var nextRecord = {
|
||||
id: editingUpstreamId || ('custom-' + Date.now()),
|
||||
name: name,
|
||||
url: url,
|
||||
staticHeaders: staticHeaders,
|
||||
authProfileId: authProfileId,
|
||||
authProfileName: authProfileName,
|
||||
authKind: authKind,
|
||||
};
|
||||
|
||||
if (editingUpstreamId) {
|
||||
upstreams = upstreams.map(function(item) {
|
||||
return item.id === editingUpstreamId ? nextRecord : item;
|
||||
});
|
||||
} else {
|
||||
upstreams.push(nextRecord);
|
||||
}
|
||||
|
||||
if (nameEl) nameEl.value = '';
|
||||
if (urlEl) urlEl.value = '';
|
||||
if (staticHeadersEl) staticHeadersEl.value = '{\n}';
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
editingUpstreamId = null;
|
||||
pickUpstream(nextRecord.id);
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(
|
||||
error.message || tKey('wizard.toast.auth_profile_error_title'),
|
||||
tKey('wizard.toast.auth_profile_error_title')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cancelNewUpstream(e) {
|
||||
e.stopPropagation();
|
||||
var form = document.getElementById('upstream-new-form');
|
||||
if (form) form.hidden = true;
|
||||
var trigger = document.getElementById('upstream-new-trigger');
|
||||
if (trigger) trigger.classList.remove('active');
|
||||
editingUpstreamId = null;
|
||||
}
|
||||
|
||||
function openQuickSecretModal(e) {
|
||||
if (e) e.preventDefault();
|
||||
var modal = document.getElementById('quick-secret-modal');
|
||||
if (!modal) return;
|
||||
setValue('quick-secret-name', '');
|
||||
setValue('quick-secret-value', '');
|
||||
setValue('quick-secret-kind', 'token');
|
||||
modal.classList.add('open');
|
||||
setTimeout(function() {
|
||||
var input = document.getElementById('quick-secret-name');
|
||||
if (input) input.focus();
|
||||
}, 30);
|
||||
}
|
||||
|
||||
function closeQuickSecretModal() {
|
||||
var modal = document.getElementById('quick-secret-modal');
|
||||
if (modal) modal.classList.remove('open');
|
||||
}
|
||||
|
||||
async function submitQuickSecret() {
|
||||
var name = textValue('quick-secret-name');
|
||||
var value = textValue('quick-secret-value');
|
||||
var kind = textValue('quick-secret-kind') || 'token';
|
||||
if (!name) throw new Error(tKey('wizard.error.secret_name_required'));
|
||||
if (!value) throw new Error(tKey('wizard.error.secret_value_required'));
|
||||
|
||||
var secret = await window.CrankApi.createSecret(wizardWorkspaceId, {
|
||||
name: name,
|
||||
kind: kind,
|
||||
value: value,
|
||||
});
|
||||
wizardSecrets.push(secret);
|
||||
renderAuthProfileOptions();
|
||||
var primarySelect = document.getElementById('new-auth-profile-secret-id');
|
||||
var usernameSelect = document.getElementById('new-auth-profile-username-secret');
|
||||
var passwordSelect = document.getElementById('new-auth-profile-password-secret');
|
||||
if (primarySelect && primarySelect.offsetParent !== null) {
|
||||
primarySelect.value = secret.id;
|
||||
} else if (usernameSelect && !usernameSelect.value) {
|
||||
usernameSelect.value = secret.id;
|
||||
} else if (passwordSelect) {
|
||||
passwordSelect.value = secret.id;
|
||||
}
|
||||
closeQuickSecretModal();
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.success(
|
||||
tKey('wizard.toast.quick_secret_created_body'),
|
||||
tKey('wizard.toast.quick_secret_created_title')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ const BUNDLES = {
|
||||
'js/streaming-form.js',
|
||||
'js/stream-test-run.js',
|
||||
'js/wizard.js',
|
||||
'js/wizard-upstreams.js',
|
||||
'js/nav.js',
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user