turnero: integración QZ Tray para impresión silenciosa en kiosko
- Genera par de llaves RSA (cert en config/qz/) - api/qz_sign.php firma los print jobs con SHA512 - assets/js/qz-print.js conecta QZ Tray y envía trabajo HTML - ver_formulario_enviado.php carga QZ cuando ?autoprint=1 - Fallback a window.print() si QZ no está instalado Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
2cfb77b1a6
commit
5dd6ee5040
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/qz_sign.php
|
||||
* Firma el request de QZ Tray con la llave privada RSA del servidor.
|
||||
* QZ Tray verifica la firma usando el digital-certificate.txt instalado en el cliente.
|
||||
*/
|
||||
header('Content-Type: text/plain');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
|
||||
$request = file_get_contents('php://input');
|
||||
if (!$request) { http_response_code(400); exit('missing request'); }
|
||||
|
||||
$keyPath = __DIR__ . '/../config/qz/private-key.pem';
|
||||
$key = openssl_pkey_get_private('file://' . $keyPath);
|
||||
if (!$key) { http_response_code(500); exit('key error'); }
|
||||
|
||||
openssl_sign($request, $signature, $key, 'SHA512');
|
||||
echo base64_encode($signature);
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* qz-print.js — Impresión silenciosa vía QZ Tray
|
||||
* Requiere qz-tray.js cargado antes de este archivo.
|
||||
* Uso: qzPrint({ url, printer })
|
||||
*/
|
||||
window.qzPrint = async function({ printer } = {}) {
|
||||
try {
|
||||
// 1. Conectar
|
||||
if (!qz.websocket.isActive()) {
|
||||
await qz.websocket.connect();
|
||||
}
|
||||
|
||||
// 2. Firma con el servidor
|
||||
qz.security.setSignatureAlgorithm('SHA512');
|
||||
qz.security.setSignaturePromise(function(toSign) {
|
||||
return function(resolve, reject) {
|
||||
fetch('/api/qz_sign.php', { method: 'POST', body: toSign })
|
||||
.then(r => r.text()).then(resolve).catch(reject);
|
||||
};
|
||||
});
|
||||
|
||||
// 3. Certificado público
|
||||
qz.security.setCertificatePromise(function(resolve) {
|
||||
fetch('/config/qz/digital-certificate.txt')
|
||||
.then(r => r.text()).then(resolve);
|
||||
});
|
||||
|
||||
// 4. Seleccionar impresora
|
||||
var p = printer || await qz.printers.getDefault();
|
||||
console.log('[QZ] Impresora:', p);
|
||||
|
||||
// 5. Imprimir la página actual como HTML
|
||||
var cfg = qz.configs.create(p);
|
||||
var data = [{
|
||||
type : 'pixel',
|
||||
format: 'html',
|
||||
flavor: 'plain',
|
||||
data : document.documentElement.outerHTML,
|
||||
}];
|
||||
|
||||
await qz.print(cfg, data);
|
||||
console.log('[QZ] Trabajo enviado correctamente');
|
||||
} catch(e) {
|
||||
console.warn('[QZ] Error:', e.message || e);
|
||||
// Fallback al diálogo del navegador si QZ no está disponible
|
||||
window.print();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
# Bloquear acceso web a la llave privada
|
||||
<Files "private-key.pem">
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
</Files>
|
||||
@@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID+TCCAuGgAwIBAgIULkHXGPyy0w0ieUoN/YOZOO5MPvkwDQYJKoZIhvcNAQEL
|
||||
BQAwgYsxCzAJBgNVBAYTAkNPMRswGQYDVQQIDBJOb3J0ZSBkZSBTYW50YW5kZXIx
|
||||
DzANBgNVBAcMBkN1Y3V0YTEjMCEGA1UECgwaTGFib3JhdG9yaW8gWGltZW5hIENh
|
||||
aWNlZG8xKTAnBgNVBAMMIGVycC5sYWJvcmF0b3Jpb3hpbWVuYWNhaWNlZG8uY29t
|
||||
MB4XDTI2MDcyMTE5NTEzNloXDTM2MDcxODE5NTEzNlowgYsxCzAJBgNVBAYTAkNP
|
||||
MRswGQYDVQQIDBJOb3J0ZSBkZSBTYW50YW5kZXIxDzANBgNVBAcMBkN1Y3V0YTEj
|
||||
MCEGA1UECgwaTGFib3JhdG9yaW8gWGltZW5hIENhaWNlZG8xKTAnBgNVBAMMIGVy
|
||||
cC5sYWJvcmF0b3Jpb3hpbWVuYWNhaWNlZG8uY29tMIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAjbzIjSUEj6qjwHvg+qNAEk0rAjfezbpyGPGkbH1AFnmI
|
||||
8rz2TZKAN5PWJSvjYS91YEtnfFttEDa0wQpvHOIPhn2l8gAsYNFoDS+i1ilXHBEA
|
||||
OtAB7uVL2TvrZBTQtcab4upI1ehUjdqA9ae51KBEfqOD3k7dc5X8IeskBDMLfI5G
|
||||
cuJejGpx6S8aFur/PfZfu3oApHW02b4GXqSguUHwBJZtGauFQWRrN/6VG3YVKXON
|
||||
g8bDEyFgPCZ2Slp4pHoLK5BIjBkF1t3PUcSq5rRZCxjYzoOcDIfoVV5f4Y+QSspM
|
||||
u3tI+cMAnqwrKma/TBmSSikTMuK9nb+qjVvlQ/kijwIDAQABo1MwUTAdBgNVHQ4E
|
||||
FgQUVnCTxhnONKLUYtxlT4SsIPH5rEgwHwYDVR0jBBgwFoAUVnCTxhnONKLUYtxl
|
||||
T4SsIPH5rEgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEL/L
|
||||
PP8b2UQavVLh3r0D3Is0V7JrVTRsT/iB2FwJLhudHAojVBinh7+tvZboEFkJ5trR
|
||||
bNG3XxXDprw76awc/BsR19YXWrjZfraROrVNYEVTW1+Mit2HYRuTiEnVxOX1EENR
|
||||
dAwZrwtFmpy3I2sZNblxQm0cZbDq6g4x/LBTwUXmXQK259Nrv5SEjs8wixeDMDNC
|
||||
wuA9vAamsw+38nqHCouNtE+yPwM7srinFkfilMnIB2d874eqaVBvEKWmUtBCsWaO
|
||||
f5OviyodKH9BtF+ACpgU2E/at03VratR5REIjje52qsWOMY1Wnn8FIYsCWJIPQYu
|
||||
m56yKz4rQLPX/kyeCQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCNvMiNJQSPqqPA
|
||||
e+D6o0ASTSsCN97NunIY8aRsfUAWeYjyvPZNkoA3k9YlK+NhL3VgS2d8W20QNrTB
|
||||
Cm8c4g+GfaXyACxg0WgNL6LWKVccEQA60AHu5UvZO+tkFNC1xpvi6kjV6FSN2oD1
|
||||
p7nUoER+o4PeTt1zlfwh6yQEMwt8jkZy4l6ManHpLxoW6v899l+7egCkdbTZvgZe
|
||||
pKC5QfAElm0Zq4VBZGs3/pUbdhUpc42DxsMTIWA8JnZKWnikegsrkEiMGQXW3c9R
|
||||
xKrmtFkLGNjOg5wMh+hVXl/hj5BKyky7e0j5wwCerCsqZr9MGZJKKRMy4r2dv6qN
|
||||
W+VD+SKPAgMBAAECggEAPqedtfsPrZx+f6ejN9hzicOQCA5/kMzjBBDJoOWrL2Qx
|
||||
PDB45qikwiy5ZLwmav8qMVOT3v6hUyIDvDPrE0cBGvAvK6+U7oWTLAULRAWJStBf
|
||||
HCB4Qk0dPt3Ee/zRmBFANspfQSPPQNe+2xj2Rj5EmQCaWerd7Or3xlymEq8n3Dp2
|
||||
E9pFn4wiEDdjXG69/fqu/LoFpz31E2Mp0q+V4ZdWvMXA4U6akH2/oGWCnjDdhm/O
|
||||
Wy1shQL5oVxdw/yhHhaC/3GZv/MKVbaW7JeBtnt3S5Szktr6A1O2obSJS+KOw9d+
|
||||
Y2rqIMAWXv+kHKzAB2LtfczpfkEDkqQ7jCbu+sjX4QKBgQC+3uwfwM9mlxoCt93E
|
||||
cerMyfrbssTjZjZSsxnHsGz+qmDr8TIyOrwcZnnYs6MNkuTJW6MvgN8hM8oO2arh
|
||||
E3s0oWrbILD42rzVQUeLr7/N/JjUwK7yyEnfZX/hnQdilRsT+rxpXBIA0HfUheih
|
||||
6KkddNoOyvs+wu1eOralO117LwKBgQC+Ge4ch1hicRchwX/Bu+h3cdkLVVgdI08+
|
||||
O7vnSCuhj8Ead7z3C5XjEqJOs75xY+LF4UxPsrqTuCxpWIOupCSnRe+RGMcJdzbR
|
||||
0i/leyL9fWhvHzP13R4T6sqAHqIXKgCfR7Hx99TW3Whc4N2GoqAnIrjqow+H+ic8
|
||||
8Hrr+Ul2oQKBgQC1AyRbWLdYS6RXP5gJXR+X51UIVZlzLtQFyeSBBEfZnCselzdL
|
||||
e3g6VtTnNjVEAjMG4uj3e/gfvMW7H6J2ocsONqbn+TDcUFUUyTvYtWvpJcyqt7Ey
|
||||
fc/RFKkahZkjXNS5NejI4pAQRaPe4L+mDMeVL+Q8czOiaapC2tusB4i38QKBgCYh
|
||||
G1Jbj03HcyVRI2ffYcQ7cJZGWvMVNvq7jnfYUPAJ3miJpbxDdZ/jB+0TPlqN91lL
|
||||
VDwUFDo20ambmGX6BGQMsf1/Y8SxRayWJQc5SI5hjgXj0084N6U1DcLe4hIVWaSZ
|
||||
A8cNt4IVTK58Z9JuYgMXgtGFPUM/2Ijvjygvix2hAoGBALSgjUzho5Jn/vAQza/M
|
||||
k4q24RZ0kBJ1Go+/b3iSOieCCVKgow9224r/frcrFvXWzbNjhKs/HAV1bB9uCrxD
|
||||
/V/1byH0XLYmYIKvhTqpTtBLCYXrv+2m+ox8pHrgPze4KuJy3VdN/V/Dj3CpY0VL
|
||||
G5Uf9TfOiMk4EiZw/pBR1YT9
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -2762,7 +2762,16 @@ const topaz = (() => {
|
||||
<?php endif; ?>
|
||||
</script>
|
||||
<?php if ($autoprint): ?>
|
||||
<script>window.addEventListener('load', function() { setTimeout(function() { window.print(); }, 600); });</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.4/qz-tray.js"></script>
|
||||
<script src="/assets/js/qz-print.js"></script>
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
setTimeout(function() {
|
||||
if (typeof qz !== 'undefined') { qzPrint(); }
|
||||
else { window.print(); }
|
||||
}, 800);
|
||||
});
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user