Commit e5cfbfcf authored by Marcelo Alejo Calle's avatar Marcelo Alejo Calle
Browse files

app_dominio_correo

parent 8e7daab3
node_modules*
package-lock.json
\ No newline at end of file
# Instalación
## Requisitos
- Node.js 8 en adelante
## Modo de uso
```bash
# Instalando librería
$ npm install git+https://gitlab.agetic.gob.bo/malejo/app-dominio-correo.git --save
```
**Nota.-** Si la instalación del paquete no avanza, ejecutar el siguiente comando para verificar si tiene permisos
```bash
$ git config --global credential.helper 'cache --timeout 1200'
$ git clone https://gitlab.agetic.gob.bo/malejo/app-dominio-correo.git /tmp/test
```
Instanciando el módulo app-dominio en un proyecto
```js
const AppDominio = require('app-dominio-correo');
const config = {
database: 'postgres',
username: 'postgres',
password: 'postgres',
host: 'localhost'
};
// Para usar await debe estar dentro una función async
const app = await AppDominio(config).catch(err => console.error(err));
// Lista completa de dominios, puede recibir parámetros de búsqueda entre otras opciones
const list = await app.findAll();
const resp = await app.dominio.verificarCorreo('example.com').then((a) => {
```
## Instalando el módulo para desarrollo
Siga los siguientes pasos:
```bash
# 1. Instalar dependencias
npm install
# 2. Correr test de pruebas
npm run lint
npm test
```
\ No newline at end of file
'use strict';
const Sequelize = require('sequelize');
const Services = require('./src/services');
const Dns = require('./src/dns');
const listaNegraDefault = [
'10minutemail.com',
'fremont.nodebalancer.linode.com',
'yopmail', 'cool.fr.nf', 'jetable.fr.nf', 'nospam.ze.tc', 'nomail.xl.cx', 'mega.zik.dj', 'speed.1s.fr', 'courriel.fr.nf', 'moncourrier.fr.nf', 'monemail.fr.nf', 'monmail.fr.nf',
'mailinator', 'binkmail.com', 'bobmail.info', 'chammy.info', 'devnullmail.com', 'letthemeatspam.com', 'mailinater.com', 'mailinator.net', 'mailinator2.com', 'notmailinator.com', 'reallymymail.com', 'reconmail.com', 'safetymail.info', 'sendspamhere.com', 'sogetthis.com', 'spambooger.com', 'spamherelots.com', 'spamhereplease.com', 'spamthisplease.com', 'streetwisemail.com', 'suremail.info', 'thisisnotmyrealemail.com', 'tradermail.info', 'veryrealemail.com', 'zippymail.info',
'guerrillamail', 'maildrop', 'mailnesia'
];
// Fuente: https://nic.bo/documentos/Politica_2019_firmado.pdf
const listaInstitucionalDefault = [
'gob.bo',
'edu.bo',
'mil.bo',
'int.bo',
'academia.bo',
'agro.bo',
'arte.bo',
'blog.bo',
'bolivia.bo',
'ciencia.bo',
'cooperativa.bo',
'democracia.bo',
'deporte.bo',
'ecologia.bo',
'economia.bo',
'empresa.bo',
'indigena.bo',
'industria.bo',
'info.bo',
'medicina.bo',
'movimiento.bo',
'musica.bo',
'natural.bo',
'nombre.bo',
'noticias.bo',
'patria.bo',
'politica.bo',
'profesional.bo',
'plurinacional.bo',
'pueblo.bo',
'revista.bo',
'salud.bo',
'tecnologia.bo',
'tksat.bo',
'transporte.bo',
'wiki.bo'
];
module.exports = async function (configApp, listaNegra, listaInstitucional) {
const config = {
database: 'app_dns',
username: 'usuario',
password: 'usuario',
logging: false,
operatorsAliases: Sequelize.Op.Aliases
};
Object.assign(config, configApp);
Object.assign(config, {
timezone: '-04:00',
dialect: 'postgres',
pool: {
max: 10,
min: 0,
idle: 10000
}
});
const sequelize = new Sequelize(config);
// Cargando modelo
const params = sequelize.import('src/model');
// Verificando conexión con la BD
await sequelize.authenticate();
// Creando las tablas
await sequelize.sync(config.force ? { force: true } : {});
// Cargando los servicios de params
let services = Services(params, Sequelize);
// Cargando servicios dns
services = Object.assign(services, Dns(services));
if (listaNegraDefault.length > 0) {
for (const dominio of listaNegraDefault) {
if (typeof (dominio) === 'string') {
await services.createOrUpdate({
dominio,
_user_created: 1
});
}
}
}
if (listaInstitucionalDefault.length > 0) {
for (const dominio of listaInstitucionalDefault) {
if (typeof (dominio) === 'string') {
await services.createOrUpdate({
dominio,
tipo: 'INSTITUCIONAL',
_user_created: 1
});
}
}
}
if (listaNegra && listaNegra.length > 0) {
for (const dominio of listaNegra) {
if (typeof (dominio) === 'string') {
await services.createOrUpdate({
dominio,
_user_created: 1
});
}
}
}
if (listaInstitucional && listaInstitucional.length > 0) {
for (const dominio of listaInstitucional) {
if (typeof (dominio) === 'string') {
await services.createOrUpdate({
dominio,
tipo: 'INSTITUCIONAL',
_user_created: 1
});
}
}
}
return services;
};
{
"name": "app-dominio-correo",
"version": "1.0.0",
"description": "Modulo para verificar el dominio de un correo",
"main": "index.js",
"author": "malejo@agetic.gob.bo",
"license": "LPG Bolivia v1",
"scripts": {
"lint": "semistandard && echo 'OK'",
"test": "mocha -t 6000 --recursive tests --exit"
},
"repository": {
"type": "git",
"url": ""
},
"keywords": [
"dominio"
],
"semistandard": {
"ignore": [
"scripts/*"
]
},
"dependencies": {
"request": "^2.88.2",
"sequelize": "^4.44.4"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^6.2.3",
"pg": "^6.4.2",
"semistandard": "^14.2.3",
"supertest": "^4.0.2"
}
}
'use strict';
const { config, handleFatalError } = require('./src/util');
const Dns = require('./');
async function setup () {
try {
const dns = await Dns(config).catch(handleFatalError);
console.log('Services!', dns);
const result = await dns.findAll({
order: 'dominio'
});
for (const i in result.rows) {
console.log('->', i, result.rows[i].dominio, result.rows[i].contador);
}
const a = await dns.dominio.verificarCorreo('example.com');
console.log(a);
console.log('Success App setup!');
} catch (error) {
console.error('Error', error);
}
process.exit(0);
}
setup();
'use strict';
const debug = require('debug')('app:dns:verificarCorreo');
const dns = require('dns');
module.exports = function dnsService (services) {
let dominioNegro = {
count: 0,
rows: []
};
let dominioInstitucional = {
count: 0,
rows: []
};
const buscarEnListaInstitucion = async function (dominio, recargar = true) {
// Obtener lista de dominios institucinal
if (recargar) {
dominioInstitucional = await services.findAll({
order: '-contador',
tipo: 'INSTITUCIONAL',
estado: 'ACTIVO'
});
}
let pos = -1;
if (dominioInstitucional.rows && dominioInstitucional.rows instanceof Array) {
dominioInstitucional.rows.find(function (value, i) {
if (dominio.indexOf(value.dominio) !== -1) {
pos = i;
return true;
}
});
if (pos >= 0) {
services.updateContador(dominioInstitucional.rows[pos].id).catch((e) => {
console.error(e);
});
return true;
}
}
return false;
};
const buscarEnListaNegra = async function (dominio, recargar = true) {
// Obtener lista de dominios no permitidos
if (recargar) {
dominioNegro = await services.findAll({
order: '-contador',
tipo: 'NO_PERMITIDO',
estado: 'ACTIVO'
});
}
let pos = -1;
if (dominioNegro.rows && dominioNegro.rows instanceof Array) {
dominioNegro.rows.find(function (value, i) {
if (dominio.indexOf(value.dominio) !== -1) {
pos = i;
return true;
}
});
if (pos >= 0) {
services.updateContador(dominioNegro.rows[pos].id).catch((e) => {
console.error(e);
});
return true;
}
}
return false;
};
const verificarDns = (dominio) => {
return new Promise((resolve, reject) => {
dns.resolve(dominio, 'MX', (err, res) => {
if (err) {
return reject(err);
}
return resolve(res);
});
});
};
const verificarDominioCorreo = async (dominio) => {
debug(`\n@${dominio}`);
const result = {};
if (!dominio) {
result.error = `El dominio "@${dominio}" no existe`;
return result;
}
if (await buscarEnListaNegra(dominio)) {
result.error = `El dominio "@${dominio}" no esta permitido`;
return result;
}
if (await buscarEnListaInstitucion(dominio)) {
result.advertencia = `El dominio "@${dominio}" es institucional`;
}
try {
const res = await verificarDns(dominio);
result.mx = res;
if (!res || !res[0]) {
console.error('[verificarDominio]', res);
result.advertencia = `El dominio "@${dominio}" no puede ser verificado.`;
} else {
if (!res[0].exchange) {
result.error = `El dominio "@${dominio}" no recibe correos.`;
}
for (const ex of res) {
if (await buscarEnListaNegra(ex.exchange, false)) {
result.error = `El dominio "@${dominio}" no esta permitido.`;
} else if (await buscarEnListaInstitucion(ex.exchange, false)) {
result.advertencia = `El dominio "@${dominio}" es institucional.`;
}
}
}
return result;
} catch (err) {
if (err.message.indexOf('ENOTFOUND') >= 0) {
result.error = `El dominio "@${dominio}", no existe.`;
return result;
}
if (err.message.indexOf('ENODATA') >= 0) {
result.error = `El dominio "@${dominio}", no recibe correos.`;
return result;
}
if (err.message.indexOf('ETIMEOUT') >= 0) {
console.error('[verificarDominio]', err);
result.advertencia = `El dominio "@${dominio}", no puede ser verificado.`;
return result;
}
console.error('[verificarDominio]', err);
throw err;
}
};
return verificarDominioCorreo;
};
'use strict';
module.exports = function setupDNS (app) {
return {
dominio: {
verificarDominioCorreo: require('./correo/verificar')(app)
}
};
};
'use strict';
const { setTimestamps } = require('./util');
module.exports = (sequelize, DataTypes) => {
let fields = {
id: {
primaryKey: true,
autoIncrement: true,
type: DataTypes.INTEGER
},
dominio: {
type: DataTypes.TEXT,
allowNull: false,
unique: 'dominioIndex'
},
contador: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
descripcion: {
type: DataTypes.TEXT,
allowNull: true
},
tipo: {
type: DataTypes.ENUM,
values: ['NO_PERMITIDO', 'INSTITUCIONAL'],
defaultValue: 'NO_PERMITIDO',
allowNull: false
},
estado: {
type: DataTypes.ENUM,
values: ['ACTIVO', 'INACTIVO'],
defaultValue: 'ACTIVO',
allowNull: false
}
};
// Agregando campos para el log
fields = setTimestamps(fields);
const serviciosDnsDominio = sequelize.define('app_dominio_correo', fields, {
timestamps: false,
tableName: 'app_dominio_correo'
});
return serviciosDnsDominio;
};
'use strict';
const { getQuery, errorHandler } = require('./util');
module.exports = function paramsServices (dns, Sequelize) {
const Op = Sequelize.Op;
function findAll (params = {}) {
const query = getQuery(params);
query.where = {};
if (params.dominio) {
query.where.dominio = {
[Op.iLike]: `%${params.dominio}%`
};
}
if (params.contador) {
query.where.contador = {
[Op.iLike]: `%${params.contador}%`
};
}
if (params.descripcion) {
query.where.descripcion = {
[Op.iLike]: `%${params.descripcion}%`
};
}
if (params.tipo) {
query.where.tipo = params.tipo;
}
if (params.estado) {
query.where.estado = params.estado;
}
return dns.findAndCountAll(query);
}
function findById (id) {
return dns.findOne({
where: {
id
}
});
}
function findByDominio (dominio) {
return dns.findOne({
where: {
dominio
}
});
}
async function createOrUpdate (data) {
const cond = {
where: {
$or: {
id: data.id,
dominio: data.dominio
}
}
};
const item = await dns.findOne(cond);
if (item) {
if (data.descripcion) item.descripcion = data.descripcion;
if (data.tipo) item.tipo = data.tipo;
if (data.estado) item.estado = data.estado;
item._updated_at = new Date();
item._user_update = data._user_update || 1;
const updated = await item.save();
if (updated.id) {
return updated.get({ plain: true });
}
return null;
}
let result;
try {
delete data.id;
result = await dns.create(data);
} catch (e) {
errorHandler(e);
}
return result.toJSON();
}
async function updateItem (id, data) {
const cond = {
where: {
id: id
}
};
const item = await dns.findOne(cond);
if (item) {
if (data.descripcion) item.descripcion = data.descripcion;
if (data.tipo) item.tipo = data.tipo;
if (data.estado) item.estado = data.estado;
item._updated_at = new Date();
item._user_update = data._user_update || 1;
const updated = await item.save();
if (updated.id) {
return updated.toJSON();
}
return null;
}
return item;
}
async function updateContador (id) {
const cond = {
where: {
id: id
}
};
const item = await dns.findOne(cond);
if (item) {
item.contador = item.contador + 1;
item._updated_at = new Date();
item._user_update = 1;
const updated = await item.save();
if (updated.id) {
return updated.toJSON();
}
return null;
}
return item;
}
return {
findAll,
findById,
findByDominio,
createOrUpdate,
updateItem,
updateContador
};
};
'use strict';
const chalk = require('chalk');
const Sequelize = require('sequelize');
const lang = {
errors: {
validation: {