Passport needs a "personal access client" to issue personal access tokens, and only the seeder created it (production runs migrations, not the seeder), so createToken threw "Personal access client not found". - New idempotent command passport:ensure-personal-client, run by the container entrypoint after the migrations. - The tokens page now shows an error when the server fails instead of doing nothing (es, en, ca). - Tests: the command creates the client once; tokens can be created. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
357 lines
16 KiB
TypeScript
357 lines
16 KiB
TypeScript
const es = {
|
|
nav: {
|
|
dashboard: 'Dashboard',
|
|
clients: 'Clientes OAuth',
|
|
tokens: 'Personal Access Tokens',
|
|
authorizedApps: 'Apps autorizadas',
|
|
security: 'Seguridad',
|
|
users: 'Usuarios',
|
|
analytics: 'Analíticas',
|
|
auditLog: 'Audit log',
|
|
apiDocs: 'API Docs',
|
|
logout: 'Salir',
|
|
},
|
|
common: {
|
|
loading: 'Cargando…',
|
|
creating: 'Creando…',
|
|
create: 'Crear',
|
|
save: 'Guardar',
|
|
cancel: 'Cancelar',
|
|
close: 'Cerrar',
|
|
edit: 'Editar',
|
|
delete: 'Borrar',
|
|
none: '(ninguno)',
|
|
unexpectedError: 'Algo salió mal. Probá de nuevo en un momento.',
|
|
},
|
|
login: {
|
|
title: 'Ingresar',
|
|
subtitle: 'Servidor OAuth2 — panel de administración.',
|
|
email: 'Email',
|
|
password: 'Contraseña',
|
|
submit: 'Ingresar',
|
|
submitting: 'Ingresando…',
|
|
noAccount: '¿No tenés cuenta?',
|
|
signUp: 'Registrate',
|
|
invalidCredentials: 'Credenciales inválidas.',
|
|
twoFactorTitle: 'Verificación en dos pasos',
|
|
twoFactorSubtitle: 'Ingresá el código de tu app autenticadora (o un código de recuperación).',
|
|
twoFactorCode: 'Código',
|
|
twoFactorSubmit: 'Verificar',
|
|
invalidTwoFactorCode: 'Código inválido.',
|
|
backToLogin: 'Volver a ingresar',
|
|
forgotPassword: '¿Olvidaste tu contraseña?',
|
|
},
|
|
forgotPassword: {
|
|
title: 'Restablecer contraseña',
|
|
subtitle: 'Ingresá tu email y te mandamos un enlace para elegir una contraseña nueva.',
|
|
email: 'Email',
|
|
submit: 'Enviar enlace',
|
|
submitting: 'Enviando…',
|
|
sent: 'Si existe una cuenta con ese email, te enviamos un enlace para restablecer la contraseña. Revisá tu bandeja de entrada.',
|
|
backToLogin: 'Volver a ingresar',
|
|
},
|
|
resetPassword: {
|
|
title: 'Elegí una contraseña nueva',
|
|
subtitle: 'Ya casi — confirmá la contraseña nueva para tu cuenta.',
|
|
password: 'Contraseña nueva',
|
|
passwordConfirm: 'Confirmar contraseña',
|
|
submit: 'Restablecer contraseña',
|
|
submitting: 'Restableciendo…',
|
|
success: 'Tu contraseña se restableció. Ya podés ingresar con la nueva — cerramos las sesiones activas por seguridad.',
|
|
goToLogin: 'Ir a ingresar',
|
|
invalidLink: 'Este enlace es inválido o venció. Pedí uno nuevo.',
|
|
missingParams: 'Falta información en el enlace. Pedí uno nuevo desde "¿Olvidaste tu contraseña?".',
|
|
},
|
|
register: {
|
|
title: 'Crear cuenta',
|
|
name: 'Nombre',
|
|
email: 'Email',
|
|
password: 'Contraseña',
|
|
passwordConfirm: 'Confirmar contraseña',
|
|
submit: 'Crear cuenta',
|
|
submitFirstUser: 'Crear cuenta de administrador',
|
|
submitting: 'Creando…',
|
|
haveAccount: '¿Ya tenés cuenta?',
|
|
signIn: 'Ingresá',
|
|
firstUserBanner: 'Todavía no hay ningún usuario. Esta cuenta va a quedar como administradora, con acceso inmediato (sin aprobación).',
|
|
pendingTitle: 'Cuenta creada',
|
|
pendingBody: 'Un administrador tiene que aprobar tu cuenta antes de que puedas ingresar. Te va a poder avisar por fuera de la app cuando esté lista.',
|
|
backToLogin: 'Volver a ingresar',
|
|
},
|
|
dashboard: {
|
|
greeting: 'Hola, {name}',
|
|
subtitle: 'Este es el panel de administración del servidor OAuth2.',
|
|
clientsTitle: 'Clientes OAuth',
|
|
clientsDesc: 'Registrá apps (Authorization Code+PKCE, Client Credentials o Password Grant).',
|
|
tokensTitle: 'Personal Access Tokens',
|
|
tokensDesc: 'Generá tokens de acceso directo a la API, como API keys.',
|
|
appsTitle: 'Apps autorizadas',
|
|
appsDesc: 'Apps de terceros a las que le diste acceso a tu cuenta.',
|
|
securityTitle: 'Seguridad',
|
|
securityDesc: 'Activá verificación en dos pasos (2FA) para tu cuenta.',
|
|
usersTitle: 'Usuarios',
|
|
usersDesc: 'Alta, edición de rol/estado y baja de cuentas (solo admins).',
|
|
analyticsTitle: 'Analíticas',
|
|
analyticsDesc: 'Sesiones activas, logins por app y usuarios más activos (solo admins).',
|
|
apiTitle: 'API REST',
|
|
apiDesc: 'Este servidor expone el recurso {resource} (scopes {read}/{write}) para que aplicaciones de terceros gestionen usuarios vía OAuth2. Requiere un access token de Passport. Documentación interactiva en {link}.',
|
|
},
|
|
clients: {
|
|
title: 'Clientes OAuth',
|
|
subtitle: 'Apps registradas que pueden pedir tokens a este servidor.',
|
|
secretWarning: 'Guardá este client secret ahora, no se puede volver a ver:',
|
|
clientIdLabel: 'client_id:',
|
|
clientSecretLabel: 'client_secret:',
|
|
newClient: 'Nuevo cliente',
|
|
name: 'Nombre',
|
|
grantType: 'Grant type',
|
|
grantAuthCode: 'Authorization Code + PKCE',
|
|
grantClientCredentials: 'Client Credentials',
|
|
grantPassword: 'Password Grant',
|
|
redirectUris: 'Redirect URIs (separadas por coma)',
|
|
confidentialLabel: 'Cliente confidencial (con secret). Desmarcá para un cliente público que solo use PKCE.',
|
|
create: 'Crear cliente',
|
|
empty: 'Todavía no creaste ningún cliente.',
|
|
revoke: 'Revocar',
|
|
revokeConfirm: '¿Revocar el cliente "{name}"? Sus tokens dejarán de funcionar.',
|
|
clientIdShort: 'client_id',
|
|
redirectLabel: 'redirect',
|
|
webhooksTitle: 'Webhooks',
|
|
hideWebhooks: 'Ocultar webhooks',
|
|
webhooksDesc: 'Notificaciones salientes a tu app cuando pasa algo relevante — sin tener que hacer polling.',
|
|
webhookSecretWarning: 'Guardá este secret ahora, no se puede volver a ver. Usalo para verificar la firma HMAC-SHA256 en el header X-Webhook-Signature.',
|
|
webhookSecretLabel: 'secret:',
|
|
webhookPaused: 'pausado',
|
|
lastDelivery: 'último envío',
|
|
pending: 'pendiente',
|
|
pause: 'Pausar',
|
|
resume: 'Reanudar',
|
|
viewHistory: 'Ver historial',
|
|
hideHistory: 'Ocultar historial',
|
|
noDeliveries: 'Todavía no hubo entregas para este webhook.',
|
|
attempts: 'intentos: {count}',
|
|
retryDelivery: 'Reintentar',
|
|
retrying: 'Reintentando…',
|
|
noWebhooks: 'Todavía no registraste ningún webhook para esta app.',
|
|
webhookUrl: 'URL del webhook',
|
|
addWebhook: 'Agregar webhook',
|
|
webhookDeleteConfirm: '¿Borrar el webhook "{url}"?',
|
|
eventTokenRevoked: 'Token revocado',
|
|
eventUserDeactivated: 'Usuario desactivado',
|
|
rotateSecret: 'Rotar secret',
|
|
rotateSecretConfirm: '¿Rotar el secret de "{name}"? El secret actual va a seguir funcionando 48 horas más para no cortar integraciones que todavía no se actualizaron.',
|
|
rotateSecretFailed: 'No se pudo rotar el secret.',
|
|
previousSecretStillValid: 'El secret anterior sigue siendo válido hasta el {date}.',
|
|
},
|
|
tokens: {
|
|
title: 'Personal Access Tokens',
|
|
subtitle: 'Tokens de acceso directo a la API, sin pasar por el flujo de authorize (tipo API key).',
|
|
tokenWarning: 'Guardá este token ahora, no se puede volver a ver:',
|
|
newToken: 'Nuevo token',
|
|
name: 'Nombre',
|
|
namePlaceholder: 'Mi script',
|
|
scopes: 'Scopes',
|
|
create: 'Crear token',
|
|
empty: 'Todavía no creaste ningún token.',
|
|
revoke: 'Revocar',
|
|
revokeConfirm: '¿Revocar el token "{name}"?',
|
|
scopesLabel: 'scopes',
|
|
},
|
|
authorizedApps: {
|
|
title: 'Apps autorizadas',
|
|
subtitle: 'Aplicaciones de terceros a las que le diste acceso a tu cuenta.',
|
|
empty: 'No autorizaste ninguna app todavía.',
|
|
revoke: 'Revocar acceso',
|
|
revokeConfirm: '¿Revocar el acceso de "{name}"?',
|
|
scopesLabel: 'scopes',
|
|
},
|
|
authorize: {
|
|
title: 'Autorizar aplicación',
|
|
wantsAccess: '{client} quiere acceder a tu cuenta ({email}) con los siguientes permisos:',
|
|
noScopes: 'No solicita permisos adicionales (acceso básico).',
|
|
approve: 'Autorizar',
|
|
cancel: 'Cancelar',
|
|
notFoundTitle: 'Solicitud no encontrada',
|
|
notFoundBody: 'Esta pantalla solo funciona cuando llegás acá redirigido por una aplicación cliente vía {path}. Volvé a intentar desde esa app.',
|
|
goDashboard: 'Ir al dashboard',
|
|
},
|
|
adminUsers: {
|
|
title: 'Usuarios',
|
|
subtitle: 'Alta, edición de rol/estado y baja de cuentas.',
|
|
newUser: 'Nuevo usuario',
|
|
name: 'Nombre',
|
|
email: 'Email',
|
|
password: 'Contraseña',
|
|
role: 'Rol',
|
|
roleUser: 'Usuario',
|
|
roleAdmin: 'Admin',
|
|
create: 'Crear usuario',
|
|
colName: 'Nombre',
|
|
colEmail: 'Email',
|
|
colRole: 'Rol',
|
|
colStatus: 'Estado',
|
|
active: 'Activo',
|
|
inactive: 'Desactivado',
|
|
noRole: '(sin rol)',
|
|
edit: 'Editar',
|
|
delete: 'Borrar',
|
|
deleteConfirm: '¿Borrar al usuario "{name}"? Va a la papelera y se puede restaurar.',
|
|
deleteFailed: 'No se pudo borrar el usuario.',
|
|
newPassword: 'Nueva contraseña',
|
|
noChange: '(sin cambios)',
|
|
save: 'Guardar',
|
|
cancel: 'Cancelar',
|
|
appRolesTitle: 'Roles por app',
|
|
appRolesDesc: 'El rol global de arriba manda en este panel. Acá se define qué rol tiene {name} dentro de cada app cuando esa app pide su usuario por API.',
|
|
noAppRoles: 'Sin roles asignados en ninguna app todavía.',
|
|
chooseApp: 'Elegir app…',
|
|
assign: 'Asignar',
|
|
assignFailed: 'No se pudo asignar el rol.',
|
|
trashTitle: 'Papelera',
|
|
viewTrash: 'Ver papelera',
|
|
viewActive: 'Ver activos',
|
|
trashEmpty: 'No hay usuarios borrados.',
|
|
deletedAt: 'Borrado el {date}',
|
|
restore: 'Restaurar',
|
|
restoreFailed: 'No se pudo restaurar el usuario.',
|
|
forceDelete: 'Borrar definitivamente',
|
|
forceDeleteConfirm: '¿Borrar definitivamente a "{name}"? Esta acción no se puede deshacer.',
|
|
forceDeleteFailed: 'No se pudo borrar definitivamente.',
|
|
pendingTitle: 'Pendientes',
|
|
viewPending: 'Pendientes',
|
|
pendingEmpty: 'No hay registros pendientes de aprobación.',
|
|
registeredAt: 'Se registró el {date}',
|
|
approve: 'Aprobar',
|
|
approveFailed: 'No se pudo aprobar el usuario.',
|
|
reject: 'Rechazar',
|
|
rejectConfirm: '¿Rechazar el registro de "{name}"? Va a la papelera y se puede restaurar después.',
|
|
rejectFailed: 'No se pudo rechazar el registro.',
|
|
sessionsTitle: 'Sesiones activas',
|
|
sessionsDesc: 'Tokens OAuth vigentes de este usuario — apps con acceso a su cuenta ahora mismo.',
|
|
noSessions: 'No hay sesiones activas.',
|
|
sessionUnknownClient: '(app borrada)',
|
|
revokeSession: 'Revocar',
|
|
revokeSessionConfirm: '¿Revocar la sesión de "{name}"? Esa app va a dejar de tener acceso.',
|
|
revokeAllSessions: 'Cerrar todas',
|
|
revokeAllSessionsConfirm: '¿Cerrar todas las sesiones activas de "{name}"? Todos sus tokens OAuth van a quedar revocados.',
|
|
},
|
|
analytics: {
|
|
title: 'Analíticas',
|
|
subtitle: 'Sesiones, logins por app y actividad de usuarios.',
|
|
last7: 'Últimos 7 días',
|
|
last30: 'Últimos 30 días',
|
|
last90: 'Últimos 90 días',
|
|
exportPdf: 'Exportar PDF',
|
|
totalUsers: 'Usuarios totales',
|
|
activeSessions: 'Sesiones activas',
|
|
activeSessionsHint: 'tokens OAuth vigentes',
|
|
loginsToday: 'Logins hoy',
|
|
logins7d: 'Logins últimos 7 días',
|
|
loginsByDay: 'Logins por día',
|
|
appsOAuth: 'Apps (OAuth)',
|
|
panelAdmin: 'Panel admin',
|
|
loginsByApp: 'Logins por app',
|
|
topUsers: 'Usuarios más activos',
|
|
noAppLogins: 'Todavía no hay logins de apps en este período.',
|
|
noUserActivity: 'Todavía no hay actividad en este período.',
|
|
recentActivity: 'Actividad reciente',
|
|
hide: 'Ocultar',
|
|
viewAll: 'Ver todo',
|
|
colUser: 'Usuario',
|
|
colApp: 'App',
|
|
colType: 'Tipo',
|
|
colIp: 'IP',
|
|
colWhen: 'Cuándo',
|
|
serviceNoUser: '(servicio, sin usuario)',
|
|
deletedApp: '(app borrada)',
|
|
recentCount: '{count} eventos recientes — click en "Ver todo" para la tabla completa.',
|
|
chartAriaLabel: 'Logins por día, apilado por tipo',
|
|
tooltipAppLabel: 'Apps (OAuth):',
|
|
tooltipSessionLabel: 'Panel admin:',
|
|
barAriaLabel: '{date}: {oauth} logins de apps, {session} al panel admin',
|
|
loginFailureRate: 'Tasa de fallos de login',
|
|
loginFailureRateHint: '{count} fallidos en 7 días',
|
|
oauthApprovalRate: 'Tasa de aprobación OAuth',
|
|
oauthApprovalRateHint: '{approved}/{attempted} en el período',
|
|
funnelTitle: 'Funnel de autorización OAuth',
|
|
funnelEmpty: 'Todavía no hay intentos de autorización en este período.',
|
|
colClient: 'Cliente',
|
|
colAttempted: 'Intentos',
|
|
colApproved: 'Aprobados',
|
|
colDenied: 'Denegados',
|
|
colApprovalRate: 'Aprobación',
|
|
activeTokensByClient: 'Tokens activos por cliente',
|
|
noActiveTokens: 'No hay tokens activos por ahora.',
|
|
scopeUsageTitle: 'Uso de scopes',
|
|
noScopeUsage: 'Todavía no se otorgó ningún scope en este período.',
|
|
},
|
|
auditLog: {
|
|
title: 'Audit log',
|
|
subtitle: 'Cambios de permisos, altas/bajas de usuarios y clientes OAuth.',
|
|
allSubjects: 'Todos los sujetos',
|
|
subjectUser: 'Usuarios',
|
|
subjectClient: 'Clientes OAuth',
|
|
allActions: 'Todas las acciones',
|
|
empty: 'Todavía no hay eventos registrados.',
|
|
colWhen: 'Cuándo',
|
|
colActor: 'Quién',
|
|
colAction: 'Acción',
|
|
colSubject: 'Sujeto',
|
|
colDetails: 'Detalles',
|
|
colIp: 'IP',
|
|
systemActor: '(sistema)',
|
|
fieldChanged: '{field} cambiada',
|
|
prevPage: 'Anterior',
|
|
nextPage: 'Siguiente',
|
|
pageInfo: 'Página {page} de {lastPage} — {total} eventos',
|
|
actions: {
|
|
userCreated: 'Usuario creado',
|
|
userUpdated: 'Usuario editado',
|
|
userDeleted: 'Usuario borrado',
|
|
userRestored: 'Usuario restaurado',
|
|
userForceDeleted: 'Usuario borrado definitivamente',
|
|
userApproved: 'Usuario aprobado',
|
|
appRoleAssigned: 'Rol por app asignado',
|
|
appRoleRevoked: 'Rol por app revocado',
|
|
clientCreated: 'Cliente OAuth creado',
|
|
clientRevoked: 'Cliente OAuth revocado',
|
|
twoFactorEnabled: '2FA activado',
|
|
twoFactorDisabled: '2FA desactivado',
|
|
twoFactorRecoveryRegenerated: 'Códigos de recuperación regenerados',
|
|
sessionRevoked: 'Sesión revocada',
|
|
sessionsRevokedAll: 'Todas las sesiones cerradas',
|
|
webhookCreated: 'Webhook creado',
|
|
webhookDeleted: 'Webhook borrado',
|
|
secretRotated: 'Secret rotado',
|
|
passwordReset: 'Contraseña restablecida',
|
|
},
|
|
},
|
|
security: {
|
|
title: 'Seguridad',
|
|
subtitle: 'Verificación en dos pasos (2FA) para tu cuenta.',
|
|
enabled: '2FA activo',
|
|
enabledDesc: 'Tu cuenta pide un código de tu app autenticadora cada vez que iniciás sesión.',
|
|
disabled: '2FA desactivado',
|
|
disabledDesc: 'Agregá una capa extra de seguridad: un código de tu teléfono además de la contraseña.',
|
|
enable: 'Activar 2FA',
|
|
disable: 'Desactivar',
|
|
disableConfirm: 'Confirmar desactivación',
|
|
regenerateCodes: 'Regenerar códigos de recuperación',
|
|
confirmPassword: 'Confirmá tu contraseña',
|
|
confirm: 'Confirmar',
|
|
setupTitle: 'Escaneá el código QR',
|
|
setupDesc: 'Usá Google Authenticator, Authy o cualquier app compatible con TOTP.',
|
|
manualEntry: '¿No podés escanear? Ingresá este código a mano:',
|
|
enterCode: 'Código de 6 dígitos',
|
|
invalidCode: 'Código inválido. Probá de nuevo.',
|
|
invalidPassword: 'Contraseña incorrecta.',
|
|
setupFailed: 'No se pudo iniciar la activación de 2FA.',
|
|
qrAlt: 'Código QR para configurar 2FA',
|
|
recoveryCodesTitle: 'Guardá estos códigos de recuperación',
|
|
recoveryCodesWarning: 'Cada uno sirve una sola vez. Usalos si perdés acceso a tu app autenticadora. No se van a volver a mostrar.',
|
|
},
|
|
};
|
|
|
|
export default es;
|
|
export type MessageSchema = typeof es;
|