Background job for sending emails + confirmation email template.
This commit is contained in:
parent
a5605a0349
commit
001e7b59a3
|
@ -7,7 +7,17 @@
|
||||||
"peersPath": "/tmp/peers",
|
"peersPath": "/tmp/peers",
|
||||||
|
|
||||||
"email": {
|
"email": {
|
||||||
"from": "no-reply@musterstadt.freifunk.net"
|
"from": "Freifunk Knotenformular <no-reply@musterstadt.freifunk.net>",
|
||||||
|
|
||||||
|
"smtp": {
|
||||||
|
"host": "mail.example.com",
|
||||||
|
"port": 465,
|
||||||
|
"secure": true,
|
||||||
|
"auth": {
|
||||||
|
"user": "user@example.com",
|
||||||
|
"pass": "pass"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"client": {
|
"client": {
|
||||||
|
|
|
@ -39,7 +39,11 @@
|
||||||
"jshint-stylish": "~2.2.0",
|
"jshint-stylish": "~2.2.0",
|
||||||
"load-grunt-tasks": "~3.5.0",
|
"load-grunt-tasks": "~3.5.0",
|
||||||
"lodash": "~4.12.0",
|
"lodash": "~4.12.0",
|
||||||
|
"moment": "~2.13.0",
|
||||||
"ng-di": "~0.2.1",
|
"ng-di": "~0.2.1",
|
||||||
|
"node-cron": "~1.1.1",
|
||||||
|
"nodemailer": "~2.4.1",
|
||||||
|
"nodemailer-html-to-text": "~2.1.0",
|
||||||
"serve-static": "~1.10.2",
|
"serve-static": "~1.10.2",
|
||||||
"sqlite3": "~3.1.4",
|
"sqlite3": "~3.1.4",
|
||||||
"time-grunt": "~1.3.0"
|
"time-grunt": "~1.3.0"
|
||||||
|
|
|
@ -12,7 +12,18 @@ var defaultConfig = {
|
||||||
peersPath: '/tmp/peers',
|
peersPath: '/tmp/peers',
|
||||||
|
|
||||||
email: {
|
email: {
|
||||||
from: 'no-reply@musterstadt.freifunk.net'
|
from: 'Freifunk Knotenformular <no-reply@musterstadt.freifunk.net>',
|
||||||
|
|
||||||
|
// For details see: https://nodemailer.com/2-0-0-beta/setup-smtp/
|
||||||
|
smtp: {
|
||||||
|
host: 'mail.example.com',
|
||||||
|
port: '465',
|
||||||
|
secure: true,
|
||||||
|
auth: {
|
||||||
|
user: 'user@example.com',
|
||||||
|
pass: 'pass'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
client: {
|
client: {
|
||||||
|
|
|
@ -7,5 +7,6 @@ CREATE TABLE email_queue (
|
||||||
email VARCHAR(255) NOT NULL,
|
email VARCHAR(255) NOT NULL,
|
||||||
data TEXT NOT NULL,
|
data TEXT NOT NULL,
|
||||||
|
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
|
created_at DATETIME DEFAULT (strftime('%s','now')) NOT NULL,
|
||||||
|
modified_at DATETIME DEFAULT (strftime('%s','now')) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
13
server/jobs/mailQueueJob.js
Normal file
13
server/jobs/mailQueueJob.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('ffffng').factory('MailQueueJob', function (Database, MailService) {
|
||||||
|
return {
|
||||||
|
run: function () {
|
||||||
|
MailService.sendPendingMails(function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
29
server/jobs/scheduler.js
Normal file
29
server/jobs/scheduler.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var glob = require('glob');
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var jobFiles = glob.sync(__dirname + '/*Job.js');
|
||||||
|
_.each(jobFiles, function (jobFile) {
|
||||||
|
require(jobFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
angular.module('ffffng').factory('Scheduler', function ($injector) {
|
||||||
|
var cron = require('node-cron');
|
||||||
|
|
||||||
|
function schedule(expr, jobName) {
|
||||||
|
var job = $injector.get(jobName);
|
||||||
|
|
||||||
|
if (!_.isFunction(job.run)) {
|
||||||
|
throw new Error('The job ' + jobName + ' does not provide a "run" function.');
|
||||||
|
}
|
||||||
|
|
||||||
|
cron.schedule(expr, job.run);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function () {
|
||||||
|
schedule('*/5 * * * * *', 'MailQueueJob');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
|
@ -14,8 +14,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
lib('_', 'lodash');
|
lib('_', 'lodash');
|
||||||
|
lib('async');
|
||||||
lib('crypto');
|
lib('crypto');
|
||||||
|
lib('deepExtend', 'deep-extend');
|
||||||
lib('fs');
|
lib('fs');
|
||||||
lib('glob');
|
lib('glob');
|
||||||
lib('deepExtend', 'deep-extend');
|
lib('moment');
|
||||||
})();
|
})();
|
||||||
|
|
106
server/mailTemplates/monitoring-confirmation.body.html
Normal file
106
server/mailTemplates/monitoring-confirmation.body.html
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<table border="0" cellpadding="10" cellspacing="0" bgcolor="#EDEDED">
|
||||||
|
<tr>
|
||||||
|
<td style="color: #444444; font-family: HelveticaNeue, 'Helvetica Neue', Helvetica, Arial, sans-serif;">
|
||||||
|
<p>
|
||||||
|
<strong style="font-size: 1.5em;">Hallo <%- node.nickname %>,</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
für einen Deiner Knoten wurde der automatisierte Versand von Status-E-Mails aktiviert. Um sicherzustellen, dass
|
||||||
|
Du wirklich der richtige Empfänger für diese E-Mails bist, bitten wir Dich, Deine E-Mail-Adresse durch einen
|
||||||
|
Klick auf den Bestätiguns-Link unten zu bestätigen. Erst danach wird der Versand von Status-E-Mails wirklich
|
||||||
|
stattfinden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="margin-top: 45px;">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table class="table" style="border: 3px dashed #666666; padding: 10px; background-color: #F5F5F5; font-size: 1.3em;">
|
||||||
|
<tr>
|
||||||
|
<th>Knoten</th>
|
||||||
|
<td><%- node.hostname %></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>Empfänger</th>
|
||||||
|
<td><a href="mailto:<%- node.email %>" style="color: #E5287A;"><%- node.email %></a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="margin-top: 45px; margin-bottom: 45px;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="font-size: 1.5em;">
|
||||||
|
<strong>
|
||||||
|
<a href="<%- confirmUrl %>" style="color: #E5287A;">» E-Mail-Adresse bestätigen «</a>
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Bei Fragen wende Dich gerne an
|
||||||
|
<a href="mailto:<%- community.contactEmail %>" style="color: #E5287A;"><%- community.contactEmail %></a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>
|
||||||
|
Viele Grüße<br />
|
||||||
|
Dein <%- community.name %>
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr style="border-top: 1px solid #333333; border-left: 0; border-right: 0; border-bottom: 0;" />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<em>
|
||||||
|
Möchtest Du keine Status-E-Mails zu Diesem Knoten mehr erhalten, so kannst Du den Versand
|
||||||
|
jederzeit <a href="<%- disableUrl %>" style="color: #E5287A;">deaktivieren</a>.
|
||||||
|
</em>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<em>
|
||||||
|
Alternativ kannst Du die Versandeinstellungen auch jederzeit unter
|
||||||
|
<a href="<%- editNodeUrl %>" style="color: #E5287A;"><%- editNodeUrl %></a>
|
||||||
|
ändern.
|
||||||
|
</em>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<em>
|
||||||
|
Bitte habe Verständnis dafür, dass das An- und Abschalten des Versands für jeden Deiner Knoten
|
||||||
|
einzeln erfolgt.
|
||||||
|
</em>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
server/mailTemplates/monitoring-confirmation.subject.txt
Normal file
1
server/mailTemplates/monitoring-confirmation.subject.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Bitte bestätige Deine E-Mail-Adresse
|
|
@ -15,6 +15,7 @@ require('./libs');
|
||||||
require('./utils/errorTypes');
|
require('./utils/errorTypes');
|
||||||
require('./utils/resources');
|
require('./utils/resources');
|
||||||
require('./utils/strings');
|
require('./utils/strings');
|
||||||
|
require('./utils/urlBuilder');
|
||||||
|
|
||||||
require('./resources/nodeResource');
|
require('./resources/nodeResource');
|
||||||
require('./resources/monitoringResource');
|
require('./resources/monitoringResource');
|
||||||
|
@ -26,8 +27,13 @@ require('./services/monitoringService');
|
||||||
require('../shared/validation/constraints');
|
require('../shared/validation/constraints');
|
||||||
require('./validation/validator');
|
require('./validation/validator');
|
||||||
|
|
||||||
require('./db/database').init(function () {
|
require('./jobs/scheduler');
|
||||||
angular.injector(['ffffng']).invoke(function (config, app, Router) {
|
|
||||||
|
var db = require('./db/database');
|
||||||
|
|
||||||
|
db.init(function () {
|
||||||
|
angular.injector(['ffffng']).invoke(function (config, app, Scheduler, Router) {
|
||||||
|
Scheduler.init();
|
||||||
Router.init();
|
Router.init();
|
||||||
|
|
||||||
app.listen(config.server.port, '::');
|
app.listen(config.server.port, '::');
|
||||||
|
|
|
@ -1,20 +1,185 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('ffffng')
|
angular.module('ffffng')
|
||||||
.service('MailService', function (Database, _) {
|
.service('MailService', function (Database, UrlBuilder, config, _, async, deepExtend, fs, moment) {
|
||||||
|
var MAIL_QUEUE_DB_BATCH_SIZE = 2;
|
||||||
|
var MAIL_QUEUE_MAX_PARALLEL_SENDING = 3;
|
||||||
|
|
||||||
|
var transporter = require('nodemailer').createTransport(deepExtend(
|
||||||
|
{},
|
||||||
|
config.server.email.smtp,
|
||||||
|
{
|
||||||
|
transport: 'smtp',
|
||||||
|
pool: true
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
var htmlToText = require('nodemailer-html-to-text').htmlToText;
|
||||||
|
transporter.use('compile', htmlToText({
|
||||||
|
tables: ['.table']
|
||||||
|
}));
|
||||||
|
|
||||||
|
function sendMail(options, callback) {
|
||||||
|
var templateBasePath = __dirname + '/../mailTemplates/' + options.email;
|
||||||
|
async.parallel({
|
||||||
|
subject: _.partial(fs.readFile, templateBasePath + '.subject.txt'),
|
||||||
|
body: _.partial(fs.readFile, templateBasePath + '.body.html')
|
||||||
|
},
|
||||||
|
function (err, templates) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = deepExtend(
|
||||||
|
{},
|
||||||
|
options.data,
|
||||||
|
{
|
||||||
|
community: config.client.community,
|
||||||
|
editNodeUrl: UrlBuilder.editNodeUrl()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
function render(field) {
|
||||||
|
console.log(field);
|
||||||
|
var rendered = _.template(templates[field].toString())(data);
|
||||||
|
console.log(rendered);
|
||||||
|
return rendered;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mailOptions;
|
||||||
|
try {
|
||||||
|
mailOptions = {
|
||||||
|
from: options.sender,
|
||||||
|
to: options.recipient,
|
||||||
|
subject: _.trim(render('subject')),
|
||||||
|
html: render('body')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return callback(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
transporter.sendMail(mailOptions, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPendingMailsBefore(beforeMoment, limit, callback) {
|
||||||
|
Database.all(
|
||||||
|
'SELECT * FROM email_queue WHERE modified_at < ? AND failures < ? ORDER BY id ASC LIMIT ?',
|
||||||
|
[beforeMoment.unix(), 5, limit], // TODO: retrycount
|
||||||
|
function (err, rows) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pendingMails;
|
||||||
|
try {
|
||||||
|
pendingMails = _.map(rows, function (row) {
|
||||||
|
return deepExtend(
|
||||||
|
{},
|
||||||
|
row,
|
||||||
|
{
|
||||||
|
data: JSON.parse(row.data)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return callback(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, pendingMails);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePendingMailFromQueue(id, callback) {
|
||||||
|
Database.run('DELETE FROM email_queue WHERE id = ?', [id], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementFailureCounterForPendingEmail(id, callback) {
|
||||||
|
var now = moment();
|
||||||
|
Database.run(
|
||||||
|
'UPDATE email_queue SET failures = failures + 1, modified_at = ? WHERE id = ?',
|
||||||
|
[now.unix(), id],
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendPendingMail(pendingMail, callback) {
|
||||||
|
console.log(pendingMail);
|
||||||
|
sendMail(pendingMail, function (err) {
|
||||||
|
if (err) {
|
||||||
|
// we only log the error and increment the failure counter as we want to continue with pending mails
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
return incrementFailureCounterForPendingEmail(pendingMail.id, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
return callback(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removePendingMailFromQueue(pendingMail.id, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enqueue: function (sender, recipient, email, data, callback) {
|
enqueue: function (sender, recipient, email, data, callback) {
|
||||||
if (!_.isPlainObject(data)) {
|
if (!_.isPlainObject(data)) {
|
||||||
return callback(new Error('Unexpected data: ' + data));
|
return callback(new Error('Unexpected data: ' + data));
|
||||||
}
|
}
|
||||||
Database.run(
|
Database.run(
|
||||||
'INSERT INTO email_queue (failures, sender, recipient, email, data) VALUES (?, ?, ?, ?, ?)',
|
'INSERT INTO email_queue ' +
|
||||||
|
'(failures, sender, recipient, email, data) ' +
|
||||||
|
'VALUES (?, ?, ?, ?, ?)',
|
||||||
[0, sender, recipient, email, JSON.stringify(data)],
|
[0, sender, recipient, email, JSON.stringify(data)],
|
||||||
function (err, res) {
|
function (err, res) {
|
||||||
debugger;
|
|
||||||
callback(err, res);
|
callback(err, res);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
sendPendingMails: function (callback) {
|
||||||
|
console.info('Start sending pending mails.');
|
||||||
|
|
||||||
|
var startTime = moment();
|
||||||
|
|
||||||
|
var sendNextBatch = function (err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
findPendingMailsBefore(startTime, MAIL_QUEUE_DB_BATCH_SIZE, function (err, pendingMails) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isEmpty(pendingMails)) {
|
||||||
|
console.info('Done sending pending mails.');
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachLimit(
|
||||||
|
pendingMails,
|
||||||
|
MAIL_QUEUE_MAX_PARALLEL_SENDING,
|
||||||
|
sendPendingMail,
|
||||||
|
sendNextBatch
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sendNextBatch(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('ffffng')
|
angular.module('ffffng')
|
||||||
.service('NodeService', function (config, _, crypto, fs, glob, MailService, Strings, ErrorTypes) {
|
.service('NodeService', function (
|
||||||
|
config,
|
||||||
|
_,
|
||||||
|
crypto,
|
||||||
|
fs,
|
||||||
|
glob,
|
||||||
|
MailService,
|
||||||
|
Strings,
|
||||||
|
ErrorTypes,
|
||||||
|
UrlBuilder
|
||||||
|
) {
|
||||||
var linePrefixes = {
|
var linePrefixes = {
|
||||||
hostname: '# Knotenname: ',
|
hostname: '# Knotenname: ',
|
||||||
nickname: '# Ansprechpartner: ',
|
nickname: '# Ansprechpartner: ',
|
||||||
|
@ -107,7 +117,7 @@ angular.module('ffffng')
|
||||||
fs.unlinkSync(file);
|
fs.unlinkSync(file);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
return callback({data: 'Could not remove old node data.', type: ErrorTypes.internalError});
|
return callback({data: 'Could not remove old node data.', type: ErrorTypes.internalError});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -121,7 +131,7 @@ angular.module('ffffng')
|
||||||
fs.writeFileSync(filename, data, 'utf8');
|
fs.writeFileSync(filename, data, 'utf8');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
return callback({data: 'Could not write node data.', type: ErrorTypes.internalError});
|
return callback({data: 'Could not write node data.', type: ErrorTypes.internalError});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +148,7 @@ angular.module('ffffng')
|
||||||
fs.unlinkSync(files[0]);
|
fs.unlinkSync(files[0]);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
return callback({data: 'Could not delete node.', type: ErrorTypes.internalError});
|
return callback({data: 'Could not delete node.', type: ErrorTypes.internalError});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,13 +207,12 @@ angular.module('ffffng')
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMonitoringConfirmationMail(node, nodeSecrets, callback) {
|
function sendMonitoringConfirmationMail(node, nodeSecrets, callback) {
|
||||||
var monitoringQueryString = '?mac=' + node.mac + '&token=' + nodeSecrets.monitoringToken;
|
var confirmUrl = UrlBuilder.monitoringConfirmUrl(node, nodeSecrets);
|
||||||
var confirmUrl = config.server.baseUrl + '/#!/monitoring/confirm' + monitoringQueryString;
|
var disableUrl = UrlBuilder.monitoringDisableUrl(node, nodeSecrets);
|
||||||
var disableUrl = config.server.baseUrl + '/#!/monitoring/disable' + monitoringQueryString;
|
|
||||||
|
|
||||||
MailService.enqueue(
|
MailService.enqueue(
|
||||||
config.server.email.from,
|
config.server.email.from,
|
||||||
node.email,
|
node.nickname + ' <' + node.email + '>',
|
||||||
'monitoring-confirmation',
|
'monitoring-confirmation',
|
||||||
{
|
{
|
||||||
node: node,
|
node: node,
|
||||||
|
@ -212,7 +221,7 @@ angular.module('ffffng')
|
||||||
},
|
},
|
||||||
function (err) {
|
function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
return callback({data: 'Internal error.', type: ErrorTypes.internalError});
|
return callback({data: 'Internal error.', type: ErrorTypes.internalError});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
39
server/utils/urlBuilder.js
Normal file
39
server/utils/urlBuilder.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('ffffng').factory('UrlBuilder', function (_, config) {
|
||||||
|
function formUrl(route, queryParams) {
|
||||||
|
var url = config.server.baseUrl;
|
||||||
|
if (route || queryParams) {
|
||||||
|
url += '/#!/';
|
||||||
|
}
|
||||||
|
if (route) {
|
||||||
|
url += route;
|
||||||
|
}
|
||||||
|
if (queryParams) {
|
||||||
|
url += '?';
|
||||||
|
url += _.join(
|
||||||
|
_.map(
|
||||||
|
queryParams,
|
||||||
|
function (value, key) {
|
||||||
|
return encodeURIComponent(key) + '=' + encodeURIComponent(value);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'&'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
editNodeUrl: function () {
|
||||||
|
return formUrl('update');
|
||||||
|
},
|
||||||
|
|
||||||
|
monitoringConfirmUrl: function (node, nodeSecrets) {
|
||||||
|
return formUrl('monitoring/confirm', { mac: node.mac, token: nodeSecrets.monitoringToken });
|
||||||
|
},
|
||||||
|
monitoringDisableUrl: function (node, nodeSecrets) {
|
||||||
|
return formUrl('monitoring/disable', { mac: node.mac, token: nodeSecrets.monitoringToken });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
Loading…
Reference in a new issue