I'm trying to build a web platform that where you can register and adapt your profile. However, I'm struggling with the editing part. Registration and Login are fine, but the rest gives an HTTP 500.
So here's what I did:
User Scheme for Mongoose:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
//enhanced userSchema
var UserSchema = new Schema({
user_id: Schema.ObjectId,
username: {type :String, required : true, unique : true}, //serves as unique identifier
password: {type : String, required: true},
name: {type : String, required : true},
surname: String,
created_at : Date,
updated_at : Date,
skills: [{type : String}],
lectures: [{type : String}],
groups: [{type : String}] //todo: Change for later cross referencing with group schemes
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
Followed by the Routing:
var express = require('express');
var router = express.Router();
var auth = require("../controllers/AuthController.js");
var profile = require ("../controllers/ProfileController");
// restrict index for logged in user only
router.get('/', auth.home);
// route to register page
router.get('/register.html', auth.register);
// route for register action
router.post('/register.html', auth.doRegister);
// route to login page
router.get('/login.html', auth.login);
// route for login action
router.post('/login.html', auth.doLogin);
// route for logout action
router.get('/logout.html', auth.logout);
//route to profile
router.get('/profile.html', profile.goToProfile);
//route for changing profile
router.post('/profile.html', profile.changeProfile);
module.exports = router;
And the profileController
/**
* Controller for editing the profile
*/
var mongoose = require("mongoose");
var passport = require("passport");
var User = require("../models/User");
var path = require('path');
//Change Name
var profileController = {};
//go to Profile
profileController.goToProfile = function (req, res){
res.sendFile(path.resolve('login.html')), {user : req.user};
}
profileController.changeProfile= function (req, res){
console.log("REQUEST: " + req.body.toString());
if (req.body.surname.isEmpty()){
}
else {
User.findByIdAndUpdate(req.user._id, { $set: { surname: req.body.surname }}, { new: true }, function (err, User) {
if (err) {
console.log(err.toString());}
res.alert('Changed surname');
console.log('changed surname')
});
};
if (req.body.name.isEmpty()){}
else {
User.findByIdAndUpdate(req.user._id, { $set: { name: req.body.name }}, { new: true }, function (err, User) {
if (err) {
console.log(err.toString());}
res.alert('Changed name');
console.log('changed name')
});
};
if (req.body.skills.length === 0){}
else {
User.findByIdAndUpdate(req.user._id, { $set: { skills: req.body.skills }}, { new: true }, function (err, User) {
console.log("Old Skills: " + User.skills.toString());
if (err) {
console.log(err.toString());}
console.log("New skills: " + User.skills.toString());
console.log('changed skills')
});
}
};
module.exports = profileController;
Which gets its data from this HTML form:
<!-- register container -->
<div class="container">
<form role="form" action="profile.html" method="post" style="max-width: 300px;">
<h2 class="form-heading">Your Profile</h2>
<input type="text" name="name" placeholder="Name" class="form-control" />
<input type="text" name="username" placeholder="Username" class="form-control" />
<input type="text" name="surname" placeholder="Last Name" class="form-control"/>
<input type="text" name="skills[]" placeholder="Your skills" class="form-control"/>
<button type="submit" class="btn btn-lg btn-primary btn-block">Save</button>
</form>
</div>
I'm very sorry for my bad code quality. This resulted from a very long day of working on it, but I simply couldn't figure out (even with tutorials and stack overflow) what went wrong.
The result is a 500 Internal Server Error.
Where is your PUT request? To update data, you need to use a PUT request. POST is for adding new entries.
Related
I am building a form that is pre-populated by the results of an axios get request to a nodejs API that returns an array (stored in MySQL). I asked this question yesterday, implemented a solution that was provided and it worked. The next day I tested it thoroughly and it turns out that on submission only the edited fields were passed but not the values from the unedited fields.
I can get the data to map onto the form, but i cannot edit the form. The idea is for it to be an "edit user" form. I suspect the issue is in the onChange portion of the input field.
The form is accessed from a table that is also mapped with the results of a get request. Upon clicking the edit button, the userID from the table row is passed to the edit form through useNavigate and useLocation (I can add this portion of code if needed).
Backend
Here is the API controller that queries the MySQL database and then sends to the frontend:
export const edit = async (req, res) => {
db.query(
"SELECT * FROM userIndex WHERE userID = ?",
[req.params.userID],
(err, rows) => {
if (!err) {
res.send(rows);
} else {
console.log(err).res.send({ alert: err });
}
}
);
};
Here's an example result of the query above:
[
{
"userID": 143,
"firstName": "Kenan",
"lastName": "Erasmus",
"role": "student",
"email": "kenan#gmail.com",
"password": "$2b$12$v3s0D6cNkGwM3/IWXPdv..TRfRZLmDNuZBfrWlUCt4vKnyRi75jWe",
"createdAt": "06/10/2022, 13:56:51",
"updatedAt": "07/10/2022, 19:45:46",
"lastLogin": null
}
]
Frontend
Here is the portion of code that performs the request to the API:
useEffect(() => {
const config = {
headers: { "x-auth-token": token },
};
const fetchData = async () => {
const results = await api.get("/users/edituser/" + userID, config);
setRows(results.data);
setFirstName(rows.firstName)
};
fetchData();
}, [setRows, userID, token]);
State for "rows" (set on API response):
const [rows, setRows] = useState([]);
const [firstName, setFirstName] = useState("");
And finally, an example input field:
<input
type="text"
className="form-control"
id="inputEmail4"
placeholder="First Name"
name="firstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
></input>
I have only included the "firstName" input as an example. In total, this form has about 6 fields.
I would greatly appreciate any assistance with this. Let me know if I can supply any more code.
Found a workaround that seems quite logical to me. I initialised new blank states for each of the input fields
const [firstName, setFirstName] = useState("");
Then mapped the form with "rows" and set each field value to its correspond state (as seen in the useEffect below)
Frontend useEffect:
useEffect(() => {
const config = {
headers: { "x-auth-token": token },
};
const fetchData = async () => {
const results = await api.get("/users/edituser/" + userID, config);
setRows(results.data);
setFirstName(results.data[0].firstName);
setLastName(results.data[0].lastName);
setEmail(results.data[0].email);
setPassword(results.data[0].password);
setRole(results.data[0].role);
};
fetchData();
}, [setRows, userID, token]);
Example input field:
<input
type="text"
className="form-control"
id="inputEmail4"
placeholder="First Name"
name="firstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
></input>
By doing it this way, the form maps through "rows", and then each input field immediately gets a new state once typing occurs.
I know it's ugly, but it is effective. I'm new to coding so I don't know all the ins and outs of React yet - but this solution works for me. Hope this helps anyone looking for a solution to the same issue!
To begin with, I'm making a simple social media application. I'm trying to submit a form which has text, images, and videos. My frontend where the form is submitted is made with React and server is ran with node.js mounted on nginx. I was trying to append the inputted files into FormData with code below:
handleSubmit = function (e) {
e.preventDefault();
const formData = new FormData();
formData.append("textBody", this.state.textBody)
for (let i = 0; i < this.state.imgInput.length; i++) {
formData.append("imgInput", this.state.imgInput.files[i], "img"+i.toString())
fetch("mywebsite.com/api/submitArticle", {
body: formData,
method: "POST",
credentials: 'include',
}).then((response) => console.log(response))
return false;
}.bind(this)
handleChange = function (e) {
e.preventDefault();
if (e.target.name === 'imgInput') {
this.setState({
imgInput: e.target.files,
showSpan: false
})
}
}.bind(this)
<form onSubmit={this.handleSubmit}>
<textarea id='textBody' name='textBody' onFocus={removeSpan} onBlur={checkSpanOn} onChange={this.handleChange}/>
<input type="file" id="imgInput" name="imgInput" accept="image/*" ref={this.imgRef} multiple={true} onChange={this.handleChange}/>
<input type="submit" id="submitButton" name="submitButton" formEncType="multipart/form-data" />
</form>
But React gave me this error upon submitting the form:
TypeError: Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'.
at "formData.append("imgInput", this.state.imgInput.files[i], "img"+i.toString())".
So when I console logged what e.target.files before setState in handleChange, I got normal FileList with all the image files listed. But when I console loggedd this.state.imgInput after setState in handleChange, I got String of C://fakepath/filename, not fileList. (Initially state.imgInput was null. When I saw other examples and codes, e.target.files was fileList so I'm puzzled elsewhere I made mistake.
I was spending half my day on this problem and I'm 5 sec before fainting so any advice would be appreciated :) Thank you for reading.
yes this happening because the event is gone you need to store the event.target in variable + the files will be in imgInput not imgInput.files so here it is:
handleSubmit = e => {
e.preventDefault();
const formData = new FormData();
formData.append("textBody", this.state.textBody);
for (let i = 0; i < this.state.imgInput.length; i++) {
formData.append("imgInput", this.state.imgInput[i], "img" + i.toString());
fetch("mywebsite.com/api/submitArticle", {
body: formData,
method: "POST",
credentials: "include"
}).then(response => console.log(response));
}
};
handleChange = e => {
e.preventDefault();
const target = e.target;
if (target.name === "imgInput") {
this.setState(current => ({
...current,
imgInput: target.files,
showSpan: false
}));
}
};
i deeply need help with this audio file upload to cloudinary issue I have had for several days now. I tried many times to make it work, but i am still struggling. I am a beginner backend developer, so please any help is appreciated.
It is an mp3 player App. When i upload a song, the title gets saved in DB, but the Audio isn't. This is the MP3 PLAYER page screenshot. It shows the title being saved and rendered from DB but not the audio file.
Audio upload form
<form class="ui form" action="/albums/<%= album._id %>/songs" method="POST" enctype="multipart/form-data">
<div class="field">
<label>Song Title:</label>
<input type="text" id="title" name="song[title]" placeholder="song title...." required>
</div>
<div class="field">
<label>Song file:</label>
<input type="file" id="song" name="audio" accept="audio/*" required>
</div>
<div class="field">
<input class="fluid ui green button" type="submit" id="submit" value="Enter">
</div>
Exit
</form>
Song model
var mongoose = require("mongoose");
//Album Schema
var audioSchema = new mongoose.Schema({
title: String,
audio: String,
date: {type: Date, default: Date.now()}
});
//exporting the Schema
module.exports = mongoose.model("Audio", audioSchema);
Backend code/ROUTE
var express = require("express"),
router = express.Router({mergeParams: true}),
middleware = require("../middleware"),
Album = require("../models/album"),
Song = require("../models/songs"),
multer = require("multer")
var storage = multer.diskStorage({
filename: function(req, file, callback) {
callback(null, Date.now() + file.originalname);
}
});
//uploader
var upload = multer({ storage: storage});
var cloudinary = require('cloudinary');
cloudinary.config({
cloud_name: 'proccess.env.CLOUDINARY_NAME',
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET
});
//Songs new Route
router.get("/albums/:id/songs/new", middleware.isLoggedIn, function(req, res) {
//find Album by id
Album.findById(req.params.id, function(err, album) {
if(err) {
console.log(err);
} else {
res.render("songs/new", {album: album});
}
});
});
//Song POST route
router.post("/albums/:id/songs", middleware.isLoggedIn, upload.single("audio"), function(req, res) {
cloudinary.uploader.upload(req.file.path, function(result) {
// add cloudinary url for the mp3 to the album object under audio property
req.body.song.audio = result.secure_url;
//find Album by ID
Album.findById(req.params.id, function(err, album) {
if(err) {
console.log(err);
res.redirect("/albums/" + req.params.id);
} else {
//Creating Album and saving it to DB
Song.create(req.body.song, function(err, song) {
if(err) {
console.log("Opps something went wrong!" + err);
res.redirect("back");
} else {
//save the song to DB
song.save();
//this saves the songs object inside
album.songs.push(song);
//save album
album.save();
res.redirect("/albums/" + album._id);
}
});
}
});
});
});
module.exports = router;
cloudinary.uploader.upload(req.file.path, resource_type: "video", function(result)
That's because you will need to use GridFS from MongoDB to store data from a file.
https://docs.mongodb.com/manual/core/gridfs/#use-gridfs
As you are using Mongoose, please check this module : https://www.npmjs.com/package/mongoose-gridfs
The mongoose-gridfs module wrap the gridfs-stream module, and seems to fit to binary data upload. If you want, you can still do it yourself, by following this tutorial : http://niralar.com/mongodb-gridfs-using-mongoose-on-nodejs/
(Update)Hi all i want to invite friend sent to mail using Nodejs. Mainly server side user and pass which can be use? and mail also can't sent then i tried many ways but unable to get the solution if any one knows the solution please help me.....
myplunker
HTML:-
<div id="container">
<center>
<input id="to" type="text" placeholder="Enter E-mail ID" /><br><br>
<input id="subject" type="text" placeholder="Write Subject" /><br><br>
<textarea id="content" cols="40" rows="5" placeholder="Message"></textarea><br><br>
<button id="send_email">Send Email</button>
</center>
<span id="message"></span>
</div>
Server:-
var smtpTransport = nodemailer.createTransport("SMTP",{
service: "Gmail",
use_authentication: true,
auth: {
user: "sample#gmail.com",
pass: "password"
}
});
app.get('/',function(req,res){
res.sendfile('index.html');
});
app.get('/send',function(req,res){
var mailOptions={
to : req.query.to,
subject : req.query.subject,
text : req.query.text
}
console.log(mailOptions);
smtpTransport.sendMail(mailOptions, function(error, response){
if(error){
console.log(error);
res.end("error");
}else{
console.log("Message sent: " + response.message);
res.end("sent");
}
});
});
The Following steps may help you:
Add var nodemailer=require("nodemailer") to the top of your server
script
Add var express=require("express"); var app=express() to the top of your server script
To Test that the email is being sent move what is in your app.get("/send") script outside the function and comment everything else (For Now) should Look similar to this:
var nodemailer=require("nodemailer");
var express=requre("express");
var app=express();
var smtpTransport = nodemailer.createTransport("SMTP",{
service: "Gmail",
use_authentication: true,
auth: {
user: "email#domain.com",
pass: "PASS"
}
});
var mailOptions={
to : "email#domain.com",
subject :"SUBJECT",
text : "MESSAGE"
}
console.log(mailOptions);
smtpTransport.sendMail(mailOptions, function(error, response){
if(error){
console.log(error);
}else{
console.log("Message sent: " + response.message);
}
});
/*
app.get('/',function(req,res){
res.sendfile('index.html');
});
app.get('/send',function(req,res){
var mailOptions={
to : req.query.to,
subject : req.query.subject,
text : req.query.text
}
console.log(mailOptions);
smtpTransport.sendMail(mailOptions, function(error, response){
if(error){
console.log(error);
res.end("error");
}else{
console.log("Message sent: " + response.message);
res.end("sent");
}
});
});*/
Make sure you have this turned on for the email you are trying to send an email with: https://www.google.com/settings/security/lesssecureapps
Make Sure your version of nodemailer is correct, It should be 0.71v for the setup you have: How to downgrade this node.js module to specific version and prevent automatic upgrade later?
Run the server script with a terminal: node fileName.js (If none of the tips help could you please copy the error stack?)
If everything works uncomment everything and delete the mailOptions and smtpTransport that are outside the app.get. Everything should work now...
Good Luck, And I'll be happy to help if you have any more problems.
I'm using SAPUI5 and I have a XML form that I wanna send the data to my REST service using Json Model.
I'm using the SAPUI5 MVC model to make my app.
How can I send the data to my server with REST and JSON?
sap.ui.controller("controller.NewTicket", {
onInit: function() {
this.router = sap.ui.core.UIComponent.getRouterFor(this);
this.router.attachRoutePatternMatched(this._handleRouteMatched, this);
},
_handleRouteMatched:function(evt){
if("NewTicket" !== evt.getParameter("name")){
return;
}
var id = evt.getParameter("arguments").id;
var model = new sap.ui.model.json.JSONModel({id:id});
this.getView().setModel(model,"data");
},
enviar:function() {
jQuery.sap.require("sap.m.MessageBox");
// open a fully configured message box
sap.m.MessageBox.show("Confirmar a abertura do chamado?",
sap.m.MessageBox.Icon.QUESTION,
"Confirmar",
[sap.m.MessageBox.Action.YES, sap.m.MessageBox.Action.NO],
function(sResult) {
if(sResult == sap.m.MessageBox.Action.YES)
{
var oModel = new sap.ui.model.json.JSONModel();
var aData = jQuery.ajax({
type : "POST",
contentType : "application/json",
url : "http://192.168.0.32:9082/maxrest/rest/mbo/sr/?description="+ **deviceModel.sr.description** +"&_format=json&_compact=true&_verbose=true",
dataType : "json",
async: false,
success : function(data,textStatus, jqXHR) {
oModel.setData({modelData : data});
sap.m.MessageBox.show("ABRIU");
},
error : function(data,textStatus, jqXHR) {
oModel.setData({modelData : data});
sap.m.MessageBox.show(textStatus);
}
})}
else
{
}
},
sap.m.MessageBox.Action.YES);
var deviceModel = new sap.ui.model.json.JSONModel({
sr : [{
description: "",
long_description: ""
}]});
deviceModel.setDefaultBindingMode("TwoWay");
sap.ui.getCore().setModel(deviceModel);
jQuery.sap.require("sap.m.MessageToast");
sap.m.MessageToast.show(deviceModel.getData().sr.description);
}
});
And the View...
<mvc:View xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m" xmlns:co="sap.ui.commons"
xmlns:f="sap.ui.layout.form" xmlns:core="sap.ui.core" controllerName="com.maximo.controller.NewTicket">
<Page id="NewTicket" enableScrolling="true" title="{i18n>newTicket}" >
<content>
<f:SimpleForm >
<core:Title level="H5"
text="O chamado será aberto em seu nome e você será o usuário afetado"/>
<Label text="Resumo"/>
<Input type="Text" maxLength="100" value="{/sr/description}"/>
<Label text="Detalhes"/>
<TextArea height="50%" cols="800" value="{/sr/long_description}"/>
</f:SimpleForm>
</content>
<customHeader>
<Bar>
<contentLeft>
<Button icon="sap-icon://nav-back" press="voltarMenu"/>
</contentLeft>
<contentMiddle>
<Label text="{i18n>newTicket}"/>
</contentMiddle>
</Bar>
</customHeader>
<footer>
<Bar>
<contentMiddle>
<Button id="btnSend" text="{i18n>send}" press="enviar" icon="sap-icon://add-activity-2"/>
</contentMiddle>
</Bar>
</footer>
</Page>
From my experience I found out that it's easier to use an OData model of "JSON" type.
var user = applicationContext.registrationContext.user;
var password = applicationContext.registrationContext.password;
var uri = "http://" + user + ":" + password + "#" + applicationContext.registrationContext.serverHost + ":8080/" + appId + "/"
var headers = {
//"Authorization" : "Basic " + btoa(applicationContext.registrationContext.user + ":" + applicationContext.registrationContext.password),
"X-SMP-APPCID" : applicationContext.applicationConnectionId
};
console.log("Try comunicating the first time");
var oModel = new sap.ui.model.odata.ODataModel(uri, {json : true}, user, password, headers, false, false, false);
oModel.setHeaders(headers);
oModel.read("/Brand", onSuccess);
function onSuccess(result) {
sap.ui.getCore()....getView().getModel("Brands").setData(result);
};
It's how I do all my requests, manual or automated (on manual event or page event).
For an "post" event, I used a token fech:
oModelRequest.setHeaders({
"Access-Control-Allow-Origin" : "*",
"Content-Type": "application/x-www-form-urlencoded",
"X-CSRF-Token":"Fetch"
});
// Declare a variable to handle the security token
var token;
// Create a read request to retreive the X-CSRF token
oModelRequest.read('/Brand', null, null, false,
function(oData, oResponse) {
if (oResponse.headers['x-csrf-token'] == undefined) {
//alert("Error on read process. No token ! Check read !");
}
token = oResponse.headers['x-csrf-token'];
},
function() {
alert(oModeli18n.getProperty("Brand_token_error"));
}
);
And after that I do the actual "POST" with the "Create" method:
// Set POST request header using the X-CSRF token
oModelRequest.setHeaders({
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "application/json",
"DataServiceVersion": "2.0",
"Accept": "application/atom+xml,application/atomsvc+xml,application/xml",
"X-CSRF-Token": token
});
// Call the create request
oModelRequest.create('/Brand', requestData, null,
function(oData, oResponse) {
alert (Success);},
function(oData) {
alert(Error));
alert(oData.response.body);}
);