Webhooks
En lugar de esperar a que finalice una Assembly y responda su solicitud a la API, también puedes configurar un webhook, también conocido como Assembly Notification. El sistema enviará una solicitud POST a una URL que elijas con un informe completo una vez que finalice una Assembly.
¿Por qué usar webhooks?
Al optar por usar webhooks, permites que tus usuarios finales disfruten de una experiencia más fluida, ya que solo necesitan esperar a que finalicen las subidas de archivos antes de cerrar la ventana del navegador. Si no hay subidas de archivos, incluso podrían cerrar la ventana del navegador de inmediato.
¿Cómo activar los webhooks?
Para activar los webhooks, agrega notify_url a las Assembly Instructions de tu
Template, en el mismo nivel del JSON que steps:
{
"steps": {
// …
},
"notify_url": "https://example.com/transloadit_pingback"
}
Cuando ejecutes tu Template, Transloadit informará a tu back-end una vez que haya finalizado todo
el procesamiento mediante el envío de una solicitud POST a la URL definida, con todo el Assembly Status JSON.
Si no quieres que tus usuarios o tu programa esperen a que finalice el encoding, normalmente también tendrás que configurar un indicador.
En el caso de Uppy, establece el parámetro waitForEncoding en false. En muchos
SDK de back-end, esperar a que finalice el encoding implica consultar explícitamente el Assembly Status, por lo que
bastará con no hacerlo.
Tu back-end debe responder con un encabezado 200; de lo contrario, Transloadit asumirá que la Notification
falló y reintentará el envío varias veces con espera exponencial.
Personalizar el contenido de la Notification
De forma predeterminada, las solicitudes de webhook incluyen el
Assembly Status JSON completo. Si tus Assemblies generan muchos
resultados, puedes reducir el tamaño del contenido con notification_payload.
Agrégalo junto a notify_url:
{
"steps": {
// …
},
"notify_url": "https://example.com/transloadit_pingback",
"notification_payload": ["without_results", "without_upload_meta_data"]
}
Valores compatibles:
without_results: omitir el objetoresultsde nivel superior del contenido del webhookwithout_result_meta_data: conservarresults, pero eliminarmetade cada archivo resultantewithout_uploads: omitir el arreglouploadsde nivel superior del contenido del webhookwithout_upload_meta_data: conservaruploads, pero eliminarmetade cada archivo subido
Puedes combinar varios valores en el mismo arreglo. Esto también se aplica cuando vuelves a enviar Notifications desde la página de la Assembly.
¿Cómo es esta solicitud POST?
Esta solicitud POST multipart contiene un campo llamado transloadit, que incluye el
Assembly Status JSON completo. Puedes consultar un ejemplo en nuestra
documentación de respuestas de la API. También incluirá un campo
signature para que puedas verificar, de manera opcional, que la solicitud realmente provino de nosotros y no
fue alterada. El siguiente ejemplo de código muestra cómo calcular esa firma con tu Auth Secret para que coincida con la que
enviamos.
Ejemplo de código
Supongamos que efectivamente especificaste "notify_url": "https://example.com/transloadit_pingback" y
que el servidor back-end que aceptaría allí las solicitudes POST entrantes está escrito en Node.js.
Este ejemplo muestra cómo verificar las firmas de webhooks entrantes de Transloadit, lo cual es diferente de generar firmas para solicitudes a la API. Para crear Assemblies con generación automática de firmas, usa nuestros SDK. Si quieres ver cómo funciona internamente la verificación de firmas, puedes consultar el código fuente del SDK de Node.
El servidor de verificación podría verse así:
import crypto from 'node:crypto'
import http from 'node:http'
import formidable from 'formidable'
const PORT = process.argv[2] || 3020
if (!/^[A-Za-z0-9]{40}$/.test(process.env.AUTH_SECRET)) {
throw new Error(`Please pass the secret from https://transloadit.com/c/template-credentials
via the AUTH_SECRET environment var. It must be the auth secret that belongs to the auth key you used for the original Assembly.`)
}
const checkSignature = (fields, authSecret) => {
const receivedSignature = fields.signature
const payload = fields.transloadit
if (!receivedSignature || !payload) {
return false
}
// If the signature contains a colon, we expect it to be of format `algo:actual_signature`.
// If there are no colons, we assume it's a legacy signature using SHA-1.
const algoSeparatorIndex = receivedSignature.indexOf(':')
const algo = algoSeparatorIndex === -1 ? 'sha1' : receivedSignature.slice(0, algoSeparatorIndex)
try {
const calculatedSignature = crypto
.createHmac(algo, authSecret)
.update(Buffer.from(payload, 'utf-8'))
.digest('hex')
// If we are in legacy signature mode, algoSeparatorIndex is -1 and we are
// comparing the whole string. Otherwise we slice out the prefixed algo.
return calculatedSignature === receivedSignature.slice(algoSeparatorIndex + 1)
} catch {
// We can assume the signature string was ill-formed.
return false
}
}
const respond = (res, code, messages) => {
if (code !== 200) {
console.error({ messages, code })
}
res.writeHead(code, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
})
if (messages) {
res.write(JSON.stringify({ messages }))
}
res.end()
}
http
.createServer((req, res) => {
if (req.method === 'OPTIONS') {
return respond(res, 204)
}
if (req.url === '/transloadit_pingback' && req.method === 'POST') {
const form = new formidable.IncomingForm()
form.parse(req, (err, fields) => {
if (err) {
return respond(res, 500, [`Error while parsing multipart form`, err])
}
if (!checkSignature(fields, process.env.AUTH_SECRET)) {
return respond(res, 403, [
`Error while checking signatures`,
`No match so payload was tampered with, or an invalid Auth Secret was used`,
])
}
let assembly = {}
try {
assembly = JSON.parse(fields.transloadit)
} catch (err) {
return respond(res, 500, [`Error while parsing transloadit field`, err])
}
console.log(`--> ${assembly.ok || assembly.error} ${assembly.assembly_ssl_url}`)
for (const upload of assembly.uploads) {
// save upload.ssl_url and metadata to your db here
console.log(` ^- uploaded '${upload.name}' ready at ${upload.ssl_url}`)
}
for (const stepName in assembly.results) {
for (const result of assembly.results[stepName]) {
// save result.ssl_url and metadata to your db here
console.log(` ^- ${stepName} '${result.name}' ready at ${result.ssl_url}`)
}
}
return respond(res, 200, [`Success!`])
})
} else {
return respond(res, 500, [
`Welcome! I only know how to handle POSTs to /transloadit_pingback`,
`No handler for req.url=${req.url}, req.method=${req.method}`,
])
}
})
.listen(PORT, () => {
console.log(`Server started, listening on http://0.0.0.0:${PORT}`)
})
Podrías ejecutar este script de la siguiente manera:
$ env AUTH_SECRET=******** node notification-backend-node.js 3020
Server started, listening on http://0.0.0.0:3020
Probar el ejemplo de código localmente
Cuando hagas pruebas locales detrás de una NAT, usa Cloudflare Tunnel, ngrok o la herramienta oficial @transloadit/notify-url-relay mediante npx -y @transloadit/notify-url-relay; a diferencia de los túneles, el relay consulta el Assembly Status público y reenvía las notificaciones finales a tu controlador local de notify_url.
Opción recomendada (relay oficial), en una pestaña nueva:
$ TRANSLOADIT_SECRET=******** npx -y @transloadit/notify-url-relay \
--notifyUrl "http://127.0.0.1:3020/transloadit_pingback" \
--log-level info
notify-url-relay [ NOTICE] Listening on http://localhost:8888, forwarding to https://api2.transloadit.com, notifying http://127.0.0.1:3020/transloadit_pingback
Cuando uses el relay, configura el endpoint de Transloadit de tu aplicación o SDK como http://127.0.0.1:8888.
Ahora puedes crear un Template y pegar las siguientes Instructions:
{
"notify_url": "http://127.0.0.1:3020/transloadit_pingback",
"steps": {
":original": {
"robot": "/upload/handle"
},
"faces_detected": {
"use": ":original",
"robot": "/image/facedetect",
"crop": true,
"faces": "max-confidence",
"crop_padding": "10%",
"format": "preserve"
}
}
}
Si prefieres usar un túnel, utiliza Cloudflare Tunnel o ngrok.
En el momento de redactar esta documentación, ngrok parece tener problemas con los rangos de AWS. Si este es tu caso, puedes probar como alternativa Cloudflare Tunnel o la herramienta oficial @transloadit/notify-url-relay.
Ahora ya puedes hacer la prueba directamente en el navegador. El conjunto de Instructions que usamos detecta un rostro; para obtener resultados óptimos, sube una foto de una persona mediante el área de pruebas del Template Editor. Puedes usar la función Webcam de Uppy si no tienes una imagen disponible.
Tu script de Node.js debería indicar que recibió correctamente la Assembly Notification cuando finalice la Assembly:
-- > ASSEMBLY_COMPLETED https://api2.transloadit.com/assemblies/b2b580bdc969427091a48f1f0d3d9d40
^- uploaded 'avatar.jpg' ready at https://s3.amazonaws.com/tmp.transloadit.com/ff89be82...
^- faces_detected 'avatar.jpg' ready at https://s3.amazonaws.com/tmp.transloadit.com/fd2f61b9...
Además, verás un registro de la Notification en la página de la Assembly, donde también podrás reintentar manualmente su envío para realizar más pruebas.
El código de ejemplo anterior muestra cómo verificar firmas de webhooks en Node.js. Para crear Assemblies con generación automática de firmas, en lugar de verificar webhooks entrantes, consulta los ejemplos de los SDK en la documentación de Signature Authentication.