#34: fix the mail counters, the reader pane, and the state between surfaces
The counts were computed inline in JSX where nothing could test them. The
sidebar matched a folder by a special-use role folders do not always carry, its
badges were fetched once and then froze - new mail did not raise them and
reading a message did not lower it - and the list header printed the size of
the loaded page, capped at 50, as the size of the folder. The rules now live in
mailCounts.ts behind tests, useFolders keeps them current from the server's
push and from this tab's own changes, and each filter chip carries its number.
Inline images never left the server: RenderMessage dropped every part a body
references as cid:, so mail written by any normal client rendered with broken
icons. They now come back as bounded data URIs, refusing image/svg+xml, and the
reader resolves them, sizes its frame from the message instead of a fixed
500px, and renders on the light surface mail assumes.
Reading mail no longer asks for a second factor - the mailbox is behind the
account's own password - while the console still requires one unconditionally,
now collected by a step-up that does not ask again for the password the session
in hand just proved. An admin sign-in issues the webmail session too, so
leaving the console is a link rather than another login.
Two defects found while testing the above:
- The admin sign-in checked the password and the second factor and never the
role, so any account that had enrolled a factor from its own settings could
obtain an admin-audience session.
- Session tokens carried nothing that varied below one second, so signing in
twice within the same second minted the identical token and collided with
the unique index on web_sessions.refresh_token_hash. A correct password
answered 500, and a double-clicked sign-in was enough to do it. Tokens now
carry a jti.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e2ea58a4c9
commit
8b69521c76
25 files changed
+2276
-380
No files matched your search
@@ -89,11 +89,15 @@
|
||||
"genericError": "Something went wrong",
|
||||
"invalidCredentials": "Invalid email or password",
|
||||
"mfaRequired": "Enter the six-digit code from your authenticator app.",
|
||||
"orRecoveryCode": "Lost your phone? A recovery code works here too.",
|
||||
"passwordLabel": "Password",
|
||||
"qrCodeAlt": "QR code for two-factor setup",
|
||||
"scanQr": "Scan this code with Google Authenticator, Aegis, Authy or 1Password.",
|
||||
"signInButton": "Sign in",
|
||||
"signOutOfAdmin": "Leave admin",
|
||||
"stepUpIntro": "You are already signed in, so only your second factor is needed. Your password is not asked for again.",
|
||||
"stepUpSignedInAs": "Signed in as {email}",
|
||||
"stepUpTitle": "Open the admin console",
|
||||
"totpCodeLabel": "Authentication code",
|
||||
"userLoginTitle": "Sign in to your mailbox",
|
||||
"verificationFailed": "Verification failed",
|
||||
@@ -145,7 +149,9 @@
|
||||
"draftSaved": "Draft saved automatically",
|
||||
"draftSaving": "Saving draft...",
|
||||
"drafts": "Drafts",
|
||||
"emptyBody": "This message has no text body.",
|
||||
"forward": "Forward",
|
||||
"imagesEnabled": "Images loaded",
|
||||
"hideCcBcc": "- Hide Cc/Bcc",
|
||||
"inbox": "Inbox",
|
||||
"insertLink": "Insert link",
|
||||
@@ -153,9 +159,18 @@
|
||||
"italic": "Italic",
|
||||
"junk": "Spam",
|
||||
"loadImages": "Load images",
|
||||
"markRead": "Mark as read",
|
||||
"markUnread": "Mark as unread",
|
||||
"messagesInFolder": "{count} messages",
|
||||
"noMatches": "Nothing matched that search",
|
||||
"noMessages": "No messages",
|
||||
"noSubject": "(No subject)",
|
||||
"numberedList": "Numbered list",
|
||||
"remoteContentBlocked": "Remote images were blocked so this message cannot track you.",
|
||||
"reply": "Reply",
|
||||
"searchMatches": "{count} found",
|
||||
"searchPlaceholder": "Search (from:, subject:)...",
|
||||
"selectAMessage": "Select a message to read it",
|
||||
"sendMessage": "Send message",
|
||||
"sending": "Sending...",
|
||||
"sent": "Sent",
|
||||
@@ -232,6 +247,7 @@
|
||||
"appPasswordsSection": "App passwords",
|
||||
"attempts": "Attempts",
|
||||
"bounceRate": "Bounce rate",
|
||||
"ccRecipients": "Cc",
|
||||
"changePassword": "Change password",
|
||||
"clientIp": "Client IP",
|
||||
"copyPasswordNow": "Copy it now. It is not shown again.",
|
||||
|
||||
@@ -89,11 +89,15 @@
|
||||
"genericError": "Algo salió mal",
|
||||
"invalidCredentials": "Correo o contraseña inválidos",
|
||||
"mfaRequired": "Introduzca el código de seis dígitos de su aplicación de autenticación.",
|
||||
"orRecoveryCode": "¿Perdiste el teléfono? Aquí también sirve un código de recuperación.",
|
||||
"passwordLabel": "Contraseña",
|
||||
"qrCodeAlt": "Código QR para configurar la verificación en dos pasos",
|
||||
"scanQr": "Escanee este código con Google Authenticator, Aegis, Authy o 1Password.",
|
||||
"signInButton": "Iniciar sesión",
|
||||
"signOutOfAdmin": "Salir de administración",
|
||||
"stepUpIntro": "Ya has iniciado sesión, así que solo hace falta tu segundo factor. No se te pide la contraseña otra vez.",
|
||||
"stepUpSignedInAs": "Sesión iniciada como {email}",
|
||||
"stepUpTitle": "Abrir la consola de administración",
|
||||
"totpCodeLabel": "Código de autenticación",
|
||||
"userLoginTitle": "Acceda a su buzón",
|
||||
"verificationFailed": "Fallo en la verificación",
|
||||
@@ -145,7 +149,9 @@
|
||||
"draftSaved": "Borrador guardado automáticamente",
|
||||
"draftSaving": "Guardando borrador...",
|
||||
"drafts": "Borradores",
|
||||
"emptyBody": "Este mensaje no tiene cuerpo de texto.",
|
||||
"forward": "Reenviar",
|
||||
"imagesEnabled": "Imágenes cargadas",
|
||||
"hideCcBcc": "- Ocultar Cc/Cco",
|
||||
"inbox": "Bandeja de entrada",
|
||||
"insertLink": "Insertar enlace",
|
||||
@@ -153,9 +159,18 @@
|
||||
"italic": "Cursiva",
|
||||
"junk": "Spam",
|
||||
"loadImages": "Cargar imágenes",
|
||||
"markRead": "Marcar como leído",
|
||||
"markUnread": "Marcar como no leído",
|
||||
"messagesInFolder": "{count} mensajes",
|
||||
"noMatches": "Nada coincide con esa búsqueda",
|
||||
"noMessages": "Sin mensajes",
|
||||
"noSubject": "(Sin asunto)",
|
||||
"numberedList": "Lista numerada",
|
||||
"remoteContentBlocked": "Se bloquearon las imágenes remotas para que este mensaje no pueda rastrearte.",
|
||||
"reply": "Responder",
|
||||
"searchMatches": "{count} encontrados",
|
||||
"searchPlaceholder": "Buscar (from:, subject:)...",
|
||||
"selectAMessage": "Selecciona un mensaje para leerlo",
|
||||
"sendMessage": "Enviar mensaje",
|
||||
"sending": "Enviando...",
|
||||
"sent": "Enviados",
|
||||
@@ -232,6 +247,7 @@
|
||||
"appPasswordsSection": "Contraseñas de aplicación",
|
||||
"attempts": "Intentos",
|
||||
"bounceRate": "Tasa de rebote",
|
||||
"ccRecipients": "Cc",
|
||||
"changePassword": "Cambiar contraseña",
|
||||
"clientIp": "IP del cliente",
|
||||
"copyPasswordNow": "Cópiela ahora. No se mostrará de nuevo.",
|
||||
|
||||
@@ -89,11 +89,15 @@
|
||||
"genericError": "Algo deu errado",
|
||||
"invalidCredentials": "E-mail ou senha inválidos",
|
||||
"mfaRequired": "Digite o código de seis dígitos do seu aplicativo autenticador.",
|
||||
"orRecoveryCode": "Perdeu o celular? Um código de recuperação também funciona aqui.",
|
||||
"passwordLabel": "Senha",
|
||||
"qrCodeAlt": "Código QR para configurar a verificação em duas etapas",
|
||||
"scanQr": "Escaneie este código com o Google Authenticator, Aegis, Authy ou 1Password.",
|
||||
"signInButton": "Entrar",
|
||||
"signOutOfAdmin": "Sair da administração",
|
||||
"stepUpIntro": "Você já está autenticado, então basta o segundo fator. A senha não é pedida de novo.",
|
||||
"stepUpSignedInAs": "Autenticado como {email}",
|
||||
"stepUpTitle": "Abrir o console administrativo",
|
||||
"totpCodeLabel": "Código de autenticação",
|
||||
"userLoginTitle": "Entre na sua caixa de e-mail",
|
||||
"verificationFailed": "Falha na verificação",
|
||||
@@ -145,7 +149,9 @@
|
||||
"draftSaved": "Rascunho salvo automaticamente",
|
||||
"draftSaving": "Salvando rascunho...",
|
||||
"drafts": "Rascunhos",
|
||||
"emptyBody": "Esta mensagem não tem corpo de texto.",
|
||||
"forward": "Encaminhar",
|
||||
"imagesEnabled": "Imagens carregadas",
|
||||
"hideCcBcc": "- Ocultar Cc/Cco",
|
||||
"inbox": "Caixa de entrada",
|
||||
"insertLink": "Inserir link",
|
||||
@@ -153,9 +159,18 @@
|
||||
"italic": "Itálico",
|
||||
"junk": "Spam",
|
||||
"loadImages": "Carregar imagens",
|
||||
"markRead": "Marcar como lida",
|
||||
"markUnread": "Marcar como não lida",
|
||||
"messagesInFolder": "{count} mensagens",
|
||||
"noMatches": "Nada corresponde a essa busca",
|
||||
"noMessages": "Nenhuma mensagem",
|
||||
"noSubject": "(Sem assunto)",
|
||||
"numberedList": "Lista numerada",
|
||||
"remoteContentBlocked": "As imagens remotas foram bloqueadas para que esta mensagem não possa rastrear você.",
|
||||
"reply": "Responder",
|
||||
"searchMatches": "{count} encontradas",
|
||||
"searchPlaceholder": "Buscar (from:, subject:)...",
|
||||
"selectAMessage": "Selecione uma mensagem para ler",
|
||||
"sendMessage": "Enviar mensagem",
|
||||
"sending": "Enviando...",
|
||||
"sent": "Enviados",
|
||||
@@ -232,6 +247,7 @@
|
||||
"appPasswordsSection": "Senhas de aplicativo",
|
||||
"attempts": "Tentativas",
|
||||
"bounceRate": "Taxa de devolução",
|
||||
"ccRecipients": "Cc",
|
||||
"changePassword": "Alterar senha",
|
||||
"clientIp": "IP do cliente",
|
||||
"copyPasswordNow": "Copie agora. Ela não será exibida novamente.",
|
||||
|
||||
Reference in new issue
Block a user