feat: complete Epic 1 production foundation

This commit is contained in:
2026-08-25 01:24:11 +03:00
parent 767428436d
commit 182bde8ac0
298 changed files with 35719 additions and 5299 deletions
+89 -15
View File
@@ -13,6 +13,10 @@ function initSecretsPage() {
error: '',
modalMode: 'create',
modalSecretId: null,
modalSubmitting: false,
deleting: {},
generation: 0,
modalGeneration: 0,
};
var modal = document.getElementById('secret-modal');
@@ -107,6 +111,10 @@ function initSecretsPage() {
}
function openModal(mode, secret) {
if (state.modalSubmitting) {
return;
}
state.modalGeneration += 1;
state.modalMode = mode;
state.modalSecretId = secret ? secret.id : null;
resetModalFields();
@@ -143,8 +151,16 @@ function initSecretsPage() {
function closeModal() {
modal.classList.remove('open');
state.modalGeneration += 1;
state.modalSubmitting = false;
state.modalMode = 'create';
state.modalSecretId = null;
resetModalFields();
modalName.disabled = false;
modalKind.disabled = false;
modalSubmit.disabled = state.modalSubmitting;
modalSubmit.textContent = tKey('secrets.modal.create_action');
renderSecrets();
}
function updateKindFields() {
@@ -290,6 +306,7 @@ function initSecretsPage() {
rotateButton.textContent = tKey('secrets.action.rotate');
rotateButton.setAttribute('data-testid', 'secret-rotate-action');
rotateButton.setAttribute('data-secret-id', secret.id);
rotateButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
rotateButton.addEventListener('click', function () {
openModal('rotate', secret);
});
@@ -301,6 +318,7 @@ function initSecretsPage() {
deleteButton.textContent = tKey('secrets.action.delete');
deleteButton.setAttribute('data-testid', 'secret-delete-action');
deleteButton.setAttribute('data-secret-id', secret.id);
deleteButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
deleteButton.addEventListener('click', async function () {
await deleteSecret(secret);
});
@@ -378,6 +396,7 @@ function initSecretsPage() {
rotateButton.className = 'btn-secondary';
rotateButton.type = 'button';
rotateButton.textContent = tKey('secrets.action.rotate');
rotateButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
rotateButton.addEventListener('click', function() {
openModal('rotate', secret);
});
@@ -387,6 +406,7 @@ function initSecretsPage() {
deleteButton.className = 'btn-secondary';
deleteButton.type = 'button';
deleteButton.textContent = tKey('secrets.action.delete');
deleteButton.disabled = Boolean(state.deleting[secret.id]) || state.modalSubmitting;
deleteButton.addEventListener('click', async function() {
await deleteSecret(secret);
});
@@ -412,12 +432,14 @@ function initSecretsPage() {
}
async function load() {
state.workspaceId = currentWorkspaceId();
var workspaceId = currentWorkspaceId();
var generation = ++state.generation;
state.workspaceId = workspaceId;
state.loading = true;
state.error = '';
renderSecrets();
if (!state.workspaceId) {
if (!workspaceId) {
state.secrets = [];
state.profiles = [];
recomputeDerivedState();
@@ -429,49 +451,79 @@ function initSecretsPage() {
try {
var results = await Promise.all([
window.CrankApi.listSecrets(state.workspaceId),
window.CrankApi.listAuthProfiles(state.workspaceId),
window.CrankApi.listSecrets(workspaceId),
window.CrankApi.listAuthProfiles(workspaceId),
]);
if (generation !== state.generation || workspaceId !== currentWorkspaceId()) {
return;
}
state.secrets = (results[0] && results[0].items) || [];
state.profiles = (results[1] && results[1].items) || [];
recomputeDerivedState();
} catch (error) {
if (generation !== state.generation || workspaceId !== currentWorkspaceId()) {
return;
}
state.error = error.message || tKey('secrets.error.load');
state.secrets = [];
state.profiles = [];
recomputeDerivedState();
} finally {
if (generation !== state.generation || workspaceId !== currentWorkspaceId()) {
return;
}
state.loading = false;
renderSecrets();
}
}
async function deleteSecret(secret) {
if (state.deleting[secret.id]) {
return;
}
if (!confirm(tfKey('secrets.confirm.delete', { name: secret.name }))) {
return;
}
var workspaceId = state.workspaceId;
state.deleting[secret.id] = true;
renderSecrets();
try {
await window.CrankApi.deleteSecret(state.workspaceId, secret.id);
await load();
if (window.CrankUi) {
await window.CrankApi.deleteSecret(workspaceId, secret.id);
if (workspaceId === currentWorkspaceId()) {
await load();
}
if (window.CrankUi && workspaceId === currentWorkspaceId()) {
window.CrankUi.success(
tfKey('secrets.toast.delete_message', { name: secret.name }),
tKey('secrets.toast.delete_title')
);
}
} catch (error) {
if (window.CrankUi) {
if (window.CrankUi && workspaceId === currentWorkspaceId()) {
window.CrankUi.error(
error.message || tKey('secrets.toast.delete_error_message'),
tKey('secrets.toast.delete_error_title')
);
}
} finally {
delete state.deleting[secret.id];
if (workspaceId === currentWorkspaceId()) {
renderSecrets();
}
}
}
async function submitModal() {
if (state.modalSubmitting) {
return;
}
var kind = modalKind.value;
var value = buildSecretValue();
var workspaceId = state.workspaceId;
var secretId = state.modalSecretId;
var submissionGeneration = ++state.modalGeneration;
var submissionMode = state.modalMode;
state.modalSubmitting = true;
modalSubmit.disabled = true;
modalSubmit.textContent = state.modalMode === 'rotate'
? tKey('secrets.modal.rotating')
@@ -479,7 +531,16 @@ function initSecretsPage() {
try {
if (state.modalMode === 'rotate') {
await window.CrankApi.rotateSecret(state.workspaceId, state.modalSecretId, { value: value });
await window.CrankApi.rotateSecret(workspaceId, secretId, { value: value });
if (
submissionGeneration !== state.modalGeneration
|| workspaceId !== currentWorkspaceId()
|| state.modalSecretId !== secretId
|| submissionMode !== state.modalMode
|| !modal.classList.contains('open')
) {
return;
}
if (window.CrankUi) {
window.CrankUi.success(
tKey('secrets.toast.rotate_message'),
@@ -491,11 +552,19 @@ function initSecretsPage() {
if (!name) {
throw new Error(tKey('secrets.validation.name_required'));
}
await window.CrankApi.createSecret(state.workspaceId, {
await window.CrankApi.createSecret(workspaceId, {
name: name,
kind: kind,
value: value,
});
if (
submissionGeneration !== state.modalGeneration
|| workspaceId !== currentWorkspaceId()
|| submissionMode !== state.modalMode
|| !modal.classList.contains('open')
) {
return;
}
if (window.CrankUi) {
window.CrankUi.success(
tKey('secrets.toast.create_message'),
@@ -506,17 +575,20 @@ function initSecretsPage() {
closeModal();
await load();
} catch (error) {
if (window.CrankUi) {
if (window.CrankUi && submissionGeneration === state.modalGeneration) {
window.CrankUi.error(
error.message || tKey(state.modalMode === 'rotate' ? 'secrets.toast.rotate_error_message' : 'secrets.toast.create_error_message'),
tKey(state.modalMode === 'rotate' ? 'secrets.toast.rotate_error_title' : 'secrets.toast.create_error_title')
);
}
} finally {
modalSubmit.disabled = false;
modalSubmit.textContent = state.modalMode === 'rotate'
? tKey('secrets.modal.rotate_action')
: tKey('secrets.modal.create_action');
if (submissionGeneration === state.modalGeneration) {
state.modalSubmitting = false;
modalSubmit.disabled = false;
modalSubmit.textContent = state.modalMode === 'rotate'
? tKey('secrets.modal.rotate_action')
: tKey('secrets.modal.create_action');
}
}
}
@@ -539,6 +611,8 @@ function initSecretsPage() {
renderSecrets();
});
window.addEventListener('crank:workspacechange', function () {
closeModal();
state.deleting = {};
void load();
});