How can I customize the ENOENT error inside an express webserver? - javascript

I'm making a text hosting service and I want it to say "TextMessage Not found! You can create one in the name by clicking here" but I don't know how to do that.
I tried making a custom 404 error, and detecting if the file exists via the node fs module, that did not work out.
this is my code:
// Importing require packages/modules
const express = require("express");
const bodyParser = require("body-parser");
const fs = require("fs");
// Defining needed variables
const app = express();
const PORT = 3000;
// Express midleware to enable body-parser
app.use(bodyParser.urlencoded({ extended: false }));
// Express static handler
app.use(express.static("public"));
// POST API To create message/file
app.post("/api/message/submit", (req, res) => {
const file = req.body.messageText;
const fileID = req.body.messageID;
fs.writeFile(__dirname + "/messageFiles/" + fileID + ".txt",
file, (err) => {
if (err) res.send("ERROR! <br>" + err);
else res.send("Saved");
});
});
// GET API To read message/file
app.get("/message/:id", (req, res) => {
const msg = req.params.id;
if (fs.existsSync(__dirname + "/messageFile/" + msg + ".txt")) {
res.sendFile(__dirname + "/messageFiles/" + msg + ".txt");
} else {
res.send("Message does not exist");
}
});
// Running the server
app.listen(PORT, () => {
console.log("Running on port: " + PORT);
});
`

You may try async writing to a file by wrapping it with trycatch block. It seems you have a problem with path to the file.
I suggest you to have a helper functions to write to a file and read from a file:
const create = async (data, fileName) => {
try {
const text = await fs.writeFile(`./files/${fileName}.txt`, data);
console.log('File write successful!');
} catch (error) {
throw new Error('File write operation failed');
}
};
const read = async (fileName) => {
try {
const fileContent = await fs.readFile(`./files/${fileName}.txt`, {encoding: 'utf-8'});
return fileContent;
} catch (error) {
throw new Error('File read operation failed');
}
};
app.post("/api/message/submit", async (req, res) => {
const fileContent = req.body.messageText;
const fileID = req.body.messageID;
const message = await create(fileContent, fileID);
message ? res.send('Successful') : res.send('Failed');
});
app.get("/message/:id", async (req, res) => {
const msgID = req.params.id;
const message = await read(msgID);
res.send(message);
});
I hope it helps!
P.S.: Don't use too many comments. Your code should be self-documenting (readable)!
P.S.S: Added async keyword before (req, res).

Related

Why combining Node Express and Redis can't set a simple Key-Value pair?

Basically, the below put service that should execute a simple Redis-cli> SET KEY VALUE can't work.
The get operations work well.
Using separately the redis module and calling the set function also works.
But when called from app.put() the KEY/VALUE pair isn't registered.
What's the hell???
// express setup
const REDIS_REST_PORT = 3000;
const express = require('express');
const router = express.Router();
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());
// redis setup
const REDIS_CONNECTION_STRING = "redis://127.0.0.1:6379";
const RedisCli = require('redis').createClient(REDIS_CONNECTION_STRING);
RedisCli.on('connect', function() {
console.log('Connected to REDIS');
});
RedisCli.on('error', function(err) {
console.log('/!\ REDIS ERROR: ' + err);
});
// GET .../get/KEY (works well !!)
app.get('/get/:key', function(req, res) {
RedisCli.get( req.params.key, function (err, result) {
if (err) {
res.send(err,500);
} else {
res.send(result);
}
});
});
// PUT .../set/KEY + body (can't work KEY/VALUE never registered ??)
app.put('/set/:key', function(req, res) {
var value = "'" + JSON.stringify(req.body) + "'";
console.log("SET " + req.params.key + " " + value);
RedisCli.set( req.params.key, value,
function (err, result) {
if (err) {
res.send(err,500);
} else {
res.send(result);
}
});
});
// Start REST server
app.listen(REDIS_REST_PORT, () =>
console.log('Listening on port '+ REDIS_REST_PORT + '...'));
Eventually it used to work - don't understand how and why - see my comment.

How come fetch only works here when I add an alert to the end of the line? Express + NodeJS + Fetch. What's a good fix here

I'm using NodeJS w/ Express to create a web app that records your audio using the VMSG library and posts the BLOB audio to my file system using HTTP Requests and multer. It also adds that instance of a recording into a MongoDB database.
I'm having an issue with the fetch command. It's not working unless I put an alert right after the fetch. The way I have it set up is that I have my main express app (index.js), and a router to the /recordingsDirectory (recordings.js) which is the endpoint for processing the posts. My main index HTML page uses Handlebars and uses a separate JS script (recorder.js) to 1) use the VMSG library and 2) fetch a POST to the /recordingsDirectory once someone submits the audio file w/ the name and the AudioBlob present. This is where I'm stuck. I can fetch in recorder.js with an alert line after the fetch, but I can't have the fetch on the end of the else if block by itself. I'd like to do it without this since the alert is ugly. A solution I've tried is that I tried to make the onsubmit function async and await fetch since I thought maybe it's waiting for a promise but that didn't work.
Here are the files. I commented CRITICAL and SUPER CRITICAL to the lines of code that you should check out and I think where the issues lie:
index.js
const express = require('express')
const handlebars = require('express-handlebars')
const path = require('path')
const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
const xhr = new XMLHttpRequest()
const db = require('./db')
const app = express()
const PORT = process.env.PORT || 8000
app.set('view engine', 'hbs')
app.engine('hbs', handlebars({
layoutsDir: path.join(__dirname, 'views', 'layouts'),
extname: 'hbs',
defaultLayout: 'index',
partialsDir: path.join(__dirname, 'views', 'partials'),
}))
app.use(express.json())
app.use(express.urlencoded({extended: false}))
app.use((err, req, res, next) => {
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
return res.status(400).send({ status: 404, message: err.message })
}
next()
})
app.get('/', (req, res) => {
res.render('main', {
title: 'Main Page'
})
})
app.get('/recordings', (req, res) => {
var database = db.get().db('AudioJungle')
database.collection('recordings').find().sort({ "date": -1 }).toArray(function(err, docs) {
res.render('recordings', {
title: 'Recordings',
recordings: docs
})
})
})
// CRITICAL
app.use('/recordingsDirectory', require('./recordings/recordings'))
app.use(express.static('public'))
app.use('/scripts', express.static(path.join(__dirname, 'node_modules', 'vmsg')))
db.connect(function(err) {
if (err) {
console.log('Unable to connect to Mongo.')
process.exit(1)
} else {
app.listen(PORT, () => console.log(`Listening on Port: ${PORT}`))
}
})
process.on('SIGINT', function() {
db.close(function () {
console.log('Disconnected on app termination');
process.exit(0);
});
});
app.use((req, res, next) => {
res.status(404).send({
status: 404,
error: 'Not found'
})
})
recordings.js (Aka the /recordingsDirectory endpoint for a fetch POST)
const express = require('express')
const router = express.Router()
const multer = require('multer')
const fs = require('fs-extra')
const db = require('../db')
const { ObjectId } = require('bson')
const moment = require('moment')
const upload = multer({
storage: multer.diskStorage({
destination: (req, file, callback) => {
let path = './public/uploads'
fs.mkdirsSync(path)
callback(null, path)
},
filename: (req, file, callback) => {
createRecording(req).then((id) => {
var file_name = id + '.mp3'
callback(null, file_name)
})
}
})
})
var type = upload.single('audio-file')
// CRITICAL
router.post('/', type, (req, res) => {
console.log('made it')
res.status(200)
res.send('OK')
})
router.delete('/delete', (req, res) => {
deleteRecording(req.body._id).then((dbResponse) => {
if (dbResponse == null || dbResponse == undefined) {
res.status(400).json({ msg: 'ID already deleted' })
} else {
res.status(200)
}
})
})
router.get('/', (req, res) => {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
recordings.findOne({"_id": ObjectId(req.query.id)}, function(err, result) {
if (err) throw err
if (result == null || result == undefined) {
return res.status(400).json({
status: 404,
error: 'Recording no longer in the database'
})
}
res.status(200)
res.json({
name: result.name,
date: result.date
})
})
})
async function createRecording(req) {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
var audioObject = {
name: req.body.name,
date: moment().format('MMMM Do YYYY, h:mm:ss a')
}
var dbResponse = await recordings.insertOne(audioObject)
return dbResponse.insertedId
}
async function deleteRecording(id) {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
var audioToDelete = {
_id: ObjectId(id)
}
var deleteResult = await recordings.deleteOne(audioToDelete)
return deleteResult
}
module.exports = router
And below is the Script the audio and name and tries to Fetch (where I need the alert for it to actually process into the /recordingsdirectory)
recorder.js
import { record } from "/scripts/vmsg.js";
let recordButton = document.getElementById("record");
var blobObj = null
recordButton.onclick = function() {
record({wasmURL: "/scripts/vmsg.wasm"}).then(blob => {
blobObj = blob
var tag = document.createElement("p")
tag.id="finishedRecording"
var text = document.createTextNode("Audio File Recorded")
tag.appendChild(text)
var element = document.getElementById("box")
element.appendChild(tag)
document.getElementById('box').appendChild(a)
})
}
let form = document.getElementById('mp3Form');
form.addEventListener("submit", submitAudio)
function submitAudio() {
var fileName = form.elements[0].value
if (fileName == "") {
alert('Please enter a name for your file')
} else if (blobObj != null) {
// CRITICAL
// SUPER CRITICAL WHERE FETCH DOESN'T WORK UNLESS I PUT AN ALERT AT THE END
const formData = new FormData()
formData.append('name', fileName)
formData.append('audio-file', blobObj)
const options = {
method: 'POST',
body: formData
}
fetch('/recordingsDirectory', options);
// If I comment an alert here, /recordingsDirectory will process the post since it console.logs 'made it'
} else {
alert('Record some Audio to upload')
}
}
Here's my file system.
Also, I'd like to mention that the fetch works properly on my Windows PC without having to add the alert, but it doesn't work without the alert on my macbook. If any one figures out a fix or an error in how I'm doing things to allow this please let me know. I've been stuck on this problem for a day now. Thanks a bunch!

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client in Node JS

I am trying to update a data using a specific ID which is not working but gives above error.
When I update, first I search the database for that specific id and then save the data to MongoDB
here is my server.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
//importing MongoDB model schema
let ToDo = require('./todo.model');
const app = express();
const todoRoutes = express.Router();
const PORT = 4000;
//middlewares
app.use(bodyParser.json());
app.use(cors());
app.use('/todos', todoRoutes);
//connection to the MongoDB database
mongoose.connect('mongodb://127.0.0.1:27017/todos', {useNewUrlParser: true});
const connection = mongoose.connection;
connection.once('open', () =>{
console.log("Connected to the MongoDB through port: 27017");
});
app.listen(PORT, () => {
console.log(`Listening to port: ${PORT}`);
});
//get all data - removed to show only the code snippet I am getting errors
//get data by an ID - removed to show only the code snippet I am getting errors
//add items to database -removed to show only the code snippet I am getting errors
//update items
todoRoutes.route('/update/:id').post((req, res) => {
let id = req.params.id;
ToDo.findById(id, (err, todo) => {
if(err) throw err;
if(!todo) res.status(400).send("No data found");
todo.todo_description = req.body.todo_description;
todo.todo_responsible = req.body.todo_responsible;
todo.todo_priority = req.body.todo_priority;
todo.todo_completed = req.body.todo_completed;
res.end();
todo.save().then(todo => {
res.json(200).send("Data Updated! " + todo);
res.end();
}).catch(err => {
res.status(400).send("Error occured! " + err);
});
});
});
This is the error I am getting...
Can someone please help me?
This error usually means that you send a response more than once.
Notice that you send two responses one after the other res.json() and res.end()
If you want for some reason to just end the response, use res.end(), otherwise use res.status(200).json({ result: ‘Data updated’ + todo })
If you send both, it will complain about trying to modify the response (via res.end()) after sending it (via res.status().json())
In the '/update/:id' route, you're sending a res.end() then doing it again 3 lines later. If you remove the first res.end(), it should work.
You should also return if todo is missing:
todoRoutes.route('/update/:id').post((req, res) => {
let id = req.params.id;
ToDo.findById(id, (err, todo) => {
if(err) throw err;
if(!todo) return res.status(400).send("No data found");
todo.todo_description = req.body.todo_description;
todo.todo_responsible = req.body.todo_responsible;
todo.todo_priority = req.body.todo_priority;
todo.todo_completed = req.body.todo_completed;
todo.save().then(todo => {
res.status(200).send("Data Updated! " + todo);
}).catch(err => {
res.status(400).send("Error occured! " + err);
});
});
});

node JS expreess UNDEFIED CALL BACK

I am working on a personal project and I do not have much experience with nodeJS, the idea is to bring a JSON that has remotely taken some data and generate some statistics, I am doing some tests before starting fully in the project and I am having problems with the callback.
the server.js works correctly,
my module is the following:
const extjson = require('remote-json');
//---------------------API CONFIG--------------------------
//apikey
const apikey ="xxxxxxxxxxxxxxxxxxxxx";
function get_sum_id(sumname){
const urlsumbySumName = "https://la2.api.riotgames.com/lol/summoner/v3/summoners/by-name/" + sumname + "?api_key=" + apikey;
var id;
extjson(urlsumbySumName).get((err, res, body)=> {
id = body.id;
});
return id;
}
module.exports = {get_sum_id
};
and the routes.js is the following:
const riot = require('./rapi.js');
const express = require('express');
//---------------------------------------------------------
const router = express.Router();
//Jtask -- task remote json
//const Task = require('../models/Task'); // taskdb
router.get('/',async (req, res) => {
res.render('index');
});
router.post('/profile', (req,res)=>{
const sum = req.body.summoners;
console.log(riot.get_sum_id(sum));
res.render('profile',{sum});
});
module.exports = router;
I want to show that id by console and it returns undefined, the idea is to pass that value to the render below to have it available in an EJS document.
Your module make an asynchronous call to another server with remote-json. It means that the callback will be called only after the request to this other server. So, this line return id; is read before this line id = body.id;.
One way to fix that is to provide the callback from the place where you call your module function.
Based on your code you could do something like that :
// module.js
const extjson = require('remote-json');
const apikey ="xxxxxxxxxxxxxxxxxxxxx";
function get_sum_id (sumname, callback) {
const urlsumbySumName = "https://la2.api.riotgames.com/lol/summoner/v3/summoners/by-name/" + sumname + "?api_key=" + apikey;
extjson(urlsumbySumName).get(callback);
}
module.exports = { get_sum_id };
// app.js
const riot = require('./rapi.js');
const router = express.Router();
router.post('/profile', function(req, res, next) {
riot.get_sum_id(req.body.summoners, function (err, resp, body) {
console.log(body);
res.json(body); // Response here
});
});
module.exports = router;
Now, requests to your server will be in pending until your callback close it with res.json(body);.
Thank you very much I am working, now I understand much better how the asynchronous functions work. I leave here the complete solution to my problem in case someone needs it in the future:
//rapi.js
const extjson = require ('remote-json');
//---------------------API CONFIG--------------------------
//apikey
const apikey ="RGAPI-77f658f1-ff2b-40e7-a74c-47f7510c8dac";
//trayendo los datos desde riot
function get_sum_id(sumname, callback){
const urlsumbySumName = "https://la2.api.riotgames.com/lol/summoner/v3/summoners/by-name/" + sumname + "?api_key=" + apikey;
extjson(urlsumbySumName).get(callback)
}
module.exports = { get_sum_id };
//routesapp.js
const riot = require('./rapi.js');
const express = require('express');
//---------------------------------------------------------
const router = express.Router();
router.get('/',async (req, res) => {
res.render('index');
});
router.post('/profile', (req, res, next)=>{
const sum = req.body.summoners;
riot.get_sum_id(sum,function (err, resp, body){
console.log(body.id);
//responces....
res.render('profile',{sum, id: body.id})
});
});
module.exports = router;
TNX very much!

Node.js: Multer upload with promise?

I have this express route with multer file-upload. When the upload is complete, I would like to encode the image to base64 and send with response.
However when I do it like this, the code tries to execute the base64 encoding before the file is created to the folder.
Edit: Added storage & upload functions
const storage = multer.diskStorage({
destination: (req, file, callback) => {
if (!fs.existsSync('./uploads')) {
fs.mkdirSync('./uploads');
}
let path = './uploads';
callback(null, path);
},
filename(req, file, cb) {
let fileExt = file.originalname.substring(file.originalname.lastIndexOf('.')).toLowerCase();
if (!imageFilter(fileExt)) {
return false;
} else {
cb(null, file.originalname);
}
},
onError: function (err, next) {
console.log('error', err);
next(err);
},
});
const upload = multer({
storage,
limits: {
fileSize: 1000 * 1000 * 2 // 2 MB
}
}).single('file');
router.post('/upload', function (req, res) {
var directory = 'uploads';
fs.readdir(directory, (err, files) => {
if (err) throw err;
for (var file of files) {
fs.unlink(path.join(directory, file), err => {
if (err) throw err;
});
}
});
upload(req, res, function (err) {
if (err) {
return res.status(404).json({
success: false,
message: 'File is too large. (Max 2MB)'
});
}
var file = req.file;
var base64str = base64_encode('./uploads/' + file.originalname);
return res.status(200).json({
success: true,
url: 'http://' + ip.address() + ':' + constants.PORT + '/api/uploads/' + file.originalname,
image: 'data:image/png;base64,' + base64str
});
});
});
What would be the smartest way to achieve the right order of operations. Possibly promises or async/await?
This solution worked for me :
Node v8.4.0 is required for this
//app.js
const fs = require('fs');
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
app.use(cors({credentials: true, origin: 'http://localhost:4200'}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const Uploader = require('./Uploader.js');
const uploader = new Uploader();
app.post('/upload', uploader.startUpload);
//Uploader.js
const util = require("util");
const crypto = require("crypto");
const multer = require('multer');
class Uploader {
constructor() {
const storageOptions = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, __dirname + '/uploads/')
},
filename: function(req, file, cb) {
crypto.pseudoRandomBytes(16, function(err, raw) {
cb(null, raw.toString('hex') + Date.now() + '.' + file.originalname);
});
}
});
this.upload = multer({ storage: storageOptions });
}
async startUpload(req, res) {
let filename;
try {
const upload = util.promisify(this.upload.any());
await upload(req, res);
filename = req.files[0].filename;
} catch (e) {
//Handle your exception here
}
return res.json({fileUploaded: filename});
}
}
Edit :
The library "util" provide you a "promisify" method which will give you the possibility to avoid something called the "callback hell". It converts a callback-based function to a Promise-based one.
This is a small example to understand my code above:
const util = require('util');
function wait(seconds, callback) {
setTimeout(() => {
callback();
}, seconds);
}
function doSomething(callType) {
console.log('I have done something with ' + callType + ' !');
}
// Default use case
wait(2 * 1000, () => {
doSomething('callback');
});
const waitPromisified = util.promisify(wait);
// same with promises
waitPromisified(2000).then((response) => {
doSomething('promise');
}).catch((error) => {
console.log(error);
});
// same with async/await
(async () => {
await waitPromisified(2 * 1000);
doSomething('async/await');
})();
Will print :
I have done something with callback !
I have done something with promise !
I have done something with async/await !

Categories