So im trying to deploy a simple twitter like app to HEROKU or MongoDB and i'm currently nailing either. For mongodb I get one out of two outcomes, either a internal server error or the actual code displaying on the browser instead of the app. Since I have two separate folders for each implementation i'm going to post subsequently.
MONGODB
Index.js (This is the server side node code)
const express = require('express');
//Cors permite que cualquiera se comunique con el server.
const cors = require('cors');
const monk = require('monk');
const Filter = require('bad-words');
const rateLimit = require('express-rate-limit');
const filter = new Filter();
const app = express();
const db = monk(process.env.MONGO_URI || 'localhost/meower');
const mews = db.get('mews');
//ORDER MATTERS, WHAT IS FIRST GETS EXECUTED FIRST
app.enable('trust proxy');
app.use(cors());
//any incoming request that is JSON will pass
app.use(express.json());
//server, when you get a request run this function.
app.get('/',(request,response) => {
res.json({
message: 'Meower!'
});
});
app.get('/mews', (req,res) => {
mews
.find()
.then(mews => {
res.json(mews);
});
});
function isvalidmew(mew){
return mew.name && mew.name.toString().trim() !== '' &&
mew.content && mew.content.toString().trim() !== '';
}
//limit the submit rate
app.use(rateLimit({
windowMs: 30 * 1000,
max: 2
}));
//this will wait for incoming data and insert in database
app.post('/mews', (req,res) => {
if(isvalidmew(req.body)){
const mew = {
name: filter.clean(req.body.name.toString()),
content: filter.clean(req.body.content.toString()),
created: new Date()
};
mews
.insert(mew)
.then(createdMew => {
res.json(createdMew);
});
} else {
res.status(422);
res.json({
message:'Hey! Name and Content are required!'
});
}
});
//abre el server en el puerto 5000
app.listen(5000, () => {
console.log('Listening on http://localhost:5000');
});
Client.js
const form = document.querySelector('form');
const loadingElement = document.querySelector('.loading');
const mewsElement = document.querySelector('.mews');
const API_URL = 'http://localhost:5000/mews';
loadingElement.style.display = '';
console.log('hola')
listallmews();
form.addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(form);
//We grab the stuff from the form
const name = formData.get('name');
const content = formData.get('content');
//We put it in an object
const mew = {
name,
content
};
//We send the data to the server
form.style.display = 'none';
loadingElement.style.display = '';
fetch(API_URL, {
method: 'POST',
body: JSON.stringify(mew),
headers : {
'content-type':'application/json'
}
}).then(response => response.json())
.then(createdMew => {
form.reset();
setTimeout(() => {
form.style.display = '';
},30000);
listallmews();
});
});
function listallmews(){
mewsElement.innerHTML = '';
fetch(API_URL)
.then(response => response.json())
.then(mews => {
console.log(mews);
mews.reverse();
mews.forEach(mew =>{
const div = document.createElement('div');
const header = document.createElement('h3');
header.textContent= mew.name
const contents = document.createElement('p')
contents.textContent= mew.content;
const date = document.createElement('small');
date.textContent = new Date(mew.created);
div.appendChild(header);
div.appendChild(contents);
div.appendChild(date);
mewsElement.appendChild(div);
});
loadingElement.style.display = 'none'
});
}
now.json
{
"name": "camitter-api",
"version": 2,
"builds": [
{
"src": "index.js",
"use": "#now/node-server"
}
],
"routes": [
{ "src": "/.*", "dest": "index.js" }
],
"env": {
"MONGO_URI": "#camitter-db"
}
}
and package.json
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
"keywords": [],
"author": "CJ R. <cj#null.computer> (https://w3cj.now.sh)",
"license": "MIT",
"dependencies": {
"bad-words": "^1.6.3",
"cors": "^2.8.4",
"express": "^4.16.3",
"express-rate-limit": "^3.1.1",
"monk": "^6.0.6",
"morgan": "^1.9.1"
},
"devDependencies": {
"nodemon": "^1.18.4"
}
}
And this is the terminal output on implementation. Currently the link shows "internal server error"
Alejandro#DESKTOP-LOJH5G7 MINGW64 ~/Desktop/Programacion/Meower
$ now secrets add camisite mongodb+srv://alenieto:myactualpassword#camisite-irtu2.mongodb.net/test?retryWrites=true&w=majority
[1] 444
Alejandro#DESKTOP-LOJH5G7 MINGW64 ~/Desktop/Programacion/Meower
$ Now CLI 18.0.0
Success! Secret camisite added under alenieto97 [709ms]
$ now -e MONGO_URI=#camisite
Now CLI 18.0.0
? Set up and deploy “~\Desktop\Programacion\Meower”? [Y/n] y
? Which scope do you want to deploy to? Alejandro Nieto
? Found project “alenieto97/meower”. Link to it? [Y/n] n
? Link to different existing project? [Y/n] n
? What’s your project’s name? camisite
? In which directory is your code located? ./
No framework detected. Default project settings:
- Build Command: `npm run now-build` or `npm run build`
- Output Directory: `public` if it exists, or `.`
� Inspect: https://zeit.co/alenieto97/camisite/ei55o9z4q [2s]
✅ Production: https://camisite.now.sh [copied to clipboard] [5s]
� Deployed to production. Run `now --prod` to overwrite later (https://zeit.ink/2F).
� To change the domain or build command, go to https://zeit.co/alenieto97/camisite/settings
[1]+ Done now secrets add camisite mongodb+srv://alenieto:lapata97#camisite-irtu2.mongodb.net/test?retryWrites=true
Alejandro#DESKTOP-LOJH5G7 MINGW64 ~/Desktop/Programacion/Meower
$ cd server
Alejandro#DESKTOP-LOJH5G7 MINGW64 ~/Desktop/Programacion/Meower/server
$ now -e MONGO_URI=#camisite
Now CLI 18.0.0
❗️ The `name` property in now.json is deprecated (https://zeit.ink/5F)
� Inspect: https://zeit.co/alenieto97/camitter/6b76zrggu [3s]
✅ Preview: https://camitter.alenieto97.now.sh [copied to clipboard] [20s]
� To deploy to production (camitter.now.sh), run `now --prod`
❗️ Zero-configuration deployments are recommended instead of a `builds` property in `now.json`. The "Build and Development Settings" in your Project will not apply.
Alejandro#DESKTOP-LOJH5G7 MINGW64 ~/Desktop/Programacion/Meower/server
HEROKU
Here i'm pretty sure im doing something wrong on the index.js or client.js or both. I saw tons of guides and have all the files necesary. When I deploy, the app simply doesn't work. Since I actually tried to adapt the code to work on Heroku I'm pretty sure the problem lies in the code itself. This is The folder:
node_modules
.gitignore
index.js
package_lock.json
client.js
favicon.ico
index.html
loading.gif
styles.css
Procfile
Index.js
const express = require('express');
//Cors permite que cualquiera se comunique con el server.
const cors = require('cors');
const Filter = require('bad-words');
const rateLimit = require('express-rate-limit');
const { Pool, Client } = require('pg');
const port = process.env.PORT;
const connectionString = 'postgres://gvvsunuvtdhxpq:e9d3239ab17ea6f38d0b6303dee62b7704b37574e5eb2783ca7edb868cc7192a#ec2-18-235-20-228.compute-1.amazonaws.com:5432/d7df9kofqifk5b'
const pool = new Pool({
connectionString: connectionString,
})
const filter = new Filter();
const app = express();
//ORDER MATTERS, WHAT IS FIRST GETS EXECUTED FIRST
app.enable('trust proxy');
app.use(cors());
//any incoming request that is JSON will pass
app.use(express.json());
//server, when you get a request run this function.
app.get('/',(request,response) => {
res.json({
message: 'Meower!'
});
});
app.get('/mews', async (req, res) => {
try {
const client = await pool.connect()
const result = await client.query('SELECT * FROM test_table');
const results = { 'results': (result) ? result.rows : null};
res.render('pages/mews', results );
client.release();
} catch (err) {
console.error(err);
res.send("Error " + err);
}
})
function isvalidmew(mew){
return mew.name && mew.name.toString().trim() !== '' &&
mew.content && mew.content.toString().trim() !== '';
}
//limit the submit rate
app.use(rateLimit({
windowMs: 30 * 1000,
max: 2
}));
//this will wait for incoming data and insert in database
app.post('/mews', (req,res) => {
if(isvalidmew(req.body)){
const mew = {
name: filter.clean(req.body.name.toString()),
content: filter.clean(req.body.content.toString()),
created: new Date()
};
mews
.insert(mew)
.then(createdMew => {
res.json(createdMew);
});
} else {
res.status(422);
res.json({
message:'Hey! Name and Content are required!'
});
}
});
app.listen(port, () => {
console.log('Listening on PORT');
});
Client.js
const form = document.querySelector('form');
const loadingElement = document.querySelector('.loading');
const mewsElement = document.querySelector('.mews');
const API_URL = 'https://camisite.herokuapp.com/mews';
loadingElement.style.display = '';
listallmews();
form.addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(form);
//We grab the stuff from the form
const name = formData.get('name');
const content = formData.get('content');
//We put it in an object
const mew = {
name,
content
};
//We send the data to the server
form.style.display = 'none';
loadingElement.style.display = '';
fetch(API_URL, {
method: 'POST',
body: JSON.stringify(mew),
headers : {
'content-type':'application/json'
}
}).then(response => response.json())
.then(createdMew => {
form.reset();
setTimeout(() => {
form.style.display = '';
},30000);
listallmews();
});
});
function listallmews(){
mewsElement.innerHTML = '';
fetch(API_URL)
.then(response => response.json())
.then(mews => {
console.log(mews);
mews.reverse();
mews.forEach(mew =>{
const div = document.createElement('div');
const header = document.createElement('h3');
header.textContent= mew.name
const contents = document.createElement('p')
contents.textContent= mew.content;
const date = document.createElement('small');
date.textContent = new Date(mew.created);
div.appendChild(header);
div.appendChild(contents);
div.appendChild(date);
mewsElement.appendChild(div);
});
loadingElement.style.display = 'none'
});
}
Procfile
web: node index.js
Package.json
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
"keywords": [],
"author": "CJ R. <cj#null.computer> (https://w3cj.now.sh)",
"license": "MIT",
"dependencies": {
"bad-words": "^1.6.3",
"cors": "^2.8.5",
"express": "^4.16.3",
"express-rate-limit": "^3.1.1",
"monk": "^6.0.6",
"morgan": "^1.9.1",
"pg": "^7.18.2"
},
"devDependencies": {
"nodemon": "^1.18.4"
}
}
I appreciate a lot any help to deploy on any of the two platforms, im trying to make the most out of this quarantine and have been trying to solve this for twenty hours straight. Cheers to any isolated folks!
Change index.js file
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Our app is running on port ${ PORT }`);
});
You can also refer https://help.heroku.com/P1AVPANS/why-is-my-node-js-app-crashing-with-an-r10-error
Related
I know this and this post both have similar titles but the solution to both is not working for me.
As you can see in my code below I've used Intents.FLAGS.GUILD_MESSAGES to create the client variable but it doesn't give any output in my console when I do the /ping command that I made. /ping however is available in when typing "/" in chat.
const { clientId, token, testGuildId } = require('./config.json');
const Discord = require('discord.js');
const fs = require('fs');
const { REST } = require('#discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const client = new Discord.Client({
intents: [
Discord.Intents.FLAGS.GUILDS,
Discord.Intents.FLAGS.GUILD_MESSAGES,
Discord.Intents.FLAGS.DIRECT_MESSAGES,
],
});
const commandFiles = fs.readdirSync(`./Commands/commandScripts`).filter(file => file.endsWith(".js"));
//for
const commandsArray = [];
client.commandsArray = new Discord.Collection();
for (const file of commandFiles){
const newCommand = require(`./Commands/commandScripts/${file}`);
commandsArray.push(newCommand.data.toJSON());
client.commandsArray.set(newCommand.data.name, newCommand);
}
client.once('ready', async () => {
const rest = new REST({
version: "9"
}).setToken(token);
(async () => {
try {
if(process.env.ENV === "production"){
await rest.put(Routes.applicationCommands(clientId), {
body: commandsArray,
});
console.log("Successfully registered commands globally.");
} else {
var temp = await rest.put(Routes.applicationGuildCommands(clientId, testGuildId), {
body: commandsArray,
});
console.log(temp);
console.log("Successfully registered commands locally.");
}
} catch (err) {
if(err) console.error(err);
}
})();
console.log(`[${time}] *** Bot Is Ready On Discord!`);
});
client.on('interactionCreate', async interaction => {
console.log(interaction);
});
client.on("messageCreate", (message) => {
console.log(message.content);
})
client.login(token);
My package.json:
{
"name": "excavator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"#discordjs/builders": "^0.12.0",
"#discordjs/opus": "^0.3.3",
"#discordjs/rest": "^0.3.0",
"axios": "^0.21.1",
"discord-api-types": "^0.27.3",
"discord.js": "^12.5.3",
"ffmpeg-static": "^4.2.7",
"fs": "0.0.1-security",
"mongoose": "^5.13.14",
"path": "^0.12.7",
"simple-youtube-api": "^5.2.1",
"yt-search": "^2.5.1",
"ytdl-core": "^4.4.5",
"ytdl-core-discord": "^1.2.5"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ExcanatorGames",
"license": "ISC"
}
Figured out the problem. I needed to uninstall discord.js and reinstall discord.js
I'm using MERN Stack and everything was working fine until I made some changes to the UI (Moving code to different components, changing styles,...).
I didn't change any code in the Axios request and only this POST request doesn't work, the other requests work normally.
I have already setup CORS in my backend
I can access my-project.herokuapp.com/insert link and there's no error in the Heroku logs. No error thrown in the client or server terminal.
When I click on the Add To List button in the form, the addToList function which contains the Axios POST request doesn't send the data to the database like it used to.
After 30 seconds - this error appears:
Please help me understand what is going on and how to fix this. I have looked for other solutions but I don't know how to apply to my case.
Thank you! :)
Here's my code:
addToList function on the client-side:
const [foodName, setFoodName] = useState('')
const [isVegetarian, setIsVegetarian] = useState('')
const [priceRange, setPriceRange] = useState('$')
const [foodUrl, setFoodUrl] = useState('')
const [foodList, setFoodList] = useState([])
const addToList = async (event) => {
event.preventDefault()
try {
await Axios.post(
"https://my-project.herokuapp.com/insert",
{
foodName: foodName,
isVegetarian: isVegetarian,
priceRange: priceRange,
foodUrl: foodUrl,
}
)
.then(() => {
setFoodName('')
setIsVegetarian('')
setPriceRange('$')
setFoodUrl('')
})
} catch(err) {
console.error(`The error is ${err}`)
}
}
Dinner.js - Mongoose Schema
const mongoose = require('mongoose')
const DinnerSchema = new mongoose.Schema({
foodName: {
type: String,
required: true,
},
isVegetarian: {
type: String,
required: true,
},
priceRange: {
type: String,
required: true,
},
foodUrl: {
type: String,
required: true,
}
})
const Dinner = mongoose.model("dinners", DinnerSchema)
module.exports = Dinner
Server's index.js and the endpoint that doesn't work:
const express = require("express")
const mongoose = require("mongoose")
const cors = require('cors')
const app = express()
require("dotenv").config()
const DinnerModel = require('./models/Dinner')
app.use(express.json())
app.use(cors())
// Connect to MongoDB
mongoose.connect(
'mongodb+srv://linktoDB',
{
useNewUrlParser: true,
}
)
// Create:
app.post("/insert", async (req, res) => {
const foodName = req.body.foodName
const isVegetarian = req.body.isVegetarian
const priceRange = req.body.priceRange
const foodUrl = req.body.foodUrl
const dinner = new DinnerModel(
{
foodName: foodName,
isVegetarian: isVegetarian,
priceRange: priceRange,
foodUrl: foodUrl
}
)
try {
await dinner.save()
res.send("Inserted successfully")
} catch(err) {
console.log(err)
}
})
// Creating a port:
app.listen(process.env.PORT || 3001, () => {
console.log("Server is connected.")
})
My backend's package.json:
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"engines": {
"node": "12.20.1"
},
"scripts": {
"start": "node index.js",
"devStart": "nodemon index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^10.0.0",
"express": "^4.17.2",
"mongoose": "^6.1.3",
"nodemon": "^2.0.15",
"validator": "^13.7.0"
}
}
UPDATE 1
I added this code and it still doesn't work:
const corsOptions = {
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204,
}
app.use(cors(corsOptions))
Here's the preflight request and response when I try to add the document. One with status 204 and one with status 503:
https://i.stack.imgur.com/v1IcS.png
https://i.stack.imgur.com/AzH8d.png
UPDATE 2
I found out what the problem is when I check the payload. Please check my answer below.
Thanks everyone for helping me with this!
I have found what caused the 503 Error when checking for the Payload of the POST request in the Network tab.
It was receiving an empty string for my isVegetarian value.
That empty string happened because I changed the isVegetarian schema type from Boolean to String but forgot to change the initial state to a specific value:
const [isVegetarian, setIsVegetarian] = useState('')
In the Schema, I set isVegetarian to required: true so it won't accept empty strings.
-> So if you have the CORS config right and still have the same 503 error. Maybe check for the Network tab for preflight requests, response & payload to see if there's any leftover code you need to review.
I am trying to dynamically populate the WORD Document using npm docx. I am trying to read the data from the SQLite database but due to async node js property the values are not getting into the variable and it shows undefined. If I make the function synchronous the npm docx throws error and doesn't populate the document.
package.json
{
"name": "demoName",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"docx": "^5.1.1",
"express": "^4.17.1",
"md5": "^2.2.1",
"sqlite3": "^4.2.0"
}
}
index.js
const docx = require('docx');
var express = require('express');
var app = express();
var db = require("./database.js")
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const { AlignmentType, Document, Footer, Header, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType, Table, TableCell, TableRow } = docx;
app.get("/doc", async(req, res) => {
var sql = "select * from DocDetails"
var params = []
//let DocDetailsData;
//let DocDetailsData = [{docId: "Some Doc Id"}];
const DocDetailsData = db.all(sql, params, (err, rows) => {
if (err) {
res.status(400).json({"error":err.message});
return;
}
console.log(rows[0]);
return rows[0];
});
console.log(DocDetailsData.docId);
const doc = new Document();
doc.addSection({
children: [
new Paragraph({
children: [
new TextRun({
text: "DEMO TEST DOCUMENT"
}),
new TextRun({
text: DocDetailsData.docId,
}),
]
}),
],
});
const b64string = await Packer.toBase64String(doc);
res.setHeader('Content-Disposition', 'attachment; filename=My Document.docx');
res.send(Buffer.from(b64string, 'base64'));
});
madeDoc = function(){
}
app.use(function(req, res){
res.status(404);
});
var server = app.listen(4041, function () {
var host = 'localhost'
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
database.js
var sqlite3 = require('sqlite3').verbose()
var md5 = require('md5')
const DBSOURCE = "db.sqlite"
let db = new sqlite3.Database(DBSOURCE, (err) => {
if (err) {
// Cannot open database
console.error(err.message)
throw err
}else{
console.log('Connected to the SQLite database.')
db.run(`CREATE TABLE DocDetails (
id INTEGER PRIMARY KEY,
docId text NOT NULL,
version float NULL,
editedBy text NULL,
editedDate text NULL,
effectiveDate text NULL)`,
(err) => {
if (err) {
// Table already created
console.log('Table not created');
}else{
console.log('Table created');
var insert = 'INSERT INTO DocDetails (docId, version, editedBy, editedDate, effectiveDate) VALUES (?,?,?,?,?)'
db.run(insert, ["NESS-RD-TEMP-EDCHB",2.1, "manab", "18-Jul-2017", "18-Jul-2020"])
}
})
}
});
module.exports = db
If you go to localhost:4041/doc, a word document should get downloaded but it shows only one row and not the data from database. I need the database value to be populated in the doc.
Thanks.
In order for this example to work, you need to understand how to work with asynchronous execution and callbacks. You cannot return anything from the callback and get it in the DocDetailsData variable as it would in synchronous code, because when you call the db.all method, the code continues to execute further, without waiting for the callback that you passed to it to work. Instead, you need to put the code for generating the doc file in the callback and do it in it. I hope that at least I could explain to you how it works. This is how your code will work correctly:
index.js
const docx = require('docx');
var express = require('express');
var app = express();
var db = require("./database.js")
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const { AlignmentType, Document, Footer, Header, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType, Table, TableCell, TableRow } = docx;
app.get("/doc", async (req, res) => {
var sql = "select * from DocDetails"
var params = []
db.all(sql, params, async (err, rows) => {
if (err) {
res.status(400).json({"error":err.message});
return;
}
const DocDetailsData = rows[0];
const doc = new Document();
doc.addSection({
children: [
new Paragraph({
children: [
new TextRun({
text: "DEMO TEST DOCUMENT"
}),
new TextRun({
text: DocDetailsData.docId,
}),
]
}),
],
});
const b64string = await Packer.toBase64String(doc);
res.setHeader('Content-Disposition', 'attachment; filename=My Document.docx');
res.send(Buffer.from(b64string, 'base64'));
});
});
app.use(function(req, res){
res.status(404);
});
var server = app.listen(4041, function () {
var host = 'localhost'
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
});
database.js does not require changes
For my internship I am making a chatbot. I created a bot using botkit framework (yo botkit) and got as far as using it in the ms Teams client. But only using my localhost + ngrok setup. when I want to use https://{myproject}.azurewebsites.net/api/messages after deployment I get a 502 error message.
To try if I actually did my deployment okay, I made another project using "yo botbuilder" without botkit framework. Following the same steps I deployed it to a fresh Group. This time using the https://{myproject}.azurewebsites.net/api/messages url worked. (standard echo bot)
After that, I copied my bot files from my first project into my new one and replaced my index.js from my standard botbuilder project with my bot.js file from my botkit framework project.
I just used the standard botbuilder -> index.js file and botkit -> bot.js file +
in my package.json I changed the main and the scripts to point to bot.js instead of index.js.
After deploying, it just gave me my 502 again.
index.js
const dotenv = require('dotenv');
const path = require('path');
const restify = require('restify');
// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
const { BotFrameworkAdapter } = require('botbuilder');
// This bot's main dialog.
const { EchoBot } = require('./bot');
// Import required bot configuration.
const ENV_FILE = path.join(__dirname, '.env');
dotenv.config({ path: ENV_FILE });
// Create HTTP server
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, () => {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
// Create adapter.
// See https://aka.ms/about-bot-adapter to learn more about how bots work.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
// Catch-all for errors.
const onTurnErrorHandler = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
// NOTE: In production environment, you should consider logging this to Azure
// application insights.
console.error(`\n [onTurnError] unhandled error: ${ error }`);
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
await context.sendActivity('The bot encountered an error or bug.');
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
};
// Set the onTurnError for the singleton BotFrameworkAdapter.
adapter.onTurnError = onTurnErrorHandler;
// Create the main dialog.
const myBot = new EchoBot();
// Listen for incoming requests.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
// Route to main dialog.
await myBot.run(context);
});
});
// Listen for Upgrade requests for Streaming.
server.on('upgrade', (req, socket, head) => {
// Create an adapter scoped to this WebSocket connection to allow storing session data.
const streamingAdapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
// Set onTurnError for the BotFrameworkAdapter created for each connection.
streamingAdapter.onTurnError = onTurnErrorHandler;
streamingAdapter.useWebSocket(req, socket, head, async (context) => {
// After connecting via WebSocket, run this logic for every request sent over
// the WebSocket connection.
await myBot.run(context);
});
});
bot.js
const { Botkit } = require('botkit');
const { BotkitCMSHelper } = require('botkit-plugin-cms');
// Import a platform-specific adapter for botframework.
const { MongoDbStorage } = require('botbuilder-storage-mongodb');
// Load process.env values from .env file
require('dotenv').config();
let storage = null;
if (process.env.MONGO_URI) {
storage = mongoStorage = new MongoDbStorage({
url : process.env.MONGO_URI,
});
}
const controller = new Botkit({
webhook_uri: '/api/messages',
adapterConfig: {
appId: process.env.APP_ID,
appPassword: process.env.APP_PASSWORD,
},
storage
});
if (process.env.CMS_URI) {
controller.usePlugin(new BotkitCMSHelper({
uri: process.env.CMS_URI,
token: process.env.CMS_TOKEN,
}));
}
// Once the bot has booted up its internal services, you can use them to do stuff.
controller.ready(() => {
// load traditional developer-created local custom feature modules
controller.loadModules(__dirname + '/features');
/* catch-all that uses the CMS to trigger dialogs */
if (controller.plugins.cms) {
controller.on('message,direct_message', async (bot, message) => {
let results = false;
results = await controller.plugins.cms.testTrigger(bot, message);
if (results !== false) {
// do not continue middleware!
return false;
}
});
}
});
controller.webserver.get('/', (req, res) => {
res.send(`This app is running Botkit ${ controller.version }.`);
});
package.json
{
"name": "my-chat-bot",
"version": "1.0.0",
"description": "Demonstrate the core capabilities of the Microsoft Bot Framework",
"author": "Generated using Microsoft Bot Builder Yeoman generator v4.7.0",
"license": "MIT",
"main": "bot.js",
"scripts": {
"start": "node ./bot.js",
"watch": "nodemon ./bot.js",
"lint": "eslint .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com"
},
"dependencies": {
"botbuilder": "~4.7.0",
"dotenv": "^8.2.0",
"restify": "~8.4.0",
"botbuilder-storage-mongodb": "^0.9.5",
"botkit": "^4.6.2",
"botkit-plugin-cms": "^1.0.3",
"firebase-admin": "^8.9.2",
"jira-client": "^6.15.0",
"request": "^2.88.2"
},
"devDependencies": {
"eslint": "^6.6.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"nodemon": "~1.19.4"
}
}
I'm trying to create a simple websocket app with NodeJS, WS and Openshift
This is my code:
package.json:
{
"name": "OpenShift-Sample-App",
"version": "1.0.0",
"description": "OpenShift Sample Application",
"keywords": [
"OpenShift",
"Node.js",
"application",
"openshift"
],
"author": {
"name": "John Smith",
"email": "example123#example.com",
"url": "http://www.google.com"
},
"homepage": "http://www.openshift.com/",
"repository": {
"type": "git",
"url": "https://github.com/openshift/origin-server"
},
"engines": {
"node": ">= 0.6.0",
"npm": ">= 1.0.0"
},
"dependencies": {
"express": "^4.12.3",
"socket.io" : "0.9.16",
"ws" : "0.4.31"
},
"devDependencies": {},
"bundleDependencies": [],
"private": true,
"main": "server.js"
}
app.js:
#!/bin/env node
// OpenShift sample Node application
var express = require('express');
var fs = require('fs');
var WebSocketServer = require('ws').Server;
var SampleApp = function () {
// Scope.
var self = this;
var url = '127.0.0.1:27017/' + process.env.OPENSHIFT_APP_NAME;
self.setupVariables = function () {
// Set the environment variables we need.
self.ipaddress = process.env.OPENSHIFT_NODEJS_IP;
self.port = process.env.OPENSHIFT_NODEJS_PORT || 8080;
if (typeof self.ipaddress === "undefined") {
// Log errors on OpenShift but continue w/ 127.0.0.1 - this
// allows us to run/test the app locally.
console.warn('No OPENSHIFT_NODEJS_IP var, using 127.0.0.1');
self.ipaddress = "127.0.0.1";
}
;
};
self.populateCache = function () {
if (typeof self.zcache === "undefined") {
self.zcache = {'index.html': ''};
}
// Local cache for static content.
self.zcache['index.html'] = fs.readFileSync('./index.html');
};
self.cache_get = function (key) {
return self.zcache[key];
};
self.terminator = function (sig) {
if (typeof sig === "string") {
console.log('%s: Received %s - terminating sample app ...',
Date(Date.now()), sig);
process.exit(1);
}
console.log('%s: Node server stopped.', Date(Date.now()));
};
self.setupTerminationHandlers = function () {
// Process on exit and signals.
process.on('exit', function () {
self.terminator();
});
// Removed 'SIGPIPE' from the list - bugz 852598.
['SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGTERM'
].forEach(function (element, index, array) {
process.on(element, function () {
self.terminator(element);
});
});
};
self.createGetRoutes = function () {
self.getRoutes = {};
self.getRoutes['/'] = function (req, res) {
res.setHeader('Content-Type', 'text/html');
res.send(self.cache_get('index.html'));
};
};
self.initializeServer = function () {
self.createGetRoutes();
self.app = express();
// Add handlers for the app (from the routes).
for (var r in self.getRoutes) {
self.app.get(r, self.getRoutes[r]);
}
}
self.initialize = function () {
self.setupVariables();
self.populateCache();
self.setupTerminationHandlers();
// Create the express server and routes.
self.initializeServer();
};
self.start = function () {
var wss = new WebSocketServer({ server: self.app
})
wss.on('connection', function connection(ws) {
console.log(".....Connected");
var location = url.parse(ws.upgradeReq.url, true);
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
self.app.listen(self.port, self.ipaddress, function () {
console.log('%s: Node server started on %s:%d ...',
Date(Date.now()), self.ipaddress, self.port);
});
};
};
var zapp = new SampleApp();
zapp.initialize();
zapp.start();
when I run:
wscat --connect ws://something.rhcloud.com:8000
I got:
connected (press CTRL+C to quit)
disconnected
What is wrong in the source code?
Thanks
Within (pre-docker) OpenShift, your application connects to the system load balancer by listening on process.env.OPENSHIFT_NODEJS_PORT.
OpenShift's load balancer then exposes your application externally on four different ports:
80 - basic http connections available
443 - basic https connections available
8080 - http and ws connections available
8443 - https and ws connections available
Unfortunately, in the pre-docker versions of OpenShift, the ws connection upgrade (from http or https) will only work correctly on ports 8080 and 8443.
You can work around this issue by including a client-side check to test if the server hostname includes the string "rhcloud.com". If it does, you'll need to establish your client's socket connection using the alternate port number.
Newer, docker-based releases of OpenShift include full websocket support on standard web ports.