feat(alta): borrador, pre-llenado desde cotización y liga remota de firma

- Borradores por vendedor (guardar/continuar/eliminar), se limpia al registrar
- Pre-llenado con ?cotizacion=<id> (cliente, contacto, proyecto, montos);
  botón 'Iniciar alta' en Mis cotizaciones del cotizador
- Liga remota pública /client/firma/<token> (JWT scope firma-remota, 72h,
  rate limit, solo datos mínimos sin PLD): firma digital + subida de
  documentos que prende checkboxes en Monday Apartado
- Tokens de liga no sirven como sesión y viceversa
- Tests: api 228, alta 82, cotizador 82
This commit is contained in:
2026-08-28 07:29:22 +00:00
parent 4f31d1c171
commit 59944668a8
18 changed files with 1589 additions and 33 deletions

View File

@@ -66,7 +66,10 @@ export interface TokenPayload {
/** Verifica un Bearer token y devuelve su payload; lanza 401 si no es válido */
export function verificarToken(token: string): TokenPayload {
try {
return jwt.verify(token, config.auth.jwtSecret) as TokenPayload
const payload = jwt.verify(token, config.auth.jwtSecret) as Partial<TokenPayload>
// Los tokens con otro scope (p. ej. la liga de firma remota) no son sesión
if (typeof payload.sub !== 'string' || !payload.sub) throw new Error('sin sub')
return payload as TokenPayload
} catch {
throw new AppError(401, 'Token inválido o expirado')
}