node js find problem with special characters - node.js

i build a simple search form with onchange event but when i put in search input something like / or ' or alert( it returning my error and this is my backend code:
const searchAds = async (req, res, next) => {
const search = req.params.search;
let ads;
try {
ads = await Ad.find({ "title": { $regex: '.*' + search,$options:'i' + '.*' }});
} catch (err) {
const error = new HttpError(
'Searching ads failed, please try again later.',
500
);
return next(error);
}
const page = parseInt(req.query.page) || 1;
const pageSize = 20;
const pager = paginate(ads.length, page, pageSize);
const pageOfItems = ads.map(ad => ad.toObject({ getters: true })).slice(pager.startIndex, pager.endIndex + 1);
return res.json({ pager, pageOfItems });
};
whats the best way to avoid this? Also is this correct way for find method?

Have you tried wrapping your search variable in encodeURI() or encodeURIComponent()?
https://www.w3schools.com/jsref/jsref_escape.asp

You can use a library such as https://www.npmjs.com/package/regex-escape to escape your search parameter.
var escape = require("regex-escape");
...
ads = await Ad.find({ "title": { $regex: '.*' + escape(search), $options:'i' + '.*' }});

Related

How can query string search a data using mongoose?

Im using mongoose-paginate for my pagination and I want to add search for my project...
How can get query string search using mongoose node like these example...
(/allStudents?stdID=440033)
My code is:
router.get('/allStudents', async (req,res) => {
const limit = parseInt(req.query.limit, 4) || 4;
const page = parseInt(req.query.page, 10) || 1;
const PAGE_SIZE = 10;
const skip = (page - 1) * PAGE_SIZE;
try {
const students = await Student.paginate({}, {limit, page, skip})
|| await Student.find({ stdID: req.query.stdID})
return res.status(200).json({
success: true,
data: students
})
} catch (err) {
console.log(err);
}
});
You should pass your query as the first parameter according to the documentation:
const students = await Student
.paginate(
{stdID: req.query.stdID},
{limit, page, skip}
);

Prevent multiple callback error when client send multiple requests

I am building my first web application with node v12.18.1 and express v4.17.1. Since the start of development, I have the same error on all routes: when i quickly click a link multiple times the server crashes with this error: screenshot of the error. It can be fixed in the front-end by disabling events on user input after a click, but I prefer to know what is wrong with my code.
Route of the index page :
let express = require('express');
let router = express.Router();
let controller_index = require("../controller/controller_index.js")
router.get('/', controller_index.get_index);
router.get('/rubrique/:category', controller_index.get_category);
module.exports = router;
Controller of the index page :
const Query = require("../lib/dbs.js");
const ObjectId = require('mongodb').ObjectId;
const utils = require('../lib/utils');
exports.get_index = async (req, res, next) => {
try {
let user = await Query.findOne("users", "_id", ObjectId(req.user_id));
let notification;
if (user) {
notification = await Query.findSortToArray("notifications", "for_user", ObjectId(user._id));
notification.count = notification.filter((notif => !notif.hasOwnProperty("readedAt"))).length;
}
if (!req.query.q) {
let page = req.query.page > 1 ? (req.query.page * 10) - 10 : req.query.page <= 0 ? undefined : 0;
let current_page = req.query.page ? Number(req.query.page) : 1;
let [countDocuments, docs] = await Promise.all([Query.countAll("articles"), Query.findAll(page)]);
let nb_pages = Math.ceil(countDocuments / 10);
if (!docs.length && current_page !== 1) {
next();
}
else {
docs = await documents_processing(docs, user, req);
res.render("../views/index.pug", { docs: docs, user: user, notification: notification, nb_pages: nb_pages, current_page: current_page })
};
}
else if (req.query.q) {
let page = req.query.page > 1 ? (req.query.page * 10) - 10 : req.query.page <= 0 ? undefined : 0;
let current_page = req.query.page ? Number(req.query.page) : 1;
let [countDocuments, docs] = await Promise.all([Query.countAllBySearch("articles", req.query.q), Query.findAllBySearch(req.query.q, page)]);
let nb_pages = Math.ceil(countDocuments / 10);
if (!docs.length && current_page !== 1) {
next();
}
else {
docs = await documents_processing(docs, user, req);
res.render("../views/index.pug", { docs: docs, search: req.query.q, user: user, notification: notification, nb_pages: nb_pages, current_page: current_page })
};
};
}
catch (err) {
console.error(err);
return next(err);
};
};
Example of static function of the query object :
const connect = require("../index.js")
module.exports = class Query {
static async findOne(collection, field, item) {
const result = await connect.client.db("blog_db").collection(collection).findOne({ [`${field}`]: item })
return result;
};
static async findOneAndUpdateOrInsertOnUser(collection, field, itemToSearch, updateItem) {
const result = await connect.client.db("blog_db").collection(collection).findOneAndUpdate({ [`${field}`]: itemToSearch }, { $set: updateItem }, { upsert: true, returnOriginal: false });
return result;
};
static async findSortToArray(collection, field, item) {
const results = await connect.client.db("blog_db").collection(collection).find({ [`${field}`]: item }).sort({ date: -1 }).toArray()
return results;
};
};
I'm fairly new to programming so any advice is welcome, thank you in advance!
----- EDIT -----
Kind of solution :
I have found people who have talked about this error on node v12 and newer, with a downgrade to v10 the issue was resolved without any clear explanation yet.
The error in the screenshot you've shared shows that the error is "Callback called multiple times", but I don't see anywhere obvious in the code you've shared where this is happening. As you're saying this bug only happens when multiple requests are made rapidly one after the other, it suggests there might be a global variable which is being shared between requests, which is something that should be avoided.
To debug the error you're seeing I would recommend commenting out all of the code in the get_index function and gradually uncommenting it in small chunks until you see the error happen again. You will probably want to do the same with the code that is called by the get_index controller function e.g. documents_processing, as the issue might possibly lie there.
Express only support callback-style, and you're using async function to handle the logic.
Basically, with async function, you call a function without waiting for it to resolve the logics. Hence, it creates too many callbacks in the event loop when that route has a large amount of concurrent coming requests.
function asyncWrapper(fn) {
return (req, res, next) => {
return Promise.resolve(fn(req))
.then((result) => res.send(result))
.catch((err) => next(err))
}
};
router.get('/', asyncWrapper(controller_index.get_index));

Scrape paginate using nodejs, cheerio

How can I scrape data from a pagination ?
My code is work well with one pages, but I need to scrap all data from page 2, page 3 ... and push into an ebooks array.
Here is my code
function searchEbooks(query) {
return fetch(getUrl(1, query))
.then(res => res.text())
.then(body => {
const ebooks = [];
$('article').each(function(i, element) {
const $element = $(element);
const $title = $element.find('.entry-title a');
const $image = $element.find('.attachment-post-thumbnail');
const $description = $element.find('.entry-summary');
const authors = [];
$(element).find('.entry-author a').each(function(i, element) {
author = $(element).text();
authors.push(author);
});
const ebook = {
image: $image.attr('src'),
title: $title.text(),
description: $description.text(),
authors: authors,
}
ebooks.push(ebook);
});
return ebooks;
});
}
I have no idea how to do this. Please give me a hint or an example.
I use cherrio, node-fetch packages.
Thank you.
Try this to get next url:
var href = $('.current+a').attr('href');
if(href){
// you can check this url
} else {
console.log('You get all page');
}

Why ctx.state did not pass to another middleware?

use koa2 ejs koa-router, ejs template how to use another middleware's ctx.state
localhost:3000/admin/usermsg
admin.get('/usermsg', async(ctx) => {
ctx.state.userMsg = {
page: Number(ctx.query.page),
limit: 4,
pages: 0,
count: count
}
var userMsg = ctx.state.userMsg;
ctx.state.users = await new Promise(function(resolve, reject){
userMsg.pages = Math.ceil(userMsg.count / userMsg.limit);
userMsg.page = userMsg.page > userMsg.pages ? userMsg.pages : userMsg.page;
userMsg.page = userMsg.page < 1 ? 1 : userMsg.page;
var skip = (userMsg.page - 1) * userMsg.limit;
User.find().limit(userMsg.limit).skip(skip).exec(function(err, doc){
if(doc){
resolve(doc);
}
if(err){
reject(err);
}
})
})
await ctx.render('admin/usermsg');
})
localhost:3000/damin/category
admin.get('/category', async(ctx) => {
await ctx.render('admin/category');
})
in the category templateļ¼Œcan not get ctx.state.userMsg.
how should i get ctx.state.userMsg in category template?
Well, assuming userMsg is something you use a lot in your views, you could make a dedicated middleware just to obtain that value.
Middleware work in 'stacks': by calling next(), you can pass control to the next one in the stack (with access to the modified ctx.state). A trivial example:
const setUserMsg = async (ctx, next) => {
ctx.state.userMsg = await myFuncThatReturnsAPromise()
await next()
}
router.get('/someroute',
setUserMsg,
ctx => { ctx.body = ctx.state.userMsg })
router.get('/someotherroute',
setUserMsg,
ctx => { ctx.body = ctx.state.userMsg })
Here, setUserMsg's sole purpose is to extract a value (presumably from the database) and add it to the context.

Pass value from middleware to view in Koa 2

I am trying to get var value from my Koa 2 middleware to display in my pug template (or other).
For example in koa-sessions I have:
app.use(ctx => {
// ignore favicon
if (ctx.path === '/favicon.ico') return;
let n = ctx.session.views || 0;
ctx.session.views = ++n; // how can I use this?
ctx.body = n + ' views'; // works, but in body directly
ctx.state.views = n + ' views'; // not working
});
Another example, with response time:
app.use(async (ctx, next) => {
const start = Date.now();
ctx.state.start = start
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); // this shows response
ctx.state.ms = await ms>0 // I have no idea what I'm doing :)
});
As per original instruction this works, but instead of using body/console, I would like to use it as template variable, so in my router/controller I would have:
...
return ctx.render("posts/index", {
title: 'Posts',
posts: posts,
ms: ctx.state.ms,
views: ctx.session.views // or views: ctx.state.views
});
None of this works. Is it related to async/await, so it does not get the value in time or it is some syntactic issue? Please be gentle as I am new in this. :)
You need to call next() in your "session" middleware, same way as in the "response time" example.
Like that:
app.use((ctx, next) => {
let n = ctx.session.views || 0;
ctx.session.views = ++n;
next();
});
app.use(ctx => {
ctx.body = 'Hello ' + ctx.session.views;
// or you can return rendering result here
});
For more info take a look at the Cascading section of their documentation

Resources