https://d226lax1qjow5r.cloudfront.net/blog/blogposts/register-to-chat-with-typeform-dr/TW_Typeform_1200x675.png

Regístrese para chatear con Typeform

Publicado el May 24, 2021

Tiempo de lectura: 44 minutos

En este artículo, aprenderá a configurar Typeform y capturar datos desde un webhook en el entorno Node.js framework Express.js. Utilizará Passport.js para autenticar un usuario, usarás SDK de Servidor Node.js de Nexmo de Nexmo para registrar un usuario y generar un JWT para usarlo con el JavaScript Client SDK de Nexmo.

Partirás de una aplicación de chat pre-construida usando JavaScript Client SDK de Nexmo y Bootstrap.

Este tutorial parte del maestro y termina en la rama tutorial-finish rama. Puede saltar hasta el final consultando tutorial-finish y siguiendo el LÉEME para empezar a trabajar rápidamente.

Requisitos previos

Nodo y NPM

Para seguir esta guía, necesitarás Node.js y NPM instalados. Esta guía utiliza Node.js 13.1 y NPM 6.12. Comprueba que tienes instaladas versiones estables o de soporte a largo plazo de Node.js, como mínimo.

node --version
npm --version

Si no tienes Node.js o NPM, o tienes versiones anteriores, dirígete a nodejs.org e instala la versión correcta si no la tienes.

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Nexmo CLI

Para configurar su aplicación, necesitará instalar la aplicación Nexmo CLI. Instálalo usando NPM en el terminal.

npm install -g nexmo-cli@beta

Ahora, configure la CLI utilizando su clave de API y el secreto, que se encuentra en su Account de Nexmo.

nexmo setup

MongoDB

Almacenaremos la información en MongoDB. Si no tienes MongoDB instalado, sigue la Guía de instalación de MongoDB Community Edition para tu sistema.

Ngrok

Dado que estará recibiendo información de un tercero, necesitará exponer la aplicación que se ejecuta en su máquina local, pero de una forma segura. Ngrok es una forma segura de utilizar un único comando para obtener una URL instantánea y segura que te permite acceder a tu máquina local, incluso a través de un NAT o firewall.

Regístrate y configura ngrok siguiendo las instrucciones de su sitio web.

Typeform

Utilizará Typeform para capturar las entradas de los usuarios, así que regístrese ahora para obtener una Account gratuita de Typeform.

Proveedor SMTP de correo electrónico

Enviarás correos electrónicos. Necesitarás el nombre de host, el puerto, un nombre de usuario y una contraseña para un proveedor SMTP.

Puede utilizar Google Mail para enviar correo electrónico desde una aplicación.

Git (opcional)

Puede utilizar git para clonar la aplicación de demostración desde GitHub.

Si no te sientes cómodo con git, esta guía también contiene instrucciones para descargar el proyecto como archivo ZIP.

Siga esta guía para instalar git

Empezar

La aplicación con la que estás empezando es una aplicación de chat construida usando Bootstrap y el Nexmo JavaScript Client SDK. Es configurable a través de la edición de archivos estáticos, pero lanzado usando Express.jsun servidor http ligero basado en Node.js.

Instalación básica

Clone la aplicación de demostración directamente desde GitHub.

git clone https://github.com/nexmo-community/nexmo-chat-typeform-magiclinks.git

O, para quienes no se sientan cómodos con los comandos git, pueden descargar la aplicación de demostración como archivo zip y descomprimirlo localmente.

Una vez clonado o descomprimido, cambie al nuevo directorio de aplicaciones de demostración.

cd nexmo-chat-typeform-magiclinks

Instale las dependencias de npm.

npm install

Junto a Node.js se instala un paquete llamado nodemonque recargará automáticamente su servidor si edita algún archivo.

Inicie la aplicación de la forma habitual.

npm start

Inicie la aplicación, pero con nodemon en su lugar.

npm run dev

Consejo: Si estás ejecutando la aplicación con nodemon durante el resto de este tutorial, cada vez que sugiera reiniciar la aplicación no tendrás que hacerlo porque nodemon lo hace por ti. Sin embargo, si usted necesita volver a autenticarse con la aplicación, usted todavía tendrá que hacer eso, ya que la información de la sesión se almacena en la memoria y no está configurado para utilizar cualquier otro almacenamiento.

Sea cual sea la forma que elijas para ejecutar la aplicación, una vez en marcha puedes probarla en tu navegador favorito, que debería ser capaz de encontrarla ejecutándose localmente: http://0.0.0.0:3000/.

Chat running locally

Como la aplicación no está configurada, verás una aplicación de chat vacía muy simple a la que no puedes enviar mensajes. En el mundo real con manejo de errores, podrías mostrar al usuario un error de conexión.

Pero, si ahora compruebas la consola del navegador, sólo verás un error de la API Nexmo por falta de token. Esto significa que la aplicación intentó conectarse pero no proporcionó un token de usuario que permitiera acceder a la API.

Compruebe que ngrok está configurado correctamente, ejecutando ngrok en una pestaña o ventana separada para npm.

ngrok http 3000

Chat running locally through ngrok

Debe ejecutar este comando ngrok comando y npm al mismo tiempo. Esto significa que necesita disponer de dos ventanas o pestañas de terminal, ambas en el directorio de aplicaciones.

Consejo: Si necesitas repetir alguna búsqueda más tarde, como enviar datos desde Typeform al webhook, puedes abrir interfaz web de ngrok en http://127.0.0.1:4040 mientras se está ejecutando y Reproducir una petición.

Una cosa a recordar es que hasta que pague por ngrok, su URL será diferente cada vez que lo inicie. Recuerda esto cuando configures tu webhook de Typeform más adelante. Si detienes ngrok, necesitarás reconfigurar Typeform con la nueva URL cuando lo inicies de nuevo.

Consejo: Si tiene confianza en el uso de una herramienta como Postman o escribiendo peticiones cURL manuales, y una vez que tengas tu primera petición webhook desde Typeform, podrías crear una petición para poder repetir esa petición más adelante.

Chatear

En los requisitos previos, configuraste tu CLI usando tu clave y secreto de la API de Nexmo. Ahora, puede ejecutar comandos CLI para crear una aplicación Nexmo, usuario, conversación, unirse al usuario a la conversación y generar un JWT para que su usuario pueda chatear.

Configuración de Nexmo

Necesitarás utilizar algunos de los ID devueltos una vez que hayas ejecutado algunos de los comandos. Toma nota, copiando y pegando tus ID de aplicación, conversación y usuario.

Crear aplicación Nexmo

Este comando crea una nueva aplicación Nexmo con capacidades RTC (comunicación en tiempo real). No va a capturar los eventos en su aplicación, por lo que puede proporcionar una dirección web de ejemplo para la URL del evento. La clave privada se enviará a una ruta de archivo de su elección.

nexmo app:create "Nexmo RTC Chat" --capabilities=rtc --rtc-event-url=http://example.com --keyfile=private.key # Application created: 4556dbae-bf...f6e33350d8 # Credentials written to .nexmo-app # Private Key saved to: private.key

Consejo: Su aplicación también se envía a un archivo de configuración (.nexmo-app) en el directorio en el que ejecutó este comando. Esto significa que algunos otros comandos de este directorio serán relevantes para esta aplicación, como la creación de usuarios y conversaciones.

Crear conversación Nexmo

Con una aplicación creada, puedes crear una conversación. La conversación será a lo que se unan tus usuarios para enviar mensajes de un lado a otro.

nexmo conversation:create display_name="Typeform Chatroom" # Conversation created: CON-a57b0...11e57f56d

Cree su usuario

Ahora, crea un usuario. Este será el usuario con el que te autentiques. Por el momento sólo necesitas un nombre de usuario y un nombre para mostrar.

nexmo user:create name= display_name= # User created: USR-6eaa4...e36b8a47f

Añadir usuario a conversación

Con tu ID de conversación y tu ID de usuario, ejecuta este comando para unirte a la conversación con tu usuario.

nexmo member:add action=join channel='{"type":"app"}' user_id= # Member added: MEM-df772...1ad7fa06

Generar token de usuario

Utilice este comando para generar un token de usuario en forma de JWT, utilizable por la API pero también por el Client SDK JavaScript de Nexmo. Te devolverá un JWT que caducará en 24 horas, u 86400 segundos.

register-to-chat-with-typeform-dr

Configurar la aplicación

Para configurar su aplicación, edite el archivo views/layout.hbs y busque la configuración de JavaScript en la línea 61.

<script>
      var userName = '';
      var displayName = '';
      var conversationId = '';
      var clientToken = '';
    </script>

En primer lugar, configura la aplicación así, pero al final de la guía podrás autenticarte con un enlace mágico y la aplicación cliente obtendrá tu token de usuario de tu sesión autorizada.

Edita la configuración con los valores que has generado en los comandos anteriores.

<script>
      var userName = 'luke.oliff@vonage.com';
      var displayName = 'Luke Oliff';
      var conversationId = 'CON-123...y6346';
      var clientToken = 'eyJhbG9.eyJzdWIiO.Sfl5c';
    </script>

Ahora, puedes volver a iniciar la aplicación y empezar a chatear... contigo mismo... porque nadie más puede conectarse.

npm start

Running chat without errors

Creación de un formulario

Puede capturar tantos datos como desee de su Typeform. Pero, para esta guía, asegúrese de que tiene al menos un campo de correo electrónico en el formulario.

Una vez que haya creado su Typeform, haga clic en el botón Conectar en la página de edición de su Typeform y haga clic en Webhooks.

Haga clic en Añadir un webhook e introduzca la URL como https://<your_url>.ngrok.io/webhooks/magiclink. A continuación, haga clic en Guardar webhook.

Configure Typeform webhook

Una vez creado, puede volver atrás y añadir un secreto para verificar que las solicitudes que llegan a su webhook proceden realmente de Typeform.

Si completa su Typeform ahora y lo envía mientras su aplicación se está ejecutando, el Typeform recibirá un mensaje de 404 Not Found error y volverá a intentarlo. Si una solicitud de webhook falla por cualquier motivo, Typeform reintentará la solicitud a su endpoint tres veces utilizando un mecanismo de back-off después de 5, 10 y 20 minutos.

Variables de entorno

A partir de aquí, vas a estar configurando tu aplicación con credenciales que no sólo pueden diferir entre entornos, sino también que no querrás confirmar junto con tu código fuente.

dotenv ya era una dependencia del proyecto inicial, así que comprueba el archivo .env donde ya contiene el puerto por defecto para la aplicación. Volverás a este archivo pronto para añadir más variables de entorno.

Añadir un Webhook

Ahora, para solucionar su posible 404 Not Found error, añada el webhook creando un nuevo archivo en la aplicación llamado routes/webhook.js. En el nuevo archivo, añade el siguiente código.

var express = require('express');
var router = express.Router();

/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
  console.log(req.body);

  // always return a response...
  res.sendStatus(200);
});

module.exports = router;

Edite app.js y añade el enrutador webhook.

// ...

var indexRouter = require('./routes/index');
var webhookRouter = require('./routes/webhook');

// ...

app.use('/', indexRouter);
app.use('/webhooks', webhookRouter);

// ...

Con npm y ngrok ejecutándose ahora deberías ser capaz de completar tu Typeform y recibir una petición webhook. La carga útil contendrá datos que se parecen a esto y se mostrará en la ventana donde se inició la aplicación con npm.

{
    ...
    "form_response": {
        ...
        "answers": [
            {
                "type": "email",
                "email": "email@example.com",
                "field": {
                    "type": "email",
                }
            }
        ]
    }
}

Captar la respuesta

Antes de editar el webhook, configure algunas variables para el Typeform y la pregunta dentro de su archivo de entorno .env. Para FORM_FIELD_REFtendrá que editar su pregunta Typeform y encontrar la referencia Referencia de la pregunta dentro de la configuración de su pregunta. FORM_URL es la URL pública para completar el formulario.

# ... port etc # typeform config FORM_URL=https://username.typeform.com/to/123456 FORM_FIELD_TYPE=email FORM_FIELD_REF=e8bafec6-5...ee-21bfe1254e81

Ahora, vuelve a tu ruta webhook en routes/webhook.js y edítala para incluir el código que extraerá la dirección de correo electrónico.

//...

require('dotenv').config();

/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
  // find answers from the typeform response
  let { answers } = req.body.form_response;

  const answer = answers
    .find(answer => process.env.FORM_FIELD_TYPE === answer.type && answer.field.ref === process.env.FORM_FIELD_REF);

  // it'll probably be an email
  const email = answer[process.env.FORM_FIELD_TYPE];

  console.log(email);

  // always return a response...
  res.sendStatus(200);
});

Este código encontrará una respuesta de tipo email con la Referencia de pregunta (¡por si capturas más de una dirección de correo electrónico en tu formulario!) y finalmente devuelve el valor de la respuesta. El tipo y la referencia se establecieron en el archivo .env archivo.

El resultado será la cadena enviada a la pregunta de Typeform.

Usuarios de la tienda

Este tutorial seguirá asumiendo que sólo está capturando un único campo de correo electrónico de Typeform y ninguna otra información del usuario. Almacenará otra información derivada sobre el usuario a medida que se vaya creando.

Utilizará Mongoose para almacenar sus usuarios en la base de datos. Mongoose proporciona una solución sencilla basada en esquemas para modelar los datos de su aplicación. Incluye fundición de tipos, validación, construcción de consultas, ganchos de lógica de negocio y mucho más.

Instalar Mongoose

Para capturar la creación y los detalles del usuario, instale mongoose en su proyecto.

npm install mongoose

Configurar la conexión a MongoDB

Configura el proyecto para que Mongoose pueda conectarse a la base de datos MongoDB. Esta guía utiliza por defecto MacOS que pueden diferir de lo que necesitas, dependiendo del entorno de desarrollo que estés usando.

Edite .env y añade la siguiente configuración.

# ... port and typeform etc # mongodb config MONGO_URL=mongodb://127.0.0.1:27017/your-database-name

Puede decidir your-database-name aquí, porque lo creará si no existe ya.

Conectarse a MongoDB

Ahora, configura tu aplicación para conectarse a Mongoose cuando se ejecute editando el archivo bin/www y colocando este código al final.

/**
 * Database config
 */

const mongoose = require('mongoose');

// Set mongoose promises to global
mongoose.Promise = global.Promise

// Set up default mongoose connection
mongoose.connect(process.env.MONGO_URL, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false });

// Get the default connection
const db = mongoose.connection;

// Bind connection to error event (to get notification of connection errors)
db.on('error', onError);

Esquema y modelo de usuario

Todo en Mongoose comienza con un esquema. Cada esquema mapea a una colección MongoDB y define la forma de los documentos dentro de esa colección. Mientras que MongoDB no tiene esquemas, Mongoose utiliza esquemas para formalizar el objeto estándar antes de modificarlo.

Cree un nuevo archivo para el esquema en schemas/user.js y añada el siguiente código.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const UserSchema = new Schema({
  name: {
    type: String,
    required: true
  },
  display_name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true
  },
  user_id: {
    type: String
  },
  member_id: {
    type: String
  }
});

module.exports = UserSchema;

Un modelo es lo que se utiliza para crear documentos que se pueden utilizar para crear, editar, actualizar y eliminar elementos en una colección MongoDB. Crea un nuevo fichero para el modelo en models/user.js y añade el siguiente código.

const mongoose = require('mongoose');
const UserSchema = require('../schemas/user');

const User = mongoose.model('User', UserSchema);

module.exports = User;

Observe cómo el modelo incluye el esquema para devolver un User documento.

Buscar y guardar usuarios

En este caso, vas a utilizar el correo electrónico como identificador de la cadena de usuarios, o nombre de usuario. Su dirección de correo electrónico también se convertirá en su nombre para mostrar. Si lo desea, puede capturar ambas cosas individualmente en su Typeform.

Edite routes/webhook.js y añade el siguiente código para encontrar usuarios por su nombre de usuario y crearlos si no existen ya.

//...
var User = require('../models/user');

/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
  // ...

  User.findOne({ name: email }, (err, user) => {
    // error handling here

    // if our user is new, save it and output it
    if (null === user) {
      user = new User({
        name: email,
        email: email,
        display_name: email
      });

      user.save((err) => {
        // error handling here

        console.log(user);

        res.sendStatus(200);
      });

    // otherwise, just output it
    } else {
      console.log(user);

      res.sendStatus(200);
    }
  });
});

Este código va a intentar encontrar un usuario por su dirección de correo electrónico, creando uno si no existe ya. Esto no permite actualizar un usuario existente. Si ya existiera, podría dar error. Más tarde, vamos a generar un enlace mágico para iniciar sesión, en lugar de darles un error.

Generar un enlace mágico

Su webhook va a enviar por correo electrónico a su usuario un enlace mágico que se puede utilizar para autenticar con el servicio.

Instale jsonwebtoken usando npm.

npm install jsonwebtoken

Edite .env para crear una clave secreta que pueda utilizarse para la generación de tokens.

# ... port etc SECRET=whatever-you-want-it-be-a-b-c-1-2-3 # ... typeform and mongo etc

Ahora edita routes/webhook.js para generar el enlace mágico y enviarlo al servidor.

//...

var jwt = require('jsonwebtoken');

var createMagicLink = (req, payload) => {
  var token = jwt.sign(payload, process.env.SECRET);

  return `${req.protocol}://${req.get('host')}/auth?token=${token}`;
}

/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {

  // ...

    // ...

    if (null === user) {

      // ...

      user.save((err) => {
        // ...

        console.log(createMagicLink(req, user.toObject()));

        res.sendStatus(200);
      });

    // otherwise, just output it
    } else {
      console.log(createMagicLink(req, user.toObject());

      res.sendStatus(200);
    }

  // ...

});

Estamos añadiendo un JWT a una URL de enlace mágico como método para identificar al usuario cuando intente acceder al sitio.

Es importante señalar que un JWT garantiza la propiedad de los datos, no su cifrado. Se trata de una forma segura de representar las reclamaciones codificándolas como objetos JSON que pueden firmarse digitalmente o cifrarse. La firma digital de un JWT permite la validación contra modificaciones. El cifrado, por su parte, garantiza que el contenido del JWT sólo pueda ser leído por determinadas partes.

En este caso, la guía no utiliza RSA u otro cifrado asimétrico, sino que se limita a firmar los datos utilizando la firma sincrónica HMAC SHA256 predeterminada de la biblioteca JWT.

El uso de un JWT de esta manera verifica que el enlace mágico se originó en su aplicación, firmado por su SECRET y no puede ser modificado.

Cuando envíe datos al webhook desde Typeform ahora, la salida debe ser un enlace a la aplicación que se parece a una versión mucho más larga de esto:

https://<your_url>.ngrok.io/webhooks/auth?token=eyJhbCJ9.eyEflLxN.N9eq6b5o

Haga clic en el enlace para obtener un error 404. Vamos a arreglarlo.

Magic link 404s

Autenticación con Passport.js

Pasaporte.js se describe a sí mismo como autenticación discreta para Node.js. Es increíblemente flexible y modular y puede integrarse discretamente en una aplicación como esta.

Instalar Passport.js

Instale passportLa passport-jwt estrategia y express-session por lo que puede ser utilizado para la autenticación y el mantenimiento de una sesión.

npm install passport passport-jwt express-session

Crear un punto final de autenticación

Cree un nuevo archivo llamado routes/auth.js con este código fuente.

var express = require('express');
var router = express.Router();

/* GET authenticate user with magic link and direct to home */
router.get('/', (req, res, next) => {
  res.redirect(req.protocol + '://' + req.get('host') + '/');
});

module.exports = router;

Este enrutador te redirigirá a la página de inicio. Sin embargo, solo llegarás a este enrutador si estás autorizado por el JWT cuando solicites la página.

Edite app.js y añade este código para añadir la autenticación de pasaporte a una nueva ruta de autenticación.

// ...

var indexRouter = require('./routes/index');
var webhookRouter = require('./routes/webhook');
var authRouter = require('./routes/auth');

// ...

var User = require('./models/user');
var session = require('express-session');
var passport = require('passport');
var jwtStrategy = require('passport-jwt').Strategy;
var jwtExtractor = require('passport-jwt').ExtractJwt;

app.use(session({ 
  secret: process.env.SECRET,
  resave: true,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser((user, done) => {
  done(null, user._id);
});

passport.deserializeUser((id, done) => {
  User.findById(id, (err, user) => {
    done(err, user);
  });
});

passport.use(new jwtStrategy({ 
  jwtFromRequest: jwtExtractor.fromUrlQueryParameter('token'),
  secretOrKey: process.env.SECRET
}, (payload, done) => {
  return done(null, payload);
}))

app.use('/', indexRouter);
app.use('/webhooks', webhookRouter);
app.use('/auth', passport.authenticate('jwt', { session: true }), authRouter);

// ...

Este código autenticará cualquier petición al /auth utilizando el extractor JWT de la estrategia passport-jwt estrategia. Intentará validar el parámetro token de un parámetro de cadena de consulta.

Una vez autenticado, la aplicación creará una sesión y los datos del usuario estarán disponibles como req.user.

Para probarlo, edita routes/index.js y añada este código antes de la línea res.render() línea.

console.log(req.user);

Ahora, reinicie la aplicación y genere un enlace mágico utilizando su solicitud de Typeform. Cuando haga clic en el enlace, será redirigido de nuevo al chat después de la autenticación. Pero en tu consola, habrás emitido algunos datos de usuario que se parecen a esto:

{ _id: 5dd0215a03174a4d8b920952, name: 'luke.oliff@vonage.com', email: 'luke.oliff@vonage.com', display_name: 'luke.oliff@vonage.com', member_id: null, user_id: null, __v: 0 }

Logged in but nothing has changed

Asegúrate de que nadie pueda acceder al chat, a menos que estén autenticados, editando el archivo routes/index.js para que se vea exactamente así.

var express = require('express');
var router = express.Router();
require('dotenv').config();

var isAuthenticated = (req, res, next) => {
  if(req.isAuthenticated()){
    next();
  } else{
    res.redirect(process.env.FORM_URL);
  }
}

/* GET home */
router.get('/', isAuthenticated, (req, res, next) => {
  res.render('index', { title: 'Nexmo Typeform Chat', user: req.user.display_name });
});

module.exports = router;

Eliminando la salida console.log que acaba de añadir arriba; el chat ya no registrará los datos del usuario actual en la consola. En su lugar, el nombre de usuario se añade al ámbito de las plantillas a renderizar. Este cambio también redirigirá al Typeform si no están conectados.

Edite views/layout.hbs y mostrar el nombre. Busque username y sustitúyalo por {{user}}el código que lo rodea debería tener este aspecto.

<ul class="nav flex-column">
              <li class="nav-item">
                <a class="nav-link active" href="#">
                  <span data-feather="home"></span>
                  {{user}}
                </a>
              </li>
            </ul>

Cuando estén conectados, mostremos también los miembros del chat (fuera de la base de datos) en la página. Edita routes/index.js y envuelve el res.render en el User.find que devuelve todos los usuarios registrados.

// ...
var User = require('../models/user');

// ...

/* GET home */
router.get('/', isAuthenticated, (req, res, next) => {
  User.find((err, users) => {
    res.render('index', { title: 'Nexmo Typeform Chat', members: users, user: req.user.display_name });
  })
});

Edite views/layout.hbs de nuevo y encontrar todo este bloque:

{{!-- {{#each members}} --}}
              <li class="nav-item">
                <a class="nav-link text-muted" href="#">
                  <span data-feather="file-text"></span>
                  other member
                </a>
              </li>
              {{!-- {{/each}} --}}

Sustitúyalo por este código funcional.

{{#each members}}
              <li class="nav-item">
                <a class="nav-link text-muted" href="#">
                  <span data-feather="file-text"></span>
                  {{this.display_name}}
                </a>
              </li>
              {{/each}}

Reinicie la aplicación y vuelva a acceder a ella a través de su enlace mágico. Ahora, deberías ver alguna información del usuario en la página.

Logged in with user info

Todavía estás accediendo al chat en el utilizando los datos de prueba hardcoded. Es hora de registrar a tus usuarios en Nexmo y permitirles acceder también a la conversación.

Consigue que los usuarios registrados chateen en Nexmo

Por el momento, los usuarios se registran pero sólo utilizan el chat a través de la información de usuario codificada.

Instalar y configurar el nodo Nexmo

En este punto, vas a empezar a interactuar con el servicio Nexmo desde dentro de tu aplicación de nodo por primera vez.

Instale nexmo ahora con este comando.

npm install nexmo@beta

Configura algunas variables para Nexmo dentro de tu fichero de entorno .env. Necesitarás la misma clave API y el mismo secreto que usaste para configurar nexmo-cli al principio. También necesitarás el ID de la aplicación y la ruta de la clave privada de cuando ejecutaste nexmo app:createasí como el ID de conversación de cuando ejecutaste nexmo conversation:create.

# ... app, typeform and mongodb etc # nexmo config NEXMO_API_KEY= NEXMO_API_SECRET= NEXMO_APP_ID=4556dbae-bf...f6e33350d8 NEXMO_PRIVATE_KEY_PATH=./private.key NEXMO_CONVERSATION_ID=CON-a57b0...11e57f56d

Cree un archivo de utilidad en util/nexmo.js que va a configurar la nexmo biblioteca.

const Nexmo = require('nexmo');
require('dotenv').config();

let options = {};

module.exports = new Nexmo({
    apiKey: process.env.NEXMO_API_KEY,
    apiSecret: process.env.NEXMO_API_SECRET,
    applicationId: process.env.NEXMO_APP_ID,
    privateKey: process.env.NEXMO_PRIVATE_KEY_PATH
  }, options);

Crear usuario Nexmo

Lo primero es lo primero, necesitas crear un usuario Nexmo en paralelo a tu usuario local cuando se registren.

Edite routes/webhook.js y reemplazar completamente el archivo con este código:

var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
require('dotenv').config();

var User = require('../models/user');
var nexmo = require('../util/nexmo');

var createMagicLink = (req, payload) => {
  var token = jwt.sign(payload, process.env.SECRET);

  return `${req.protocol}://${req.get('host')}/auth?token=${token}`;
}

/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {
  // find answers from the typeform response
  let { answers } = req.body.form_response;

  const answer = answers
    .find(answer => process.env.FORM_FIELD_TYPE === answer.type && answer.field.ref === process.env.FORM_FIELD_REF);

  // it'll probably be an email
  const email = answer[process.env.FORM_FIELD_TYPE];

  User.findOne({ name: email }, (err, user) => {
    // error handling here

    // if we can't find an existing user, prepare a new user document
    if (null === user) {
      user = new User({
        name: email,
        email: email,
        display_name: email
      });
    }

    if (null === user.user_id) {
      nexmo.users.create(user.toObject(), (err, nexmoUser) => {
        // error handling here

        user.user_id = nexmoUser.id;

        nexmo.conversations.members.create(process.env.NEXMO_CONVERSATION_ID, {
          action: 'join',
          user_id: nexmoUser.id,
          channel: { type: 'app' }
        }, (err, member) => {
          // error handling here

          user.member_id = member.id;

          user.save((err) => {
            // error handling here

            console.log(createMagicLink(req, user.toObject()));

            res.sendStatus(200);
          });
        });
      });
    } else {
      console.log(createMagicLink(req, user.toObject()));

      res.sendStatus(200);
    }
  });
});

module.exports = router;

Este nuevo código webhook va a comprobar si hay un usuario en la base de datos y creará uno cuando sea nuevo, igual que antes. Pero ahora, se creará un usuario Nexmo y conectar el usuario a la conversación, la actualización de su registro de base de datos con el ID de usuario Nexmo y un ID de miembro.

Reinicie la aplicación y genere un nuevo enlace mágico para su usuario. Haz clic en él para autenticarlo. Ahora verá que no hay ningún usuario Nexmo, cree uno, añádalo a la conversación y guárdelo en el registro de usuario.

Cuando se te redirija a la aplicación de chat, verás que tu usuario creado se ha unido a la conversación. Sin embargo, seguirás chateando como tu usuario codificado.

New user joins the conversation

Generar un token para el Client SDK

Tus usuarios pueden registrarse, iniciar sesión e incluso unirse a la conversación. Pero ahora mismo, sólo chatean utilizando datos de usuario codificados. Es hora de solucionarlo y permitirles hablar como ellos mismos.

Abra routes/index.js y crea una nueva ruta /jwtporque principalmente expondrás un nuevo JWT específicamente para el servicio Nexmo, utilizable por el Client SDK.

// ...
var nexmo = require('../util/nexmo');

/* GET home */
// ...

/* GET user data and jwt */
router.get('/jwt', isAuthenticated, (req, res, next) => {
  const aclPaths = {
    "paths": {
      "/*/users/**": {},
      "/*/conversations/**": {},
      "/*/sessions/**": {},
      "/*/devices/**": {},
      "/*/image/**": {},
      "/*/media/**": {},
      "/*/push/**": {},
      "/*/knocking/**": {}
    }
  };

  const expires_at = new Date();
  expires_at.setDate(expires_at.getDate() + 1);

  const jwt = nexmo.generateJwt({
    application_id: process.env.NEXMO_APP_ID,
    sub: req.user.name,
    exp: Math.round(expires_at/1000),
    acl: aclPaths
  });

  res.json({
    user_id: req.user.user_id,
    name: req.user.name,
    member_id: req.user.member_id,
    display_name: req.user.display_name,
    client_token: jwt,
    conversation_id: process.env.NEXMO_CONVERSATION_ID,
    expires_at: expires_at
  });
})

// ...

Esta nueva ruta utiliza la sesión existente del usuario para proporcionar datos al navegador. La página de inicio proporciona esto como HTML, pero este nuevo punto final devuelve JSON.

Reinicie la aplicación, siga el enlace mágico y, a continuación, vaya a https://<your_url>.ngrok.io/jwt. Verá información basada en su usuario actual, incluido un client_token para utilizar en el Client SDK.

JWT endpoint shares client token

Eliminar la configuración codificada

Es hora de dejar de hardcoding config dentro de la aplicación. Edite el archivo views/layout.hbs y busque la configuración que ha añadido dentro de las etiquetas <script> tags. Se veía algo como esto.

<script>
      var userName = 'luke.oliff@vonage.com';
      var displayName = 'Luke Oliff';
      var conversationId = 'CON-123...y6346';
      var clientToken = 'eyJhbG9.eyJzdWIiO.Sfl5c';
    </script>

Borra las etiquetas script y su contenido, totalmente.

Si quieres ver lo que le ha hecho a tu aplicación, reinicia y autentica para encontrar que está casi como al principio, con el chat roto. Al menos sigues conectado.

Logged in with broken chat

Solicitar token de usuario cliente

Puedes acceder al token de cliente del usuario desde una URL como datos JSON. Por lo tanto, edite public/javascripts/chat.js y cambia el método authenticateUser para que obtenga estos datos y los utilice cuando se conecte a la conversación.

// ...

  authenticateUser() {
    var req = new XMLHttpRequest();
    req.responseType = 'json';
    req.open('GET', '/jwt', true);

    var obj = this;
    req.onload  = function() {
       obj.joinConversation(req.response);
    };

    req.send(null);
  }

  // ...

Reinicie la aplicación, autentifíquese y ¡disfrute de un rápido juego de detectar la diferencia!

Logged in with Nexmo user

Ahora estás conectado como un usuario diferente. Los mensajes de otros usuarios tienen un formato diferente. Así que cuando te unas a la conversación, tendrá este aspecto.

Chatting with myself

Tienes un enlace mágico, pero sigue saliendo en la consola. Es hora de enviarlo por correo electrónico en su lugar.

Instalar y configurar una biblioteca SMTP

Instale nodemailer ahora con este comando.

npm install nodemailer

Configure algunas variables para la nodemailer dentro de su archivo de entorno .env.

# ... app, typeform, mongodb, nexmo etc # smtp config SMTP_HOST= SMTP_PORT= SMTP_AUTH_USER= SMTP_AUTH_PASS=

Si utiliza Google u otro host de correo conocido con la verificación en 2 pasos activada, probablemente tendrá que configurar una contraseña de aplicación. Te permitirá autenticarte desde la aplicación sin necesidad de Verificación en 2 Pasos.

Crear un nuevo archivo de utilidad que configurará nodemailer en util/mailer.js con este código:

const mailer = require('nodemailer');
require('dotenv').config();

let options = {
  host: process.env.SMTP_HOST,
  port: process.env.SMTP_PORT,
  secure: true,
  auth: {
      user: process.env.SMTP_AUTH_USER,
      pass: process.env.SMTP_AUTH_PASS
  }
};

module.exports = mailer.createTransport(options);

Enviar enlaces mágicos por correo electrónico

La edición final de routes/webhook.js será añadir la función sendEmail y usarla para reemplazar los comandos console.log por completo.

// ...

var mailer = require('../util/mailer');

// ...

var sendEmail = (magicLink, email) => {
  var mailOptions = {
      to: email,
      subject: 'Magic Link',
      text: 'Click to login: ' + magicLink,
      html: `<a href="${magicLink}">Click to Login</a>`
  };

  mailer.sendMail(mailOptions);
}

/* POST webhook generates a magic link email to the provided email address */
router.post('/magiclink', (req, res, next) => {

  // ...

    if (null === user.user_id) {

      // ...

        // ...
        
          user.save((err) => {
            // ...

            sendEmail(createMagicLink(req, user.toObject()), user.email);

            res.sendStatus(200);
          });

        // ...

      // ...

    } else {
      sendEmail(createMagicLink(req, user.toObject()), user.email);

      res.sendStatus(200);
    }
    
  // ...

});

// ...

Para el último tipo, reinicie la aplicación y envíe una solicitud webhook utilizando los datos de Typeform.

Si todo funciona según lo previsto, recibirás un correo electrónico en la dirección que enviaste a Typeform con un enlace mágico. Haz clic en el enlace mágico para autenticarte con la aplicación y unirte a la conversación.

Es hora de invitar a algunos amigos.

Other people can now join chat

Eso es todo, amigos.

Si estás interesado en cómo se construyó la interfaz de usuario para este tutorial, echa un vistazo a mi último post Crear una interfaz de usuario de mensajería simple con Bootstrap.

Además, aquí hay algunas cosas a tener en cuenta si estás construyendo esto para su uso en el mundo real:

  • Utilice un formulario independiente para gestionar la autenticación después de que un usuario ya se haya registrado.

  • Capture un nombre de usuario y una imagen de usuario dentro de su Typeform.

  • Utilice una cadena opaca revocable en lugar de un JWT dentro de un enlace mágico.

  • Permitir a los usuarios actualizar sus datos una vez autenticados.

  • Mostrar en el menú lateral todo lo que está actualmente en línea.

  • Permitir a los usuarios cerrar sesión.

  • Permitir a los usuarios borrar mensajes.

  • Permite a los usuarios compartir contenido multimedia.

  • Ampliar las URL compartidas como vistas previas.

Si desea habilitar el audio dentro de una aplicación de chat existente como esta, puede consultar mi guía para Cómo añadir funciones de voz a una aplicación de chat existente.

Gracias por leer y déjame saber lo que piensas en el Slack de la comunidad o en la sección de comentarios más abajo 👇.

Compartir:

https://a.storyblok.com/f/270183/250x250/451101b4f0/lukeoliff.png
Luke OliffAntiguos alumnos de Vonage

Amable educador tecnológico, padre de familia, defensor de la diversidad, probablemente discuta demasiado. Anteriormente ingeniero de backend. Háblame de JavaScript (frontend o backend), el increíble Vue.js, DevOps, DevSecOps, cualquier cosa JamStack. Escritor en DEV.to