Commit a477d380 by Fernando Martínez Santiago

Merge branch 'develop' of http://gitlab.ujaen.es/yotta/pictogram into develop

parents ac5b0f46 b254c0a8
...@@ -2,6 +2,7 @@ Estos son scripts de automatización para las siguientes tareas en el servidor: ...@@ -2,6 +2,7 @@ Estos son scripts de automatización para las siguientes tareas en el servidor:
- Renovación de certificados - Renovación de certificados
- Backup automático - Backup automático
- Rotación de logs
## Renovación de certificados ## Renovación de certificados
...@@ -40,4 +41,11 @@ Desde AWS IAM, debemos crear un usuario dentro de un grupo. Los permisos son los ...@@ -40,4 +41,11 @@ Desde AWS IAM, debemos crear un usuario dentro de un grupo. Los permisos son los
} }
``` ```
El comando cron que hay de muestra en `crontab` se encargará de hacer una copia cada día, para los últimos 30 días. El comando cron que hay de muestra en `crontab` se encargará de hacer una copia cada día, para los últimos 30 días.
\ No newline at end of file
## Rotación de logs
Se realiza con logrotate. Lo ideal es tener una entrada en el cron de root con
@daily /usr/sbin/logrotate -s sails/cron/logrotate.status sails/cron/logrotate.conf
No hay que olvidar que el fichero logrotate debe tener los permisos 644 y pertenecer a root:root
\ No newline at end of file
/**
* Utility Functions
*/
// Dependencies
var mysql = require('mysql');
var _ = require('lodash');
var url = require('url');
// Module Exports
var utils = module.exports = {};
/**
* Parse URL string from config
*
* Parse URL string into connection config parameters
*/
utils.parseUrl = function (config) {
if(!_.isString(config.url)) {
return config;
}
var obj = url.parse(config.url);
config.host = obj.hostname || config.host;
config.port = obj.port || config.port;
if(_.isString(obj.pathname)) {
config.database = obj.pathname.split('/')[1] || config.database;
}
if(_.isString(obj.auth)) {
config.user = obj.auth.split(':')[0] || config.user;
config.password = obj.auth.split(':')[1] || config.password;
}
return config;
};
/**
* Prepare values
*
* Transform a JS date to SQL date and functions
* to strings.
*/
utils.prepareValue = function(value) {
if(_.isUndefined(value) || value === null) {
return value;
}
// Cast functions to strings
if (_.isFunction(value)) {
value = value.toString();
}
// Store Arrays and Objects as strings
if (_.isArray(value) || value.constructor && value.constructor.name === 'Object') {
try {
value = JSON.stringify(value);
} catch (e) {
// just keep the value and let the db handle an error
value = value;
}
}
// Cast dates to SQL
if (_.isDate(value)) {
value = utils.toSqlDate(value);
}
return mysql.escape(value);
};
/**
* Builds a Select statement determining if Aggeregate options are needed.
*/
utils.buildSelectStatement = function(criteria, table, schemaDefs) {
var query = '';
if(criteria.groupBy || criteria.sum || criteria.average || criteria.min || criteria.max) {
query = 'SELECT ';
// Append groupBy columns to select statement
if(criteria.groupBy) {
if(_.isArray(criteria.groupBy)) {
_.each(criteria.groupBy, function(opt){
query += opt + ', ';
});
} else {
query += criteria.groupBy + ', ';
}
}
// Handle SUM
if (criteria.sum) {
if(_.isArray(criteria.sum)) {
_.each(criteria.sum, function(opt){
query += 'SUM(' + opt + ') AS ' + opt + ', ';
});
} else {
query += 'SUM(' + criteria.sum + ') AS ' + criteria.sum + ', ';
}
}
// Handle AVG (casting to float to fix percision with trailing zeros)
if (criteria.average) {
if(_.isArray(criteria.average)) {
_.each(criteria.average, function(opt){
query += 'AVG(' + opt + ') AS ' + opt + ', ';
});
} else {
query += 'AVG(' + criteria.average + ') AS ' + criteria.average + ', ';
}
}
// Handle MAX
if (criteria.max) {
if(_.isArray(criteria.max)) {
_.each(criteria.max, function(opt){
query += 'MAX(' + opt + ') AS ' + opt + ', ';
});
} else {
query += 'MAX(' + criteria.max + ') AS ' + criteria.max + ', ';
}
}
// Handle MIN
if (criteria.min) {
if(_.isArray(criteria.min)) {
_.each(criteria.min, function(opt){
query += 'MIN(' + opt + ') AS ' + opt + ', ';
});
} else {
query += 'MIN(' + criteria.min + ') AS ' + criteria.min + ', ';
}
}
// trim trailing comma
query = query.slice(0, -2) + ' ';
// Add FROM clause
return query += 'FROM `' + table + '` ';
}
/**
* If no aggregate options lets just build a normal query
*/
// Add all keys to the select statement for this table
query += 'SELECT ';
var selectKeys = [],
joinSelectKeys = [];
if ( !schemaDefs[table] ) {
throw new Error('Schema definition missing for table: `'+table+'`');
}
_.each(schemaDefs[table], function(schemaDef, key) {
selectKeys.push({ table: table, key: key });
});
// Check for joins
if(criteria.joins || criteria.join) {
var joins = criteria.joins || criteria.join;
_.each(joins, function(join) {
if(!join.select) {
return;
}
_.each(_.keys(schemaDefs[join.child.toLowerCase()]), function(key) {
var _join = _.cloneDeep(join);
_join.key = key;
joinSelectKeys.push(_join);
});
// Remove the foreign key for this join from the selectKeys array
selectKeys = selectKeys.filter(function(select) {
var keep = true;
if(select.key === join.parentKey && join.removeParentKey) {
keep = false;
}
return keep;
});
});
}
// Add all the columns to be selected that are not joins
_.each(selectKeys, function(select) {
query += '`' + select.table + '`.`' + select.key + '`, ';
});
// Add all the columns from the joined tables
_.each(joinSelectKeys, function(select) {
// Create an alias by prepending the child table with the alias of the join
var alias = select.alias.toLowerCase() + '_' + select.child.toLowerCase();
// If this is a belongs_to relationship, keep the foreign key name from the AS part
// of the query. This will result in a selected column like: "user"."id" AS "user_id__id"
if(select.model) {
return query += mysql.escapeId(alias) + '.' + mysql.escapeId(select.key) + ' AS ' +
mysql.escapeId(select.parentKey + '__' + select.key) + ', ';
}
// If a junctionTable is used, the child value should be used in the AS part of the
// select query.
if(select.junctionTable) {
return query += mysql.escapeId(alias) + '.' + mysql.escapeId(select.key) + ' AS ' +
mysql.escapeId(select.alias + '__' + select.key) + ', ';
}
// Else if a hasMany attribute is being selected, use the alias plus the child
return query += mysql.escapeId(alias) + '.' + mysql.escapeId(select.key) + ' AS ' +
mysql.escapeId(select.alias + '__' + select.key) + ', ';
});
// Remove the last comma
query = query.slice(0, -2) + ' FROM `' + table + '` ';
return query;
};
utils.toSqlDate = function toSqlDate(date) {
date = date.getFullYear() + '-' +
('00' + (date.getMonth()+1)).slice(-2) + '-' +
('00' + date.getDate()).slice(-2) + ' ' +
('00' + date.getHours()).slice(-2) + ':' +
('00' + date.getMinutes()).slice(-2) + ':' +
('00' + date.getSeconds()).slice(-2) + "." +
('00' + date.getMilliseconds()).slice(-3);
return date;
};
...@@ -1435,15 +1435,18 @@ module.exports = { ...@@ -1435,15 +1435,18 @@ module.exports = {
var action = req.param('action'); var action = req.param('action');
var attributes = req.param('attributes'); var attributes = req.param('attributes');
attributes.ui = attributes.ui ? attributes.ui : 'PCB'; attributes.ui = attributes.ui ? attributes.ui : 'PCB';
var room = sails.hooks.rooms.student(attributes.id_stu);
if (req.isSocket) { if (req.isSocket) {
sails.hooks.rooms.subscribeToRoom( sails.hooks.rooms.subscribeToRoom(
sails.hooks.rooms.student(attributes.id_stu), room,
req.socket, req.socket,
attributes.ui attributes.ui
); );
} }
res.ok({msg: "Subscribed to student "}); sails.hooks.rooms.getUICount(room, (info) => {
res.ok(info);
});
}, },
// //
......
...@@ -15,12 +15,11 @@ module.exports = function roomsHook (sails) { ...@@ -15,12 +15,11 @@ module.exports = function roomsHook (sails) {
return { return {
/************************************************************************** /**************************************************************************
* Notifies to all the room's subscribers about the number of different * Counts the number of UIs connected to a room:
* connections by UI:
* pdb_count (number of connections to the given room from Pictogram Web) * pdb_count (number of connections to the given room from Pictogram Web)
* pcb_count (number of connections to the given room from Pictogram Communicator/Supervisor) * pcb_count (number of connections to the given room from Pictogram Communicator/Supervisor)
*/ */
notifyRoom: function(room) { getUICount: function (room, cb) {
// Let's count number of connections per room and per UI // Let's count number of connections per room and per UI
var counter = {'PCB': 0, 'PDB': 0}; var counter = {'PCB': 0, 'PDB': 0};
//console.log('notifyRoom (' + room + ')---->\n' + JSON.stringify(sockets) + '\n' + JSON.stringify(rooms)); //console.log('notifyRoom (' + room + ')---->\n' + JSON.stringify(sockets) + '\n' + JSON.stringify(rooms));
...@@ -34,22 +33,31 @@ module.exports = function roomsHook (sails) { ...@@ -34,22 +33,31 @@ module.exports = function roomsHook (sails) {
(err) => { (err) => {
if (err) if (err)
sails.debug.log("Error when notifying room " + JSON.stringify(err)); sails.debug.log("Error when notifying room " + JSON.stringify(err));
else { else
// Broadcast data to room's peers cb({
sails.hooks.events.broadcastEvent( 'room': room,
room, 'pdb_count': counter['PDB'],
sails.hooks.events.roomSubscribersChange({ 'pcb_count': counter['PCB']
'room': room, });
'pdb_count': counter['PDB'],
'pcb_count': counter['PCB']
})
);
}
} }
); );
}, },
/************************************************************************** /**************************************************************************
* Notifies to all the room's subscribers about the number of different
* connections by UI
*/
notifyRoom: function(room) {
// Broadcast data to room's peers
sails.hooks.rooms.getUICount(room, (info) => {
sails.hooks.events.broadcastEvent(
room,
sails.hooks.events.roomSubscribersChange(info)
);
});
},
/**************************************************************************
* Special function that subscribes a socket to a given room. * Special function that subscribes a socket to a given room.
* This creates the connection and logs to debug with this format: * This creates the connection and logs to debug with this format:
* *
...@@ -183,6 +191,5 @@ module.exports = function roomsHook (sails) { ...@@ -183,6 +191,5 @@ module.exports = function roomsHook (sails) {
sails.hooks.rooms.unsubscribeFromRoom(sockets[socket.id].sup_room, socket); sails.hooks.rooms.unsubscribeFromRoom(sockets[socket.id].sup_room, socket);
} }
} }
}; };
}; };
...@@ -289,7 +289,7 @@ ...@@ -289,7 +289,7 @@
"no_students": "Su cuenta no está asociada a ningún estudiante", "no_students": "Su cuenta no está asociada a ningún estudiante",
"no_students_desc": "Pulse en 'Añadir estudiante' para vincularse a cuentas de alumnos existentes o dar de alta nuevas", "no_students_desc": "Pulse en 'Añadir estudiante' para vincularse a cuentas de alumnos existentes o dar de alta nuevas",
"no_space_in_category": "No queda espacio en la categoría", "no_space_in_category": "No queda espacio en la categoría",
"no_subscribed": "Sin conexión a la cuenta del estudiante", "no_subscribed": "Se ha perdido la conexión",
"no_supervisors": "No tiene tutores, padres o terapeutas asociados.", "no_supervisors": "No tiene tutores, padres o terapeutas asociados.",
"no_supervisors_desc": "Introduzca el correo electrónico del supervisor en el campo de arriba para añadirlo.", "no_supervisors_desc": "Introduzca el correo electrónico del supervisor en el campo de arriba para añadirlo.",
"no_supervisor_linked": "No se pudo asociar al supervisor", "no_supervisor_linked": "No se pudo asociar al supervisor",
...@@ -435,7 +435,7 @@ ...@@ -435,7 +435,7 @@
"student_pictograms": "Pictogramas del estudiante", "student_pictograms": "Pictogramas del estudiante",
"student_updated": "Estudiante actualizado", "student_updated": "Estudiante actualizado",
"students": "Alumnos", "students": "Alumnos",
"subscribed": "Conectado a la cuenta del alumno", "subscribed": "Se ha recuperado la conexión",
"sup_already_added": "El supervisor ya está en la lista", "sup_already_added": "El supervisor ya está en la lista",
"sup_not_added": "El supervisor no se ha podido añadir al estudiante.", "sup_not_added": "El supervisor no se ha podido añadir al estudiante.",
"sup_not_deleted": "El supervisor no se ha podido desvincular del alumno.", "sup_not_deleted": "El supervisor no se ha podido desvincular del alumno.",
......
...@@ -59,8 +59,8 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl( ...@@ -59,8 +59,8 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
io.socket.on('disconnect', function () { io.socket.on('disconnect', function () {
$scope.studentData.pcb_count = 0; $scope.studentData.pcb_count = 0;
$scope.studentData.pdb_count = 0; $scope.studentData.pdb_count = 0;
$scope.$apply();
ngToast.warning($translate.instant('no_subscribed')); ngToast.warning($translate.instant('no_subscribed'));
$scope.$apply();
}); });
io.socket.on('reconnect', function () { io.socket.on('reconnect', function () {
...@@ -76,9 +76,9 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl( ...@@ -76,9 +76,9 @@ dashboardControllers.controller('StudentCtrl', function StudentCtrl(
function (data) { function (data) {
if (data.room.search('student') != -1) { if (data.room.search('student') != -1) {
$scope.studentData.pcb_count = data.pcb_count; $scope.studentData.pcb_count = data.pcb_count;
$scope.studentData.pdb_count = data.pdb_count; $scope.studentData.pdb_count = data.pdb_count == 0 ? 1 : data.pdb_count; // because subscription is fastar than counter update
ngToast.success($translate.instant('subscribed'));
$scope.$apply(); $scope.$apply();
ngToast.warning($translate.instant('subscribed'));
} }
}); });
}); });
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
module.exports.sockets = { module.exports.sockets = {
pingTimeout: 10000, // set timeout to 10 secs pingTimeout: 5000, // set timeout to 5 secs
/*************************************************************************** /***************************************************************************
* * * *
......
...@@ -468,7 +468,7 @@ function sqlTypeCast(attr) { ...@@ -468,7 +468,7 @@ function sqlTypeCast(attr) {
break; break;
case 'datetime': case 'datetime':
expandedType = 'DATETIME'; expandedType = 'DATETIME(3)';
break; break;
case 'time': case 'time':
...@@ -499,7 +499,8 @@ function toSqlDate(date) { ...@@ -499,7 +499,8 @@ function toSqlDate(date) {
('00' + date.getDate()).slice(-2) + ' ' + ('00' + date.getDate()).slice(-2) + ' ' +
('00' + date.getHours()).slice(-2) + ':' + ('00' + date.getHours()).slice(-2) + ':' +
('00' + date.getMinutes()).slice(-2) + ':' + ('00' + date.getMinutes()).slice(-2) + ':' +
('00' + date.getSeconds()).slice(-2); ('00' + date.getSeconds()).slice(-2) + '.' +
('00' + date.getMilliseconds()).slice(-3);
return date; return date;
} }
......
...@@ -242,7 +242,8 @@ utils.toSqlDate = function toSqlDate(date) { ...@@ -242,7 +242,8 @@ utils.toSqlDate = function toSqlDate(date) {
('00' + date.getDate()).slice(-2) + ' ' + ('00' + date.getDate()).slice(-2) + ' ' +
('00' + date.getHours()).slice(-2) + ':' + ('00' + date.getHours()).slice(-2) + ':' +
('00' + date.getMinutes()).slice(-2) + ':' + ('00' + date.getMinutes()).slice(-2) + ':' +
('00' + date.getSeconds()).slice(-2); ('00' + date.getSeconds()).slice(-2) + "." +
('00' + date.getMilliseconds()).slice(-3);
return date; return date;
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment