This commit is contained in:
Cassio Santos
2020-05-23 17:19:42 -03:00
commit 47f152a145
48 changed files with 19316 additions and 0 deletions

1
backend/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

45
backend/app.js Normal file
View File

@@ -0,0 +1,45 @@
const express = require("express");
const cors = require("cors");
const sequelize = require("./util/database");
const wBot = require("./controllers/wbot");
const Contact = require("./models/Contact");
const wbotMessageListener = require("./controllers/wbotMessageListener");
const messageRoutes = require("./routes/message");
const ContactRoutes = require("./routes/contacts");
const AuthRoutes = require("./routes/auth");
const app = express();
app.use(cors());
app.use(express.json());
app.use(messageRoutes);
app.use(ContactRoutes);
app.use("/auth", AuthRoutes);
app.use((error, req, res, next) => {
console.log(error);
const status = error.statusCode || 500;
const message = error.message;
const data = error.data;
res.status(status).json({ message: message, data: data });
});
sequelize
.sync()
.then(() => {
const server = app.listen(8080);
const io = require("./socket").init(server);
io.on("connection", socket => {
console.log("Cliente Connected");
});
wBot.init();
wbotMessageListener();
console.log("Server started");
})
.catch(err => {
console.log(err);
});

View File

@@ -0,0 +1,60 @@
const { validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/User");
exports.signup = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
const error = new Error("Validation failed");
error.statusCode = 422;
error.data = errors.array();
throw error;
}
const { name, password, email } = req.body;
try {
const hashedPw = await bcrypt.hash(password, 12);
const user = User.build({
email: email,
password: hashedPw,
name: name,
});
const result = await user.save();
res.status(201).json({ message: "User created!", userId: result.id });
} catch (err) {
next(err);
}
};
exports.login = async (req, res, next) => {
const { email, password } = req.body;
let loadedUser;
try {
const user = await User.findOne({ where: { email: email } });
if (!user) {
const error = new Error("Usuário não encontrado");
error.statusCode = 401;
throw error;
}
loadedUser = user;
const isEqual = await bcrypt.compare(password, user.password);
if (!isEqual) {
const error = new Error("Senha incorreta");
error.statusCode = 401;
throw error;
}
const token = jwt.sign(
{ email: loadedUser.email, userId: loadedUser.id },
"mysecret",
{ expiresIn: "1h" }
);
return res
.status(200)
.json({ token: token, username: loadedUser.name, userId: loadedUser.id });
} catch (err) {
next(err);
}
};

View File

@@ -0,0 +1,18 @@
const Contact = require("../models/Contact");
const Message = require("../models/Message");
// const io = require("../socket");
exports.getContacts = async (req, res) => {
// const contacts = await Contact.findAll();
const contacts = await Contact.findAll({
include: {
model: Message,
where: {
read: false,
},
required: false,
},
});
return res.json(contacts);
};

View File

@@ -0,0 +1,55 @@
const Message = require("../models/Message");
const Contact = require("../models/Contact");
const io = require("../socket");
const wbot = require("./wbot");
exports.getContactMessages = async (req, res) => {
const { contactId } = req.params;
const contact = await Contact.findByPk(contactId);
const contactMessages = await contact.getMessages();
return res.json(contactMessages);
};
exports.postCreateContactMessage = async (req, res) => {
const { contactId } = req.params;
const message = req.body;
const contact = await Contact.findByPk(contactId);
const newMessage = await contact.createMessage(message);
if (!newMessage) {
return res.status(500).json({ message: "A mensagem não foi criada" });
}
wbot.getWbot().sendMessage(`${contact.number}@c.us`, message.messageBody);
io.getIO().emit("appMessage", {
action: "create",
message: newMessage.dataValues,
});
return res.json(newMessage);
};
exports.postUpdateMessageStatus = async (req, res) => {
const { messagesToSetRead } = req.body;
await Promise.all(
messagesToSetRead.map(async message => {
await Message.update(
{ read: 1 },
{
where: {
id: message.id,
},
}
);
})
);
res.json({ message: "Mensagens lidas!" });
};

View File

@@ -0,0 +1 @@
{"WABrowserId":"\"ynZiZqR67F6o3M5nCIE3Ig==\"","WASecretBundle":"{\"key\":\"3kFQ7T0G4sbiCrb3ZwzDKzDdKLo6hObbUfygGby91H0=\",\"encKey\":\"ezpzST8/4752gQLd+Lj+yfZGqwzFqBlkvpelptnJ5dI=\",\"macKey\":\"3kFQ7T0G4sbiCrb3ZwzDKzDdKLo6hObbUfygGby91H0=\"}","WAToken1":"\"9g0Q+hqE6PdVX8yf2m2poDqpN+VB1NDbsSF3bp0xxE0=\"","WAToken2":"\"1@DnfzCyncYsMRZnUA+PnrZfYpux6mUSqrGPAFBgkU8b7NS6mge9SuJ4QDKUvt8HthVmH9mvovbb26zQ==\""}

View File

@@ -0,0 +1,48 @@
const path = require("path");
const qrCode = require("qrcode-terminal");
const fs = require("fs");
const { Client } = require("whatsapp-web.js");
let wbot;
module.exports = {
init: () => {
const SESSION_FILE_PATH = path.join(__dirname, "/session.json");
let sessionCfg;
if (fs.existsSync(SESSION_FILE_PATH)) {
sessionCfg = require(SESSION_FILE_PATH);
}
wbot = new Client({
session: sessionCfg,
});
wbot.initialize();
wbot.on("qr", qr => {
qrCode.generate(qr, { small: true });
});
wbot.on("authenticated", session => {
console.log("AUTHENTICATED");
sessionCfg = session;
fs.writeFile(SESSION_FILE_PATH, JSON.stringify(sessionCfg), function (
err
) {
if (err) {
console.error(err);
}
});
});
wbot.on("auth_failure", msg => {
console.error("AUTHENTICATION FAILURE", msg);
});
wbot.on("ready", () => {
console.log("READY");
});
return wbot;
},
getWbot: () => {
if (!wbot) {
throw new Error("Wbot not initialized");
}
return wbot;
},
};

View File

@@ -0,0 +1,42 @@
const io = require("../socket");
const Contact = require("../models/Contact");
const wbot = require("./wbot");
const wbotMessageListener = () => {
wbot.getWbot().on("message", async msg => {
const msgContact = await msg.getContact();
try {
let contact = await Contact.findOne({
where: { number: msgContact.number },
});
if (!contact) {
try {
contact = await Contact.create({
name: msgContact.number.toString(),
number: msgContact.number,
imageUrl: "",
});
io.getIO().emit("contact", {
action: "create",
contact: contact.dataValues,
});
} catch (err) {
console.log(err);
}
}
const newMessage = await contact.createMessage({
messageBody: msg.body,
});
io.getIO().emit("appMessage", {
action: "create",
message: newMessage.dataValues,
});
} catch (err) {
console.log(err);
}
});
};
module.exports = wbotMessageListener;

View File

@@ -0,0 +1,21 @@
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
let decodedToken;
try {
const token = req.get("Authorization").split(" ")[1];
decodedToken = jwt.verify(token, "mysecret");
} catch (err) {
err.statusCode = 500;
next(err);
}
if (!decodedToken) {
const error = new Error("Falha na autenticação");
error.statusCode = 401;
next(error);
}
req.userId = decodedToken.userId;
next();
};

21
backend/models/Contact.js Normal file
View File

@@ -0,0 +1,21 @@
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const Message = require("./Message");
const Contact = sequelize.define("contact", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
name: { type: Sequelize.STRING(100), allowNull: false },
number: { type: Sequelize.STRING(15), allowNull: false },
imageURL: { type: Sequelize.STRING(200) },
});
Contact.hasMany(Message);
module.exports = Contact;

10
backend/models/Message.js Normal file
View File

@@ -0,0 +1,10 @@
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const Message = sequelize.define("message", {
userId: { type: Sequelize.INTEGER, defaultValue: 0 },
messageBody: { type: Sequelize.STRING(250), allowNull: false },
read: { type: Sequelize.BOOLEAN, defaultValue: false },
});
module.exports = Message;

17
backend/models/User.js Normal file
View File

@@ -0,0 +1,17 @@
const Sequelize = require("sequelize");
const sequelize = require("../util/database");
const User = sequelize.define("user", {
id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
name: { type: Sequelize.STRING(100), allowNull: false },
password: { type: Sequelize.STRING(100), allowNull: false },
email: { type: Sequelize.STRING(100), allowNull: false },
});
module.exports = User;

2797
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
backend/package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"nodemonConfig": {
"ignore": [
"controllers/session.json"
]
},
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-validator": "^6.4.1",
"jsonwebtoken": "^8.5.1",
"mysql2": "^2.1.0",
"qrcode-terminal": "^0.12.0",
"sequelize": "^5.21.8",
"sequelize-cli": "^5.5.1",
"socket.io": "^2.3.0",
"whatsapp-web.js": "^1.5.1"
},
"devDependencies": {
"nodemon": "^2.0.3"
}
}

30
backend/routes/auth.js Normal file
View File

@@ -0,0 +1,30 @@
const express = require("express");
const { body } = require("express-validator");
const User = require("../models/User");
const authController = require("../controllers/auth");
const routes = express.Router();
routes.put(
"/signup",
[
body("email")
.isEmail()
.withMessage("Email inválido")
.custom((value, { req }) => {
return User.findOne({ where: { email: value } }).then(user => {
if (user) {
return Promise.reject("Um cadastro com este email já existe!");
}
});
})
.normalizeEmail(),
body("password").trim().isLength({ min: 5 }),
body("name").trim().not().isEmpty(),
],
authController.signup
);
routes.post("/login", authController.login);
module.exports = routes;

View File

@@ -0,0 +1,11 @@
const express = require("express");
const isAuth = require("../middleware/is-auth");
const ContactController = require("../controllers/contact");
const routes = express.Router();
routes.get("/contacts", isAuth, ContactController.getContacts);
// routes.post(ContactController.postCreateContact);
module.exports = routes;

24
backend/routes/message.js Normal file
View File

@@ -0,0 +1,24 @@
const express = require("express");
const isAuth = require("../middleware/is-auth");
const MessangeController = require("../controllers/message");
const routes = express.Router();
routes.post(
"/messages/setread",
isAuth,
MessangeController.postUpdateMessageStatus
);
routes.get(
"/messages/:contactId",
isAuth,
MessangeController.getContactMessages
);
routes.post(
"/messages/:contactId",
isAuth,
MessangeController.postCreateContactMessage
);
module.exports = routes;

14
backend/socket.js Normal file
View File

@@ -0,0 +1,14 @@
let io;
module.exports = {
init: httpServer => {
io = require("socket.io")(httpServer);
return io;
},
getIO: () => {
if (!io) {
throw new Error("Socket IO not initialized");
}
return io;
},
};

9
backend/util/database.js Normal file
View File

@@ -0,0 +1,9 @@
const Sequelize = require("sequelize");
const sequelize = new Sequelize("econo_whatsbot", "root", "nodecomplete", {
dialect: "mysql",
host: "localhost",
logging: false,
});
module.exports = sequelize;