I've been trying to implement the reset password functionality in my project which uses nodejs and loopback version 3.0 . Loopback provides in built method for this reset password functionality in the user.js.
When I run the project and and test the reset password it runs without giving any errors but the email is not received.
This is the inbuilt method given by loopback for password reset functionality.
User.resetPassword = function(options, cb) {
// console.log("options : "+options);
// console.log("cb : "+cb);
cb = cb || utils.createPromiseCallback();
var UserModel = this;
var ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL;
options = options || {};
if (typeof options.email !== 'string') {
var err = new Error(g.f('Email is required'));
err.statusCode = 400;
err.code = 'EMAIL_REQUIRED';
cb(err);
return cb.promise;
}
try {
if (options.password) {
UserModel.validatePassword(options.password);
}
} catch (err) {
return cb(err);
}
var where = {
email: options.email,
};
if (options.realm) {
where.realm = options.realm;
}
UserModel.findOne({where: where}, function(err, user) {
if (err) {
return cb(err);
}
if (!user) {
err = new Error(g.f('Email not found'));
err.statusCode = 404;
err.code = 'EMAIL_NOT_FOUND';
return cb(err);
}
// create a short lived access token for temp login to change password
// TODO(ritch) - eventually this should only allow password change
if (UserModel.settings.emailVerificationRequired && !user.emailVerified) {
err = new Error(g.f('Email has not been verified'));
err.statusCode = 401;
err.code = 'RESET_FAILED_EMAIL_NOT_VERIFIED';
return cb(err);
}
if (UserModel.settings.restrictResetPasswordTokenScope) {
const tokenData = {
ttl: ttl,
scopes: ['reset-password'],
};
user.createAccessToken(tokenData, options, onTokenCreated);
} else {
// We need to preserve backwards-compatibility with
// user-supplied implementations of "createAccessToken"
// that may not support "options" argument (we have such
// examples in our test suite).
user.createAccessToken(ttl, onTokenCreated);
}
function onTokenCreated(err, accessToken) {
if (err) {
return cb(err);
}
cb();
UserModel.emit('resetPasswordRequest', {
email: options.email,
accessToken: accessToken,
user: user,
options: options,
}
);
}
});
return cb.promise;
};
When i enter the email from loopback api for password reset it gives no errors in the console but the email is not working.
The method resetPassword is called during the process.Console log inside the method is printed as shown below.
{ email: '**********#gmail.com',
authorizedRoles: { '$everyone': true } }
[Function: callback]
The thing which confuses me is that the verify email method is working
which is also comes inbuilt in the user.js .The following is printed in the console when the verification email is sent.
mx resolved: [ { exchange: 'alt1.gmail-smtp-in.l.google.com', priority: 10 },
{ exchange: 'alt2.gmail-smtp-in.l.google.com', priority: 20 },
{ exchange: 'gmail-smtp-in.l.google.com', priority: 5 },
{ exchange: 'alt4.gmail-smtp-in.l.google.com', priority: 40 },
{ exchange: 'alt3.gmail-smtp-in.l.google.com', priority: 30 } ]
MX connection created: alt1.gmail-smtp-in.l.google.com
recv gmail.com>220 mx.google.com ESMTP 1si9238203plw.390 - gsmtp
send gmail.com>EHLO gmail.com
recv gmail.com>250-mx.google.com at your service, [112.135.5.40]
recv gmail.com>250-SIZE 157286400
recv gmail.com>250-8BITMIME
recv gmail.com>250-STARTTLS
recv gmail.com>250-ENHANCEDSTATUSCODES
recv gmail.com>250-PIPELINING
recv gmail.com>250 SMTPUTF8
send gmail.com>MAIL FROM:<hasikasadaruwan.mgtuk#gmail.com>
recv gmail.com>452 (IP, Sender) first encounter.
It would be a great help if anyone help me to solve this problem, I've been stuck here for days.
THANKS in advance.
You must handle resetPasswordRequest endpoint in your extended model like this.
MyUser.on("resetPasswordRequest", function(info) {
console.log(info.email); // the email of the requested user
console.log(info.accessToken.id); // the temp access token to allow password reset
var url = "http://**********";
var html =
'Click <a href="' +
url +
"?access_token=" +
info.accessToken.id +
'">here</a> to reset your password.</br><h2>Link will be expired in 15 minutes.';
//'here' in above html is linked to : 'http://<host:port>/reset-password?access_token=<short-lived/temporary access token>'
MyUser.app.models.Email.send(
{
to: info.email,
from: senderAddress,
subject: "Password reset",
html: html
},
function(err) {
if (err) return console.log("> error sending password reset email");
console.log("> sending password reset email to:", info.email);
}
); });
Provide URL of your form. On that form submit use reset-password endpoint.
Checkout this reference: loopback-example-user-management
This worked for me. Thank you!
Related
I am trying to integrate PayPal server-side payment on a website with a new v2 since v1 is deprecated. Since I had to include quite a lot of code from their git, i will post here everything for a working example
In v1 i could do it really easily:
app.get('/create', function (req, res) {
//build PayPal payment request
let payReq = JSON.stringify({
'intent': 'sale',
'redirect_urls': {
'return_url': 'http://localhost:8081/process',
'cancel_url': 'http://localhost:8081/cancel'
},
'payer': {
'payment_method': 'paypal'
},
'transactions': [{
'amount': {
'total': '7.47',
'currency': 'USD'
},
'description': 'This is the payment transaction description.'
}]
});
paypal.payment.create(payReq, function (error, payment) {
if (error) {
console.error(error);
} else {
//capture HATEOAS links
let links = {};
payment.links.forEach(function (linkObj) {
links[linkObj.rel] = {
'href': linkObj.href,
'method': linkObj.method
};
})
//if redirect url present, redirect user
if (links.hasOwnProperty('approval_url')) {
res.redirect(links['approval_url'].href);
} else {
console.error('no redirect URI present');
}
}
});
});
app.get('/process', function (req, res) {
let paymentId = req.query.paymentId;
let payerId = {'payer_id': req.query.PayerID};
paypal.payment.execute(paymentId, payerId, function (error, payment) {
if (error) {
console.error(error);
} else {
if (payment.state == 'approved') {
const payerCountry = payment.payer.payer_info.country_code;
const total = payment.transactions[0].amount.total;
const currency = payment.transactions[0].amount.currency;
savePayment(payerCountry, total, currency);
res.send('payment completed successfully ' + cnt++);
} else {
res.send('payment not successful');
}
}
});
});
The create endpoint basically creates the order, the paypal API returns hateos links and the controller says the browser should redirect to that link. Once redirected , if user approves payment on paypal site he is redirected to on of
'redirect_urls': {
'return_url': 'http://localhost:8081/process',
'cancel_url': 'http://localhost:8081/cancel'
},
process endpoints retrieves PAYMENT ID and PAYER ID from query and captures the order - allowing me to do whatever i want to do ( e.g save payment in db ) in callback.
Now v2 seems like huge mess:
Following devs guide i have created
Paypal button (copy paste ):
import React, {useEffect} from "react";
import {PayPalButtons, PayPalScriptProvider, usePayPalScriptReducer} from "#paypal/react-paypal-js";
// This values are the props in the UI
const amount = "2";
const currency = "USD";
const style = {"layout": "vertical"};
const ButtonWrapper = ({currency, showSpinner}) => {
// usePayPalScriptReducer can be use only inside children of PayPalScriptProviders
// This is the main reason to wrap the PayPalButtons in a new component
const [{options, isPending}, dispatch] = usePayPalScriptReducer();
useEffect(() => {
dispatch({
type: "resetOptions",
value: {
...options,
currency: currency,
},
});
}, [currency, showSpinner]);
const createOrder = (data, actions) => {
console.log("create")
return fetch('http://localhost:8081/create', {
method: 'post'
}).then(function (res) {
return res.json();
}).then(function (orderData) {
console.log(orderData);
window.location = orderData.redirect;
});
}
// Call your server to finalize the transaction
const onApprove = (data, actions) => {
console.log("approve")
return fetch('/process', {
method: 'post'
}).then(function (res) {
return res.json();
}).then(function (orderData) {
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show confirmation or thank you
// This example reads a v2/checkout/orders capture response, propagated from the server
// You could use a different API or structure for your 'orderData'
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
return actions.restart(); // Recoverable state, per:
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
if (errorDetail.description) msg += '\n\n' + errorDetail.description;
if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
return alert(msg); // Show a failure message (try to avoid alerts in production environments)
}
// Successful capture! For demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction ' + transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
});
}
return (<>
{(showSpinner && isPending) && <div className="spinner"/>}
<PayPalButtons
style={style}
disabled={false}
forceReRender={[amount, currency, style]}
fundingSource={undefined}
createOrder={(data, actions) => createOrder(data, actions)}
onApprove={(data, actions) => onApprove(data, actions)}
/>
</>
);
}
export default function PayPalButton() {
return (
<div style={{ maxWidth: "750px", minHeight: "200px" }}>
<PayPalScriptProvider
options={{
"client-id": "test",
components: "buttons",
currency: "USD"
}}
>
<ButtonWrapper
currency={currency}
showSpinner={false}
/>
</PayPalScriptProvider>
</div>
);
}
And then followed the flow from paypal's github example
created their HttpClient
const checkoutNodeJssdk = require('#paypal/checkout-server-sdk');
/**
* Returns PayPal HTTP client instance with environment which has access
* credentials context. This can be used invoke PayPal API's provided the
* credentials have the access to do so.
*/
function client() {
return new checkoutNodeJssdk.core.PayPalHttpClient(environment());
}
/**
* Setting up and Returns PayPal SDK environment with PayPal Access credentials.
* For demo purpose, we are using SandboxEnvironment. In production this will be
* LiveEnvironment.
*/
function environment() {
let clientId = process.env.PAYPAL_CLIENT_ID || '<clientId>';
let clientSecret = process.env.PAYPAL_CLIENT_SECRET || '<secret>';
if (process.env.NODE_ENV === 'production') {
return new checkoutNodeJssdk.core.LiveEnvironment(clientId, clientSecret);
}
return new checkoutNodeJssdk.core.SandboxEnvironment(clientId, clientSecret);
}
async function prettyPrint(jsonData, pre=""){
let pretty = "";
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
for (let key in jsonData){
if (jsonData.hasOwnProperty(key)){
if (isNaN(key))
pretty += pre + capitalize(key) + ": ";
else
pretty += pre + (parseInt(key) + 1) + ": ";
if (typeof jsonData[key] === "object"){
pretty += "\n";
pretty += await prettyPrint(jsonData[key], pre + "\t");
}
else {
pretty += jsonData[key] + "\n";
}
}
}
return pretty;
}
module.exports = {client: client, prettyPrint:prettyPrint};
created their CreateOrder:
/**
* PayPal SDK dependency
*/
const checkoutNodeJssdk = require('#paypal/checkout-server-sdk');
/**
* PayPal HTTP client dependency
*/
const payPalClient = require('./PayPalClient');
/**
* Setting up the JSON request body for creating the Order. The Intent in the
* request body should be set as "CAPTURE" for capture intent flow.
*
*/
function buildRequestBody() {
return {
"intent": "CAPTURE",
"application_context": {
"return_url": "http://localhost:8081/process",
"cancel_url": "https://www.example.com",
"locale": "en-US",
"landing_page": "BILLING",
"user_action": "CONTINUE"
},
"purchase_units": [
{
"soft_descriptor": "Donation",
"amount": {
"currency_code": "USD",
"value": "220.00"
}
}
]
};
}
/**
* This is the sample function which can be sued to create an order. It uses the
* JSON body returned by buildRequestBody() to create an new Order.
*/
async function createOrder(debug=false) {
try {
const request = new checkoutNodeJssdk.orders.OrdersCreateRequest();
request.headers["prefer"] = "return=representation";
request.requestBody(buildRequestBody());
const response = await payPalClient.client().execute(request);
if (debug){
console.log("Status Code: " + response.statusCode);
console.log("Status: " + response.result.status);
console.log("Order ID: " + response.result.id);
console.log("Intent: " + response.result.intent);
console.log("Links: ");
response.result.links.forEach((item, index) => {
let rel = item.rel;
let href = item.href;
let method = item.method;
let message = `\t${rel}: ${href}\tCall Type: ${method}`;
console.log(message);
});
console.log(`Gross Amount: ${response.result.purchase_units[0].amount.currency_code} ${response.result.purchase_units[0].amount.value}`);
// To toggle print the whole body comment/uncomment the below line
console.log(JSON.stringify(response.result, null, 4));
}
return response;
}
catch (e) {
console.log(e)
}
}
/**
* This is the driver function which invokes the createOrder function to create
* an sample order.
*/
if (require.main === module){
(async() => await createOrder(true))();
}
/**
* Exports the Create Order function. If needed this can be invoked from the
* order modules to execute the end to flow like create order, retrieve, capture
* and refund(Optional)
*/
module.exports = {createOrder:createOrder};
And endpoints:
const createUsersOrder = async (res) => {
try {
let response = await createOrder();
console.log("Creating Order...");
let orderId = "";
if (response.statusCode === 201){
console.log("Created Successfully");
orderId = response.result.id;
console.log("Links:");
response.result.links.forEach((item, index) => {
let rel = item.rel;
let href = item.href;
let method = item.method;
let message = `\t${rel}: ${href}\tCall Type: ${method}`;
console.log(message);
});
let links = {};
response.result.links.forEach(function (linkObj) {
links[linkObj.rel] = {
'href': linkObj.href,
'method': linkObj.method
};
})
//if redirect url present, redirect user
if (links.hasOwnProperty('approve')) {
var returnObj = {redirect : links['approve'].href}
console.log("Returning " + returnObj)
res.send(returnObj);
} else {
console.error('no redirect URI present');
}
}
console.log("Copy approve link and paste it in browser. Login with buyer account and follow the instructions.\nOnce approved hit enter...");
return
} catch (error) {
console.log('There was an error: ', error);
}
};
app.post("/create", function(req,res) {
createUsersOrder(res);
})
Here, this is called when button is clicked, as "createOrder" method is called. I create order just like in the v1 code, and then redirect browser to the url. However when the user approves transaction on paypal, and thus is being redirected to one of
"application_context": {
"return_url": "http://localhost:8081/process",
"cancel_url": "http://localhost:8081/cancel",
"locale": "en-US",
"landing_page": "BILLING",
"user_action": "CONTINUE"
},
return url ( /process route for success ), the request DOES NOT contain payment_id, only PAYER_ID. But the payment_id ( or order_id in v2 ) is needed to capture the order.
Since i have found literally zero blogs, tutorials, guide for v2 ( only millions for v1) i am confused where to get the order id. Do i really need to save it in DB after i create the order? OR is there any other trick?
Also, the button contains onApprove method, but after creating order, the browser is redirected to paypal, and the paypal redirects user to http://localhost:8081/process endpoint - thus the onApprove method is never invoked and useless (?).
This whole flow of v2 is really confusing, is there something i am missing?
Thanks for help
With your v2 code, do not use any redirects. At all. Your button should call 2 endpoints on your server. These two endpoints should (respectively) do the API operations of creating and capturing the order, and return the JSON result in each case (the capture route can do any server-side operations like storing the transaction result in the database before forwarding the JSON result to the client caller, since the client needs to handle any capture error situations as well as showing a success message). You can find a full stack node.js example in the PayPal integration guide, but it's fine to keep your #paypal/react-paypal-js code pretty much as-is for the front end.
I'm writing a Nodejs app using Express framework. I try to send one Ether from one account to another and even though I get the transaction hash, the transaction doesn't go through. I've already read that gas price might be too low, but again it didn't work as I set it to even 5.000.000. When I call web3.eth.getTransactionReceipt('txHash'), it returns null, so I guess the transaction failed.
Here is my code:
router.get('/', (req, res, next) => {
// Build a transaction object
web3.eth.getTransactionCount(ad2, (err, txCount) => {
const txObject = {
nonce: toHex(txCount),
to: ad1,
value: toHex(web3.utils.toWei('1', 'ether')),
gasLimit: toHex(21000),
gasPrice: toHex(web3.utils.toWei('12', 'gwei'))
}
// Sign the transaction
const tx = new Tx(txObject);
tx.sign(pk2);
const serializedTx = tx.serialize();
const raw = '0x' + serializedTx.toString('hex')
// Broadchast the transaction to the network
web3.eth.sendSignedTransaction(raw, (err, txHash) => {
console.log('txHash:', txHash);
web3.eth.getTransactionReceipt(txHash, (err, receipt) => {
console.log('receipt: ', receipt);
if (receipt == null) {
// Render the page
res.render('sendTransaction', {
receiptSuccess: 0,
blockHash: 'No Receipt Found'
});
} else {
// Render the page
res.render('sendTransaction', {
receiptSuccess: 1,
blockHash: receipt.blockHash,
blockNumber: receipt.blockNumber,
transactionHash: receipt.transactionHash,
from: receipt.from,
to: receipt.to,
contractAddress: receipt.contractAddress,
gasUsed: receipt.gasUsed
});
}
});
});
});
});
What should I do to send a transaction from an address1 to an address2 successfully? What is missing in the above code?
Thanks!
As part of small email CRM project, i have created a Nodejs app. While i send email through gmail send api, the messages are not grouped.
const sendObject = {}
output.data.messages.map(each => {
sendObject.threadId = each.threadId // ThreadID is unique.
each.payload.headers.map(head => {
if (head.name === 'From') {
sendObject.replyTo = head.value
} else if (head.name === 'Subject') {
if (head.value.indexOf('Re:') >= 0) {
sendObject.subject = head.value
} else {
sendObject.subject = 'Re: ' + head.value
}
} else if (head.name === 'Message-Id') {
sendObject.inReplyTo = head.value // The last thread messageId is inserted into In-Reply-To tag
sendObject.reference.push(head.value) // All the messageId are appended as part of References tag
}
})
})
const email_lines = []
email_lines.push('References:' + sendObject.reference.join(' '))
email_lines.push('In-Reply-To:' + sendObject.inReplyTo)
email_lines.push('Subject: ' + sendObject.subject)
email_lines.push('To:' + sendObject.replyTo)
email_lines.push('')
email_lines.push(req.body.body)
const email = email_lines.join('\r\n').trim()
let base64EncodedEmail = new Buffer(email).toString('base64')
base64EncodedEmail = base64EncodedEmail.replace(/\+/g, '-').replace(/\//g, '_')
emailConfig.gmail.users.messages.send({
userId: 'me',
threadId: sendObject.threadId,
resource: {
raw: base64EncodedEmail
}
}, async (err) => {
if (err) {
console.log('The Threads API returned an error: ' + err)
}
})
When i login to gmail through browser, i can see 2 different emails instead of one thread.
You should put threadId inside of the resource.
const response = await this.gmail.users.messages.send({
auth: this.oAuth2Client,
userId: this.gmailAddress,
uploadType: 'multipart',
resource: {
threadId: email.threadId,
raw: raw
}
})
For more details, you can check my Gmail-APIs-wrapper here
I am using passport.js for my backend (node.js) app to signin up the user. I am getting always following error:
ERROR SyntaxError: Unexpected token < in JSON at position 0
at Object.parse ()
at Response.webpackJsonp.../../../http/#angular/http.es5.js.Body.json
(http.es5.js:797)
at MapSubscriber.project (signup.component.ts:31)
After refresh the site I am signed-up. What is wrong with my code?
Here is my angular function:
signUp() {
this.userNotExist = "";
this.userExist="";
this.http.post('/signup',this.signform.value).map((res: any) => res.json())
.subscribe((res: any) => {
console.log('TTTTTTTTT')
console.log(res)
console.log('TTTTTTTTT')
if(res=='EXIST'){
this.userNotExist = "";
this.userExist = 'The email already exists';
}else {
this.userExist = "";
this.userNotExist = 'Congrats! You are now signed up';
window.location.reload();
}
}
),(error: any) => {
console.log(error);
}
}
My Node.js app:
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
nameMainField : 'nameMain',
firstNameField: 'firstName',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }).lean().exec( function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'Email already exists.'));
} else {
var newUserS = new User();
// set the user's local credentials
newUserS.local.email = email;
newUserS.local.password = newUserS.generateHash(password); // use the generateHash function in our user model
newUserS.local.nameMain = req.body.firstName + ' ' + req.body.nameMain;
newUserS.local.firstName = req.body.firstName;
newUserS.role=0;
newUserS.profileImage='/assets/fonts/male.png';
// save the user
newUserS.save(function(err, u) {
if (err)
throw err;
return done(null, newUserS);
});
}
});
}));
You are getting xml response instead of JSON response. I am pretty sure it is something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN"
"http://dtd.WorldPay.com/paymentService_v1.dtd">
<paymentService version="1.4" merchantCode="ExampleCode1"> <!--The merchantCode you supplied originally-->
<reply>
<orderStatus orderCode="ExampleOrder1"> <!--Not present for parse or security violation errors-->
<error code="2">
<![CDATA[Invalid address: Postal code is missing or empty.]]>
</error>
</orderStatus>
</reply>
</paymentService>
The exception comes from the first char at the xml response.
I am learning Node js with socket. I got a sample program from Here
When I try to run it, it is not showing the error, but also nothing happening.
I tried and find out that the socket.emit and socket.io are not triggering.
Client side
$("#addComment").click(function(event){
var userName = $("#name").val();
var userComment = $("#comment").text();
if(userName === "" || userComment === "") {
alert("Please fill the form.");
return;
}
socket.emit('comment_added',{user : userName, comment : userComment});
socket.on('notify_everyone',function(msg){
notifyMe(msg.user,msg.comment);
});
});
function notifyMe(user,message) {
// Let's check if the browser supports notifications
if (!("Notification" in window)) {
alert("This browser does not support desktop notification");
}
// Let's check if the user is okay to get some notification
else if (Notification.permission === "granted") {
// If it's okay let's create a notification
var options = {
body: user + "Posted a comment" + message,
dir : "ltr"
};
var notification = new Notification("Hi there",options);
}
// Otherwise, we need to ask the user for permission
// Note, Chrome does not implement the permission static property
// So we have to check for NOT 'denied' instead of 'default'
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
// Whatever the user answers, we make sure we store the information
if (!('permission' in Notification)) {
Notification.permission = permission;
}
// If the user is okay, let's create a notification
if (permission === "granted") {
var options = {
body: user + "Posted a comment" + message,
dir : "ltr"
};
var notification = new Notification("New comment added.",options);
}
});
}
// At last, if the user already denied any notification, and you
// want to be respectful there is no need to bother them any more.
}
Server side
io.on('connection',function(socket){
console.log('We have user connected !');
io.sockets.on('comment_added',function(data){
console.log(data);
db.addComment(data.user,data.comment,mysql,pool,function(error,result){
if (error) {
io.emit('error');
} else {
socket.broadcast.emit("notify_everyone",{user : data.user,comment : data.comment});
}
});
});
});
You need to put the
socket.on('notify_everyone',function(msg){
notifyMe(msg.user,msg.comment);
});
outside of the function it is currently in, or else it won't be run (that code is only run when the comment is clicked).
For example,
$("#addComment").click(function(event){
var userName = $("#name").val();
var userComment = $("#comment").text();
if(userName === "" || userComment === "") {
alert("Please fill the form.");
return;
}
socket.emit('comment_added',{user : userName, comment : userComment});
});
socket.on('notify_everyone',function(msg){
notifyMe(msg.user,msg.comment);
});
Also, there is documentation on the socket.io website as well as a chat app tutorial