Everything worked for me until I added csrf. I use in public/js/editor.js fetch to send the image file to the server:
fetch('/upload', {
method: 'post',
body: formdata
}).then(res => res.json())
.then(data => {
if (uploadType == 'image')
{
addImage(data, file.name);
}
else if (uploadType == 'banner')
{
bannerPath = `${location.origin}/${data}`;
banner.style.backgroundImage = `url("${bannerPath}")`
}
else
{
console.error('Данный тип файла не поддерживается');
}
})
In the server.js I accept the file:
app.post('/upload', (req, res) => {
console.log(req.files);
let file = req.files.image;
let date = new Date();
console.log('test post');
// image name
let imagename = date.getDate() + date.getTime() + file.name;
// image upload path
let path = 'public/uploads/' + imagename;
// create upload
file.mv(path, (err, result) => {
if (err) {
throw err;
} else {
// our image upload path
res.json(`uploads/${imagename}`)
}
})
})
After adding csrf files began to look like this:
Become:
editor.js FULL
const csrfToken = getCookie('XSRF-TOKEN');
console.log(csrfToken);
const headers = new Headers({
'Content-Type': 'x-www-form-urlencoded',
'X-CSRF-Token': csrfToken
});
fetch('/upload', {
method: 'post',
headers: headers,
credentials: 'include',
body: formdata
}).then(res => res.json())
.then(data => {
if (uploadType == 'image')
{
addImage(data, file.name);
}
else if (uploadType == 'banner')
{
bannerPath = `${location.origin}/${data}`;
banner.style.backgroundImage = `url("${bannerPath}")`
}
else
{
console.error('Данный тип файла не поддерживается');
}
})
function getCookie(name) {
if (!document.cookie) {
return null;
}
const xsrfCookies = document.cookie.split(';')
.map(c => c.trim())
.filter(c => c.startsWith(name + '='));
if (xsrfCookies.length === 0) {
return null;
}
return decodeURIComponent(xsrfCookies[0].split('=')[1]);
}
and server.js FULL
const cookieParser = require("cookie-parser");
const csrf = require("csurf");
const csrfMiddleware = csrf({ cookie: true });
app.use(cookieParser());
app.use(csrfMiddleware);
app.all("*", (req, res, next) => {
var token = req.csrfToken();
res.cookie("XSRF-TOKEN", token);
res.locals.csrfToken = token;
next();
});
app.use(function (req, res, next) {
var token = req.csrfToken();
res.cookie('XSRF-TOKEN', token);
res.locals.csrfToken = token;
next();
});
//upload link
app.post('/upload', (req, res) => {
console.log(req.files);
let file = req.files.image;
let date = new Date();
console.log('test post');
// image name
let imagename = date.getDate() + date.getTime() + file.name;
// image upload path
let path = 'public/uploads/' + imagename;
// create upload
file.mv(path, (err, result) => {
if (err) {
throw err;
} else {
// our image upload path
res.json(`uploads/${imagename}`)
}
})
})
Problem
But now after uploading the image to editor.js , an error occurs in server.js:
TypeError: Cannot read properties of undefined (reading 'image')
The variable req.files has become undefined
What is the problem?
'Content-Type': 'x-www-form-urlencoded',
You're overriding the Content-Type header the browser was sending with the request.
Since the new value is wrong, the server doesn't know how to parse the body.
Don't do that.
Related
like the title says, here is my server file, I have tried every solution I could find on google yet I am still getting CORS errors. specifically XHROPTIONShttps://slug-panel-api.onrender.com/login
server.js:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser')
const mongoose = require('mongoose')
const userSchema = require('../../SlugAPI/Schemas/SlugSchemas')
const divisionSchema = require('../src/SlugSchemas/DivisionSchemas/DivisionSchema')
const subDivisionSchema = require('../src/SlugSchemas/DivisionSchemas/SubDivisionSchema')
const teamSchema = require('../src/SlugSchemas/DivisionSchemas/TeamSchema')
const divisionMemberSchema = require('../src/SlugSchemas/DivisionSchemas/DivisionMemberSchema')
let CryptoJS = require('crypto-js')
const PORT = process.env.PORT || 8080;
const app = express();
app.use(cors({
origin: "https://slug-panel.onrender.com",
headers: {
"Access-Control-Allow-Origin": "https://slug-panel.onrender.com",
"Access-Control-Allow-Credentials": true
},
}));
mongoose.set("debug")
const usar_db = mongoose.createConnection("mongodb:/<username>:<password>#slug-db:27017/usarAdmin?authSource=admin");
const User = usar_db.model('User', userSchema)
const Division = usar_db.model('Division', divisionSchema)
const SubDivision = usar_db.model('SubDivision', subDivisionSchema)
const Team = usar_db.model('Team', teamSchema)
const DivisionMember = usar_db.model('Division_Member', divisionMemberSchema)
function generateUserRegistrationKey(username, discord_id, rank, authentication_level) {
let key = username + '/' + discord_id.toString() + '/' + rank + '/' + authentication_level
const ciphertext = CryptoJS.AES.encrypt(key, 'secret').toString()
return ciphertext
}
function decryptUserRegistrationKey(key) {
const bytes = CryptoJS.AES.decrypt(key, 'secret')
const originalText = bytes.toString(CryptoJS.enc.Utf8)
return originalText
}
app.post('/login', bodyParser.json(), async (req, res, next) => {
const user = req.body.username
let pw = req.body.password
pw = CryptoJS.SHA256(pw)
let exists = await User.findOne({username: user})
if (exists) {
if (pw.toString() === exists['password']) {
res.send({
token: 'test123'
})
} else {
res.send({
error: 'passwordNotFound'
})
}
} else {
res.send({
error: 'userNotFound'
})
}
});
app.post('/generate', bodyParser.json(), async function (req, res, next) {
let username = req.body.username
let discord_id = req.body.discord_id
let rank = req.body.rank
let authentication_level = req.body.authentication_level
let exists = await User.exists({discord_id: discord_id})
let regKey = generateUserRegistrationKey(username, discord_id, rank, authentication_level)
const newUser = User({
username: username,
discord_id: discord_id,
rank: rank,
regKey: regKey,
authentication_level: authentication_level,
})
if (!exists) {
newUser.save()
.then(r => console.log("User " + username + " added to db"))
res.send({regKey: regKey})
}
})
app.post('/register', bodyParser.json(), async function (req, res, next) {
let key = req.body.regKey
let pw = CryptoJS.SHA256(req.body.password).toString()
let decryptedKey = decryptUserRegistrationKey(key).split('/')
let exists = await User.find({regKey: key}, function(err, docs) {
if (err) {
console.log(err)
} else {
console.log('Result: ', docs)
console.log(pw)
}
}).clone()
if (!exists) {
res.send({user: null})
} else {
res.send(JSON.stringify(exists))
}
await User.findOneAndUpdate({regKey: key}, { is_registered: true, password: pw, authentication_level: decryptedKey[decryptedKey.length - 1]})
})
app.post('/createDivision', bodyParser.json(), async function (req, res, next) {
let div_name = req.body.division_name
let div_id = req.body.division_id
let exists = await Division.findOne({division_name: div_name}, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log(docs)
}
}).clone()
let idexists = await Division.findOne({division_id: div_id}, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log(docs)
}
}).clone()
if (!exists || !idexists) {
const newDivision = new Division({
division_name: div_name,
division_id: div_id
})
newDivision.save()
.then(() => console.log('Division ' + div_name + ' has been added to the db'))
res.send(JSON.stringify(newDivision))
} else {
res.send({errorcode: 420})
}
})
app.post('/createSubDivision/:divid', bodyParser.json(), async function (req, res, next) {
const division = req.params['divid']
const sub_name = req.body.subdivision_name
const sub_id = req.body.subdivision_id
let exists = await Division.findOne({division_id: division}, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log(docs)
}
}).clone()
if (exists) {
let subdivid_exists = await Division.findOne({
division_id: division,
subdivisions: {
$elemMatch: {subdivision_id: sub_id}
}
})
let subdiv_exists = await Division.findOne({
division_id: division,
subdivisions: {
$elemMatch: {subdivision_name: sub_name}
}
})
if (!subdivid_exists || !subdiv_exists) {
const subDiv = new SubDivision({
subdivision_name: sub_name,
subdivision_id: sub_id,
})
await Division.findOneAndUpdate({division_id: division}, { $push: {subdivisions: subDiv}})
console.log('subdivision ' + sub_name + ' added to: ' + exists.division_name)
res.send(JSON.stringify(subDiv))
} else {
res.send({division:'exists'})
}
}
})
app.listen(PORT, () => console.log('API is running on ' + PORT));
Tried every solution I could find both on random google websites and on stackoverflow. as stated previously it worked fine on the development server hosted locally.
for reference, here is how I am using fetch throughout the frontend
async function loginUser(credentials) {
return fetch('https://slugga-api.onrender.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': 'true'
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://slugga-api.onrender.com/login. (Reason: CORS request did not succeed). Status code: (null).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://slugga-api.onrender.com/login. (Reason: CORS request did not succeed). Status code: (null).
Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource. asyncToGenerator.js:6:4
Babel 6
c Login.js:20
React 11
bind_applyFunctionN self-hosted:1683
Wt self-hosted:1640
React 3
forEach self-hosted:4909
React 2
<anonymous> index.js:7
<anonymous> index.js:17
<anonymous> index.js:17
You're misusing those CORS headers, both on the client side and on the server side. You should familiarise better with CORS and with the API of Express.js's CORS middleware.
Client side
The Access-Control-Allow-Origin header is a response header; including it in a request makes no sense.
async function loginUser(credentials) {
return fetch('https://slugga-api.onrender.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': 'true' // incorrect
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
Server side
The headers property of your CORS config corresponds to request headers that you wish to allow, but what you've listed are response headers.
app.use(cors({
origin: "https://slug-panel.onrender.com",
headers: {
"Access-Control-Allow-Origin": "https://slug-panel.onrender.com", // incorrect
"Access-Control-Allow-Credentials": true // incorrect
},
}));
Instead, you most likely want something like
app.use(cors({
origin: "https://slug-panel.onrender.com",
headers: ["Content-Type"],
credentials: true,
}));
Fixed the issue by changing the CORS initialization in server.js
const app = express();
app.use(cors({
origin: "https://slug-panel.onrender.com"
}
))
app.options('*', cors())
It should redirect back to the "/" root url, but somehow it's not working properly.
it gives the 302 confirmation, but it wont get there
const fs = require("fs");
function requestHandler(req, res) {
const url = req.url;
const method = req.method;
if (url === "/") {
res.write("<html>");
res.write("<head><title> Minha primeira página! </title></head>");
res.write(
"<body><form action='/message' method='POST'><input type='text' name ='message'><button type='submit'>Enviar</button></form></body>"
);
res.write("</html>");
return res.end();
}
//console.log(req.url, req.method, req.headers);
if (url === "/message" && method === "POST") {
const body = [];
req.on("data", (chunk) => {
console.log(chunk);
body.push(chunk);
});
req.on("end", () => {
const parsedBody = Buffer.concat(body).toString();
const message = parsedBody.split("=")[1];
fs.writeFile("message.txt", message, (err) => {});
});
res.statusCode = 302;
res.setHeader = ("Location", "/");
return res.end();
}
}
Redirect to "/" after solving /message
You can use redirect to go to the home page like
res.redirect('/');
axios script.js file
const creatClient = async (client) => {
try {
const res = await axios({
method: 'POST',
withCredentials: true,
url: '/[url]',
data: client,
}).then(location.assign('/[newUrl]'));
} catch (error) {
console.log(error);
}
};
submitbtn.addEventListener('click', (e) => {
e.preventDefault;
const name = document.getElementById('name').value;
const phone = document.getElementById('phone').value;
const createdATT = new Date(document.getElementById('date').value);
const followUp = new Date(document.getElementById('date2').value);
const images = document.getElementById('img').value;
const insurance = document.getElementById('insurance').value;
const client = { name, phone, insurance, images, createdATT, followUp };
console.log(client);
client ? creatClient(...client) : console.log('no object created');
});
controller file
the console log for req.body [Object: null prototype] {*** the object ***}
const multer = require('multer');
const Client = require('../models/clientModel');
const multerStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/img');
},
filename: (req, file, cb) => {
const ext = file.mimetype.split('/')[1];
cb(null, `user-${Date.now()}.${ext}`);
},
});
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cd(console.log('select image'), false);
}
};
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter,
});
exports.uploadImages = upload.single('images');
//
exports.createClients = async (req, res, next) => {
try {
if (req.file) req.body.images = req.file.filename;
const newClient = { ...req.body };
await Client.create(req.body).then(
res.status(200).json({
status: 'success',
newClient,
})
);
} catch (err) {
console.log(err);
}
};
also with postman sending request give success response with no errors
i've tried location.replace() but also it didn't work for me
and is there another trick from server to get to the desired location out from client side
then accepts a callback as a parameter.
then(() => location.assign('/[newUrl]'))
It works fine when I use localhost. I am also using the correct URl in the frontend. It works for getting data and editing data but donot work when i try to post data using formdata using heroku URL.
Order.controller.js
const { cloudinaryUpload } = require('../middleware/cloudinary');
const Order = require('../Model/Order');
const addData= (req, res, next) => {
let data = JSON.parse(req.body.order);
data.files = req.files
console.log(data);
const OrderData = new Order({})
let mappedData = mapOrder(OrderData, data);
mappedData.save((err, saved) => {
if (err) {
console.log(err)
return next({
msg: err,
status: 400
})
}
res.json({
msg: "Submitted successfully"
})
})
// cloudinaryUpload(mappedData.referenceImage1.image)
// .then((response) => {
// mappedData.referenceImage1.image = response.public_id
// })
// .catch((error) => {
// console.log(err)
// return next({
// msg: error,
// status: 400
// })
// })
}
const GetData=(req, res, next) => {
Order.find({ orderStatus: "processing" }, (err, orders) => {
if (err) {
return next({
msg: err,
status: 404
})
}
res.json({
orders: orders
})
})
}
const EditData=(req, res, next) => {
let ID = req.query.id
Order.find({ OrderId: ID }, (err, order) => {
if (err) {
return next({
msg: err,
status: 404
})
}
order[0].orderStatus = "ReadyToPickUp"
order[0].save((err, updated) => {
if (err) {
return next({
msg: err
})
}
res.json({
order: updated
})
})
})
}
function mapOrder(OrderData, data) {
OrderData.orderType = data.OrderType
OrderData.customer.fullname = data.name;
OrderData.customer.phone = data.phone;
OrderData.customer.optionalPhone = data.optionalPhone || "";
OrderData.customer.address = data.address;
OrderData.cakeDescription.weight = data.Weight;
OrderData.cakeDescription.flavor = data.Flavor;
OrderData.cakeDescription.shape = data.Shape;
OrderData.cakeDescription.eggless = data.eggless;
OrderData.cakeDescription.messageoncake = data.messageoncake || "";
OrderData.orderStatus = "processing";
if (data.files[0]) {
OrderData.referenceImage1.image = data.files[0].filename
}
if (data.files[1]) {
OrderData.referenceImage2.image = data.files[1].filename
}
OrderData.referenceImage1.note = data.ImageNote1
OrderData.referenceImage2.note = data.ImageNote2
OrderData.deliveryDate = data.date;
OrderData.deliveryTime = data.time;
OrderData.amount = data.Amount;
OrderData.advancePaid = data.advance;
return OrderData;
}
module.exports = {
addData,GetData,EditData
};
Imagehandler.js
const multer = require('multer');
const path = require('path');
const storage= multer.diskStorage({
destination: function(req,file,cb){
cb(null,path.join(process.cwd(),'/uploads'))
},
filename : function(req,file,cb){
cb(null,Date.now()+'-'+file.originalname)
}
})
const fileFilter=(req,file,cb)=>{
let image = file.mimetype // mimetype is type of image/jpeg
let fileType = image.split('/')
if(fileType[0] === "image" ){
cb(null,true)
}
else{
req.fileError = true;
cb(null,false)
}
}
var upload = multer({ storage: storage, fileFilter: fileFilter})
module.exports = upload
app.route.js
const router = require('express').Router();
const upload = require('./middleware/imagehandler');
const {addData,EditData,GetData} = require('./api/order.controller');
router.post('/addorder',upload.array('images',3),addData)
router.put('/editorder',EditData)
router.get('/getorder',GetData)
module.exports = router
App.js
const express = require('express');
const app = express();
const path = require('path')
const cors = require('cors')
const orderRouter = require('./api/order.controller');
const dotenv = require('dotenv').config(); // it checks defalut .env file if present and cofigure it.
require('./db.initialize');
app.use(cors());
app.use('/api',orderRouter);
app.use('/files',express.static(path.join(process.cwd(),'/uploads')))
app.use((req,res,next)=>{
res.json({
msg: "Error occured",
satus: 400
})
})
// it recieves error from next and we can use the error information
app.use((error,req,res,next)=>{ // error handler
res.status(error.status || 400)
res.json({
msg : error.msg || "Error",
status: error.status || 400
})
})
app.listen(process.env.PORT || 8080,(err,done)=>{
if(err){
console.log("Server failed")
}
else{
console.log("Server Started at",process.env.PORT);
}
})
db.initialize.js
const mongoose = require('mongoose');
const {DB_NAME} = require('./configs/index.config')
let DBURL=`mongodb+srv://bibekdb:password#webapp.iqxjb.mongodb.net/${DB_NAME}?retryWrites=true&w=majority`
mongoose.connect(process.env.MONGODB_URI || DBURL,{
useNewUrlParser: true,
useUnifiedTopology: true
})
.then((done)=>{
console.log("Heroku");
console.log("DB connected successfully..",process.env.DB_NAME);
})
.catch((error)=>{
console.log("Db connection failed")
})
httpClient.js
I am hitting the correct API using xhr and sending data using formdata. I dont know what i am missing
import axios from 'axios';
const base_URL = process.env.REACT_APP_BASE_URL;
const https = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
timeout: 10000,
timeoutErrorMessage: "Server timeout",
responseType: "json",
headers: {
'Content-Type': "application/json"
}
})
const GET =(url)=>{
return https.get(url)
}
const EDIT =(url,params)=>{
return https.put(url,null,{
params
})
}
const Upload = (method, url, data = {}, files = []) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const formdata = new FormData()
const orderData = JSON.stringify(data);
formdata.append('order',orderData)
files.forEach((file, index) => {
formdata.append('images', file, file.name)
})
xhr.onreadystatechange=()=> {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.response)
}
else {
reject(xhr.response)
}
}
}
xhr.open(method, `${base_URL}${url}`)
xhr.send(formdata);
})
}
I am using gTTS module to convert text to .mp3 saving it temporarily. After saving I am try to stream the file but when I look at response object returned by the endpoint the arraybuffer looks empty.
const express = require('express')
, router = express.Router()
, bodyParser = require('body-parser')
, gtts = require('node-gtts')('en')
, path = require('path')
, filePath = path.join(__dirname, 'temp', 'temp.mp3')
, fs = require('fs')
, ms = require('mediaserver')
router.use(bodyParser.urlencoded({
extended: true
}));
router.use(bodyParser.json());
router.get('/speech', function(req, res) {
console.log("query", req.query.text);
saveFile(req.query.text,req.query.lang)
.then(response => {
console.log('looking for file', filePath)
fs.exists(filePath, (exists) => {
if (exists) {
// console.log('going to stream');
// ms.pipe(req, res, filePath);
// console.log("findigh");
const stat = fs.statSync(filePath)
const fileSize = stat.size
const range = req.headers.range
console.log('size ', fileSize);
if (range) {
const parts = range.replace(/bytes=/, "").split("-")
const start = parseInt(parts[0], 10)
const end = parts[1] ? parseInt(parts[1], 10) : fileSize-1
const chunksize = (end-start)+1
const file = fs.createReadStream(path, {start, end})
const head = {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'audio/mp3',
}
res.writeHead(206, head);
file.pipe(res);
} else {
const head = {
'Content-Length': fileSize,
'Content-Type': 'audio/mp3',
}
res.writeHead(200, head)
fs.createReadStream(filePath).pipe(res)
}
} else {
console.log('file not found');
res.send('Error - 404');
res.end();
}
})
})
.catch(err => {
console.log('error in saving file' ,err);
});
});
const saveFile = (text, language) => {
return new Promise((resolve, reject) => {
gtts.save(filePath, text, function() {
console.log('create file')
resolve("done");
})
});
}
module.exports = router`
The fetch call looks like this:
fetch(`/speech?lang=en&text=${translationBody.value}`, {
method:'GET',
headers: new Headers({'content-type': 'application/json'})
})
.then(res => res)
.then(res => console.log(res))
.catch(err => console.log('err', err))
Is there something wrong in the endpoint or should I change my fetch call?
Yes, you do need a bit of extra footwork here, setting a couple of headers. Sample code would look like this:
const http = require('http');
const fileSystem = require('fs');
const path = require('path');
http.createServer(function(request, response) {
const filePath = path.join(__dirname, 'file.mp3');
const stat = fileSystem.statSync(filePath);
response.writeHead(200, {
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size
});
const readStream = fileSystem.createReadStream(filePath);
readStream.pipe(response);
})
.listen(3000);