I am trying to make a server that listens to changes made in the firebase database and then sends an email to some email address. I manage to implement firebase and sendgrid, I already tested by adding some entry to the database and also sending a test mail. So sendgrid and firebase are working. The problem here relies on how I manage to send the emails, every time I open the app it sends the email.
So here comes the issue, If every time the apps opens it executes the code in the js file. When I add the code to listen to a child added event in the database it will get called every time I open the app, so multiple listener will be active and I assume multiple emails will be send regarding one single event.
So I'm clueless , is there anyway that this is only called once? or I should not open the "app" more than one time? or am I not deploying the server the right way?
My objective is that once I deploy the server it will automatically do whats its written, without the need to actually open the "app" in heroku, or at least that when the app is open the code do not get called again, just once when deployed.
Here is the code for the server.js
var firebase = require('firebase');
var helper = require('sendgrid').mail;
firebase.initializeApp({
serviceAccount: "./DB-A2312SDA.json",
databaseURL: "https://DATABASENAME.firebaseio.com/"
});
// Email
var from_email = new helper.Email('example#example.com');
var to_email = new helper.Email('example#gmail.com');
var subject = 'Hello World from the SendGrid Node.js Library!';
var content = new helper.Content('text/plain', 'Hello, Email!');
var mail = new helper.Mail(from_email, subject, to_email, content);
var sg = require('sendgrid')(process.env.SG_API_KEY);
var request = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: mail.toJSON(),
});
sg.API(request, function(error, response) {
console.log(response.statusCode);
console.log(response.body);
console.log(response.headers);
});
This is the package.json
{
"name": "server_test",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"engines": {
"node": "6.9.0"
},
"author": "",
"license": "ISC",
"dependencies": {
"firebase": "^3.5.1",
"sendgrid": "^4.7.0"
}
}
and the procfile
web: node server.js
Typically in such a scenario you'll have a node in your database that indicates the addresses that you need to send a message to. For example, if you want to send a welcome message to people that have signed up for something, you could model that as:
welcomeMessageQueue
$pushid
email: "rialcom#company.com"
firstName: "Ralcom"
Not you can attach a listener in your node script that:
for each message in this queue
sends a message to that email address
and then removes the item from the queue
The important thing here is that you remove the item from the queue once you've sent the message. That way, the message will only be sent once.
A simple way to do this would be:
var ref = firebase.database().ref();
var queue = ref.child('welcomeMessageQueue');
queue.on('child_added', function(snapshot) {
var message = snapshot.val();
var sg = require('sendgrid')(process.env.SG_API_KEY);
var request = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: mail.toJSON(),
});
sg.API(request)
.then(function(response) {
snapshot.ref.remove();
});
})
For a more scalable way to do this, have a look at firebase-queue.
For a simple tutorial that uses this approach, see this blog post on sending push notifications.
Related
I created the "mission" collection. I want to send an email to a personalized recipient for each new recording on the mission table.
According to the Directus documentation, I saw that this is possible via webHooks.
enter link description here
However, I don't quite understand the logic. Especially since in the Directus administration interface, there is a page to add webhooks and link them to the collection concerned.
Can you tell me where I should start to achieve my POC.
I also put some screenshots on the architecture of my app, you can tell me if this is really how it should be or not. I have doubts.
{
"name": "test1-directus",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "directus start"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"directus": "^9.0.0-rc.91",
"mysql": "^2.18.1",
"nodemailer": "^6.6.3"
}
}
I created a project with the command: npx create-directus-project test1directus
My project is running on port 8055 with a reverse proxy setting on nginx.
Is everything OK or did I miss a step?
Thank you in advance for your help.
I found this example to put in: extensions / hooks / sync-with-external / index.js
After several modifications, this error persists on my writing:
An error was thrown while executing hook "items.create"
Cannot destructure property 'mission' of 'undefined' as it is undefined.
The console.log doesn't show me anything.
const axios = require("axios");
module.exports = function registerHook({ services, exceptions }) {
const { MailService } = services;
const { ServiceUnavailableException, ForbiddenException } = exceptions;
return {
// Force everything to be admin-only at all times
"items.*": async function ({ item, accountability }) {
if (accountability.admin !== true) throw new ForbiddenException();
},
// Sync with external recipes service, cancel creation on failure
"items.create": async function (input, { mission, schema }) {
console.log(items);
if (mission !== "recipes") return input;
const mailService = new MailService({ schema });
try {
await axios.post("https://example.com/items", input);
await mailService.send({
to: "pseudo.pseudo#gmail.com",
template: {
name: "item-created",
data: {
collection: mission,
},
},
});
} catch (error) {
throw new ServiceUnavailableException(error);
}
input[0].syncedWithExample = true;
return input;
},
};
};
You can now use Directus Flows from Settings > Flows.
Read the docs here: https://docs.directus.io/configuration/flows
I am new to node-apn. I have implemented it in nodejs application. Below is my code.
var APN = require('apn')
var apnProvider = new APN.Provider({
token: {
key: "PATH_TO_FILE",
keyId: "KEY",
teamId: "TEAM"
},
production: false
});
module.exports = {
send: function (tokens, message, callBackFn) {
var note = new APN.Notification({
alert: "Breaking News: I just sent my first Push Notification",
});
// The topic is usually the bundle identifier of your application.
note.topic = "BUNDLE";
console.log(`Sending: ${note.compile()} to ${tokens}`);
service.send(note, tokens).then(callBackFn);
}
};
So in some documentation it says we should shutdown apnProvider.
So my question is should i create apnProvider globally (like i have done)?
OR should i create per send request (inside send function) & call shutdown after send notification.
I tried reading online. But i couldn't find any example like my requirements.
Most node.js botframework examples involve the notion of the waterfall dialog model, which is a great way to structure conversations, but we don't need that since we have our own chat system. We simply receive messages via the webhook, process them and respond without the dialog system.
Also, and now we are getting to the heart of the matter;), the examples I have seen communicate back to the botframework in the web context:
var connector = new builder.ChatConnector({config});
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());
bot.dialog('/', (session, args) => {
session.sendTyping();
session.send( 'Echo'+ session.message.text);
})
The above example simply responds with an 'echo' and before that set's the typing status.
Our system works asynchronous, the way we currently work is by bypassing the connector listen and dialog scheme. A simplified example of how we queue a botframework message.
server.post('/api/messages/:name', (req, res, next)=>{
queue.post('botframework',req.params.name,req.body)
.then(()=>res.sendStatus(200))
});
In the queue processing, we construct the botframework objects:
//the ':name' from the snippet above is used to identify
//the bot a retrieve credentials
const connector = new botbuilder.ChatConnector({
appId: bot.properties.botframework_id.value.appId,
appPassword: bot.properties.botframework_id.value.appPassword
});
const username=message.from.name.replace(/[\s-;:"';$]/g,"_")+"_skype";
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let msg = new botbuilder.Message();
//processing...
//create the outgoing message...
bot.send(msg);
The problem here for us is that we simply don't know how to create a session object from a raw webhook message which is needed for the typing indicator and to ensure the order of messages when many messages are sent in quick succession.
Here is what we wish to accomplish:
//the ':name' from the snippet above is used to identify
//the bot a retrieve credentials
//the context is non HHTP
const connector = new botbuilder.ChatConnector({
appId: bot.properties.botframework_id.value.appId,
appPassword: bot.properties.botframework_id.value.appPassword
});
const username=message.from.name.replace(/[\s-;:"';$]/g,"_")+"_skype";
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let session = bot.createSession();
session.sendTyping();
let message = getNextMessageFromChatServer(message);
session.send(message);
//more messages to be send?
//....
So the question: How can we create a session object from the raw data send to the botframework webhook?
You should be able to build a session using loadSession
var address = {
"channelId": message.channelId,
"user": message.from,
"bot": message.recipient,
"serviceUrl": message.serviceUrl,
"useAuth": true
};
let bot = new botbuilder.UniversalBot(connector);
let msg = new botbuilder.Message();
bot.loadSession(address, (err, session) => {
session.send("Message");
})
I'm using Admin SDK for node.js for sending the push notifications. Followed the tutorial and initialized the multiple projects with like examples given with this link.
I need to know how to send push notifications with two projects using with node.js. Used below methods for sending notifications two projects based its working with default project but another project getting error like below
exports.send_test_mailer = function(req, res) {
// Default project
var registrationToken = ["f-vRsDouUFQ:APA91bGktVzu3WjKGqeXqdiYPI8B0lQXs34TkJS4p7LaMiFGfp5LdfB1ZjEhO3CY5ci92apqgt1hEJY0ml11C4hxYUaPfDl7PeDHhcmDGur0JUx5l3M2mLEj30epwRBWVsE4xMSTls4f"];
var payload = {
notification: {
title: "driver app",
body: "driver app push notfications on the day."
},
data: {
score: "850",
time: "2:45"
}
};
firebaseAdmin.messaging().sendToDevice(registrationToken, payload)
.then(function(response) {
console.log("Successfully sent message driver:", JSON.stringify(response));
})
.catch(function(error) {
console.log("Error sending message driver:", JSON.stringify(error));
});
// Second project
var registrationTokens = ["dzXRXUMIB5w:APA91bHSArtroO8M33IHxaslQTugTcEzJcfkbsXEhwbXbvVzBws-aqG4aqKNr37j8WpZev7lolX7cFQlAKYZ1QV_EgC6zTGeT41n3lvSpcDyBg6t4SZZaoPe7nUO9sbdcXA2KDguxAbk"];
var payloads = {
notification: {
title: "customer app",
body: "customer app push notfications on the day."
},
data: {
score: "850",
time: "2:45"
}
};
firebaseAdmin.messaging().sendToDevice(registrationTokens, payloads)
.then(function(response) {
console.log("Successfully sent message customer:", JSON.stringify(response));
})
.catch(function(error) {
console.log("Error sending message customer:", JSON.stringify(error));
});
};
Error
{"results":[{"error":{"code":"messaging/registration-token-not-registered","message":"The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages."}},{"error":{"code":"messaging/mismatched-credential","message":"The credential used to authenticate this SDK does not have permission to send messages to the device corresponding to the provided registration token. Make sure the credential and registration token both belong to the same Firebase project."}}],"canonicalRegistrationTokenCount":0,"failureCount":2,"successCount":0,"multicastId":9014981858701063000}
Here is my answer
var ServiceAccount = require("./path your default app file.json");
var ServiceAccount1 = require("./path your second app file.json");
var serviceAppConfig = {
credential: firebaseAdmin.credential.cert(ServiceAccount),
databaseURL: "https://your firebase default app url"
};
// Initialize the default app
var serviceApp = firebaseAdmin.initializeApp(serviceAppConfig);
//console.log(serviceApp.name); // "[DEFAULT]"
// Retrieve services via the defaultApp variable...
var serviceAuth = serviceApp.auth();
var serviceDatabase = serviceApp.database();
// Get the Messaging service for the default app
global.serviceMessaging = firebaseAdmin.messaging();
var service1AppConfig = {
credential: firebaseAdmin.credential.cert(ServiceAccount1),
databaseURL: "https://your firebase url second app"
};
// Initialize another app with a different config
var service1App = firebaseAdmin.initializeApp(service1AppConfig, "App2 name");
// Use the otherApp variable to retrieve the other app's services
var service1Auth = service1App.auth();
var service1Database = service1App.database();
// Get the Messaging service for a given app
global.service1Messaging = firebaseAdmin.messaging(service1App);
I am trying to use the Object Storage Service at IBM Bluemix Cloud, but I can't send images from my nodejs server. How can I do this? Follow my server code:
unirest
.post(MY_CONTAINER + new_fname)
.headers({'Content-Type': 'multipart/form-data', 'X-Auth-Token': token})
.field({ 'max_file_count': 1 })
.field({ 'max_file_size': 1 })
.attach({ 'file': file.originalname, 'relative file': streamFile })
.end(function (resp) {
//response
console.log(resp.status);
console.log(resp.body);
});
The main problem is to find the right way to send an image (png or jpg) to the bluemix storage using the API (I've aready uploaded it to our server).
I used the pkgcloud-bluemix-objectstorage to fix the OpenStack authentication bug that previously used the v2 and has been changed to use the v3.
here's the link
Bluemix - object storage - node.js - pkgcloud - openstack returns 401
the #libik writes an example.
var pkgcloud = require('pkgcloud-bluemix-objectstorage');
var fs = require('fs');
// Create a config object
var config = {};
// Specify Openstack as the provider
config.provider = "openstack";
// Authentication url
config.authUrl = 'https://identity.open.softlayer.com/';
config.region= 'dallas';
// Use the service catalog
config.useServiceCatalog = true;
// true for applications running inside Bluemix, otherwise false
config.useInternal = false;
// projectId as provided in your Service Credentials
config.tenantId = '234567890-0987654';
// userId as provided in your Service Credentials
config.userId = '098765434567890';
// username as provided in your Service Credentials
config.username = 'admin_34567890-09876543';
// password as provided in your Service Credentials
config.password = 'sdfghjklkjhgfds';
**//This is part which is NOT in original pkgcloud. This is how it works with newest version of bluemix and pkgcloud at 22.12.2015.
//In reality, anything you put in this config.auth will be send in body to server, so if you need change anything to make it work, you can. PS : Yes, these are the same credentials as you put to config before.
//I do not fill this automatically to make it transparent.
config.auth = {
forceUri : "https://identity.open.softlayer.com/v3/auth/tokens", //force uri to v3, usually you take the baseurl for authentication and add this to it /v3/auth/tokens (at least in bluemix)
interfaceName : "public", //use public for apps outside bluemix and internal for apps inside bluemix. There is also admin interface, I personally do not know, what it is for.
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"id": "098765434567890", //userId
"password": "sdfghjklkjhgfds" //userPassword
}
}
},
"scope": {
"project": {
"id": "234567890-0987654" //projectId
}
}
};**
//console.log("config: " + JSON.stringify(config));
// Create a pkgcloud storage client
var storageClient = pkgcloud.storage.createClient(config);
// Authenticate to OpenStack
storageClient.auth(function (error) {
if (error) {
console.error("storageClient.auth() : error creating storage client: ", error);
} else {
//OK
var new_fname = dir + "__" + file.originalname;
var readStream = fs.createReadStream('uploads/' + file.filename);
var writeStream = storageClient.upload({
container: 'chat-files',
remote: new_fname
});
writeStream.on('error', function(err) {
// handle your error case
console.log("concluido o upload com erro!");
console.log(err);
});
writeStream.on('success', function(file) {
// success, file will be a File model
console.log("concluido o upload com sucesso!");
});
readStream.pipe(writeStream);
}
});
#JeffSloyer wrote a Node.js sample application to upload files to an Object Storage instance in Bluemix.
You can find the code here:
https://github.com/IBM-Bluemix/node-file-upload-swift
The code above fails to authenticate using Open Stack Swift v3, so I made a modification to the skipper-openstack module to use pkgcloud-bluemix-objectstorage:
https://github.com/adasilva70/skipper-openstack.git#adasilva70-patch-1
Clone Jeff's repository and follow the instructions in the README.md file to run the code. Make sure you modify the package.json file with the one below to get my changes:
{
"name": "node-file-upload-swift",
"version": "0.0.0",
"dependencies": {
"bower": "^1.7.1",
"cf-deployment-tracker-client": "*",
"cfenv": "^1.0.3",
"dotenv": "^1.2.0",
"express": "~4.x",
"skipper": "^0.5.8",
"skipper-openstack": "git+https://github.com/adasilva70/skipper-openstack.git#adasilva70-patch-1",
"stream": "0.0.2",
"underscore": "^1.8.3"
},
"main": "server.js",
"scripts": {
"start": "node server.js",
"postinstall": "bower install --allow-root --config.interactive=false"
}
}