I'm trying to do a notify function in my website. The button his on HTML (client side) and on press calls a node js function to execute a python script that sends an e-mail to myself (tested and working).
This is my code on the client side (index.html)
<body>
...
<div class="form-popup" id="myForm">
<form class="form-container" name="form-owner">
<h1>Notify Owner</h1>
<label><b>Name</b></label>
<input id="name" style="width:90%" type="text" placeholder="Enter your name" required>
<label><b>Message</b></label>
<input id="context" style="width:90%" type="text" placeholder="Enter Reason" required>
<button type="submit" class="btn" onclick="notifyOwner()">Submit</button>
<button type="button" class="btn cancel" onclick="closeForm()">Close</button>
</form>
</div>
</div>
...
The code on the server side (app.js)
const express = require('express');
const child_process = require('child_process')
const app = express()
const cheerio = require('cheerio');
const port = 80
'use strict';
var request = require('request');
...
app.post("/api/notify", async (req, res) => {
try{
const $ = cheerio.load('<input id="name" style="width:90%" type="text" placeholder="Enter your name" required>');
var subject = $('[id=name]').text();
var body = "ok";
child_process.execSync('python3 sendEmail.py ' + subject + " " + body);
}catch(error){
console.error(error);
}
});
The varialbe 'subject' turns out as null and the script is not runned because that argument is missing
I believe there's some confusion here. Cheerio is used to parse and manipulate HTML strings, but that's not what your front end code is sending. The string you're telling Cheerio to manipulate has no relationship to the request form payload in any way, nor is it a necessary tool for processing the POST request.
You appear to be using JS to submit JSON or form data to the server (as opposed to an HTML form action). req.body and req.query would contain this parsed payload respectively, depending on how your server is set up.
Here's an example of how you can set this up using JSON. Note that I've promisified the exec function to avoid blocking the event loop with a synchronous subprocess call.
Also, the form name and context don't seem to correspond well with subject and body--I assume you'll make this consistent.
You'll want to escape and properly quote your subprocess argument string as well.
public/index.html:
<!DOCTYPE html>
<html lang="en">
<head><title>Test</title></head>
<body>
<form>
<h1>Notify Owner</h1>
<label>
<b>Name</b>
<input id="name" placeholder="Enter your name" required>
</label>
<label>
<b>Message</b>
<input id="context" placeholder="Enter Reason" required>
</label>
<button type="submit">Submit</button>
</form>
<script>
document.querySelector("form").addEventListener("submit", event => {
event.preventDefault();
const name = event.target.querySelector("#name").value;
const message = event.target.querySelector("#context").value;
fetch("/api/notify", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({name, message})
})
.then(res => {
if (!res.ok) {
throw Error(res.statusText);
}
return res.json();
})
.then(data => {
console.log(data);
event.target.reset();
})
.catch(err => console.error(err));
});
</script>
</body>
</html>
server.js:
const {promisify} = require("util");
const exec = promisify(require("child_process").exec);
const express = require("express");
const app = express();
app.use(express.json());
app.use(express.static("public"));
app.post("/api/notify", async (req, res) => {
const {name, message} = req.body; // TODO validate
// do stuff with name and message
console.log(name, message);
try {
//await exec(`python3 sendEmail.py ${subject} ${body}`);
res.json({message: "email sent"});
}
catch (err) {
res.json({error: "failed to send email"});
}
});
app.listen(8000, () => console.log("listening on port 8000"));
Related
I'm trying to create a contact form in react with node mailer but Im having the following issue:
Error shown in console on the browser
It says that 404 NOT FOUND but I dont know where is the mistake i have tried changing the listening part of the server file but no luck:
import React from 'react';
import { useState } from 'react';
import './contactForm.css';
import { Footer } from '../../containers';
const FORM_ENDPOINT = "";
const ContactForm = () => {
const [status, setStatus] = useState("Submit");
const handleSubmit = async (e) => {
e.preventDefault();
setStatus("Sending...");
const { name, email, message } = e.target.elements;
let details = {
name: name.value,
email: email.value,
message: message.value,
};
let response = await fetch("http://localhost:3000/contactForm", {
method: 'POST',
headers: {
"Content-type": "application/json;charset=utf-8",
},
body: JSON.stringify(details),
});
setStatus("Submit");
// let result = await response.json();
// alert(result.status);
};
return (
<div className='RO__ContactForm' id='contactForm'>
<div className='RO__ContactForm-title'>
<h3>Contact</h3>
<h1>I'm here to help you level up</h1>
</div>
<div className='RO__ContactForm-content'>
<div className='RO__ContactForm-content_description'>
<p>I'm just on click away to help you take your company
to the next level. Fill in the form to share more
details about the project or your favorite movie.
Either way, I'd love to talk.</p>
<p></p>
</div>
<form
className='RO__ContactForm-content_form'
action = {FORM_ENDPOINT}
onSubmit = {handleSubmit}
method = 'POST'
target='_blank'
>
<div className='RO__ContactForm-content_form_name'>
<div className='RO__ContactForm-content_form_nameTitle'>
<h5>What's your name?</h5>
</div>
<input
className='RO_ContactForm-content_form_nameInput'
type= 'text'
id='name'
name='name' required
/>
</div>
<div className='RO__ContactForm-content_form_email'>
<div className='RO__ContactForm-content_form_emailTitle'>
<h5>Your email</h5>
</div>
<input
className='RO__ContactForm-content_form_emailInput'
type='email'
id='email['
name='email' required
/>
</div>
<div className='RO__ContactForm-content_form_info'>
<div className='RO__ContactForm-content_form_infoTitle'>
<h5>What can I help you with?</h5>
</div>
<textarea
className='RO__ContactForm-content_form_infoContent'
id='message'
name='message' required
/>
</div>
<div className='RO__ContactForm-content_form_button'>
<button type='submit'>{status}</button>
</div>
</form>
</div>
<div className='RO__ContactForm-footer'>
<Footer />
</div>
</div>
)
}
export default ContactForm
and here is the server file that I use to initialize the server to send the email. That file is made for the contact form front end, and the following file (server.js) its the following;:
const express = require("express");
const router = express.Router();
const cors = require("cors");
const nodemailer = require("nodemailer");
const app = express();
app.use(cors());
app.use(express.json());
app.use('/', router);
app.listen(3000, () => console.log("Server Running"));
const contactEmail = nodemailer.createTransport({
service: "Hotmail",
auth: {
user: '*************#hotmail.com',
pass: '************',
},
});
contactEmail.verify((error) => {
if(error){
console.log(error);
}
else{
console.log("Ready to Send");
}
});
router.post("/contactForm", (req, res) => {
const name = rew.body.name;
const email = req.body.email;
const message = req.body.message;
const mail = {
from: name,
to: 'invariant.rafael.3096#getDefaultNormalizer.com',
subject: 'Contact Form Submission',
hmtl: '<p>Name: ${name} </p> <p>Email: ${email}</p> <p>Message: ${message}</p>',
};
contactEmail.sendMail(mail, (error) => {
if(error){
res.json({ status: 'Error'});
}
else{
res.json({ status: 'Message Sent' });
}
});
});
React typically hosts on port 3000. Change the express server to run on a different port: app.listen(CHANGEME, () => console.log("Server Running"));
If that doesn't work try making some fetch requests somewhere else e.g. https://api.publicapis.org/entries which is a free testing API. Then you will know whether the problem is with your server or not.
I am trying to create a basic user registration system. All I just want right now is that once I register with the HTML form, I should be able to console.log it in the server. I tried doing this using fetch but I get this error:
Fetch API cannot load file:///C:/api/register. URL scheme "file" is not supported.
This is the code:
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Register</title>
</head>
<body>
<h1>Registration</h1>
<form id="reg-form">
<input type="text" placeholder="Username" id="username" autocomplete="off"/>
<input type="password" placeholder="Password" id="password"/>
<input type="submit" value="Submit Form"/>
</form>
</body>
<script>
const form = document.getElementById('reg-form')
form.addEventListener('submit', registerUser)
async function registerUser(event) {
event.preventDefault()
const username = document.getElementById('username').value
const password = document.getElementById('password').value
const result = fetch('/api/register', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({username, password})
}).then(res => res.json())
console.log(result)
}
</script>
</html>
Express Server:
const express = require('express')
const path = require('path')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json())
app.post('/api/register', async (req, res) => {
console.log(req.body)
res.json({status: 'ok'})
})
app.listen(4000, () => console.log('Server up at 4000'))
I also tried doing the POST request directly from the HTML form element by setting the method attribute to POST and the action attribute pointing to the post route in my express server. When I try to submit the form, it redirects me to an empty page that says "Your file couldn't be accessed. It may have been moved, edited, or deleted."
What can I do to fix this?
You're opening the html file manually, not serving it from the server, so /api/register isn't going to your backend. Either serve the file from your server, or fetch localhost:4000/api/register
To serve your file from the server, you can do this:
const path = require('path');
...
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, '../relative/path/to/index.html'));
});
Instead of fetching /api/register try http://localhost:4000/api/register
also to print the result try this:
fetch('http://localhost:4000/api/register', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({username, password})
})
.then(res => res.json())
.then(data => console.log(data))
I am trying to create a small recipe app to learn express and mongoDB.
I created a form and I am able to get the values back and sent to the DB , but what i would like would be to be able to add ingredients in an array in the form and then export the array alongside the other values.
Here is my server file
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/recipes', {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => {
console.log('connected to DB')
})
.catch(e => {
console.log('Something went wrong');
console.log(e);
})
const ingredients = require('atittude');
const express = require('express');
const app = express();
const path = require ('path');
const methodOverride = require ('method-override');
const fs = require('fs');
const multer = require('multer');
const port = 3000;
app.use(express.static(path.join(__dirname, '/public')))
app.use(express.urlencoded({extended: true}))
app.use(methodOverride('_method'))
app.set('view engine', 'ejs')
app.set('views', path.join(__dirname, '/views'))
const recipeSchema = new mongoose.Schema({
name : {
type: String,
required: true
},
category : {
type : [String],
required : true,
enum : ['comfort', 'healthy', 'favorite', 'quick', 'cheap', 'fancy']
},
time : Number,
ingredient: [String],
recipeBody : String,
img: {
data: Buffer,
contentType: String
}
})
const Recipe = mongoose.model('Recipe', recipeSchema);
app.get('/', (req, res) => {
res.render('home')
})
app.post('/add', (req,res) => {
const {name, category, time, ingredient, recipeBody} = req.body
console.log(`${name},${category},${time},${ingredient},${recipeBody},`)
const img = req.file
const recipe = new Recipe({name, category, time, ingredient, recipeBody})
recipe.save()
.then(data => {
console.log("It worked.")
console.log(data);
})
.catch(err => {
console.log('error : ')
console.log(err)
})
res.render('home', {name, category, time, ingredient, recipeBody})
})
app.get('/add', (req, res) => {
res.render('new')
})
app.listen(3000, () => {
console.log(`Listening on port ${port}`);
})
Here is my ejs file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Recipe</title>
</head>
<body>
<h1>Add a new recipe</h1>
<form action="/add" method="POST" id="mainform" enctype="x-www-form-urlencoded">
<label for="name">Name : </label>
<input type="text" placeholder="Name of the recipe..." name="name">
<label for="category">Categories : </label>
<select id="category" name="category" size="6" multiple>
<option value="comfort">Comfort</option>
<option value="healthy">Healthy</option>
<option value="favorite">Kids' favorites</option>
<option value="quick">Quick</option>
<option value="cheap">Cheap</option>
<option value="fancy">Fancy</option>
</select>
<label for="time">Time : </label>
<input name="time" type="text">
<label for="ingredient">Add ingredient </label>
<input name="ingredient" id="ingredient" type="text">
<button id="addingredient">Add !</button>
<textarea id="recipe" name="recipeBody" rows="4" cols="50">
Recipe here...
</textarea>
<input type="file" name="img" />
<button>Submit</button>
</form>
<ul id="ingList">
</ul>
<script type="module" src="/js/atittude.js"></script>
</body>
</html>
and here is my js file to add ingredients to the page
const adding = document.querySelector('#addingredient');
let currentIngredient = document.querySelector('#ingredient');
let ingredients = [];
const list = document.querySelector('#ingList');
adding.addEventListener("click", (event) =>{
event.preventDefault()
ingredients.push(currentIngredient.value)
console.log(ingredients);
const newIngredient = document.createElement('li')
newIngredient.innerText = ingredients[ingredients.length-1];
list.append(newIngredient);
export {ingredients};
})
I get an error atittude.js:13 Uncaught SyntaxError: Unexpected token 'export
I would like to be able to export my ingredients array to app.js to then send it to mongo.
Or should i do code the option to add ingredients to the list server side on in my ejs file using <%= ?
Sorry if some of the code is bad practice i'm learning.
Thanks a lot.
that's good that you're learning. If I get your question correctly, you are trying to send the array of ingredients to your server in order to save it in your DB. There are many ways to solve your problem here and they all involve sending a request from your client (the browser with the rendered ejs file) to your server (your nodejs app).
A straightforward way you can do this is to use the fetch api (you can check out the Mozilla Docs, they have excellent tutorias) to POST the ingredients like so:
fetch('/add', {
method: 'post',
body: JSON.stringify(ingredientsObj)
})
.then(function(response) {
// Do stuff with the response
})
.catch(function(error) {
console.log('Looks like there was a problem: \n', error);
});
You might want to consider securing your request with access controls and other parameters, as well as sanitizing your input to make sure it arrives in the right format you expect.
I am trying to make a post request from a html form and cant figure out where im going wrong.
> <form action="/api" method="POST">
<label for="username">username or email address</label>
<input name="username" id="username" type="text">
<label for="password">password</label>
<input id="password"name="password" type="text">
<button >Log in</button>
</form>
here is my main javascript file for the html (not the server)
"use strict"
let options = {
headers:{
"Content-Type" : "application/json"
},
method: "POST",
}
// fetch("/api",options)
And here is my node js server
"use strict"
//Installing express
let express = require(`express`)
let app = express()
app.use(express.json())
//running the server
app.listen(3000,()=>{
console.log("server is running boi");
})
//Middleware to load the static content
app.use(express.static(`public`))
//Database stuff
let Datastore = require('nedb')
let db = new Datastore({ filename: 'database.db' });
db.loadDatabase()
db.insert({username:"sid", password:"westham"})
//Handler for any post requests made
app.post(`/api`,(req,res)=>{
console.log("request was made");
console.log(req.body);
})
Two Observations
No middleware found in your server.js file for handling form data,
use body-parser http://expressjs.com/en/resources/middleware/body-parser.html
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true });
In your html form, if you're not submitting form with JavaScript then mentiod button type as submit
<button type="submit" >Log in</button>
This is my first app doing it with node.js and express. This is a basic app where I connect to an external API to show temperature and take a user input "city and feeling" and show it to the UI. I can't get the URL right. I don't know why.
I ran the app and entered data in the city and feeling text area, I debugged the app.js file and found that when it tries to fetch the URL I'm passing its data it gives the error "400 bad requests". What am I doing wrong?
the server.js
// Setup empty JS object to act as endpoint for all routes
projectData = {};
// Require Express to run server and routes
const express = require('express');
// Start up an instance of app
const app = express();
/* Middleware*/
//body-parser as the middle ware to the express to handle HTTP POST
const bodyParser = require('body-parser');
//Here we are configuring express to use body-parser as middle-ware.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Cors for cross origin allowance
const cors = require('cors');
app.use(cors());
// Initialize the main project folder , this line allows talking between server and client side
app.use(express.static('website'));
// Setup Server
const port = 8000;
const server = app.listen(port , ()=>{console.log(`the server running on localhost: ${port}`);});
//GET function
app.get('/fakeData' , getFakeData); //string represents a url path >> / means home
function getFakeData (req,res) {
// body...
console.log(projectData);
res.send(projectData);
}
var project = [];
app.get('/all', sendData);
function sendData (request, response) {
response.send(project);
console.log(project);
};
//POST function
app.post('/addAnimal' ,addAnimal);
function addAnimal (req,res) {
// body...
newEntry = {
date: req.body.date,
temp: req.body.main.temp,
feeling: req.body.feeling
}
project.push(newEntry)
res.send(project)
console.log(project)
}
website/app.js
/* Global Variables */
//let baseURL = 'http://api.openweathermap.org/data/2.5/weather?q=Egypt&APPID=';
let baseURL = `http://api.openweathermap.org/data/2.5/weather?city=`;
let apiKey = '&APPID=bb95e29dbedc4d929be90b0dd99954e0';
// Create a new date instance dynamically with JS
let d = new Date();
let newDate = d.getMonth()+'.'+ d.getDate()+'.'+ d.getFullYear();
//GET request to handle user input
document.getElementById('generate').addEventListener('click', performAction);
function performAction(e){
//Take user input
//const zipcode = document.getElementById('zip').value; //no
const feeling = document.getElementById('feelings').value;
const city = document.getElementById('zip').value;
//the fake api call
//getAnimal('/fakeAnimalData')
getTemp(baseURL ,city , apiKey )
.then (function(data) {
// body...
console.log(data)
postData('/addAnimal' ,{temp:data.main.temp ,date:newDate, feeling:feeling} )
//updateUI()
})
.then(
updateUI()
)
};
const getTemp = async(baseURL ,city , apiKey)=>{
const res = await fetch(baseURL+city+apiKey)
try{
const data = await res.json();
console.log(data)
return data;
}
catch(error){
console.log("error" , error);
}
}
//make a POST request to our route , POST to store locally user-input data
const postData = async(url='' , data={})=>{
//console.log(data);
const response = await fetch(url , {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
// Body data type must match "Content-Type" header
body: JSON.stringify(data),
});
try {
const newData = await response.json();
console.log(newData);
return newData
}catch(error){
console.log("error", error);
}
}
const updateUI = async () => {
const request = await fetch('/all');
try{
const allData = await request.json()
console.log(allData);
document.getElementById('date').innerHTML = allData[0].date;
document.getElementById('temp').innerHTML = allData[0].temp;
document.getElementById('content').innerHTML = allData[0].feeling;
}catch(error){
console.log("error", error);
}
}
website/index.js
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Weather Journal</title>
<link href="https://fonts.googleapis.com/css?family=Oswald:400,600,700|Ranga:400,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id = "app">
<div class ="holder headline">
Weather Journal App
</div>
<div class ="holder zip">
<label for="zip">Enter Zipcode here</label>
<input type="text" id="zip" placeholder="enter zip code here">
</div>
<div class ="holder feel">
<label for="feelings">How are you feeling today?</label>
<textarea class= "myInput" id="feelings" placeholder="Enter your feelings here" rows="9" cols="50"></textarea>
<button id="generate" type = "submit"> Generate </button>
</div>
<div class ="holder entry">
<div class = "title">Most Recent Entry</div>
<div id = "entryHolder">
<div id = "date"></div>
<div id = "temp"></div>
<div id = "content"></div>
</div>
</div>
</div>
<script src="app.js" type="text/javascript"></script>
</body>
</html>
There seems no issue in your url making. I have opened http://api.openweathermap.org/data/2.5/weather?city=cairo&APPID=bb95e29dbedc4d929be90b0dd99954e0 in browser and its returning HTTP 400 Bad Request as status code and due to 400 status code the browser is telling that the request failed.
Here is response. {"cod":"400","message":"Nothing to geocode"}
The original issue, it seems, is your city parameter that your are sending.
However if you change city parameter in your url to q, it seems to work.
http://api.openweathermap.org/data/2.5/weather?q=cairo&appid=bb95e29dbedc4d929be90b0dd99954e0
Here is response. {"coord":{"lon":31.25,"lat":30.06},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base":"stations","main":{"temp":295,"feels_like":293.65,"temp_min":294.82,"temp_max":295.15,"pressure":1015,"humidity":64},"visibility":10000,"wind":{"speed":4.1,"deg":290},"clouds":{"all":75},"dt":1584185538,"sys":{"type":1,"id":2514,"country":"EG","sunrise":1584158752,"sunset":1584201751},"timezone":7200,"id":360630,"name":"Cairo","cod":200}