fix: localize Secrets operation errors
This commit is contained in:
parent
bee365ad22
commit
1f03226378
|
|
@ -167,6 +167,38 @@
|
|||
render();
|
||||
}
|
||||
|
||||
function userFacingError(action, err) {
|
||||
var raw = (err && err.message) ? err.message : String(err || '');
|
||||
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
||||
console.error('[verstak.secrets] ' + action + ' failed', err);
|
||||
}
|
||||
if (action === 'unlock') {
|
||||
var minimumLength = raw.match(/master password must be at least (\d+) characters/i);
|
||||
if (minimumLength) {
|
||||
return tr('ui.masterPasswordMinLength', { count: minimumLength[1] }, 'Master password must be at least ' + minimumLength[1] + ' characters.');
|
||||
}
|
||||
if (/master password is empty/i.test(raw)) {
|
||||
return tr('ui.masterPasswordRequired', null, 'Enter a master password.');
|
||||
}
|
||||
if (/invalid master password/i.test(raw)) {
|
||||
return tr('ui.masterPasswordInvalid', null, 'The master password is incorrect.');
|
||||
}
|
||||
}
|
||||
return tr('ui.' + action + 'Error', null, {
|
||||
status: 'Could not check the secret storage. Please try again.',
|
||||
load: 'Could not load secrets. Please try again.',
|
||||
read: 'Could not open this secret. Please try again.',
|
||||
save: 'Could not save the secret. Please try again.',
|
||||
delete: 'Could not delete the secret. Please try again.',
|
||||
copyLink: 'Could not copy the secret link. Please try again.',
|
||||
unlock: 'Could not unlock secrets. Check the master password and try again.'
|
||||
}[action] || 'Could not complete this action. Please try again.');
|
||||
}
|
||||
|
||||
function setUserFacingError(action, err) {
|
||||
setStatus(userFacingError(action, err), true);
|
||||
}
|
||||
|
||||
function recordWorkspaceRoot(record) {
|
||||
var scope = record && record.scope || {};
|
||||
return scope.kind === ScopeWorkspace ? cleanWorkspace(scope.workspaceRootPath) : '';
|
||||
|
|
@ -249,7 +281,7 @@
|
|||
return loadRecords();
|
||||
}).catch(function (err) {
|
||||
unlockBtn.disabled = false;
|
||||
setStatus((err && err.message) ? err.message : String(err), true);
|
||||
setUserFacingError('unlock', err);
|
||||
});
|
||||
}
|
||||
}, [tr('ui.unlock', null, 'Unlock')]);
|
||||
|
|
@ -458,7 +490,7 @@
|
|||
selectedValue = '';
|
||||
return loadRecords();
|
||||
}).catch(function (err) {
|
||||
setStatus((err && err.message) ? err.message : String(err), true);
|
||||
setUserFacingError('save', err);
|
||||
});
|
||||
}
|
||||
}, [tr('ui.save', null, 'Save')]),
|
||||
|
|
@ -505,6 +537,8 @@
|
|||
selectedValue = '';
|
||||
mode = 'selected';
|
||||
render();
|
||||
}).catch(function (err) {
|
||||
setUserFacingError('load', err);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -522,7 +556,7 @@
|
|||
render();
|
||||
}).catch(function (err) {
|
||||
if (disposed) return;
|
||||
setStatus((err && err.message) ? err.message : String(err), true);
|
||||
setUserFacingError('read', err);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -549,7 +583,7 @@
|
|||
selectedValue = '';
|
||||
return loadRecords();
|
||||
}).catch(function (err) {
|
||||
setStatus((err && err.message) ? err.message : String(err), true);
|
||||
setUserFacingError('delete', err);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -559,7 +593,7 @@
|
|||
setStatus(tr('ui.linkCopied', null, 'Secret link copied'), false);
|
||||
});
|
||||
}).catch(function (err) {
|
||||
setStatus((err && err.message) ? err.message : String(err), true);
|
||||
setUserFacingError('copyLink', err);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -569,7 +603,7 @@
|
|||
if (unlocked) return loadWorkspaceOptions().then(loadRecords);
|
||||
render();
|
||||
}).catch(function (err) {
|
||||
statusText = (err && err.message) ? err.message : String(err);
|
||||
statusText = userFacingError('status', err);
|
||||
statusError = true;
|
||||
renderLocked();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
"ui.masterPassword": "Master password",
|
||||
"ui.repeatPassword": "Repeat master password",
|
||||
"ui.passwordMismatch": "Master passwords do not match",
|
||||
"ui.masterPasswordMinLength": "Master password must be at least {count} characters.",
|
||||
"ui.masterPasswordRequired": "Enter a master password.",
|
||||
"ui.masterPasswordInvalid": "The master password is incorrect.",
|
||||
"ui.unlock": "Unlock",
|
||||
"ui.createMaster": "Create master password",
|
||||
"ui.password": "Password",
|
||||
|
|
@ -38,6 +41,13 @@
|
|||
"ui.search": "Search secrets",
|
||||
"ui.chooseWorkspace": "Choose a Deal",
|
||||
"ui.workspaceRequired": "Choose a Deal for this secret.",
|
||||
"ui.statusError": "Could not check the secret storage. Please try again.",
|
||||
"ui.loadError": "Could not load secrets. Please try again.",
|
||||
"ui.readError": "Could not open this secret. Please try again.",
|
||||
"ui.saveError": "Could not save the secret. Please try again.",
|
||||
"ui.deleteError": "Could not delete the secret. Please try again.",
|
||||
"ui.copyLinkError": "Could not copy the secret link. Please try again.",
|
||||
"ui.unlockError": "Could not unlock secrets. Check the master password and try again.",
|
||||
"ui.value": "Value",
|
||||
"ui.save": "Save",
|
||||
"ui.cancel": "Cancel",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
"ui.masterPassword": "Мастер-пароль",
|
||||
"ui.repeatPassword": "Повторите мастер-пароль",
|
||||
"ui.passwordMismatch": "Мастер-пароли не совпадают",
|
||||
"ui.masterPasswordMinLength": "Мастер-пароль должен содержать не менее {count} символов.",
|
||||
"ui.masterPasswordRequired": "Введите мастер-пароль.",
|
||||
"ui.masterPasswordInvalid": "Мастер-пароль указан неверно.",
|
||||
"ui.unlock": "Разблокировать",
|
||||
"ui.createMaster": "Создать мастер-пароль",
|
||||
"ui.password": "Пароль",
|
||||
|
|
@ -38,6 +41,13 @@
|
|||
"ui.search": "Поиск секретов",
|
||||
"ui.chooseWorkspace": "Выберите дело",
|
||||
"ui.workspaceRequired": "Выберите дело для этого секрета.",
|
||||
"ui.statusError": "Не удалось проверить хранилище секретов. Повторите попытку.",
|
||||
"ui.loadError": "Не удалось загрузить секреты. Повторите попытку.",
|
||||
"ui.readError": "Не удалось открыть секрет. Повторите попытку.",
|
||||
"ui.saveError": "Не удалось сохранить секрет. Повторите попытку.",
|
||||
"ui.deleteError": "Не удалось удалить секрет. Повторите попытку.",
|
||||
"ui.copyLinkError": "Не удалось скопировать ссылку на секрет. Повторите попытку.",
|
||||
"ui.unlockError": "Не удалось разблокировать секреты. Проверьте мастер-пароль и повторите попытку.",
|
||||
"ui.value": "Значение",
|
||||
"ui.save": "Сохранить",
|
||||
"ui.cancel": "Отмена",
|
||||
|
|
|
|||
|
|
@ -96,10 +96,15 @@ function makeDocument() {
|
|||
};
|
||||
}
|
||||
|
||||
function loadComponent(document) {
|
||||
function loadComponent(document, errorLog) {
|
||||
const registry = {};
|
||||
vm.runInNewContext(source, {
|
||||
console,
|
||||
console: {
|
||||
...console,
|
||||
error(...args) {
|
||||
errorLog.push(args.map((value) => String(value)).join(' '));
|
||||
},
|
||||
},
|
||||
document,
|
||||
window: {
|
||||
VerstakPluginRegister(pluginId, bundle) {
|
||||
|
|
@ -138,13 +143,15 @@ async function flush() {
|
|||
if (!(manifest.contributes.settingsPanels || []).some((item) => item.component === 'SecretsView')) throw new Error('secrets settings panel missing');
|
||||
|
||||
const document = makeDocument();
|
||||
const component = loadComponent(document);
|
||||
const errorLog = [];
|
||||
const component = loadComponent(document, errorLog);
|
||||
const records = [
|
||||
{ id: 'global.server', title: 'Global Server', username: 'root', scope: { kind: 'global' }, updatedAt: '2026-06-29T00:00:00Z' },
|
||||
{ id: 'client-a.db', title: 'Client A DB', username: 'app', scope: { kind: 'workspace', workspaceRootPath: 'ClientA' }, updatedAt: '2026-06-29T00:00:00Z' },
|
||||
];
|
||||
let initialized = false;
|
||||
let unlocked = false;
|
||||
let unlockError = '';
|
||||
const readCalls = [];
|
||||
const copied = [];
|
||||
const deleted = [];
|
||||
|
|
@ -152,6 +159,7 @@ async function flush() {
|
|||
secrets: {
|
||||
status: async () => ({ initialized, unlocked }),
|
||||
unlock: async (password) => {
|
||||
if (unlockError) throw new Error(unlockError);
|
||||
if (password !== 'master-password') throw new Error('bad password');
|
||||
initialized = true;
|
||||
unlocked = true;
|
||||
|
|
@ -197,6 +205,21 @@ async function flush() {
|
|||
const confirmInput = walk(container, (node) => node.getAttribute && node.getAttribute('data-secret-master-password-confirm') === '');
|
||||
const unlockButton = walk(container, (node) => node.getAttribute && node.getAttribute('data-secret-unlock') === '');
|
||||
if (!passwordInput || !confirmInput || !unlockButton) throw new Error('setup controls missing');
|
||||
unlockError = '[plugin:verstak.secrets] secrets.unlock failed: master password must be at least 8 characters';
|
||||
passwordInput.value = 'short';
|
||||
confirmInput.value = 'short';
|
||||
unlockButton.click();
|
||||
await flush();
|
||||
if (!container.textContent.includes('Master password must be at least 8 characters')) {
|
||||
throw new Error('weak master password error was not explained clearly');
|
||||
}
|
||||
if (container.textContent.includes('[plugin:') || container.textContent.includes('secrets.unlock')) {
|
||||
throw new Error('technical unlock details leaked into the Secrets UI');
|
||||
}
|
||||
if (!errorLog.some((entry) => entry.includes('[plugin:verstak.secrets] secrets.unlock failed'))) {
|
||||
throw new Error('technical unlock details were not retained in the console log');
|
||||
}
|
||||
unlockError = '';
|
||||
passwordInput.value = 'master-password';
|
||||
confirmInput.value = 'master-password';
|
||||
unlockButton.click();
|
||||
|
|
|
|||
Loading…
Reference in New Issue