refactor: better error handling and prevention of creating ephemeral password protected profiles

This commit is contained in:
zhom
2026-05-12 13:03:34 +04:00
parent 06b5a41b37
commit 2633e2ba09
20 changed files with 170 additions and 9 deletions
+1 -3
View File
@@ -1931,9 +1931,7 @@ export function ProfilesDataTable({
const meta = table.options.meta as TableMeta;
const profile = row.original;
const browser = profile.browser;
const IconComponent = profile.password_protected
? LuLock
: getProfileIcon(profile);
const IconComponent = getProfileIcon(profile);
const isCrossOs = isCrossOsProfile(profile);
const isSelected = meta.isProfileSelected(profile.id);
+1
View File
@@ -1734,6 +1734,7 @@
"profileNotProtected": "Profile is not password protected",
"profileAlreadyProtected": "Profile is already password protected",
"profileRunning": "Cannot perform this action while the profile is running",
"profileEphemeral": "Ephemeral profiles cannot be password-protected — their data wipes on quit.",
"profileMissingSalt": "Profile is missing its encryption salt",
"profileLocked": "Profile is locked. Enter the password first.",
"invalidProfileId": "Invalid profile id",
+1
View File
@@ -1734,6 +1734,7 @@
"profileNotProtected": "El perfil no está protegido por contraseña",
"profileAlreadyProtected": "El perfil ya está protegido por contraseña",
"profileRunning": "No se puede realizar esta acción mientras el perfil está en ejecución",
"profileEphemeral": "Los perfiles efímeros no pueden tener contraseña — sus datos se borran al salir.",
"profileMissingSalt": "Al perfil le falta su sal de cifrado",
"profileLocked": "El perfil está bloqueado. Introduce la contraseña primero.",
"invalidProfileId": "ID de perfil no válido",
+1
View File
@@ -1734,6 +1734,7 @@
"profileNotProtected": "Le profil n'est pas protégé par mot de passe",
"profileAlreadyProtected": "Le profil est déjà protégé par mot de passe",
"profileRunning": "Impossible d'effectuer cette action pendant que le profil est en cours d'exécution",
"profileEphemeral": "Les profils éphémères ne peuvent pas être protégés par mot de passe — leurs données s'effacent à la fermeture.",
"profileMissingSalt": "Le sel de chiffrement du profil est manquant",
"profileLocked": "Le profil est verrouillé. Entrez d'abord le mot de passe.",
"invalidProfileId": "Identifiant de profil non valide",
+1
View File
@@ -1734,6 +1734,7 @@
"profileNotProtected": "プロファイルはパスワード保護されていません",
"profileAlreadyProtected": "プロファイルはすでにパスワード保護されています",
"profileRunning": "プロファイルの実行中はこの操作を実行できません",
"profileEphemeral": "エフェメラル プロファイルにはパスワードを設定できません — 終了時にデータが消去されます。",
"profileMissingSalt": "プロファイルに暗号化ソルトがありません",
"profileLocked": "プロファイルはロックされています。先にパスワードを入力してください。",
"invalidProfileId": "無効なプロファイルIDです",
+1
View File
@@ -1734,6 +1734,7 @@
"profileNotProtected": "O perfil não está protegido por senha",
"profileAlreadyProtected": "O perfil já está protegido por senha",
"profileRunning": "Não é possível realizar esta ação enquanto o perfil está em execução",
"profileEphemeral": "Perfis efêmeros não podem ser protegidos por senha — seus dados são apagados ao sair.",
"profileMissingSalt": "O perfil está sem o sal de criptografia",
"profileLocked": "O perfil está bloqueado. Digite a senha primeiro.",
"invalidProfileId": "ID de perfil inválido",
+1
View File
@@ -1734,6 +1734,7 @@
"profileNotProtected": "Профиль не защищён паролем",
"profileAlreadyProtected": "Профиль уже защищён паролем",
"profileRunning": "Невозможно выполнить это действие, пока профиль запущен",
"profileEphemeral": "Эфемерные профили не могут быть защищены паролем — их данные удаляются при выходе.",
"profileMissingSalt": "У профиля отсутствует соль шифрования",
"profileLocked": "Профиль заблокирован. Сначала введите пароль.",
"invalidProfileId": "Недействительный идентификатор профиля",
+1
View File
@@ -1734,6 +1734,7 @@
"profileNotProtected": "配置文件未受密码保护",
"profileAlreadyProtected": "配置文件已受密码保护",
"profileRunning": "配置文件运行时无法执行此操作",
"profileEphemeral": "临时配置文件无法设置密码 — 退出时数据会被清除。",
"profileMissingSalt": "配置文件缺少加密盐",
"profileLocked": "配置文件已锁定。请先输入密码。",
"invalidProfileId": "配置文件 ID 无效",
+3
View File
@@ -11,6 +11,7 @@ export type BackendErrorCode =
| "PROFILE_NOT_PROTECTED"
| "PROFILE_ALREADY_PROTECTED"
| "PROFILE_RUNNING"
| "PROFILE_EPHEMERAL"
| "PROFILE_MISSING_SALT"
| "PROFILE_LOCKED"
| "INVALID_PROFILE_ID"
@@ -74,6 +75,8 @@ export function translateBackendError(t: TFunction, err: unknown): string {
return t("backendErrors.profileAlreadyProtected");
case "PROFILE_RUNNING":
return t("backendErrors.profileRunning");
case "PROFILE_EPHEMERAL":
return t("backendErrors.profileEphemeral");
case "PROFILE_MISSING_SALT":
return t("backendErrors.profileMissingSalt");
case "PROFILE_LOCKED":
+7
View File
@@ -9,6 +9,7 @@ import {
FaFire,
FaFirefox,
} from "react-icons/fa";
import { LuLock } from "react-icons/lu";
/**
* Map internal browser names to display names
@@ -42,7 +43,13 @@ export function getBrowserIcon(browserType: string) {
export function getProfileIcon(profile: {
browser: string;
ephemeral?: boolean;
password_protected?: boolean;
}) {
// `password_protected` and `ephemeral` are mutually exclusive (the backend
// rejects setting a password on an ephemeral profile), so the order here
// doesn't matter — checking lock first only matters if the invariant is
// ever violated, in which case showing the lock is the safer default.
if (profile.password_protected) return LuLock;
if (profile.ephemeral) return FaFire;
return getBrowserIcon(profile.browser);
}