Está en la página 1de 62

Desarrollo

de una red social


Como sabemos, las redes sociales han tomado un

protagonismo muy fuerte en el mundo de la comunicación,

debido a la experiencia que generan en los usuarios de

eventos en tiempo real y escalabilidad.

Desarrollaremos un proyecto utilizando Node como lenguaje

de programación y Redis como modelo de persistencia.

▼ El porqué del proyecto .............. 2 Lista de amigos conectados ............. 40


Informar cuando
▼ Definición del proyecto.............. 2 se conecta un usuario....................... 43
Creación del proyecto ........................ 3 Creación del sistema de chat............ 48
Configuración del archivo app.js......... 4 Creación del sistema de posts .......... 53
Registro y login de usuarios ............. 12 Vista de la base de datos.................. 59
Creación de la página principal ........ 25
Definición de los eventos ▼ Resumen................................... 61
para un usuario logueado ................. 27
Envío de una solicitud de amistad .... 31 ▼ Actividades............................... 62

Servicio de atención al lector: usershop@redusers.com


2 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

El porqué del proyecto


La razón por la cual desarrollaremos una red social es porque reúne
todas las características y condiciones que deben tener los sistemas
escalables estudiados a lo largo de este libro.
Una red social tiene que ser capaz de manejar eventos de interacción
con los usuarios en tiempo real y debe funcionar bajo un modelo de
persistencia que permita operar con millones de registros y solicitudes
en el menor tiempo posible. A nuestro ejemplo lo llamaremos
SocialRedis y utilizará las siguientes tecnologías:

• *Node.js
• *Socket.IO
• *Express
• *Redis
• *Crypto (módulo para encriptación)
• *Ejs (motor de plantillas)
• *Session.socket.io (módulo para manejo de sesiones en Socket.IO)

El sistema se encargará de funcionar de manera autónoma como


servidor propio, y ofrecerá todas las características específicas (tales
como ruteo, manejo de plantillas y estilos css, eventos de tiempo
real e interacción con la base de datos).

Definición del proyecto


Consiste en el desarrollo de una red social similar a Facebook, donde
los usuarios podrán registrarse; desde el primer momento se mostrará
en tiempo real la cantidad de usuarios registrados. Consideremos
que podrán enviar solicitudes de amistad y contar con un sistema de
notificaciones, que permitirá aceptar o rechazar una solicitud en el
mismo instante en que otro usuario la envía. Además, se podrán crear
posts para compartir entre los amigos. Por último, implementaremos
un sistema de chat donde los usuarios podrán tener múltiples
conversaciones en forma simultánea.

www.redusers.com
SISTEMAS WEB ESCALABLES 3

Figura 1. En la pantalla principal los usuarios pueden compartir


información e interactuar en tiempo real con los demás usuarios.

Instalación de herramientas previas


Para comenzar, necesitamos una herramienta que nos permita
administrar la base de datos. Como vamos a trabajar exclusivamente
con Redis y Node, podemos utilizar el módulo Redis Commander
instalándolo mediante el siguiente comando:

npm install -g redis-commander GRACIAS A NODEMON


NO SERÁ NECESARIO
También es recomendable utilizar Nodemon,
ya que nos evitaremos reiniciar la aplicación en REINICIAR LA
cada cambio que hagamos. Lo podemos instalar APLICACIÓN LUEGO
mediante el siguiente comando:
DE CADA CAMBIO
npm install -g nodemon

Creación del proyecto


Para dar inicio a nuestro ejemplo debemos abrir una consola
y dirigirnos a la unidad C:; una vez ahí, ejecutaremos los siguientes
comandos para crear la aplicación e instalar los módulos necesarios:

www.redusers.com
4 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

express –e SocialRedis
cd SocialRedis && npm install
npm install redis
npm install crypto
npm install socket.io
npm install session.socket.io

Utilizamos el parámetro –e para crear la aplicación, con lo cual


indicamos que vamos a utilizar el módulo ejs como motor de plantillas;
luego instalamos los módulos redis, crypto, socket.io y sesión.socket.io.
Una vez creado el entorno de trabajo, podemos ejecutar la aplicación
con Nodemon y comprobar que está en funcionamiento. En la consola
escribimos el siguiente comando y posteriormente abrimos un
navegador para verificar su funcionamiento:

nodemon app.js

Por defecto, el sistema va a funcionar en la siguiente dirección:


http://127.0.0.1:3000.

Configuración del archivo app.js


Ahora trabajaremos directamente en la aplicación. Lo primero que
haremos es configurar el archivo principal, dirigiéndonos a la raíz
del proyecto y abriendo el archivo app.js. En este punto tenemos que
realizar algunos cambios importantes, por lo que vamos a borrar todo
el contenido del archivo e iremos agregando el código necesario. Para
comenzar, vamos a incluir los módulos:

APLICACIONES DE EJEMPLO DE EXPRESS

Express provee una extensa lista de ejemplos que cubren todos los aspectos del framework, como auten-
ticación, manejo de plantillas, cookies, solución de errores, parámetros de entrada y administración de
sesiones, entre otros. Podemos acceder a los ejemplos en la siguiente dirección: https://github.com/
visionmedia/express/tree/master/examples.

www.redusers.com
SISTEMAS WEB ESCALABLES 5

// definicion de modulos
var express = require(‘express’)
, routes = require(‘./routes’)
, user = require(‘./routes/user’)
, http = require(‘http’)
, path = require(‘path’)
, redis = require(‘redis’)
, crypto = require(‘crypto’)
, ssio = require(‘session.socket.io’);

Inicializamos las variables agregando lo siguiente:

// inicializacion de variables
var app = express()
, server = http.createServer(app)
, io = require(‘socket.io’).listen(server)
, sessionStore = new express.session.MemoryStore()
, cookieParser = express.cookieParser(‘!@#$%^&*()1234567890qwerty’)
, sessionIO = new ssio(io, sessionStore, cookieParser);

Para continuar definimos el puerto que utilizará el sistema, el


directorio y también el motor de plantillas:

// configuraciones para todos los entornos


app.set(‘port’, process.env.PORT || 3000);
app.set(‘views’, __dirname + ‘/views’);
app.set(‘view engine’, ‘ejs’);

SELECCIONAR UN FRAMEWORK

Actualmente los desarrolladores nos encontramos con una gran cantidad de frameworks MVC para organizar y
estructurar las aplicaciones en JS. Para ayudarnos a resolver el problema existe un proyecto llamado TodoMVC,
el cual ofrece una funcionalidad similar para varios frameworks como Backbone, Ember o AngularJS.

www.redusers.com
6 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Para continuar nos encargamos de establecer las herramientas


o middlewares de Express que utilizaremos:

// middlewares de Express
app.use(express.favicon());
app.use(express.logger(‘dev’));
app.use(express.bodyParser());
app.use(cookieParser);
app.use(express.session({ store: sessionStore }));
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, ‘public’)));

Posteriormente realizamos la definición del uso del manejador de


errores para el entorno de desarrollo:

// configuraciones para el entorno de desarrollo


if (‘development’ == app.get(‘env’)) {
app.use(express.errorHandler());
}

Para continuar, determinamos las variables globales que serán


visibles desde todos los archivos del sistema:

// definicion de variables globales


global.usrEnLinea = [];
global.titulo = ‘Social Redis’;
global.autor = ‘Carlos Alberto Benitez [2013]’;
global.db = redis.createClient(6379, ‘localhost’);
global.io = io;
global.sessionIO = sessionIO;
global.crypto = crypto;

La variable usrEnLinea servirá para almacenar los usuarios conectados


al sistema; titulo y autor son parte de la leyenda del sitio; db es un objeto

www.redusers.com
SISTEMAS WEB ESCALABLES 7

que contendrá la conexión a la base de datos; io


es también un objeto y contendrá la instancia de LA VARIABLE CRYPTO
Socket.io; en sessionIO almacenamos la instancia del PERMITIRÁ LA
objeto sesión.socket.io, que nos servirá para acceder
a las variables de sesión desde una conexión por ENCRIPTACIÓN DE
socket; por último, la variable crypto nos servirá LAS CLAVES
para encriptar las claves de acceso.
A continuación debemos realizar la definición DE ACCESO
de las rutas que manejaremos en el sistema.
Primero ruteamos las solicitudes por GET para el
acceso y salida del sistema:

// GET - definicion de las rutas


app.get(‘/’, routes.index);
//app.get(‘/salir’, user.logout);

Notemos que, para el ruteo de la página principal, utilizamos el


método index del archivo index.js y, para la salida, el método logout del
archivo user.js. Consideremos que esta última está comentada ya que
aún no realizamos la definición del método.
Luego, a través del método POST, determinamos el ruteo para las
acciones de registro, login, creación de un post, solicitud de
amistad y respuesta a una solicitud de amistad.

// POST - definicion de las rutas


//app.post(‘/registro’, user.registro);
//app.post(‘/login’, user.login);
//app.post(‘/setPost’, user.setPost);
//app.post(‘/setSolicitud’, user.setSolicitud);
//app.post(‘/setRespuestaSolicitud’, user.setRespuestaSolicitud);

Todas las acciones definidas utilizarán los métodos del archivo


user.js, pero como todavía no las hemos creado están comentadas.
A medida que vayamos creando los métodos iremos descomentando
el ruteo para cada una de ellas. Por último, ponemos en marcha el
servidor indicándole el puerto que vamos a utilizar:

www.redusers.com
8 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

// listen
server.listen(app.get(‘port’), function(){
console.log(‘Express server listening on port ‘ + app.get(‘port’));
});

Definición de la apariencia del sistema


Vamos a establecer la apariencia del sistema. Primero debemos
dirigirnos al directorio public/ y renombrar las carpetas images por
img, javascripts por js y stylesheets por css; haremos estos cambios
únicamente para simplificar la legibilidad de los archivos.
Dentro de la carpeta css/, vamos a crear un archivo con el nombre
style.css, con el siguiente código:

body{background: url(“/img/bg.jpg”) repeat scroll 0 0 transparent; font:


12px/1.5em Arial,Helvetica,sans-serif; margin: 0; padding: 0; text-align: center;
color: #444444;}

header{height: 50px; padding: 20px 5px;}

header #buscador{width: 370px; float: left;}

header #buscador #tBuscar{width: 270px;}

header #buscador #bBuscar{height: 28px;}

header #notificacion{float: right; padding: 9px 0 0; width: 203px;}

header #notificacion #usr{float: left; font-size: 14px; height: 100%; margin:


5px 0; width: 150px;}

h1{background: url(“/img/icon-profile.png”) no-repeat scroll left top transparent;


color: #666; float: left; font-size: 29px; line-height: 55px; margin: 0; padding: 0
10px 0 42px; text-align: left; text-shadow: 1px 1px 0 #FFF; width: 175px;}

input[type=”text”],

www.redusers.com
SISTEMAS WEB ESCALABLES 9

input[type=”password”]{margin: 7px 0 0;}

a{color: #444444; display: block; text-decoration: none;}

nav{clear: both; float: left; width: 140px; padding: 0 10px;}

nav ul{list-style: none outside none; padding: 0; margin: 0;}

nav ul li{border: 1px solid transparent; cursor: pointer; font-size: 15px; height:
100%; line-height: 46px; padding: 0; width: auto;}

nav ul li:hover,nav ul .active {background-color: #EEEEEE; background-


image: -moz-linear-gradient(center top , #EEEEEE, #E0E0E0); border: 1px solid
#CCCCCC; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset; color: #333333;}

input[type=”submit”]{background: url(“/img/bg-verde.png”) repeat-x left top


#47C516; border: 1px solid #149E1F; border-radius: 2px 2px 2px 2px; color:
#FFFFFF; padding: 5px; text-align: center;}

footer{clear: both; height: 80px; padding: 10px; text-align: center;}

footer p{margin: 0 auto; padding: 30px 0; width: 300px;}

.contenedor{background: url(“/img/bg-sombras.png”) no-repeat scroll center top


transparent; margin: 0 auto; min-height: 600px; width: 850px;}

.marco{border: 1px solid #CCCCCC; border-radius: 5px; -moz-border-radius:5px;


-webkit-border-radius: 5px; padding: 7px;}

.marco.blanco{background-color: #FFFFFF;}

.marco.gris{background-color: #F8F8F8;}

.marco.celeste{background-color: #BAD9F1;}

.login{float: right; padding: 5px 15px; text-align: right; width: 401px; font-size:
15px;}

www.redusers.com
10 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

.tecnologias {float: left; height: 400px; width: 400px;}

.tecnologias img{margin: 20px 0;}

.registro{min-height: 215px; padding: 5px 15px;}

.registro input{display: block; width: 395px;}

.error {background: url(“/img/icono_error.gif”) no-repeat 10px 50% #FAEBE7;


border: 1px solid #F16048; color: #DF280A; padding: 3px 3px 3px 30px; text-
align: justify;}

.columna_derecha{float: right; margin: 0 5px; width: 431px;}

.usuarios_registrados{font-size: 15px; margin: 15px 0; padding: 5px 15px;


width: 399px;}

.usuario{text-align: right; min-width: 140px; float: left; padding: 11px 10px 0;


font-size: 14px;}

.contenido{width: 438px; float: left;}

.usuarios{float: left; margin: 0 5px; width: 180px; text-align: left;}

.usuarios span{font-size: 13px; font-weight: bold;}

#notificacion ul, .usuarios ul{margin: 0; min-height: 40px; list-style: none; pad-


ding: 0;}

#notificacion ul li, .usuarios ul li{border-bottom: 1px solid #CCCCCC; margin:


10px 0; cursor: pointer; padding: 5px;}

#notificacion ul li:hover, .usuarios ul li:hover{background-color: #47C516;}

.chat{clear: both; height: 181px; bottom: 0; margin: 0 5px; padding: 0 3px;


width: 828px; text-align: left; position: absolute; z-index: 1000;}

www.redusers.com
SISTEMAS WEB ESCALABLES 11

.ventana{margin: 0 2px; float: left; width: 186px;}

.ventana input[type=”text”]{margin: 0 2px; width: 179px;}

.usuariosLista{min-height: 400px;}

.avatar{float: left; width: 30px; height: 30px;}

.avatar img{width: 30px; height: 30px;}

.bloque{text-align: left; margin-bottom: 5px; float: left; width: 421px;}

.bloque .header{line-height: 45px; height: 30px; border-bottom: 1px solid #CCC;


text-align: left; margin-bottom: 10px;}

.bloque .header img{margin-right: 10px; float: left; width: 30px; height: 30px;}

.bloque .header .bloque_usuario{font-weight: bold; margin: 0 5px 0 0;}

.bloque .header .bloque_fecha{float: right;}

.contador{background: url(“/img/bg-azul.png”) repeat-x left top #53AFEC;


border: 1px solid #315EA8; border-radius: 2px 2px 2px 2px; color: #FFFFFF;
padding: 5px; text-align: center; display: block; float: left; font-size: 18px; font-
weight: bold; height: 17px; width: 30px;}

#postFrm{width: 436px;}

#postFrm textarea {display: block; height: 63px; width: 422px;}

#postFrm input[type=”submit”]{margin-right: 0; float: right;}

#solicitudes{height: 200px; width: 200px; background-color: #F8F8F8; posi-


tion: absolute; z-index: 100; display: none; top: 64px;}

.bSalir{display: inline-block; position: relative; cursor: pointer; text-shadow: 1px


1px 0 #FFF; top: -57px; width: 81px; right: -106px;}

www.redusers.com
12 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Dado que solo hemos definido los aspectos visuales, no se va a


explicar el código en detalle.

Registro y login de usuarios


Ahora vamos a trabajar sobre la primera pantalla. Para esto,
primero crearemos las plantillas que se reutilizarán en la aplicación.
Generaremos un archivo nuevo llamado header.ejs, que guardaremos
en el directorio views/ con el siguiente contenido:

<!DOCTYPE html>
<html lang=”es”>
<head>
<meta charset=”utf-8” />
<title><%= titulo %></title>
<link rel=”stylesheet” href=”/css/style.css” />
<link rel=”shortcut icon” href=”/img/favicon.png” >
</head>
<body>
<div class=”contenedor”>

Simplemente hemos creado una estructura HTML básica que luego


incluiremos en las otras plantillas. El elemento <title> contendrá la
variable titulo, que hemos definido anteriormente en el archivo app.js.
Vamos a crear un nuevo archivo llamado footer.ejs con código que sigue:

<footer>
<p>
<b><%= titulo %></b> - <%= autor %>
</p>
</footer>
</div>
<script type=”text/javascript” src=”/socket.io/socket.io.js”></script>
<script type=”text/javascript” src=”http://ajax.googleapis.com/ajax/libs/jque-
ry/1.9.0/jquery.min.js”></script>
<script type=”text/javascript” src=”/js/script.js”></script>

www.redusers.com
SISTEMAS WEB ESCALABLES 13

<!--[if lt IE 9]><script src=”http://html5shiv.googlecode.com/svn/trunk/html5.


js”></script><![endif]-->
</body>
</html>

Al igual que header.ejs, este archivo contendrá las variables globales


titulo y autor definidas en el archivo app.js; además, se incluyen los
archivos JavaScript que se necesitarán del lado del cliente. El archivo
html5.js permite utilizar elementos HTML5 en versiones de Internet
Explorer menores a 9.
Ya estamos en condiciones de crear la pantalla de acceso.
Generaremos un archivo dentro del directorio views/ con el nombre
index.ejs y el siguiente contenido:

<!-- inluimos el archivo header.ejs -->


<% include header %>

<header>
<h1><%= titulo %></h1>
<!-- formulario de login -->
<div class=”login marco blanco”>
<form id=”loginFrm” action=””>
<input type=”text” name=”usuario” value=”” placeholder=”Usuario”/>
<input type=”password” name=”clave” value=”” placeholder=”Clave”/>
<input type=”submit” id=”loginBtn” value=”Entrar” />
</form>
</div>

REPLICAR EL EJEMPLO UTILIZANDO PHP

Como hemos visto, Express ofrece un entorno en el cual podemos, entre otras cosas, manejar el ruteo,
y con Node es posible crear nuestro propio servidor web. Una interesante propuesta es desarrollar el
mismo sistema utilizando PHP y Apache para identificar las ventajas y desventajas de cada uno y definir
cuándo utilizar una arquitectura u otra.

www.redusers.com
14 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

</header>
<!-- logos de Redis y Node -->
<section class=”tecnologias”>
<img src=”img/bg_redis.png” alt=”Redis”/>
<img src=”img/bg_nodejs.png” alt=”Node.js”/>
</section>
<!-- formulario de registro -->
<section class=”columna_derecha”>
<article class=”registro marco blanco”>
<h2>Usuario Nuevo:</h2>
<div>
<form id=”nuevoFrm” action=””>
<input type=”text” name=”nombre” value=”” placeholder=”Nombre” />
<input type=”text” name=”apellido” value=”” placeholder=”Apellido” />
<input type=”text” name=”usuario” value=”” placeholder=”Usuario” />
<input type=”password” name=”clave” value=”” placeholder=”Clave” />
<input type=”text” name=”correo” value=”” placeholder=”Correo” />
<input type=”submit” id=”nuevoBtn” value=”Registrar” />
</form>
</div>
</article>
<!-- elemento para mostrar los usuarios registrados -->
<aside class=”usuarios_registrados marco blanco”>
<p>Ya se registraron <span id=”totalUsuarios”>0</span> usuarios</p>
</aside>
</section>
<!-- inluimos el archivo footer.ejs -->
<% include footer %>

En este código, primero incluimos la plantilla header y definimos el


título mediante la variable global. Luego, creamos un archivo llamado
home.ejs y lo guardamos en el mismo directorio que index.ejs, por el
momento sin contenido. También generamos el formulario de login
y, más abajo, agregamos los logos de Redis y Node para lograr una
apariencia más atractiva. Creamos el formulario de registro de usuarios
y, debajo, un elemento que permite mostrar la cantidad de usuarios
registrados en el sistema. Por último, incluimos la plantilla footer.

www.redusers.com
SISTEMAS WEB ESCALABLES 15

Una vez definida la plantilla de inicio, necesitamos crear la función


que se encargará de renderizar el contenido cuando se accede al
sistema. Para esto, abrimos el archivo index.js dentro del directorio
routes/ y reemplazamos todo el contenido por lo siguiente:

exports.index = function(req, res){

// inicializamos la variable se sesion de usuario


req.session.usr = req.session.usr || ‘’;
// definimos la plantilla a renderizar
plantilla = req.session.usr === ‘’ ? ‘index’ : ‘home’;
// renderizamos la palntilla
res.render(plantilla,
{ titulo: titulo,
autor: autor,
usuario: req.session.usr
}
);
};

En este código exportamos la función index para que sea accesible


desde cualquier archivo y, dentro de ella, verificamos si la variable req.
session.ur tiene algún contenido: como aún no la hemos definido, está
vacía. Por lo tanto, renderizaremos la plantilla index.ejs pasándole las
variables globales titulo y autor; la variable usuario, en esta instancia,
está vacía. Si ejecutamos la aplicación y accedemos a la dirección
http://127.0.0.1:3000 deberemos ver lo que muestra la Figura 2.

GRUPO DE USUARIOS DE EXPRESS

Siempre es buena idea formar parte de los grupos de usuarios de las tecnologías que nos interesan.
Express posee una lista que actualmente cuenta con alrededor de dos mil usuarios, discutiendo cerca de
cinco mil temas. El grupo utiliza la plataforma Google Groups y podemos unirnos a través del siguiente
enlace: https://groups.google.com/forum/#!forum/express-js.

www.redusers.com
16 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Figura 2. Al ingresar al sistema, los usuarios podrán


registrarse o iniciar sesión para poder interactuar con amigos.

A continuación definiremos el archivo que manejará las acciones


del usuario, creando un archivo llamado script.js, que guardaremos
en el directorio js/ con el siguiente contenido:

$(document).ready(function(){

// registro de usuario
$(document).on(‘submit’, ‘#nuevoFrm’, function(e){
e.preventDefault();
setUsuario($(this));
});
});

// funcion para mostrar el mensaje de error en los formularios


function mostrarMensajeFormulario(form, mensaje){
if (form.find(‘p’).size())
form.find(‘p’).show().html(mensaje);
else

www.redusers.com
SISTEMAS WEB ESCALABLES 17

form.prepend(‘<p class=”error”>’ + mensaje + ‘</p>’);


form.find(‘p’).fadeOut(7000);
}

// metodo POST de registro


function setUsuario(form) {
$.post(“/registro”, form.serialize(),
function(respuesta){
if (respuesta.codigo === 201)
window.location = respuesta.mensaje;
else
mostrarMensajeFormulario(form, respuesta.mensaje);
}
);
}

En el código definimos la acción para el formulario de registro,


donde ejecutamos la función setUsuario()
para enviar los datos al servidor. Si la
respuesta del servidor es 201, redirigimos al UNA RESPUESTA 201
usuario a la pantalla principal del sistema; REDIRIGE AL USUARIO
en el caso contrario, ejecutamos la función
mostrarMensajeFormulario(), que creará un elemento A LA PANTALLA
<p> para mostrar el mensaje recibido. PRINCIPAL
En la función setUsuario() hacemos un POST
por Ajax a la ruta /registro (en el archivo app.js DEL SISTEMA
tenemos comentado el ruteo para esta solicitud).
Ahora simplemente tenemos que descomentarla,
pasando de esto:

//app.post(‘/registro’, user.registro);

a esto:

app.post(‘/registro’, user.registro);

www.redusers.com
18 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Debemos, ahora, definir el método registro, para lo cual abrimos


el archivo user.js (ubicado en el directorio routes/), borramos todo el
contenido y escribimos lo siguiente:

// registramos el usuario
exports.registro = function(req, res) {

var nombre = req.param(‘nombre’),


apellido = req.param(‘apellido’),
usuario = req.param(‘usuario’),
clave = req.param(‘clave’),
correo = req.param(‘correo’);

if (nombre.length < 1 || apellido.length < 1 || usuario.length < 1 || clave.length <


1 || correo.length < 1 ){
res.send({codigo: 204, mensaje: ‘Complete todos los campos’ });
return;
}else{
// guardar en DB
db.get(‘usuario:’ + usuario + ‘:uid’, function (err, reply) {
if (reply === null){
// obtenemos el proximo uid
db.incr(‘global:ultimoUid’, function (err, uid) {
// seteamos el uid al usuario
db.set(‘usuario:’ + usuario + ‘:uid’, uid);
// encripto las claves
var hashClave = crypto.createHash(‘sha256’).update(clave).digest(‘hex’);
// seteamos los campos al usuario
db.hmset(‘uid:’ + uid, {‘nombre’ : nombre,
‘apellido’ : apellido,
‘usuario’ : usuario,
‘clave’ : hashClave,
‘correo’ : correo
}
);
// incremento la cantidad de usuarios (ver si dejar o no)
db.sadd(‘usuarios’,uid);

www.redusers.com
SISTEMAS WEB ESCALABLES 19

// seteamos las variables de session


req.session.usr = {‘uid’ : uid,
‘nombre’ : nombre,
‘apellido’ : apellido,
‘usuario’ : usuario
};
// emitimos el total de usuarios
getTotalUsuarios();

res.send({codigo: 201, mensaje: ‘/’ });


});
}else
res.send({codigo: 204, mensaje: ‘Error: El usuario ya existe’ });
});
}
};

En este código obtenemos los parámetros


enviados del formulario y verificamos que ya LUEGO DE ENCRIPTAR
no esté registrado el usuario; incrementamos la LA CLAVE DEBEMOS
clave global:ultimoUid y creamos un usuario con
este valor. Encriptamos la clave y guardamos los GUARDAR LOS
atributos en la base de datos, agregamos el uid a ATRIBUTOS DE LA
usuarios y creamos la variable de sesión con uid,
nombre, apellido y usuario. Hacemos una llamada BASE DE DATOS
a la función getTotalUsuarios(), y retornamos el
código 201 con la ruta para la redirección.
A continuación, debemos definir la función getTotalUsuarios() que
hemos utilizado anteriormente:

// emitimos el total de usuarios


var getTotalUsuarios = function () {
db.scard(‘usuarios’, function (err, cant) {
io.sockets.emit(“setTotalUsuarios”, cant);
});
}

www.redusers.com
20 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Utilizaremos la función getTotalUsuarios() para obtener la cantidad de


usuarios registrados y emitir el resultado a través del evento llamado
setTotalUsuarios, utilizando Socket.IO. Al final del archivo exportamos la
función para que pueda ser invocada desde cualquier archivo del sistema:

// exportamos las funciones


exports.getTotalUsuarios = getTotalUsuarios;

En síntesis, cuando se registra un usuario, emitimos un evento para


informar a todos los miembros la cantidad de usuarios registrados. El
paso siguiente es retornar al archivo script.js para capturar el evento
setTotalUsuarios. Primero nos conectamos al socket, incluyendo la
siguiente línea al principio del archivo:

// nos conectamos al socket


var sockets = io.connect(‘http://127.0.0.1:3000’);

Luego, dentro de la función $(document).ready(), capturamos el evento:

// io - cuando se registra un usuario muestro el total


sockets.on(‘setTotalUsuarios’, mostrarTotalUsuarios);

En este código hemos indicado que, cuando se reciba el evento, se


ejecutará la función mostrarTotalUsuarios(), que definimos de la siguiente
manera:

// cuando recibe el total de usuarios lo muestra en el contenedor


function mostrarTotalUsuarios(data){
$(‘#totalUsuarios’).html(data);
}

En este código obtenemos la cantidad de usuarios y los mostramos


en el elemento. Podemos probar cómo se ve el proyecto abriendo un
navegador y registrando un usuario. Si todo salió bien, el usuario

www.redusers.com
SISTEMAS WEB ESCALABLES 21

nuevo será redirigido, por ahora, a una página en blanco o, en caso


contrario, se mostrará un error.

Figura 3. Para que un usuario pueda registrarse debe completar


todos los campos; de otra manera, se mostrará un mensaje de error.

A continuación vamos a crear el login de usuario. En el archivo script.


js, dentro de la función $(document).ready(), agregamos lo siguiente:

// login
$(document).on(‘submit’, ‘#loginFrm’, function(e){
e.preventDefault();
login($(this));
});

CONTRIBUIR CON REDIS

Redis provee una vía para testear, agregar y mejorar características de la base de datos: solo es necesa-
rio clonar el repositorio oficial de Github. Otra alternativa para contribuir con Redis es reportar bugs o re-
solver los existentes. Ambas opciones son definidas en detalle en el enlace http://redis.io/community.

www.redusers.com
22 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

En el mismo archivo agregamos la función login():

// metodo POST de login


function login(form) {
$.post(“/login”, form.serialize(),
function(respuesta){
if (respuesta.codigo === 201)
window.location = respuesta.mensaje;
else
mostrarMensajeFormulario(form, respuesta.mensaje);
}
);
}

En el código anterior hicimos un POST por Ajax a la ruta /login,


en el cual enviamos los datos del formulario al igual que el registro.
En el caso de que la respuesta del servidor sea 201, redireccionamos
al usuario a la ruta indicada; en el caso contrario, mostramos el
mensaje mediante la función mostrarMensajeFormulario(). Ahora
descomentamos el ruteo para /login en el archivo principal (es decir,
en app.js), pasando de esto:

//app.post(‘/login’, user.login);

a esto:

app.post(‘/login’, user.login);

Necesitamos crear la función login(). Escribiremos lo siguiente en el


archivo user.js:

// verificamos el usuario y creamos la variable de sesion


exports.login = function(req, res){
var usuario = req.param(‘usuario’),
clave = req.param(‘clave’);

www.redusers.com
SISTEMAS WEB ESCALABLES 23

if (usuario.length < 1 || clave.length < 1){


res.send({codigo: 204, mensaje: ‘Complete todos los campos’ });
return;
}else{
db.get(‘usuario:’ + usuario + ‘:uid’, function (err, uid) {

if (uid === null)


res.send({codigo: 204, mensaje: ‘Error: El Usuario no existe’ });
else{
// obtenemos todos los atributos del usuario
db.hgetall(‘uid:’ + uid, function (err, usuario) {
// encriptamos la clave
var hashClave = crypto.createHash(‘sha256’).update(clave).digest(‘hex’);
if (hashClave == usuario.clave) {
// creamos la variable se sesion
req.session.usr = {‘uid’ : uid,
‘nombre’ : usuario.nombre,
‘apellido’ : usuario.apellido,
‘usuario’ : usuario.usuario
};

res.send({codigo: 201, mensaje: ‘/’ });


}else
res.send({codigo: 204, mensaje: ‘Error: Usuario o clave incorrectos’ });
});
}
});
}
}

En este código, primero verificamos que los parámetros sean


diferentes a vacío y luego comprobamos que el usuario exista: en caso
de existir obtenemos los atributos del usuario y comparamos la clave.
Si la clave almacenada en la base de datos coincide con la ingresada,
creamos la variable de sesión con los datos del usuario, devolvemos el
código 201 y la ruta para redireccionar. En el caso contrario, retornamos
el código 204 y generamos un mensaje de error.

www.redusers.com
24 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Figura 4. Cuando un usuario intente acceder


sin completar los campos, se mostrará una advertencia.

Es importante considerar que cada vez que se intente acceder con


un nombre de usuario que no se encuentre registrado en el sistema, se
procederá a mostrar un mensaje de carácter informativo a los usuarios
que están intentando conectarse.

Figura 5. En el caso de que un usuario no exista


en la base de datos, simplemente mostramos otro mensaje.

www.redusers.com
SISTEMAS WEB ESCALABLES 25

Creación de la página principal


Una vez que el usuario se ha registrado o ha iniciado sesión, es
redireccionado a la página principal con la variable de sesión creada.
Por lo tanto, renderizamos la plantilla home.ejs. A continuación
abriremos este archivo y agregaremos el siguiente código:

<!-- inluimos el archivo header.ejs -->


<% include header %>

<header>
<h1><%= titulo %></h1>
<!-- formulario de busqeda -->
<div id=”buscador”>
<input type=”text” id=”tBuscar” placeholder=”Buscar...” class=”marco” />
<input type=”submit” id=”bBuscar” value=”Buscar” />
</div>

<!-- elemento para mostrar las notificaciones -->


<div id=”notificacion”>
<span class=”usuario” ><%= usuario.nombre %> <%= usuario.apellido
%></span>
<span class=”contador”>
<span id=”valorContador”>0</span>
</span>

<!-- elemento para mostrar la lista de solicitudes -->


<ul id=”solicitudes” class=”marco blanco”></ul>
</div>

<a href=”/salir” class=”bSalir”>Salir</a>


</header>

<!-- columna para la lista de amigos -->


<aside class=”usuarios marco blanco”>
<span>Podes chatear con:</span>
<div id=”usuariosAmigos” class=”usuariosLista”>
<p>No hay amigos conectados...</p>

www.redusers.com
26 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

</div>
</aside>

<!-- formulario para crear y mostrar los post -->


<section class=”contenido”>
<article class=”bloque”>
<form id=”postFrm” action=””>
<textarea name=”post” placeholder=”Contale al mundo...”
class=”marco”></textarea>
<input type=”submit” id=”postBtn” value=”Postear” />
</form>
</article>
<div id=”posts”></div>
</section>

<!-- columna para usuarios nuevos -->


<aside class=”usuarios marco blanco”>
<span>Podes ser amigo de:</span>
<div id=”usuariosNuevos” class=”usuariosLista”>
<p>No hay usuarios nuevos...</p>
</div>
</aside>

<!-- contenedor de las ventanas de chat -->


<aside class=”chat”></aside>
<!-- inluimos el archivo footer.ejs -->
<% include footer %>

En este código hemos incluido el archivo header.ejs y definimos el título


con el contenido de la variable global y los elementos para el buscador.
Luego determinamos el área para las notificaciones y la lista de
solicitudes, y definimos el enlace para salir del sistema y el contenedor
donde el usuario verá la lista de amigos conectados. Creamos un
contenedor para el formulario y el listado de posts. Definimos el
contenedor para los usuarios que no son amigos del usuario actual.
Generamos el contenedor de las ventanas de chat e incluimos footer.ejs.
Una vez creada la plantilla podemos acceder para ver su estructura.

www.redusers.com
SISTEMAS WEB ESCALABLES 27

Figura 6. Cuando el usuario inicie sesión, verá la lista


de amigos, el área central para los posts y la columna con usuarios nuevos.

Definición de los eventos


para un usuario logueado
A continuación vamos a crear los eventos que emitirá y recibirá el
usuario cuando inicia sesión a través de Socket.IO. Para esto, abrimos
el archivo index.js y agregamos lo siguiente en la primera línea para
tener acceso a las funciones definidas en el archivo user.js:

var user = require(‘./user.js’);

Luego, dentro de la función index(), vamos a utilizar el objeto


sessionIO para acceder a la variable de sesión y desencadenar los
eventos. Para esto escribimos lo siguiente:

sessionIO.on(‘connection’, function (err, socket, session) {


//usuarios logueados
if(session.usr != ‘’){
// guardamos el socket.id del usuario actual

www.redusers.com
28 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

usrEnLinea[session.usr.uid] = socket.id;

// obtenemos los usuarios que no son amigos


user.getUsuariosNuevos(session.usr.uid);
}else{
// obtenemos la cantidad de usuarios
user.getTotalUsuarios();
}
});

En este código, capturamos la conexión y verificamos si la variable


session.usr contiene información. En el caso de que un usuario se
haya logueado, almacenamos el identificador del socket en el array
global usrEnLinea con su uid como identificador, lo que nos permitirá
identificar el socket mediante el cual está conectado cada usuario para
poder enviarle mensajes privados. Luego, vamos a invocar a la función
getUsuariosNuevos() del archivo user.js, al cual le pasaremos como
parámetro el uid del usuario actual. Por lo tanto, debemos crear esta
función en el archivo user.js:

// obtenemos los usuarios que no son amigos


var getUsuariosNuevos = function(uid){
db.sdiff(‘usuarios’, ‘uid:’ + uid + ‘:amigos’, function (err, usuarios) {
if (usuarios) {
var usrNuevos = [];

usuarios.forEach(function(id){
// verificamos que no sea el usuario actual
if (uid != id){
// verificamos que no tenga una solicitud pendiente
db.sismember(‘uid:’ + id + ‘:solicitudes’, uid, function (err, solicitudEnviada)
{
if(solicitudEnviada == 0){
// obtenemos la informacion del usuario
db.hgetall(‘uid:’ + id, function (err, usuario) {
usrNuevos.push({‘uid’: id,

www.redusers.com
SISTEMAS WEB ESCALABLES 29

‘nombre’ : usuario.nombre,
‘apellido’ : usuario.apellido
});
// emitimos los usuarios
if (usrNuevos.length == usuarios.length - 1)
io.sockets.socket(usrEnLinea[uid]).emit(‘setUsuariosNuevos’, usr-
Nuevos);
});
}
});
}
});
}
});
}

En este código utilizamos el comando sdiff de Redis para obtener los


usuarios que no son amigos del usuario actual; luego, con cada uno
verificamos que no sea el uid del usuario conectado, y comprobamos
que no tenga una solicitud de amistad pendiente. Obtenemos los datos
y los almacenamos en un array. Cuando finaliza la lista, emitimos el
evento setUsuariosNuevos al usuario actual a través del array usrEnLinea
con el índice identificado por el uid pasado como parámetro de la
función. Para que la función esté disponible desde cualquier lugar,
agregamos lo siguiente al final del archivo:

exports.getUsuariosNuevos = getUsuariosNuevos;

Ahora necesitamos capturar el evento del lado del cliente. Para esto,
agregamos lo siguiente al método $(document).ready() del archivo script.js:

//io - mostramos los usuarios que no son amigos


sockets.on(‘setUsuariosNuevos’, mostrarUsuariosNuevos);

En el código, recibimos el evento y ejecutamos la función


mostrarUsuariosNuevos(), que definiremos de la siguiente manera:

www.redusers.com
30 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

// muestra los usuarios nuevos


function mostrarUsuariosNuevos(data){
if (data.length) {
$(‘#usuariosNuevos’).html(‘<ul>’);
data.forEach(function (usuario) {
var nombreUsuario = usuario.nombre + ‘ ‘ + usuario.apellido ;
$(‘#usuariosNuevos ul’).append(‘<li uid=”’ + usuario.uid +’”>’ + nombreUsu-
ario + ‘</li>’);
});
$(‘#usuariosNuevos’).append(‘</ul>’);
};
}

En el código que acabamos de presentar agregamos al elemento


#usuariosNuevos, de esta manera podremos acceder a la lista recibida
de los usuarios que aún no son amigos del que se encuentra conectado
actualmente. Podemos efectuar la prueba de esta funcionalidad
registrando varios usuarios.

Figura 7. Cuando el usuario inicie


sesión obtendrá una lista con los usuarios que no son amigos,
a quienes les podrá enviar una solicitud.

www.redusers.com
SISTEMAS WEB ESCALABLES 31

Envío de una solicitud de amistad


Vamos a desarrollar la funcionalidad que nos permitará que el
usuario envíe una solicitud de amistad.

Figura 8. Para enviar una solicitud de amistad solo será


necesario hacer clic en algún nombre de la lista de usuarios.

Necesitamos capturar el evento click de la lista. Para esto, agregamos


lo siguiente en el método $(document).ready() del archivo script.js:

// se envia una solicitud de amistad


$(document).on(‘click’, ‘#usuariosNuevos ul li’, function(){
setSolicitud($(this).attr(‘uid’));
});

INSTALAR EXPRESS CON EJS

Por defecto, Express usa el motor de plantillas Jade, el cual es muy bueno pero abstrae de una manera
significante la utilización de elementos HTML. Una alternativa es utilizar EJS, más amigable y simple al
crear estructuras complejas. Para instalarlo cuando creamos una aplicación, debemos escribir el siguien-
te comando: express -e nombreDeLaApp.

www.redusers.com
32 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

En este código hemos capturado el evento click de la lista de


usuarios y ejecutamos la función setSolicitud(), que definimos en
el mismo archivo de la siguiente manera:

// se envia una solicitud de amistad


function setSolicitud(uid) {
$.post(“/setSolicitud”, ‘uid=’ + uid,
function(respuesta){
if (respuesta.codigo === 201)
$(‘#usuariosNuevos ul li[uid=”’ + uid + ‘”]’).fadeOut(1000);
else
$(‘#usuariosNuevos ul li[uid=”’ + uid + ‘”]’).html(respuesta.mensaje);
}
);
}

Aquí hicimos un POST por Ajax a la ruta /


setSolicitud, enviando el uid del usuario a que se
CON UN CÓDIGO 201 le mandará la solicitud. En el caso de tener como

EN LA RESPUESTA respuesta el código 201, quitamos al usuario de la


lista, y en el caso contrario, mostramos el mensaje
QUITAMOS AL en el mismo lugar.

USUARIO DE Para continuar es necesario que


descomentemos el ruteo para la petición /
LA LISTA setSolicitud en el archivo principal, para lo cual
abriremos y cambiaremos el archivo app.js.
Pasaremos de esto:

//app.post(‘/setSolicitud’, user.setSolicitud);

a esto:

app.post(‘/setSolicitud’, user.setSolicitud);

Ya sabemos qué hacer en caso de recibir un POST a esta ruta. Lo


siguiente es definir la función setSolicitud() en el archivo user.js, del
modo que mostramos a continuación:

www.redusers.com
SISTEMAS WEB ESCALABLES 33

// registramos y envia una solicitud de amistad


exports.setSolicitud = function(req, res){
var uid = req.param(‘uid’);

if (uid.length < 1){


res.send({codigo: 204, mensaje: ‘Usuario Invalido’ });
}else{
// agregamos el usuario a la lista de solicitudes
db.sadd(‘uid:’ + uid + ‘:solicitudes’, req.session.usr.uid);
// obtenemos la solicitudes y la enviamos al usuario
getSolicitudes(uid);

res.send({codigo: 201, mensaje: ‘’ });


}
}

En el código, recibimos el uid del usuario receptor, al que le


agregamos el usuario actual en la clave solicitudes. Luego invocamos el
método getSolicitudes() con el uid del usuario receptor como parámetro
para obtener las solicitudes pendientes del usuario. Crearemos la
función getSolicitudes()en el mismo archivo de la siguiente manera:

// obtenemos la cantidad de solicitudes pendientes


var getSolicitudes = function(uid){
db.smembers(‘uid:’ + uid + ‘:solicitudes’, function (err, solicitudesRecibidas) {
if (solicitudesRecibidas) {
var solicitudes = [];

// obtenemos los datos de cada uid


solicitudesRecibidas.forEach(function(id){
db.hgetall(‘uid:’ + id, function (err, usuario) {
solicitudes.push({‘uid’: id,
‘nombre’ : usuario.nombre,
‘apellido’ : usuario.apellido
});

www.redusers.com
34 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

// emitimos las solicitudes


if (solicitudes.length == solicitudesRecibidas.length)
io.sockets.socket(usrEnLinea[uid]).emit(‘getSolicitudes’, solicitudes);
});
});
}
});
}

En el código anterior verificamos las solicitudes pendientes del


uid pasado por parámetro y, por cada una de ellas, obtenemos la
información. Al obtener todas las solicitudes, emitimos el evento
getSolicitudes con la lista completa al usuario recibido. Luego, al final
del archivo, exportamos la función:

exports.getSolicitudes = getSolicitudes;

Necesitamos capturar el evento getSolicitudes del lado del cliente;


por lo tanto, agregamos lo siguiente a la función $(document).ready() del
archivo script.js:

// io - cuando se recibe una solicitud


sockets.on(‘getSolicitudes’, mostrarSolicitudes);

Cuando ocurra getSolicitudes, vamos a ejecutar la función


mostrarSolicitudes(), que debemos definir como sigue:

DOCUMENTACIÓN DE EXPRESS 2X

Si bien actualmente Express se encuentra en la versión 3, debido a que se han creado miles de apli-
caciones con la versión 2 fue necesario dejar la documentación accesible para consultas de desarro-
lladores que necesitan mantener sistemas escritos con esta versión. Para ver la documentación corres-
pondiente a Express en su versión 2, deberemos acceder a la página que se encuentra en la dirección
http://expressjs.com/2x.

www.redusers.com
SISTEMAS WEB ESCALABLES 35

// se muestran las solicitudes de amistad


function mostrarSolicitudes(solicitudes) {
if (solicitudes.length) {
$(‘#solicitudes’).html(‘’);
solicitudes.forEach(function (usuario) {
var contenido = ‘’;
contenido += ‘<li uid=”’ + usuario.uid +’” >’;
contenido += ‘ <span>’ + usuario.nombre + ‘ ‘ + usuario.apellido + ‘ quiere
ser tu amigo!’ + ‘</span>’;
contenido += ‘ <div>’;
contenido += ‘ <input type=”button” value=”Aceptar” uid=”’ + usuario.
uid +’” />’;
contenido += ‘ <input type=”button” value=”Cancelar” uid=”’ + usu-
ario.uid +’” />’;
contenido += ‘ </div>’;
contenido += ‘</li>’;

$(‘#solicitudes’).prepend(contenido);
});

$(‘#valorContador’).fadeOut(500, function() {
$(this).html(solicitudes.length).fadeIn(500)
});
}
}

En el código que mostramos arriba


recibimos las solicitudes y, por cada una POR CADA SOLICITUD
de ellas, creamos un ítem en la lista con DE AMISTAD RECIBIDA
el nombre del usuario que ha enviado la
solicitud original. Además, se procede a agregar CREAMOS UN ÍTEM
los botones que necesitamos para aceptarla EN LA LISTA DEL
y también para cancelarla. Posteriormente,
incrementamos el valor del contador con la USUARIO CONECTADO
cantidad de solicitudes que han sido recibidas.
Cuando un usuario reciba una solicitud de
amistad, verá incrementado el contador.

www.redusers.com
36 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Figura 9. Por cada solicitud de amistad


recibida, el contador incrementará su valor.

Debemos lograr que, cuando el usuario haga clic en el contador,


se muestre la ventana con las solicitudes; para lograrlo, agregamos
lo siguiente al método $(document).ready() del archivo script.js:

// contador de solicitudes
$(document).on(‘click’, ‘.contador’, function(){
$(‘#solicitudes’).fadeToggle(500);
});

En el código, mostramos la lista de solicitudes con un efecto de


aparición suave y, en caso de que la lista ya esté visible, la ocultamos.

SOCKET.IO EN OTROS LENGUAJES

Sockt.IO es una librería en lenguaje JavaScript, pero también es posible utilizar otras versiones, escri-
tas en diferentes lenguajes, como Java, Objective-C, C, C++, Go, Python y PHP, entre otros. Esto es,
sin duda, una gran ventaja, ya que es posible desarrollar sistemas basados en lenguajes como PHP en
tiempo real. Podemos ver la lista en https://github.com/learnboost/socket.io/wiki.

www.redusers.com
SISTEMAS WEB ESCALABLES 37

Figura 10. Al recibir una solicitud


el usuario puede optar por aceptarla o cancelarla.

Antes definimos que, cuando se envía una solicitud de amistad, el


usuario la recibe en tiempo real. Ahora debemos asegurarnos de que
el usuario no conectado la reciba al conectarse. Para esto, agregamos
el código siguiente en el archivo index.js, en el método sessionIO.on(),
dentro del condicional de sesión:

// obtenemos las solicitudes pendientes


user.getSolicitudes(session.usr.uid);

Con este código, cada vez que se conecta, el usuario puede obtener
el listado de las solicitudes pendientes.

PROYECTOS USANDO SOCKET.IO

En el repositorio oficial de Socket.IO hay una lista con varios sistemas que pueden servirnos como base
para el desarrollo de algún proyecto en tiempo real. Un ejemplo muy útil es collabshot, una herramienta
colaborativa de edición de imágenes, notas y chat. Podemos acceder a la lista de proyectos a través
del siguiente enlace: https://github.com/LearnBoost/Socket.IO/wiki/Projects-using-Socket.IO.

www.redusers.com
38 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Respuesta a una solicitud de amistad


Vamos a definir las acciones que puede tomar el usuario cuando
recibe una solicitud de amistad. Primero, agregamos lo siguiente al
método $(document).ready() del archivo script.js:

// boton de aceptar/cancelar solicitudes


$(document).on(‘click’, ‘#solicitudes input[type=”button”]’, function(){
setRespuestaSolicitud($(this));
});

En este código capturamos el evento click para aceptar o cancelar una


solicitud, mediante el cual ejecutamos la función setRespuestaSolicitud(),
que debemos definir en el mismo archivo del siguiente modo:

// se responde una solicitud de amistad


function setRespuestaSolicitud(boton) {
$.post(“/setRespuestaSolicitud”, ‘uid=’ + boton.attr(‘uid’) + ‘&accion=’ + boton.
attr(‘value’),
function(respuesta){
if (respuesta.codigo === 201){
$(‘#solicitudes li[uid=”’ + boton.attr(‘uid’) + ‘”]’).fadeOut(1000);

$(‘#valorContador’).fadeOut(500, function() {
$(this).html(parseInt($(this).html()) - 1).fadeIn(500)
});
}else
$(‘#solicitudes li[uid=”’ + boton.attr(‘uid’) + ‘”]’).html(respuesta.mensaje);
}
);
}

En el código anterior, enviamos un POST por Ajax a la ruta


/setRespuestaSolicitud, con el uid del usuario al cual se responde la
solicitud y con la acción (es decir, Aceptar o Cancelar). Cuando se recibe la
respuesta, si se obtiene el código 201 se elimina el nombre del usuario y
se descuenta el contador. De ocurrir lo contrario, se muestra el mensaje.

www.redusers.com
SISTEMAS WEB ESCALABLES 39

Lo siguiente es habilitar el ruteo para /setRespuestaSolicitud, para lo cual


descomentamos la siguiente línea en el archivo app.js, pasando de esto:

//app.post(‘/setRespuestaSolicitud’, user.setRespuestaSolicitud);

al código que presentamos a continuación:

app.post(‘/setRespuestaSolicitud’, user.setRespuestaSolicitud);

Necesitaremos definir la función setRespuestaSolicitud en el archivo


user.js, de la siguiente manera:

// respondemos la solicitud de amistad


exports.setRespuestaSolicitud = function(req, res){
var uid = req.param(‘uid’),
accion = req.param(‘accion’);

db.get(‘uid:’ + uid , function (err, reply) {


if (reply === null){
res.send({codigo: 204, mensaje: ‘Usuario Invalido’ });
}else{
if (accion == ‘Aceptar’){
// agregamos como amigo a ambos usuarios
db.sadd(‘uid:’ + uid + ‘:amigos’, req.session.usr.uid);
db.sadd(‘uid:’ + req.session.usr.uid + ‘:amigos’, uid);
}
// eliminamos la solicitud actual
db.srem(‘uid:’ + req.session.usr.uid + ‘:solicitudes’, uid);

res.send({codigo: 201, mensaje: ‘’ });


}
});
}

En este código, verificamos si existe el usuario recibido y, en


caso de que se haya aceptado la solicitud, se agregan como amigos

www.redusers.com
40 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

mutuamente a través de la clave amigos. Luego se elimina la solicitud


en ambos casos, es decir, tanto si se ha aceptado la solicitud como si
no se ha hecho.

Figura 11. Una solicitud de amistad


permanecerá en la lista hasta que el usuario realice
una de las dos acciones definidas.

Lista de amigos conectados


Vamos a crear una función para obtener los amigos que están
conectados cuando un usuario inicia sesión. Para hacerlo, agregamos
el código siguiente en el archivo llamado index.js, en la función
sessionIO.on(), dentro del condicional de sesión:

// obtenemos los usuarios conectados


user.getAmigosConectados(session.usr.uid);

En el código definimos que, cuando un usuario se conecta, se ejecuta


la función getAmigosConectados(). Por lo tanto, vamos a definirla en el
archivo user.js de la siguiente manera:

www.redusers.com
SISTEMAS WEB ESCALABLES 41

// obtenemos los amigos conectados


var getAmigosConectados = function(uid){
db.smembers(‘uid:’ + uid + ‘:amigos’, function (err, amigos) {
if (amigos) {
var usrConectados = [];
var i = 0;

amigos.forEach(function(id){
i++;
// verificamos que el usuario se encuentre conectado
if (usrEnLinea[id]){
// obtenemos la informacion del usuario
db.hgetall(‘uid:’ + id, function (err, usuario) {
usrConectados.push({‘uid’: id,
‘nombre’ : usuario.nombre,
‘apellido’ : usuario.apellido
});

// emitimos los amigos


if (i == amigos.length)
io.sockets.socket(usrEnLinea[uid]).emit(‘setAmigosConectados’, usrCo-
nectados);
});
}
});
}
});
}

HERRAMIENTAS PARA FRONTEND

Fred Sarmento ha creado un portal con recursos para los frontend, donde se incluyen librerías y plugins
como jQuery, Normalize.css, herramientas de debug y testeo como Firebug y Chrome Developer Tools,
tutoriales en línea, y editores de código como Sublime Text3. Podemos ver la lista completa en el siguien-
te enlace: http://fredsarmento.me/frontend-tools.

www.redusers.com
42 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

En el código anterior hemos obtenido los amigos del usuario actual


y, para cada uno, verificamos si se encontraba conectado, es decir que
existiera en el array usrEnLinea. De estos usuarios hemos obtenido el
uid, el nombre y el apellido. Cuando terminamos de obtener los amigos,
emitimos el evento setAmigosConectados con la lista.
En este punto debemos exportar la función para que esté disponible
desde cualquier lugar del sistema. Para ello, agregamos el siguiente
código al final del archivo llamado user.js:

exports.getAmigosConectados = getAmigosConectados;

Vamos a capturar este evento del lado del cliente, definiendo lo que
sigue en el archivo script.js dentro del método $(document).ready():

// io - mostramos los amigos conectados


sockets.on(‘setAmigosConectados’, mostrarAmigosConectados);

Hasta este momento hemos definido que, cuando se recibe el evento


setAmigosConectados, se ejecuta la función mostrarAmigosConectados(); por
lo tanto, debemos crearla en el mismo archivo:

// muestra los amigos conectados


function mostrarAmigosConectados(data){
if (data.length) {
$(‘#usuariosAmigos’).html(‘<ul></ul>’);
data.forEach(function (usuario) {
var nombreUsuario = usuario.nombre + ‘ ‘ + usuario.apellido;
$(‘#usuariosAmigos ul’).prepend(‘<li uid=”’ + usuario.uid +’”>’ + nombreUs-
uario + ‘</li>’);
});
};
}

Con este código obtenemos la lista de amigos conectados, agregando


a cada uno de ellos en el contenedor #usuariosAmigos.

www.redusers.com
SISTEMAS WEB ESCALABLES 43

Figura 12. Cuando los usuarios inicien sesión verán


en la columna izquierda la lista de los amigos conectados.

Informar cuando
se conecta un usuario
Vamos a crear una función para informar a los amigos, en tiempo
real, que un usuario se ha conectado. Primero nos encargamos de
agregar el siguiente código en el archivo denominado index.js, en la
función sessionIO.on(), dentro del condicional de sesión:

// informamos a los amigos que se ha conectado el usuario


user.setAmigoConectado(session.usr);

INTERNET

Según los últimos informes de Cisco, para el año 2017 habrá cerca de 3.600 millones de usuarios de
Internet, esto se puede resumir como casi el 50% de la población mundial. Implicaría un aumento del
tráfico mundial por tres, donde el servicio será accesible desde notebooks, netbooks, smartphones,
tablets y televisores inteligentes.

www.redusers.com
44 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

En el código anterior definimos que, cuando un usuario se conecte,


se ejecutará la función setAmigoConectado(), a la que le pasamos como
parámetro todos los datos del usuario actual.
En este momento hacemos la definición de la función
setAmigoConectado() en el archivo user.js:

// informamos a los amigos que se ha conectado el usuario


var setAmigoConectado = function(usr){
// obtenemos todos los amigos
db.smembers(‘uid:’ + usr.uid + ‘:amigos’, function (err, amigos) {
amigos.forEach(function(id){
// verificamos que el usuario se encuentre conectado
if (usrEnLinea[id])
io.sockets.socket(usrEnLinea[id]).emit(‘setAmigoConectado’, usr);
});
});
}

En el código anterior, obtenemos los amigos del usuario actual de


la base de datos y, a cada uno de ellos, le informamos los datos del
usuario mediante el evento setAmigoConectado. Luego, exportamos la
función para que esté disponible en todo el sistema:

exports.setAmigoConectado = setAmigoConectado;

Vamos a capturar el evento del lado del cliente mediante el siguiente


código, en el $(document).ready() del archivo script.js:

// io - mostramos cuando se conecta un amigo


sockets.on(‘setAmigoConectado’, mostrarAmigoConectado);

En el código anterior, definimos que se ejecutará la función


mostrarAmigoConectado() cuando se reciba el evento setAmigoConectado.
Por lo tanto, la definimos en el mismo archivo:

www.redusers.com
SISTEMAS WEB ESCALABLES 45

// agrega un usuario a la lista de amigos cuando se conecta


function mostrarAmigoConectado(data){
if ($(‘#usuariosAmigos ul li[uid=”’ + data.uid + ‘”]’).size() === 0){
// creamos la lista si no existe
if($(‘#usuariosAmigos p’).size()){
$(‘#usuariosAmigos p’).remove();
$(‘#usuariosAmigos’).append(‘<ul></ul>’);
}

var nombreUsuario = data.nombre + ‘ ‘ + data.apellido;


$(‘#usuariosAmigos ul’).prepend(‘<li uid=”’ + data.uid +’”>’ + nombreUsuario
+ ‘</li>’);

}
}

En este código nos encargamos de verificar que el usuario recibido


no exista en la lista de amigos y lo agregamos. También podemos
corroborar si existe el elemento <p>, para eliminarlo en caso de que
sea el primer amigo que se muestre en la lista.

Informar cuando
se desconecta un usuario
Vamos a crear una función para informar a los amigos cuando un
usuario se desconecta. Si observamos home.ejs veremos que hemos
definido el enlace salir y apunta a la ruta /salir. Para que nuestro sistema
pueda efectuar alguna acción cuando recibe esta ruta, necesitaremos
descomentar la siguiente línea del archivo app.js, pasando de esto:

//app.get(‘/salir’, user.logout);

a esto:

app.get(‘/salir’, user.logout);

www.redusers.com
46 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

En este código pudimos definimos que, cuando se solicite la ruta


/salir, se deberá ejecutar la función logout(). Por lo tanto, debemos
proceder a crearla en el archivo llamado user.js:

// logout
exports.logout = function(req, res){
// informamos a los amigos que se ha desconectado el usuario
setAmigoDesconectado(req.session.usr.uid);
// eliminamos el uid del array de usuarios y la session
delete usrEnLinea[req.session.usr.uid];
// eliminamos la clave se session
req.session.usr = ‘’;
// redireccionamos
res.redirect(‘/’);
}

En el código nos encargamos de ejecutar la función


setAmigoDesconectado(), que vamos a crear a continuación; luego,
borramos el usuario del array de usuarios en línea, vaciamos la
variable de sesión y redirigimos al usuario a la pagina inicial.
A continuación vamos a definir la función setAmigoDesconectado()
en el mismo archivo con el que estamos trabajando:

// informamos a los amigos que se ha desconectado el usuario


var setAmigoDesconectado = function(uid){
// obtenemos todos los amigos
db.smembers(‘uid:’ + uid + ‘:amigos’, function (err, amigos) {
amigos.forEach(function(id){
// verificamos que el usuario se encuentre conectado
if (usrEnLinea[id])
io.sockets.socket(usrEnLinea[id]).emit(‘setAmigoDesconectado’, uid);
});
});
}

www.redusers.com
SISTEMAS WEB ESCALABLES 47

Aquí hemos obtenido todos los amigos del usuario y, para cada uno
de ellos, hemos verificado si estaba conectado, emitiendo el evento
setAmigoDesconectado con el uid del usuario actual.
Para continuar nos encargamos de exportar la función para que esté
disponible desde cualquier lugar del sistema:

exports.setAmigoDesconectado = setAmigoDesconectado;

Ahora necesitaremos realizar la captura del evento desde el lado


del cliente. Para efectuar esta tarea debemos proceder a escribir lo
que mostramos a continuación, en el método denominado $(document).
ready() dentro del archivo script.js:

// io - mostramos cuando se desconecta un amigo


sockets.on(‘setAmigoDesconectado’, mostrarAmigoDesconectado);

Con el código establecemos que, cuando se reciba el


evento setAmigoDesconectado, se deberá ejecutar la función
mostrarAmigoDesconectado(), que definiremos en el mismo archivo:

// elimina un usuario de la lista de amigos cuando se desconecta


function mostrarAmigoDesconectado(data){
// eliminamos el amigo de la lista
$(‘#usuariosAmigos ul li[uid=”’ + data + ‘”]’).remove();
// si no hay amigos mostramos el mensaje
if ($(‘#usuariosAmigos ul li’).size() == 0){
$(‘#usuariosAmigos ul’).remove();
$(‘#usuariosAmigos’).prepend(‘<p>No hay amigos conectados...</p>’);
}

// eliminamos la ventana de chat si existe


if ($(‘#ventana-’ + data).size())
$(‘#ventana-’ + data).remove();
}

www.redusers.com
48 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Eliminamos el ítem de la lista de amigos y verificamos si existen


otros conectados; en el caso contrario mostramos un elemento <p>
con el mensaje de que no hay amigos conectados y, luego, verificamos
si existe una ventana de chat abierta y la borramos. A continuación
trabajaremos en el chat con amigos.

Creación del sistema de chat


Para continuar vamos a desarrollar el sistema de chat entre los
amigos que están conectados. La idea es que, al hacer clic en un amigo
de la lista, se abra una ventana típica de chat.
Primero vamos a capturar el evento click. En el método $(document).
ready(), en el archivo script.js, agregamos lo siguiente:

// abre ventanas de chat


$(document).on(‘click’, ‘#usuariosAmigos ul li’, function(){
abrirVentanaChat($(this).attr(‘uid’));
});

Así, definimos que, cuando se haga clic en un elemento de la lista,


se ejecutará la función abrirVentanaChat(), y pasamos como parámetro
el atributo uid del usuario de la lista.
A continuación debemos proceder a definir la función denominada
abrirVentanaChat() en el mismo archivo:

// abre una ventana de chat


function abrirVentanaChat(uid) {
if (!$(‘#ventana-’ + uid).size()){
var nombre = $(‘#usuariosAmigos ul li[uid=”’ + uid + ‘”]’).html();

var ventana = ‘<div class=”ventana marco celeste” id=”ventana-’ + uid + ‘”


uid=”’ + uid + ‘”>’;
ventana += ‘ <span>’+ nombre + ‘</span>’;
ventana += ‘ <textarea cols=”20” rows=”7” readonly=”true”></tex-
tarea>’;

www.redusers.com
SISTEMAS WEB ESCALABLES 49

ventana += ‘ <input type=”text” class=”chat-text” maxlength=”20” />’;


ventana += ‘</div>’;

$(‘.chat’).append(ventana);
}
}

En el código, obtenemos el uid del usuario con


el que se va a chatear, y se crea una ventana que LUEGO DE OBTENER
contendrá un elemento <input> para escribir y un EL UID DEL USUARIO
elemento <textarea> para mostrar los mensajes;
agregamos toda la ventana al elemento chat. SE CREA UNA
Vamos a definir el evento que va a capturar el VENTANA APTA PARA
mensaje para enviarlo al receptor. Para hacerlo,
agregamos el siguiente código en el método UNA SESIÓN DE CHAT
$(document).ready() del mismo archivo:

// cuando el usuario presiona enter emite el mensaje


$(document).on(‘keypress’, ‘.chat-text’, function(e){
if (e.which == 13)
enviarMensaje($(this).parent().attr(‘uid’), $(this).val());
});

En este código simplemente ejecutamos la función enviarMensaje()


cuando se presiona la tecla ENTER, pasándole como parámetros el uid
del usuario receptor y el mensaje.
Debemos definir la función enviarMensaje() en el mismo archivo:

EXTENSIONES PARA EXPRESS

Además de las herramientas que vienen integradas por defecto en Express, integrantes de la comunidad
oficial han creado decenas de extensiones muy útiles para ser utilizadas en los desarrollos y ahorrar
tiempo de programación. Podemos conocerlas accediendo al siguiente enlace: https://github.com/
senchalabs/connect/wiki.

www.redusers.com
50 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

// se envia un mensaje en el chat


function enviarMensaje(uid, msg) {
var data = { para : uid,
mensaje: msg,
fecha : new Date()
};

// emitimos el mensaje
sockets.emit(‘enviarMensaje’, data);
// agregamos el mensaje al textarea
$(‘#ventana-’+ uid +’ textarea’).val($(‘#ventana-’+ uid +’ textarea’).val() + ‘yo:
‘ + msg + ‘\r\n’);
// limpiamos la caja de texto
$(‘#ventana-’+ uid +’ .chat-text’).val(‘’);
}

En el código, creamos un objeto con la clave para (donde le


asignamos el uid receptor, el mensaje y la fecha de emisión) y luego
emitimos el mensaje enviarMensaje al servidor; por último, agregamos
a <textarea> el mensaje y limpiamos la caja de texto.
Ahora necesitamos capturar el evento del lado del servidor. Debemos
agregar lo que sigue en el archivo index.js, en la función sessionIO.on()
dentro del condicional de sesión:

// Emitimos el mensaje al usuario


socket.on(‘enviarMensaje’, function (data){
data.de = session.usr.uid;
data.nombre = session.usr.nombre + ‘ ‘ + session.usr.apellido;
user.enviarMensaje(data);
});

En el código hemos capturado el evento enviarMensaje y hemos


agregado la clave de con el uid del usuario actual, y la clave nombre
con los atributos nombre y apellido concatenados. Ejecutamos la función
enviarMensaje() pasándole como parámetro el objeto recibido.
Lo que sigue es crear la función enviarMensaje() en el archivo user.js:

www.redusers.com
SISTEMAS WEB ESCALABLES 51

// se envia el mensaje del chat


var enviarMensaje = function (data){
io.sockets.socket(usrEnLinea[data.para]).emit(‘mensajeRecibido’, data);
}

Simplemente, enviamos el objeto recibido al usuario receptor


a través del evento mensajeRecibido. Luego, exportamos la función
para que esté disponible desde cualquier lugar del sistema:

exports.enviarMensaje = enviarMensaje;

Necesitamos capturar este evento del lado del cliente, dentro de la


función $(document).ready():

// io - mostramos el mensaje recibido del chat


sockets.on(‘mensajeRecibido’, mostrarMensajeRecibido);

En este código hemos definido que, cuando se recibe el evento


mensajeRecibido, se ejecuta la función mostrarMensajeRecibido(), por lo
que debemos definirla en el mismo archivo. Antes de la función vamos
a establecer una variable global llamada ultimaFechaMsg:

var ultimaFechaMsg = 0;

// se muestra un mensaje recibido del chat


function mostrarMensajeRecibido(data){
// si se recibe mensajes duplicados
if (ultimaFechaMsg == data.fecha)
return;
else{
// actualizamos la fecha del ultimo mensahe recibido
ultimaFechaMsg = data.fecha;
// si no existe la ventana la creamos
if ($(‘#ventana-’ + data.de).size() == 0)
abrirVentanaChat(data.de);

www.redusers.com
52 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

// agregamos la informacion del mensaje


$(‘#ventana-’ + data.de + ‘ span’).html(data.nombre);
$(‘#ventana-’ + data.de + ‘ textarea’).val($(‘#ventana-’ + data.de + ‘ textar-
ea’).val() + data.nombre + ‘: ‘ + data.mensaje + ‘\r\n’);
}
}

En el código anterior, declaramos una variable global que


utilizaremos en la función para comparar la fecha de recepción de
mensajes en el caso de que existan mensajes duplicados. Luego, si
la ventana de chat no existe, la abrimos y agregamos el mensaje.

Figura 13. En la imagen podemos ver la ventana de chat con amigos.

ESTADÍSTICAS MEDIANTE STATCOUNTER

StatCounter es un sitio que publica estadísticas globales acerca de diferentes tecnologías. Entre las opcio-
nes que ofrece se encuentra la posibilidad de ver qué tecnología comparar, permite seleccionar el tipo de
gráfico (líneas, barras o mapa) y además permite descargar el gráfico en formato JPG o CSV. Podemos
conocerlo mejor a través del siguiente enlace: http://gs.statcounter.com.

www.redusers.com
SISTEMAS WEB ESCALABLES 53

Debemos destacar que el sistema de chat desarrollado permite al


usuario tener múltiples conversaciones en simultáneo, ya que en el
servidor mantenemos el id del Socket que identifica a cada uno.

Figura 14. Mediante el sistema de chat desarrollado,


los usuarios pueden mantener varias conversaciones a la vez.

Creación del sistema de posts


Vamos a desarrollar el sistema de publicación de post con amigos.
Primero necesitamos poder enviar al servidor los posts escritos, para
lo cual debemos capturar el contenido del formulario. En el archivo
script.js, dentro del método $(document).ready(), escribiremos lo siguiente:

// nuevo post
$(document).on(‘submit’, ‘#postFrm’, function(e){
e.preventDefault();
setPost($(this));
});

Así, definimos que, cuando se envía el formulario, se ejecuta la


función setPost() enviando como parámetro el mismo objeto.
A continuación definimos la función setPost() en el mismo archivo:

www.redusers.com
54 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

// metodo POST cuando se escrine un post nuevo


function setPost(form) {
$.post(“/setPost”, form.serialize(),
function(respuesta){
if (respuesta.codigo === 201){
var bloque = ‘<article class=”bloque marco blanco”>’;
bloque += ‘ <div class=”header”>’;
bloque += ‘ <img src=”/img/icon-profile.png” />’;
bloque += ‘ <span class=”bloque_usuario”>yo </span>’;
bloque += ‘ <span class=”bloque_fecha”> hace unos segundos...</
span>’;
bloque += ‘ </div>’;
bloque += ‘ <div class=”value”>’ + $(‘#postFrm textarea’).val() + ‘</
div>’;
bloque += ‘</article>’;

$(‘#posts’).prepend(bloque);
$(‘#postFrm textarea’).val(‘’);
}else
mostrarMensajeFormulario(form, respuesta.mensaje);

}
);
}

En el código hacemos un POST por Ajax a la ruta /setPost con los


elementos del formulario serializado. Si la respuesta es 201, creamos
un bloque con el post escrito y se lo mostramos al mismo usuario.
En el caso contrario, mostramos el mensaje de error. Como estamos
haciendo un POST descomentamos la siguiente línea en app.js. De esto:

//app.post(‘/setPost’, user.setPost);

debemos pasar a lo que mostramos a continuación:

app.post(‘/setPost’, user.setPost);

www.redusers.com
SISTEMAS WEB ESCALABLES 55

En este código nos encargamos de definir que el ruteo deberá


ejecutar la función setPost(). Por esta razón, tendremos que realizar
esta definición en el archivo denominado user.js:

// registramos el post para el usuario actual y los amigos


exports.setPost = function(req, res){
var post = req.param(‘post’);

if (post.length < 1){


res.send({codigo: 204, mensaje: ‘Mensaje Invalido’ });
return;
}else{
db.incr(‘global:ultimoPid’, function (err, pid) {
var fecha = formatearFecha();
var uid = req.session.usr.uid;

// seteamos el post y la fecha/hora actual


db.hmset(‘post:’ + pid, {‘uid’ : uid,
‘fecha’ : fecha,
‘post’ : post
}
);

// incremento la cantidad de posts para el usuario actual


db.incr(‘uid:’ + uid + ‘:nposts’);

var postID = pid;


// obtenemos los amigos
db.smembers(“uid:” + uid + “:amigos”, function (err, amigos) {
// agrego el usuario actual
amigos.push(uid);
// agrego el id del post a cada amigo
amigos.forEach(function(sid){
db.lpush(‘uid:’ + sid + ‘:posts’, postID);
});
});

www.redusers.com
56 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

res.send({codigo: 201, mensaje: ‘Mensaje publicado’});


});
}
};

En el código anterior, recibimos el post y verificamos que tenga


contenido; luego, incrementamos la clave ultimoPid y llamamos a
la función formatearFecha() para obtener la fecha actual formateada.
Después, obtenemos el usuario actual y guadamos en la base de datos
el post con la información obtenida. Seguidamente, nos encargamos de
incrementar la cantidad de posts para este usuario y la guardamos en
simultáneo para cada uno de los amigos.
A continuación vamos a definir la función formatearFecha():

// funcion para formatear la fecha


function formatearFecha(fecha) {
var d = new Date(fecha || Date.now()),
dia = d.getDate(),
mes = (d.getMonth() + 1),
anio = d.getFullYear(),
hora = d.getHours(),
minuto = d.getMinutes(),
segundo = d.getSeconds();

if (mes.length < 2) mes += ‘0’;


if (dia.length < 2) dia += ‘0’;

return [dia, mes, anio].join(‘-’) + ‘ ‘ + [hora, minuto, segundo].join(‘:’);


}

En el código anterior hemos obtenido la fecha actual, en el caso de


que no la pasemos por parámetro, y la formateamos de manera legible.
Lo que nos queda, ahora, es obtener los posts cuando el usuario inicia
sesión. Para esto, agregamos el siguiente código en el archivo index.js,
en la función sessionIO.on(), dentro del condicional de sesión:

www.redusers.com
SISTEMAS WEB ESCALABLES 57

// obtenemos los posts


user.getPosts(session.usr.uid);

De este modo definimos que, al conectarse un usuario, se ejecutará


la función getPosts(), a la cual le pasaremos como parámetro el uid
actual. Debemos definir la función que corresponde en el archivo
llamado user.js de la siguiente manera:

var getPosts = function(uid){


db.lrange(‘uid:’ + uid + ‘:posts’, 0, 10, function (err, posts) {
if (posts) {
var arrayPosts = [];
var i = 0;
// obtenemos los atributos de cada post
posts.forEach(function(pid) {
db.hgetall(‘post:’ + pid, function (err, post) {
var usuarioNombre;
// obtenemos los atributos del usuario
db.hgetall(‘uid:’ + post.uid, function (err, usuario) {
i++;
arrayPosts.push({‘uid’ : post.uid,
‘nombre’ : usuario.nombre,
‘apellido’: usuario.apellido,
‘fecha’ : post.fecha,
‘mensaje’ : post.post
});
// al final de la lista de post se emite al usuario
if (i == posts.length)
io.sockets.socket(usrEnLinea[uid]).emit(‘setPosts’, arrayPosts);
});
});
});
}
});
}

www.redusers.com
58 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Hemos obtenido los últimos diez posts del usuario actual y, de cada
uno de ellos, la información completa y del usuario que la escribió, que
almacenamos en un array. Por último, debemos realizar la emisión del
evento llamado setPosts con el array creado.
A continuación debemos capturar el evento setPosts del lado del
cliente. Para realizar esto agregaremos lo que sigue en el archivo
script.js en el método $(document).ready():

// io - mostramos los posts


sockets.on(‘setPosts’, mostrarPosts);

Así determinamos que, cuando ocurra el evento setPosts,


ejecutaremos la función mostrarPosts(), que definiremos en el mismo
archivo tal como mostramos:

// muestra los posts en el contenedor


function mostrarPosts(data){
if (data.length) {
$(‘#posts’).html(‘’);
data.forEach(function (post) {
var nombreUsuario = post.nombre + ‘ ‘ + post.apellido;
var bloque = ‘<article class=”bloque marco blanco”>’;
bloque += ‘ <div class=”header”>’;
bloque += ‘ <img src=”/img/icon-profile.png” />’;
bloque += ‘ <span class=”bloque_usuario”>’ + nombreUsuario + ‘</
span>’;
bloque += ‘ <span class=”bloque_fecha”>’+ post.fecha + ‘</span>’;
bloque += ‘ </div>’;
bloque += ‘ <div class=”value”>’ + post.mensaje + ‘</div>’;
bloque += ‘</article>’;

$(‘#posts’).append(bloque);
});
};
}

www.redusers.com
SISTEMAS WEB ESCALABLES 59

En el código nos hemos encargado de obtener


los posts correspondientes y, por cada uno de POR CADA POST SE
ellos, creamos un bloque con el usuario que lo CREA UN BLOQUE
escribió, la fecha y el mensaje, y lo agregamos
al contenedor #posts. CON EL USUARIO QUE
Por último, recordemos que había quedado LO ESCRIBIÓ Y OTROS
pendiente explicar la llamada a la función user.
getTotalUsuarios() del archivo index.js, en caso de DATOS IMPORTANTES
que el usuario haya iniciado sesión. Ya hemos
desarrollado esta función, que simplemente
realiza la devolución del total de usuarios registrados en el sistema
(debemos considerar que serán mostrados en la parte inferior
de la pantalla principal del sistema).

Figura 15. En la imagen podemos


observar la apariencia de los posts y que el usuario
tiene acceso a los posts de sus amigos.

Vista de la base de datos


Inspeccionando la base de datos de nuestro sistema podemos
ver la estructura generada; por ejemplo, los usuarios registrados y
conectados, las listas de amigos generadas para cada usuario, los posts
creados por los usuarios y también los contadores.

www.redusers.com
60 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Figura 16. Cada usuario contiene, además de sus datos,


una lista de amigos, una lista de posts y un contador de posts.

También podemos observar qué información tenemos de cada


post. Entre los datos que es posible identificar para los post escritos
encontramos el autor, la fecha en que se creó y también el mensaje
que corresponde, así como el identificador del usuario.

Figura 17. En la base de datos guardamos la fecha,


el mensaje y el identificador del usuario que lo escribió.

www.redusers.com
SISTEMAS WEB ESCALABLES 61

Hasta aquí hemos realizado el desarrollo completo de un sistema


medianamente complejo, que nos sirve como base para cualquier tipo
de sistema escalable con funciones y características en tiempo real.
Como sabemos, una red social cumple los requisitos mencionados, lo
que la convierte en el ejemplo ideal para el tema que hemos tratado a
lo largo de los capítulos que componen esta obra.

RESUMEN

Hemos aplicado todos los conocimientos y temas tratados a lo largo del libro mediante el desarrollo de
una red social que propone un gran cambio en la manera de pensar las acciones, ya que la mayoría de las
interacciones deben ser reflejadas en tiempo real y desencadenan un efecto en los demás usuarios. Las
bases de datos NoSQL, como Redis, cuentan con ventaja en la disponibilidad para grandes volúmenes
de datos, ofreciendo un gran desempeño en el funcionamiento de cualquier sistema; al mismo tiempo,
mediante Socket.IO es posible emitir y recibir eventos completamente personalizados, que entregan un
aspecto único a la interacción de los usuarios con los sistemas.

www.redusers.com
62 APÉNDICE. DESARROLLO DE UNA RED SOCIAL

Actividades
TEST DE AUTOEVALUACIÓN

1 ¿Express ofrece la posibilidad de definir un sistema autosuficiente?

2 ¿Es posible definir diferentes entornos de funcionamiento en Express?

3 ¿Con qué característica debe contar una variable para tener un alcance global?

4 ¿ Qué característica debe tener una función para tener un alcance global?

5 ¿Socket.IO maneja sesiones? Justificar.

6 ¿Es posible utilizar un mecanismo de cookies, en vez de sesiones, para manejar


usuarios conectados?

7 ¿on() y emit() son métodos de Socket.IO de Express?

8 ¿Qué utilidad tiene la exportación de las funciones?

9 ¿Los métodos on() y emit() pueden ser utilizados en cliente y en servidor


indistintamente?

10 ¿Un sistema desarrollado con Node y Express necesita de un servidor web como
Apache?

EJERCICIOS PRÁCTICOS

1 Implemente un buscador para localizar posts, un sistema de comentario con


Socket.IO para notificar a los amigos y un sistema de puntuación para los posts.

2 Instale el módulo nodemailer para enviar correos electrónicos.

3 Genere la función Ver y editar el perfil actual y Acceder al perfil de los amigos.

4 Implemente un sistema de cierre de sesión y notificación cuando el usuario


cierra el navegador.

PROFESOR EN LÍNEA

Si tiene alguna consulta técnica relacionada con el contenido, puede contactarse


con nuestros expertos: profesor@redusers.com

www.redusers.com

También podría gustarte