feat: entry update links existing secrets (link_secret_names)
- secrets-core: update flow validates and applies secret links - secrets-mcp: MCP tool params and UI for managing links on edit - Align errors and templates; minor crypto/.gitignore tweaks Made-with: Cursor
This commit is contained in:
@@ -123,7 +123,7 @@
|
||||
background: var(--bg);
|
||||
}
|
||||
table {
|
||||
width: max-content;
|
||||
width: 100%;
|
||||
min-width: 960px;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@@ -142,12 +142,12 @@
|
||||
td { font-size: 13px; line-height: 1.45; }
|
||||
tbody tr:nth-child(2n) td { background: rgba(255, 255, 255, 0.01); }
|
||||
.mono { font-family: 'JetBrains Mono', monospace; }
|
||||
.col-type { min-width: 108px; }
|
||||
.col-type { min-width: 108px; width: 1%; }
|
||||
.col-name { min-width: 180px; max-width: 260px; }
|
||||
.col-tags { min-width: 160px; max-width: 220px; }
|
||||
.col-secrets { min-width: 220px; max-width: 420px; vertical-align: top; }
|
||||
.col-secrets .secret-list { max-height: 120px; overflow: auto; }
|
||||
.col-actions { min-width: 132px; }
|
||||
.col-actions { min-width: 132px; width: 1%; }
|
||||
.cell-name, .cell-tags-val {
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
@@ -961,6 +961,114 @@ var SECRET_TYPE_OPTIONS = JSON.parse(document.getElementById('secret-type-option
|
||||
showDeleteErr('');
|
||||
}
|
||||
|
||||
function refreshListAfterSave(entryId, body, secretRows) {
|
||||
var tr = document.querySelector('tr[data-entry-id="' + entryId + '"]');
|
||||
if (!tr) { window.location.reload(); return; }
|
||||
var nameCell = tr.querySelector('.cell-name');
|
||||
if (nameCell) nameCell.textContent = body.name;
|
||||
var typeCell = tr.querySelector('.cell-type');
|
||||
if (typeCell) typeCell.textContent = body.type;
|
||||
var notesCell = tr.querySelector('.cell-notes-val');
|
||||
if (notesCell) {
|
||||
if (body.notes) { notesCell.textContent = body.notes; }
|
||||
else { var notesWrap = tr.querySelector('.cell-notes'); if (notesWrap) notesWrap.innerHTML = ''; }
|
||||
}
|
||||
var tagsCell = tr.querySelector('.cell-tags-val');
|
||||
if (tagsCell) tagsCell.textContent = body.tags.join(', ');
|
||||
var secretsList = tr.querySelector('.secret-list');
|
||||
if (secretsList) {
|
||||
secretsList.innerHTML = '';
|
||||
secretRows.forEach(function (info) {
|
||||
var chip = document.createElement('span');
|
||||
chip.className = 'secret-chip';
|
||||
var nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'secret-name';
|
||||
nameSpan.textContent = info.newName;
|
||||
nameSpan.title = info.newName;
|
||||
var typeSpan = document.createElement('span');
|
||||
typeSpan.className = 'secret-type';
|
||||
typeSpan.textContent = info.newType || 'text';
|
||||
var unlinkBtn = document.createElement('button');
|
||||
unlinkBtn.type = 'button';
|
||||
unlinkBtn.className = 'btn-unlink-secret';
|
||||
unlinkBtn.setAttribute('data-secret-id', info.secretId);
|
||||
unlinkBtn.setAttribute('data-secret-name', info.newName);
|
||||
unlinkBtn.title = t('unlinkTitle');
|
||||
unlinkBtn.textContent = '\u00d7';
|
||||
chip.appendChild(nameSpan);
|
||||
chip.appendChild(typeSpan);
|
||||
chip.appendChild(unlinkBtn);
|
||||
secretsList.appendChild(chip);
|
||||
});
|
||||
}
|
||||
tr.setAttribute('data-entry-folder', body.folder);
|
||||
tr.setAttribute('data-entry-metadata', JSON.stringify(body.metadata));
|
||||
var updatedSecrets = secretRows.map(function (info) {
|
||||
return { id: info.secretId, name: info.newName, secret_type: info.newType || 'text' };
|
||||
});
|
||||
tr.setAttribute('data-entry-secrets', JSON.stringify(updatedSecrets));
|
||||
}
|
||||
|
||||
function refreshListAfterDelete(entryId) {
|
||||
var tr = document.querySelector('tr[data-entry-id="' + entryId + '"]');
|
||||
var folder = tr ? tr.getAttribute('data-entry-folder') : null;
|
||||
if (tr) tr.remove();
|
||||
var tbody = document.querySelector('table tbody');
|
||||
if (tbody && !tbody.querySelector('tr[data-entry-id]')) {
|
||||
var card = document.querySelector('.card');
|
||||
if (card) {
|
||||
var tableWrap = card.querySelector('.table-wrap');
|
||||
if (tableWrap) tableWrap.remove();
|
||||
var existingEmpty = card.querySelector('.empty');
|
||||
if (!existingEmpty) {
|
||||
var emptyDiv = document.createElement('div');
|
||||
emptyDiv.className = 'empty';
|
||||
emptyDiv.setAttribute('data-i18n', 'emptyEntries');
|
||||
emptyDiv.textContent = t('emptyEntries');
|
||||
var filterBar = card.querySelector('.filter-bar');
|
||||
if (filterBar) { card.insertBefore(emptyDiv, filterBar.nextSibling); }
|
||||
else { card.appendChild(emptyDiv); }
|
||||
}
|
||||
}
|
||||
}
|
||||
var allTab = document.querySelector('.folder-tab[data-all-tab="1"]');
|
||||
if (allTab) {
|
||||
var count = parseInt(allTab.getAttribute('data-count') || '0', 10);
|
||||
if (count > 0) {
|
||||
count -= 1;
|
||||
allTab.setAttribute('data-count', String(count));
|
||||
allTab.textContent = t('allTab') + ' (' + count + ')';
|
||||
}
|
||||
}
|
||||
if (folder) {
|
||||
document.querySelectorAll('.folder-tab:not([data-all-tab])').forEach(function (tab) {
|
||||
if (tab.textContent.trim().indexOf(folder) === 0) {
|
||||
var m = tab.textContent.match(/\((\d+)\)/);
|
||||
if (m) {
|
||||
var c = parseInt(m[1], 10);
|
||||
if (c > 0) {
|
||||
c -= 1;
|
||||
tab.textContent = folder + ' (' + c + ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function refreshListAfterUnlink(entryId, secretId) {
|
||||
var tr = document.querySelector('tr[data-entry-id="' + entryId + '"]');
|
||||
if (!tr) return;
|
||||
var chip = tr.querySelector('.btn-unlink-secret[data-secret-id="' + secretId + '"]');
|
||||
if (chip && chip.parentElement) chip.parentElement.remove();
|
||||
var secrets = tr.getAttribute('data-entry-secrets');
|
||||
try {
|
||||
var arr = JSON.parse(secrets);
|
||||
arr = arr.filter(function (s) { return s.id !== secretId; });
|
||||
tr.setAttribute('data-entry-secrets', JSON.stringify(arr));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
document.getElementById('delete-cancel').addEventListener('click', closeDelete);
|
||||
deleteOverlay.addEventListener('click', function (e) {
|
||||
if (e.target === deleteOverlay) closeDelete();
|
||||
@@ -975,8 +1083,9 @@ var SECRET_TYPE_OPTIONS = JSON.parse(document.getElementById('secret-type-option
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
var deletedId = pendingDeleteId;
|
||||
closeDelete();
|
||||
window.location.reload();
|
||||
refreshListAfterDelete(deletedId);
|
||||
})
|
||||
.catch(function (e) { showDeleteErr(e.message || String(e)); });
|
||||
});
|
||||
@@ -1086,7 +1195,7 @@ var SECRET_TYPE_OPTIONS = JSON.parse(document.getElementById('secret-type-option
|
||||
}));
|
||||
}).then(function () {
|
||||
closeEdit();
|
||||
window.location.reload();
|
||||
refreshListAfterSave(currentEntryId, body, secretRows);
|
||||
}).catch(function (e) {
|
||||
showEditErr(e.message || String(e));
|
||||
});
|
||||
@@ -1102,7 +1211,6 @@ var SECRET_TYPE_OPTIONS = JSON.parse(document.getElementById('secret-type-option
|
||||
var secretId = btn.getAttribute('data-secret-id');
|
||||
var secretName = btn.getAttribute('data-secret-name') || '';
|
||||
if (!entryId || !secretId) return;
|
||||
if (!confirm(tf('confirmUnlinkSecret', { name: secretName }))) return;
|
||||
fetch('/api/entries/' + encodeURIComponent(entryId) + '/secrets/' + encodeURIComponent(secretId), {
|
||||
method: 'DELETE',
|
||||
credentials: 'same-origin'
|
||||
@@ -1112,7 +1220,7 @@ var SECRET_TYPE_OPTIONS = JSON.parse(document.getElementById('secret-type-option
|
||||
return data;
|
||||
});
|
||||
}).then(function () {
|
||||
window.location.reload();
|
||||
refreshListAfterUnlink(entryId, secretId);
|
||||
}).catch(function (err) {
|
||||
alert(err.message || String(err));
|
||||
});
|
||||
@@ -1126,7 +1234,6 @@ var SECRET_TYPE_OPTIONS = JSON.parse(document.getElementById('secret-type-option
|
||||
var secretId = btn.getAttribute('data-secret-id');
|
||||
var secretName = btn.getAttribute('data-secret-name') || '';
|
||||
if (!entryId || !secretId) return;
|
||||
if (!confirm(tf('confirmUnlinkSecret', { name: secretName }))) return;
|
||||
fetch('/api/entries/' + encodeURIComponent(entryId) + '/secrets/' + encodeURIComponent(secretId), {
|
||||
method: 'DELETE',
|
||||
credentials: 'same-origin'
|
||||
@@ -1136,7 +1243,12 @@ var SECRET_TYPE_OPTIONS = JSON.parse(document.getElementById('secret-type-option
|
||||
return data;
|
||||
});
|
||||
}).then(function () {
|
||||
window.location.reload();
|
||||
btn.closest('.secret-edit-row').remove();
|
||||
var tableRow = document.querySelector('tr[data-entry-id="' + entryId + '"]');
|
||||
if (tableRow) {
|
||||
var chip = tableRow.querySelector('.btn-unlink-secret[data-secret-id="' + secretId + '"]');
|
||||
if (chip && chip.parentElement) chip.parentElement.remove();
|
||||
}
|
||||
}).catch(function (err) {
|
||||
alert(err.message || String(err));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user