diff --git a/api/login.js b/api/login.js index 5c2fa0b..399136a 100644 --- a/api/login.js +++ b/api/login.js @@ -6,42 +6,50 @@ const router = require("express").Router(); router.get("/", function(req, res) { res.sendFile(path.join(__dirname + "/../public/index.html")); }); -/* + router.get("/register", function(req, res) { res.sendFile(path.join(__dirname + "/../public/index.html")); }); -router.post("/register", function(req, res, next) { - console.log("registering user"); - User.register( - new User({ username: req.body.username }), - req.body.password, - function(err) { - if (err) { - console.log("error while user register!", err); - return next(err); - } +// router.post("/register", function(req, res, next) { +// User.register( +// new User({ username: req.body.username }), +// req.body.password, +// function(err) { +// if (err) { +// console.log("error while user register!", err); +// if (err.name == "UserExistsError") +// res.status(409).send({ success: false, message: err.message }) +// else if (err.name == "MissingUsernameError" || err.name == "MissingPasswordError") +// res.status(400).send({ success: false, message: err.message }) +// return next(err); +// } - console.log("user registered!"); +// console.log("user registered!", req.body.username); + +// res.redirect("/#/") +// } +// ); +// }); - res.redirect("/"); - } - ); -}); -*/ router.get("/login", function(req, res) { res.sendFile(path.join(__dirname + "/../public/index.html")); }); -router.post( - "/login", - passport.authenticate("local", { - failureRedirect: "/#/login" - }), - function(req, res) { - res.redirect("/#/update"); - } -); +router.post("/login", function(req, res, next) { + passport.authenticate("local", function(err, user, info) { + if (err) { + if (err.name == "MissingUsernameError" || err.name == "MissingPasswordError") + return res.status(400).send({ success: false, message: err.message }) + return next(err); + } + + if (!user) return res.status(404).send({ success: false, message: "Incorrect username or password" }) + + console.log("user logged in:", user) + res.redirect("/#/update") + })(req, res, next); +}); router.get("/logout", function(req, res) { req.logout(); diff --git a/api/subscriptions.js b/api/subscriptions.js index 087f48a..c79779c 100644 --- a/api/subscriptions.js +++ b/api/subscriptions.js @@ -9,6 +9,8 @@ mongoose.connect("mongodb://localhost:27017/vinlottis", { useNewUrlParser: true }); +const mustBeAuthenticated = require(path.join(__dirname + "/../middleware/mustBeAuthenticated")) + const config = require(path.join(__dirname + "/../config/defaults/push")); const Subscription = require(path.join(__dirname + "/../schemas/Subscription")); const lotteryConfig = require(path.join( @@ -67,12 +69,7 @@ const saveToDatabase = async subscription => { } }; -router.route("/send-notification").post(async (req, res) => { - if (!req.isAuthenticated()) { - res.send(false); - return; - } - +router.route("/send-notification").post(mustBeAuthenticated, async (req, res) => { const message = JSON.stringify({ message: req.body.message, title: "Vinlotteri!" diff --git a/api/update.js b/api/update.js index ec1b130..23db875 100644 --- a/api/update.js +++ b/api/update.js @@ -7,6 +7,7 @@ mongoose.connect("mongodb://localhost:27017/vinlottis", { }); const sub = require(path.join(__dirname + "/../api/subscriptions")); +const mustBeAuthenticated = require(path.join(__dirname + "/../middleware/mustBeAuthenticated")) const Subscription = require(path.join(__dirname + "/../schemas/Subscription")); const Purchase = require(path.join(__dirname + "/../schemas/Purchase")); @@ -20,11 +21,7 @@ router.use((req, res, next) => { next(); }); -router.route("/log/wines").post(async (req, res) => { - if (!req.isAuthenticated()) { - res.send(false); - return; - } +router.route("/log/wines").post(mustBeAuthenticated, async (req, res) => { const wines = req.body; for (let i = 0; i < wines.length; i++) { let wine = wines[i]; @@ -51,12 +48,18 @@ router.route("/log/wines").post(async (req, res) => { res.send(true); }); -router.route("/log").post(async (req, res) => { - if (!req.isAuthenticated()) { - res.send(false); - return; - } +router.route("/log/schema").get(mustBeAuthenticated, async (req, res) => { + let schema = {...PreLotteryWine.schema.obj}; + let nulledSchema = Object.keys(schema).reduce( + (accumulator, current) => { + accumulator[current] = ""; + return accumulator + }, {}); + res.send(nulledSchema) +}) + +router.route("/log").post(mustBeAuthenticated, async (req, res) => { await PreLotteryWine.deleteMany(); const purchaseBody = req.body.purchase; diff --git a/api/wineinfo.js b/api/wineinfo.js new file mode 100644 index 0000000..237de19 --- /dev/null +++ b/api/wineinfo.js @@ -0,0 +1,31 @@ +const express = require("express"); +const path = require("path"); +const router = express.Router(); +const fetch = require('node-fetch') + +const mustBeAuthenticated = require(path.join(__dirname + "/../middleware/mustBeAuthenticated")) + +router.use((req, res, next) => { + next(); +}); + +router.route("/wineinfo/:ean").get(async (req, res) => { + const vinmonopoletResponse = await fetch("https://app.vinmonopolet.no/vmpws/v2/vmp/products/barCodeSearch/" + req.params.ean) + .then(resp => resp.json()) + + if (vinmonopoletResponse.errors != null) { + return vinmonopoletResponse.errors.map(error => { + if (error.type == "UnknownProductError") { + return res.status(404).json({ + message: error.message + }) + } else { + return next() + } + }) + } + + res.send(vinmonopoletResponse); +}); + +module.exports = router; diff --git a/config/env/lottery.config.example.js b/config/env/lottery.config.example.js index 37f881b..cde3d8c 100644 --- a/config/env/lottery.config.example.js +++ b/config/env/lottery.config.example.js @@ -4,5 +4,6 @@ module.exports = { price: 10, message: "VINLOTTERI", date: 5, - hours: 15 + hours: 15, + apiUrl: undefined }; diff --git a/config/webpack.config.common.js b/config/webpack.config.common.js index 26d6fbf..50680e6 100644 --- a/config/webpack.config.common.js +++ b/config/webpack.config.common.js @@ -78,6 +78,7 @@ const webpackConfig = function(isDev) { __MESSAGE__: JSON.stringify(env.message), __DATE__: env.date, __HOURS__: env.hours, + __APIURL__: JSON.stringify(env.apiUrl), __PUSHENABLED__: JSON.stringify(require("./defaults/push") != false) }) ] diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 6922179..ca13cfb 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -45,7 +45,7 @@ webpackConfig = merge(webpackConfig, { }, plugins: [ new HtmlPlugin({ - template: "src/templates/Index.html", + template: "src/templates/Create.html", chunksSortMode: "dependency" }) ] diff --git a/middleware/mustBeAuthenticated.js b/middleware/mustBeAuthenticated.js new file mode 100644 index 0000000..b537ed5 --- /dev/null +++ b/middleware/mustBeAuthenticated.js @@ -0,0 +1,13 @@ + +const mustBeAuthenticated = (req, res, next) => { + if (!req.isAuthenticated()) { + return res.status(401).send({ + success: false, + message: "Du må være logget inn." + }) + } + + return next() +} + +module.exports = mustBeAuthenticated; \ No newline at end of file diff --git a/package.json b/package.json index e004752..a240431 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "license": "ISC", "dependencies": { "@babel/polyfill": "~7.2", + "@zxing/library": "^0.16.0", "body-parser": "^1.19.0", "chart.js": "^2.9.3", "clean-webpack-plugin": "^3.0.0", diff --git a/server.js b/server.js index a812b10..1f69199 100644 --- a/server.js +++ b/server.js @@ -8,6 +8,7 @@ const updateApi = require(path.join(__dirname + "/api/update")); const retrieveApi = require(path.join(__dirname + "/api/retrieve")); const subscriptionApi = require(path.join(__dirname + "/api/subscriptions")); const loginApi = require(path.join(__dirname + "/api/login")); +const wineinfoApi = require(path.join(__dirname + "/api/wineinfo")); const bodyParser = require("body-parser"); const mongoose = require("mongoose"); @@ -80,6 +81,7 @@ app.use("/dist", express.static(path.join(__dirname, "public/dist"))); app.use("/", loginApi); app.use("/api/", updateApi); app.use("/api/", retrieveApi); +app.use("/api/", wineinfoApi); app.use("/subscription", subscriptionApi); app.get("/dagens", function(req, res) { diff --git a/src/api.js b/src/api.js new file mode 100644 index 0000000..a79cfca --- /dev/null +++ b/src/api.js @@ -0,0 +1,177 @@ +const BASE_URL = __APIURL__ || "http://localhost:30030/"; + +const statistics = () => { + const url = new URL('/api/purchase/statistics', BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + +const colorStatistics = () => { + const url = new URL("/api/purchase/statistics/color", BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + +const highscoreStatistics = () => { + const url = new URL("/api/highscore/statistics", BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + +const overallWineStatistics = () => { + const url = new URL("/api/wines/statistics/overall", BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + + +const chartWinsByColor = () => { + const url = new URL("/api/purchase/statistics/color", BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + +const chartPurchaseByColor = () => { + const url = new URL("/api/purchase/statistics", BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + + +const prelottery = () => { + const url = new URL("/api/wines/prelottery", BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + +const log = (sendObject) => { + const url = new URL("/api/log", BASE_URL) + + const options = { + headers: { + "Content-Type": "application/json" + }, + method: "POST", + body: JSON.stringify(sendObject) + } + + return fetch(url.href, options) + .then(resp => resp.json()) +} + +const logWines = (wines) => { + const url = new URL("/api/log/wines", BASE_URL) + + const options = { + headers: { + "Content-Type": "application/json" + }, + method: "POST", + body: JSON.stringify(wines) + } + + return fetch(url.href, options) + .then(resp => resp.json()) +} + +const wineSchema = () => { + const url = new URL("/api/log/schema", BASE_URL) + + return fetch(url.href) + .then(resp => resp.json()) +} + +const barcodeToVinmonopolet = (id) => { + const url = new URL("/api/wineinfo/" + id, BASE_URL) + + return fetch(url.href) + .then(async (resp) => { + if (!resp.ok) { + if (resp.status == 404) { + throw await resp.json() + } + } else { + return resp.json() + } + }) +} + +const handleErrors = async (resp) => { + if ([400, 409].includes(resp.status)) { + throw await resp.json() + } else { + console.error("Unexpected error occured when login/register user:", resp) + throw await resp.json() + } +} + +const login = (username, password) => { + const url = new URL("/login", BASE_URL) + const options = { + headers: { + "Content-Type": "application/json" + }, + method: "POST", + redirect: "follow", + body: JSON.stringify({ username, password }) + } + + return fetch(url.href, options) + .then(resp => { + if (resp.ok) { + if (resp.bodyUsed) + return resp.json() + else + return resp + } else { + return handleErrors(resp) + } + }) +} + +const register = (username, password) => { + const url = new URL("/register", BASE_URL) + const options = { + headers: { + "Content-Type": "application/json" + }, + method: "POST", + redirect: 'follow', + body: JSON.stringify({ username, password }) + } + + return fetch(url.href, options) + .then(resp => { + if (resp.ok) { + if (resp.bodyUsed) + return resp.json() + else + return resp + } else { + return handleErrors(resp) + } + }) +} + +export { + statistics, + colorStatistics, + highscoreStatistics, + overallWineStatistics, + chartWinsByColor, + chartPurchaseByColor, + prelottery, + log, + logWines, + wineSchema, + barcodeToVinmonopolet, + login, + register +} diff --git a/src/components/AllWinesPage.vue b/src/components/AllWinesPage.vue index 6ef37e2..174fc99 100644 --- a/src/components/AllWinesPage.vue +++ b/src/components/AllWinesPage.vue @@ -3,7 +3,7 @@