Three separate gaps, all of which made the F4 interface non-functional or unprotected: The admin console rendered for anyone. There was no middleware and no guard in the layout, so /admin/dashboard returned 200 unauthenticated - only its data calls failed. middleware.ts now redirects both surfaces to their login when the session cookie is missing, expired, carries the wrong audience, or is a pre-MFA challenge token. It reads the claims without verifying the signature on purpose: the signing key belongs to the auth service and must not reach the browser bundle. That is sound because this is not the boundary - every /api/v1 request is verified for real upstream. Nothing served /api/v1/*. next.config.ts had no rewrites and there were no route handlers, so every call the pages made 404'd against the Next server, while AUTH_SERVICE_URL sat unused. A catch-all route now proxies to the auth service, forwarding the client address and preserving multiple Set-Cookie headers, which logout needs to clear both surfaces. Session cookies also move to Path=/, since a cookie pinned to /user or /admin was never sent with the /api/v1 requests that needed it. i18n existed only as files. The three bundles had perfect key parity and real translations, but no library, no imports and no switcher: the UI was hardcoded English and i18n.test.ts compared keys while the feature was absent. There is now a provider, a hook, a language switcher, and server-side Accept-Language negotiation that honours q-values and matches a bare "pt" to pt-BR. Logins, both layouts and the settings screen consume it; the admin pages still have English titles. The settings screen invented app passwords in the browser with Math.random and never stored them. It now calls the API, which returns the password once and keeps only its hash. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
81 lines
2.9 KiB
JSON
81 lines
2.9 KiB
JSON
{
|
|
"common": {
|
|
"appName": "LambdaMail",
|
|
"userPortal": "Webmail",
|
|
"adminPortal": "Consola de Gestión",
|
|
"loading": "Cargando...",
|
|
"save": "Guardar Cambios",
|
|
"cancel": "Cancelar",
|
|
"delete": "Eliminar",
|
|
"confirm": "Confirmar",
|
|
"search": "Buscar",
|
|
"status": "Estado",
|
|
"actions": "Acciones",
|
|
"refresh": "Actualizar",
|
|
"logout": "Cerrar Sesión"
|
|
},
|
|
"auth": {
|
|
"userLoginTitle": "Iniciar Sesión en Webmail",
|
|
"adminLoginTitle": "Iniciar Sesión en Consola Admin",
|
|
"emailLabel": "Dirección de Correo",
|
|
"passwordLabel": "Contraseña",
|
|
"signInButton": "Iniciar Sesión",
|
|
"totpCodeLabel": "Código del Autenticador (6 dígitos)",
|
|
"verifyCodeButton": "Verificar Código",
|
|
"invalidCredentials": "Correo o contraseña no válidos",
|
|
"mfaRequired": "Código de autenticación de dos factores requerido"
|
|
},
|
|
"mail": {
|
|
"inbox": "Bandeja de Entrada",
|
|
"sent": "Enviados",
|
|
"drafts": "Borradores",
|
|
"trash": "Papelera",
|
|
"junk": "Spam",
|
|
"archive": "Archivo",
|
|
"compose": "Redactar Correo",
|
|
"noMessages": "No hay mensajes en esta carpeta",
|
|
"unreadCount": "{count, plural, =0 {Sin mensajes no leídos} one {# mensaje no leído} other {# mensajes no leídos}}",
|
|
"loadImages": "Cargar Imágenes Remotas",
|
|
"reply": "Responder",
|
|
"forward": "Reenviar",
|
|
"undoSend": "Deshacer Envío ({seconds}s restantes)"
|
|
},
|
|
"settings": {
|
|
"title": "Configuración de la Cuenta",
|
|
"security": "Seguridad y 2FA",
|
|
"appPasswords": "Contraseñas de Aplicación",
|
|
"sessions": "Sesiones Activas",
|
|
"mfaStatus": "Estado de Autenticación de Dos Factores",
|
|
"mfaEnabled": "2FA está Activo",
|
|
"mfaDisabled": "2FA está Desactivado",
|
|
"enableMfa": "Activar 2FA",
|
|
"generateAppPassword": "Generar Contraseña de Aplicación",
|
|
"recoveryCodesTitle": "Códigos de Recuperación",
|
|
"saveRecoveryCodes": "Guarde estos códigos de recuperación en un lugar seguro. No se volverán a mostrar.",
|
|
"language": "Idioma",
|
|
"signOut": "Cerrar sesión"
|
|
},
|
|
"admin": {
|
|
"dashboardTitle": "Panel del Sistema",
|
|
"domainsTitle": "Dominios y DNS (13 Registros)",
|
|
"mailboxesTitle": "Buzones y Cuentas",
|
|
"aliasesTitle": "Alias y Enrutamiento",
|
|
"queueTitle": "Cola de Entrega de Salida",
|
|
"dmarcTitle": "Análisis DMARC",
|
|
"securityTitle": "Seguridad y Registro de Auditoría",
|
|
"preflightTitle": "Diagnóstico Preflight",
|
|
"reconcileDns": "Reconciliar Registros DNS",
|
|
"dnsStatusVerified": "VERIFICADO",
|
|
"dnsStatusDrift": "DESVÍO DETECTADO",
|
|
"dnsStatusPartial": "PARCIAL",
|
|
"retryJob": "Reintentar Entrega",
|
|
"cancelJob": "Cancelar Trabajo",
|
|
"freezeDomain": "Congelar Destino"
|
|
},
|
|
"errors": {
|
|
"unauthorized": "Sesión expirada o no autorizada",
|
|
"forbidden": "Acceso prohibido para esta superficie de cuenta",
|
|
"serverError": "Ocurrió un error interno en el servidor"
|
|
}
|
|
}
|