diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..4402a3e --- /dev/null +++ b/.env-example @@ -0,0 +1,4 @@ +MONGODB_URL = Your mongodb connection URL + +JWT_SECRET = Any string to be secret +BASE_URL = App base url \ No newline at end of file diff --git a/app.js b/app.js index b23b2c1..5e73742 100644 --- a/app.js +++ b/app.js @@ -1,81 +1,83 @@ -const express = require("express"); -const mongoose = require("mongoose"); -const bodyParser = require("body-parser"); -const cors = require("cors"); - -const url = require("./routes/api/url"); -const users = require("./routes/api/users"); -const auth = require("./routes/api/auth"); - -const Url = require("./models/url"); - -const app = express(); - -app.use(bodyParser.urlencoded({ extended: false })); -app.use(bodyParser.json()); -app.use(cors()); - -mongoose - .connect( - `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}@cluster0-jwfcs.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`, - { - useNewUrlParser: true, - useUnifiedTopology: true, - useCreateIndex: true, - } - ) - .then(() => { - console.log("MongoDb connected....."); - }) - .catch((err) => { - console.log("Cannot connect to db due to " + err); - }); - -app.use("/api/urls", url); -app.use("/api/register", users); -app.use("/api/login", auth); - -app.get("/:code", async (req, res) => { - try { - const url = await Url.findOne({ code: req.params.code }); - - if (url) { - if ( - url.lastDate && - url.lastDate.toISOString() < new Date().toISOString() - ) { - const user = await User.findById(url.creator); - - for (let i = 0; i < user.shortenedUrl.length; i++) { - if (user.shortenedUrl[i].toString() === id) { - user.shortenedUrl.splice(i, 1); - break; - } - } - await user.save(); - await url.deleteOne(); - return res - .status(404) - .json({ msg: "This Compress URL does not exist any more!" }); - } - if (!url.status) { - return res - .status(200) - .json({ msg: "This url is temporarily out of service!" }); - } - - url.clicks = url.clicks + 1; - url.save(); - return res.redirect(url.longUrl); - } else { - return res.status(404).json({ msg: "No Compressed URL Found" }); - } - } catch (err) { - console.log(err); - res.status(500).json({ msg: "Internal Server Error" }); - } -}); - -const port = process.env.PORT || 5000; - -app.listen(port, () => console.log(`Server Started on port ${port}`)); +const express = require("express"); +const mongoose = require("mongoose"); +const bodyParser = require("body-parser"); +const cors = require("cors"); + +const url = require("./routes/api/url"); +const users = require("./routes/api/users"); +const auth = require("./routes/api/auth"); + +const Url = require("./models/url"); + +const app = express(); + +app.use(express.urlencoded({ extended: false })); +app.use(express.json()); +app.use(cors()); + +require('dotenv').config(); + +mongoose + .connect( + process.env.MONGODB_URL, + { + useNewUrlParser: true, + useUnifiedTopology: true, + useCreateIndex: true, + } + ) + .then(() => { + console.log("MongoDb connected....."); + }) + .catch((err) => { + console.log("Cannot connect to db due to " + err); + }); + +app.use("/api/urls", url); +app.use("/api/register", users); +app.use("/api/login", auth); + +app.get("/:code", async (req, res) => { + try { + const url = await Url.findOne({ code: req.params.code }); + + if (url) { + if ( + url.lastDate && + url.lastDate.toISOString() < new Date().toISOString() + ) { + const user = await User.findById(url.creator); + + for (let i = 0; i < user.shortenedUrl.length; i++) { + if (user.shortenedUrl[i].toString() === id) { + user.shortenedUrl.splice(i, 1); + break; + } + } + await user.save(); + await url.deleteOne(); + return res + .status(404) + .json({ msg: "This Compress URL does not exist any more!" }); + } + if (!url.status) { + return res + .status(200) + .json({ msg: "This url is temporarily out of service!" }); + } + + url.clicks = url.clicks + 1; + url.save(); + return res.redirect(url.longUrl); + } else { + return res.status(404).json({ msg: "No Compressed URL Found" }); + } + } catch (err) { + console.log(err); + res.status(500).json({ msg: "Internal Server Error" }); + } +}); + +const port = process.env.PORT || 5000; + +app.listen(port, () => console.log(`Server Started on port ${port}`)); diff --git a/routes/api/auth.js b/routes/api/auth.js index ab58960..288ebd7 100644 --- a/routes/api/auth.js +++ b/routes/api/auth.js @@ -7,7 +7,7 @@ const auth = require("../../middleware/auth"); const User = require("../../models/user"); router.post("/", async (req, res) => { - const { name, email, password } = req.body; + const { email, password } = req.body; if (!email || !password) { return res.status(400).send({ msg: "Please enter all fields" }); diff --git a/routes/api/url.js b/routes/api/url.js index 65b06b8..9448064 100644 --- a/routes/api/url.js +++ b/routes/api/url.js @@ -27,17 +27,17 @@ router.get("/", middleAuth, async (req, res) => { }); router.post("/compress", middleAuth, async (req, res) => { - const { longUrl, lastDate } = req.body; + const { longUrl, lastDate, customCode } = req.body; - // const baseUrl = "http://localhost:5000"; + // base url = "http://localhost:5000" const baseUrl = process.env.BASE_URL; if (!validUrl.isUri(baseUrl)) { return res.status(400).send({ msg: "Invalid Base Url" }); } - const urlCode = shortId.generate(); - if (validUrl.isUri(longUrl)) { + + try { let url = await Url.findOne({ longUrl: longUrl }); @@ -46,6 +46,18 @@ router.post("/compress", middleAuth, async (req, res) => { .status(400) .send({ msg: "Compressed URL already exists", url: url.shortUrl }); } else { + let urlCode; + if (customCode) { + urlCode = customCode; + + if (await Url.findOne({ code: urlCode })) { + return res.status(409).send({ msg: "This code is already in use, try another code" }); + }; + + } else { + urlCode = shortId.generate(); + } + const shortUrl = baseUrl + "/" + urlCode; url = new Url({ diff --git a/routes/api/users.js b/routes/api/users.js index 869a047..ce9c8cd 100644 --- a/routes/api/users.js +++ b/routes/api/users.js @@ -5,6 +5,8 @@ const jwt = require("jsonwebtoken"); const User = require("../../models/user"); +const { validatePassword, validateEmail } = require("../../validators/validators"); + router.post("/", async (req, res) => { const { name, email, password } = req.body; @@ -12,6 +14,14 @@ router.post("/", async (req, res) => { return res.status(400).send({ msg: "Please enter all fields" }); } + if (!validateEmail(email)) { + return res.status(400).send({ msg: "Invalid email address format specified" }); + } + + if (!validatePassword(password)) { + return res.status(400).send({ msg: "Password must be at least 8 characters in length and must contain at least 1 lowercase letter, 1 uppercase letter, 1 numeric digit, and 1 special character" }); + } + const existingUser = await User.findOne({ email }); if (existingUser) { res.status(400).send({ msg: "Already registered user with this email" }); diff --git a/validators/validators.js b/validators/validators.js new file mode 100644 index 0000000..29804ab --- /dev/null +++ b/validators/validators.js @@ -0,0 +1,22 @@ +const validatePassword = (password) => { + // 1 special character + // 1 digit + // 1 lowercase character + // 1 uppercase character + const passwordRegex = /^(?=.*[^A-Za-z0-9])(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/; + + return passwordRegex.test(password); +}; + +const validateEmail = (email) => { + // from https://emailregex.com/ + // claims to match 99.99% of all email addresses + const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + return emailRegex.test(email); +}; + +module.exports = { + validatePassword, + validateEmail, +}; \ No newline at end of file