Add sms capabilities, and online-fixing of everything
This commit is contained in:
		
							
								
								
									
										70
									
								
								api/message.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								api/message.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					const request = require("request");
 | 
				
			||||||
 | 
					const path = require("path");
 | 
				
			||||||
 | 
					const config = require(path.join(__dirname + "/../config/defaults/lottery"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function sendMessage(winnerObject) {
 | 
				
			||||||
 | 
					  console.log("sent message to ", winnerObject);
 | 
				
			||||||
 | 
					  winnerObject.timestamp_sent = new Date().getTime();
 | 
				
			||||||
 | 
					  winnerObject.timestamp_limit = new Date().getTime() * (600000 * 10000);
 | 
				
			||||||
 | 
					  await winnerObject.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await sendMessageToUser(
 | 
				
			||||||
 | 
					    winnerObject.phoneNumber,
 | 
				
			||||||
 | 
					    `Gratulerer som heldig vinner av vinlotteriet ${winnerObject.name}! Her er linken for å velge hva slags vin du vil ha, du har 10 minutter på å velge ut noe før du blir lagt bakerst i køen. /#/winner/${winnerObject.id}`
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function sendMessageTooLate(winnerObject) {
 | 
				
			||||||
 | 
					  await sendMessageToUser(
 | 
				
			||||||
 | 
					    winnerObject.phoneNumber,
 | 
				
			||||||
 | 
					    `Hei ${winnerObject.name}, du har dessverre brukt mer enn 10 minutter på å velge premie og blir derfor puttet bakerst i køen. Du vil få en ny SMS når det er din tur igjen.`
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function sendMessageToUser(phoneNumber, message) {
 | 
				
			||||||
 | 
					  console.log("num", phoneNumber);
 | 
				
			||||||
 | 
					  console.log("message", message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  request.post(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      url: `https://gatewayapi.com/rest/mtsms?token=${config.token}`,
 | 
				
			||||||
 | 
					      json: true,
 | 
				
			||||||
 | 
					      body: {
 | 
				
			||||||
 | 
					        sender: "Vinlottis",
 | 
				
			||||||
 | 
					        message: message,
 | 
				
			||||||
 | 
					        recipients: [{ msisdn: `47${phoneNumber}` }]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    function(err, r, body) {
 | 
				
			||||||
 | 
					      console.log(err ? err : body);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function sendUpdate(winners) {
 | 
				
			||||||
 | 
					  let numbers = [];
 | 
				
			||||||
 | 
					  for (let i = 0; i < winners.length; i++) {
 | 
				
			||||||
 | 
					    numbers.push({ msisdn: `47${winners[i].phoneNumber}` });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  request.post(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      url: `https://gatewayapi.com/rest/mtsms?token=${config.token}`,
 | 
				
			||||||
 | 
					      json: true,
 | 
				
			||||||
 | 
					      body: {
 | 
				
			||||||
 | 
					        sender: "Vinlottis",
 | 
				
			||||||
 | 
					        message:
 | 
				
			||||||
 | 
					          "Gratulerer som vinner av vinlottisen! Du vil snart få en SMS med oppdatering om hvordan gangen går!",
 | 
				
			||||||
 | 
					        recipients: numbers
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    function(err, r, body) {
 | 
				
			||||||
 | 
					      console.log(err ? err : body);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports.sendUpdate = sendUpdate;
 | 
				
			||||||
 | 
					module.exports.sendMessage = sendMessage;
 | 
				
			||||||
 | 
					module.exports.sendMessageTooLate = sendMessageTooLate;
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
const express = require("express");
 | 
					const express = require("express");
 | 
				
			||||||
const path = require("path");
 | 
					const path = require("path");
 | 
				
			||||||
const router = express.Router();
 | 
					const router = express.Router();
 | 
				
			||||||
 | 
					const crypto = require("crypto");
 | 
				
			||||||
const mongoose = require("mongoose");
 | 
					const mongoose = require("mongoose");
 | 
				
			||||||
mongoose.connect("mongodb://localhost:27017/vinlottis", {
 | 
					mongoose.connect("mongodb://localhost:27017/vinlottis", {
 | 
				
			||||||
  useNewUrlParser: true
 | 
					  useNewUrlParser: true
 | 
				
			||||||
@@ -9,11 +10,13 @@ let io;
 | 
				
			|||||||
const mustBeAuthenticated = require(path.join(
 | 
					const mustBeAuthenticated = require(path.join(
 | 
				
			||||||
  __dirname + "/../middleware/mustBeAuthenticated"
 | 
					  __dirname + "/../middleware/mustBeAuthenticated"
 | 
				
			||||||
));
 | 
					));
 | 
				
			||||||
 | 
					const config = require(path.join(__dirname + "/../config/defaults/lottery"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Attendee = require(path.join(__dirname + "/../schemas/Attendee"));
 | 
					const Attendee = require(path.join(__dirname + "/../schemas/Attendee"));
 | 
				
			||||||
const VirtualWinner = require(path.join(
 | 
					const VirtualWinner = require(path.join(
 | 
				
			||||||
  __dirname + "/../schemas/VirtualWinner"
 | 
					  __dirname + "/../schemas/VirtualWinner"
 | 
				
			||||||
));
 | 
					));
 | 
				
			||||||
 | 
					const Message = require(path.join(__dirname + "/../api/message"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.use((req, res, next) => {
 | 
					router.use((req, res, next) => {
 | 
				
			||||||
  next();
 | 
					  next();
 | 
				
			||||||
@@ -133,7 +136,9 @@ router.route("/winner").get(mustBeAuthenticated, async (req, res) => {
 | 
				
			|||||||
    red: winner.red,
 | 
					    red: winner.red,
 | 
				
			||||||
    blue: winner.blue,
 | 
					    blue: winner.blue,
 | 
				
			||||||
    green: winner.green,
 | 
					    green: winner.green,
 | 
				
			||||||
    yellow: winner.yellow
 | 
					    yellow: winner.yellow,
 | 
				
			||||||
 | 
					    id: sha512(winner.phoneNumber, genRandomString(10)),
 | 
				
			||||||
 | 
					    timestamp_drawn: new Date().getTime()
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  await Attendee.update(
 | 
					  await Attendee.update(
 | 
				
			||||||
@@ -145,6 +150,56 @@ router.route("/winner").get(mustBeAuthenticated, async (req, res) => {
 | 
				
			|||||||
  res.json(winner);
 | 
					  res.json(winner);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const genRandomString = function(length) {
 | 
				
			||||||
 | 
					  return crypto
 | 
				
			||||||
 | 
					    .randomBytes(Math.ceil(length / 2))
 | 
				
			||||||
 | 
					    .toString("hex") /** convert to hexadecimal format */
 | 
				
			||||||
 | 
					    .slice(0, length); /** return required number of characters */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sha512 = function(password, salt) {
 | 
				
			||||||
 | 
					  var hash = crypto.createHmac("md5", salt); /** Hashing algorithm sha512 */
 | 
				
			||||||
 | 
					  hash.update(password);
 | 
				
			||||||
 | 
					  var value = hash.digest("hex");
 | 
				
			||||||
 | 
					  return value;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.route("/finish").get(mustBeAuthenticated, async (req, res) => {
 | 
				
			||||||
 | 
					  if (!config.token) {
 | 
				
			||||||
 | 
					    res.json(false);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let winners = await VirtualWinner.find({ timestamp_sent: undefined }).sort({
 | 
				
			||||||
 | 
					    timestamp_drawn: 1
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (winners.length == 0) {
 | 
				
			||||||
 | 
					    res.json(false);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let firstWinner = winners[0];
 | 
				
			||||||
 | 
					  messageSent = false;
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    let messageSent = await Message.sendMessage(firstWinner);
 | 
				
			||||||
 | 
					    Message.sendUpdate(winners.slice(1));
 | 
				
			||||||
 | 
					    if (!messageSent) {
 | 
				
			||||||
 | 
					      res.json(false);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    res.json(false);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  firstWinner.timestamp_sent = new Date().getTime();
 | 
				
			||||||
 | 
					  firstWinner.timestamp_limit = new Date().getTime() + 600000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await firstWinner.save();
 | 
				
			||||||
 | 
					  res.json(true);
 | 
				
			||||||
 | 
					  return;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.route("/attendees").get(async (req, res) => {
 | 
					router.route("/attendees").get(async (req, res) => {
 | 
				
			||||||
  let attendees = await Attendee.find();
 | 
					  let attendees = await Attendee.find();
 | 
				
			||||||
  let attendeesRedacted = [];
 | 
					  let attendeesRedacted = [];
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										173
									
								
								api/virtualRegistration.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								api/virtualRegistration.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
				
			|||||||
 | 
					const express = require("express");
 | 
				
			||||||
 | 
					const path = require("path");
 | 
				
			||||||
 | 
					const router = express.Router();
 | 
				
			||||||
 | 
					const mongoose = require("mongoose");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mongoose.connect("mongodb://localhost:27017/vinlottis", {
 | 
				
			||||||
 | 
					  useNewUrlParser: true
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Message = require(path.join(__dirname + "/../api/message"));
 | 
				
			||||||
 | 
					const config = require(path.join(__dirname + "/../config/defaults/push"));
 | 
				
			||||||
 | 
					const VirtualWinner = require(path.join(
 | 
				
			||||||
 | 
					  __dirname + "/../schemas/VirtualWinner"
 | 
				
			||||||
 | 
					));
 | 
				
			||||||
 | 
					const Highscore = require(path.join(__dirname + "/../schemas/Highscore"));
 | 
				
			||||||
 | 
					const Wine = require(path.join(__dirname + "/../schemas/Wine"));
 | 
				
			||||||
 | 
					const PreLotteryWine = require(path.join(
 | 
				
			||||||
 | 
					  __dirname + "/../schemas/PreLotteryWine"
 | 
				
			||||||
 | 
					));
 | 
				
			||||||
 | 
					const lotteryConfig = require(path.join(
 | 
				
			||||||
 | 
					  __dirname + "/../config/defaults/lottery"
 | 
				
			||||||
 | 
					));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.use((req, res, next) => {
 | 
				
			||||||
 | 
					  next();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.route("/winner/:id").get((req, res) => {
 | 
				
			||||||
 | 
					  res.redirect(`/#/winner/${req.params.id}`);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.route("/:id").get(async (req, res) => {
 | 
				
			||||||
 | 
					  let id = req.params.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let foundWinner = await VirtualWinner.findOne({ id: id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!foundWinner) {
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      existing: false,
 | 
				
			||||||
 | 
					      turn: false
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let allWinners = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (
 | 
				
			||||||
 | 
					    allWinners[0].id != foundWinner.id ||
 | 
				
			||||||
 | 
					    foundWinner.timestamp_limit == undefined ||
 | 
				
			||||||
 | 
					    foundWinner.timestamp_sent == undefined
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    res.json({ existing: true, turn: false });
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  res.json({
 | 
				
			||||||
 | 
					    existing: true,
 | 
				
			||||||
 | 
					    turn: true,
 | 
				
			||||||
 | 
					    name: foundWinner.name,
 | 
				
			||||||
 | 
					    color: foundWinner.color
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  return;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.route("/:id").post(async (req, res) => {
 | 
				
			||||||
 | 
					  let id = req.params.id;
 | 
				
			||||||
 | 
					  let wineName = req.body.wineName;
 | 
				
			||||||
 | 
					  let foundWinner = await VirtualWinner.findOne({ id: id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!foundWinner) {
 | 
				
			||||||
 | 
					    res.json(false);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (foundWinner.timestamp_limit < new Date().getTime()) {
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      success: false,
 | 
				
			||||||
 | 
					      limit: true
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let date = new Date();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let prelotteryWine = await PreLotteryWine.findOne({ name: wineName });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!prelotteryWine) {
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					      success: false,
 | 
				
			||||||
 | 
					      wine: true
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let wonWine = await Wine.findOne({ name: prelotteryWine.name });
 | 
				
			||||||
 | 
					  if (wonWine == undefined) {
 | 
				
			||||||
 | 
					    let newWonWine = new Wine({
 | 
				
			||||||
 | 
					      name: prelotteryWine.name,
 | 
				
			||||||
 | 
					      vivinoLink: prelotteryWine.vivinoLink,
 | 
				
			||||||
 | 
					      rating: prelotteryWine.rating,
 | 
				
			||||||
 | 
					      occurences: 1,
 | 
				
			||||||
 | 
					      image: prelotteryWine.image,
 | 
				
			||||||
 | 
					      id: prelotteryWine.id
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    await newWonWine.save();
 | 
				
			||||||
 | 
					    wonWine = newWonWine;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    wonWine.occurences += 1;
 | 
				
			||||||
 | 
					    wonWine.image = prelotteryWine.image;
 | 
				
			||||||
 | 
					    wonWine.id = prelotteryWine.id;
 | 
				
			||||||
 | 
					    await wonWine.save();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await prelotteryWine.delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const person = await Highscore.findOne({
 | 
				
			||||||
 | 
					    name: foundWinner.name
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (person == undefined) {
 | 
				
			||||||
 | 
					    let newPerson = new Highscore({
 | 
				
			||||||
 | 
					      name: foundWinner.name,
 | 
				
			||||||
 | 
					      wins: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          color: foundWinner.color,
 | 
				
			||||||
 | 
					          date: date,
 | 
				
			||||||
 | 
					          wine: wonWine
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await newPerson.save();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    person.wins.push({
 | 
				
			||||||
 | 
					      color: foundWinner.color,
 | 
				
			||||||
 | 
					      date: date,
 | 
				
			||||||
 | 
					      wine: wonWine
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    person.markModified("wins");
 | 
				
			||||||
 | 
					    await person.save();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await foundWinner.delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let nextWinner = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
 | 
				
			||||||
 | 
					  if (nextWinner.length > 0) {
 | 
				
			||||||
 | 
					    Message.sendMessage(nextWinner[0]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  startTimeout(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  res.json({
 | 
				
			||||||
 | 
					    success: true
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  return;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function startTimeout(id) {
 | 
				
			||||||
 | 
					  setTimeout(async () => {
 | 
				
			||||||
 | 
					    let virtualWinner = await VirtualWinner.findOne({ id: id });
 | 
				
			||||||
 | 
					    if (!virtualWinner) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Message.sendMessageTooLate(virtualWinner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtualWinner.timestamp_drawn;
 | 
				
			||||||
 | 
					    virtualWinner.timestamp_limit = null;
 | 
				
			||||||
 | 
					    virtualWinner.timestamp_sent = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await virtualWinner.save();
 | 
				
			||||||
 | 
					  }, 600000);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = router;
 | 
				
			||||||
@@ -11,6 +11,7 @@ try {
 | 
				
			|||||||
    message: "INSERT MESSAGE",
 | 
					    message: "INSERT MESSAGE",
 | 
				
			||||||
    date: 5,
 | 
					    date: 5,
 | 
				
			||||||
    hours: 15,
 | 
					    hours: 15,
 | 
				
			||||||
    apiUrl: "https://lottis.vin"
 | 
					    apiUrl: "https://lottis.vin",
 | 
				
			||||||
 | 
					    token: undefined
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								config/env/lottery.config.example.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								config/env/lottery.config.example.js
									
									
									
									
										vendored
									
									
								
							@@ -5,5 +5,6 @@ module.exports = {
 | 
				
			|||||||
  message: "VINLOTTERI",
 | 
					  message: "VINLOTTERI",
 | 
				
			||||||
  date: 5,
 | 
					  date: 5,
 | 
				
			||||||
  hours: 15,
 | 
					  hours: 15,
 | 
				
			||||||
  apiUrl: undefined
 | 
					  apiUrl: undefined,
 | 
				
			||||||
 | 
					  token: undefined
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										36
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -9302,9 +9302,9 @@
 | 
				
			|||||||
      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
 | 
					      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "psl": {
 | 
					    "psl": {
 | 
				
			||||||
      "version": "1.7.0",
 | 
					      "version": "1.8.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
 | 
					      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "public-encrypt": {
 | 
					    "public-encrypt": {
 | 
				
			||||||
      "version": "4.0.3",
 | 
					      "version": "4.0.3",
 | 
				
			||||||
@@ -9356,7 +9356,8 @@
 | 
				
			|||||||
    "punycode": {
 | 
					    "punycode": {
 | 
				
			||||||
      "version": "1.4.1",
 | 
					      "version": "1.4.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
 | 
				
			||||||
      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
 | 
					      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "q": {
 | 
					    "q": {
 | 
				
			||||||
      "version": "1.5.1",
 | 
					      "version": "1.5.1",
 | 
				
			||||||
@@ -9821,9 +9822,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "request": {
 | 
					    "request": {
 | 
				
			||||||
      "version": "2.88.0",
 | 
					      "version": "2.88.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
 | 
				
			||||||
      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
 | 
					      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
 | 
				
			||||||
      "requires": {
 | 
					      "requires": {
 | 
				
			||||||
        "aws-sign2": "~0.7.0",
 | 
					        "aws-sign2": "~0.7.0",
 | 
				
			||||||
        "aws4": "^1.8.0",
 | 
					        "aws4": "^1.8.0",
 | 
				
			||||||
@@ -9832,7 +9833,7 @@
 | 
				
			|||||||
        "extend": "~3.0.2",
 | 
					        "extend": "~3.0.2",
 | 
				
			||||||
        "forever-agent": "~0.6.1",
 | 
					        "forever-agent": "~0.6.1",
 | 
				
			||||||
        "form-data": "~2.3.2",
 | 
					        "form-data": "~2.3.2",
 | 
				
			||||||
        "har-validator": "~5.1.0",
 | 
					        "har-validator": "~5.1.3",
 | 
				
			||||||
        "http-signature": "~1.2.0",
 | 
					        "http-signature": "~1.2.0",
 | 
				
			||||||
        "is-typedarray": "~1.0.0",
 | 
					        "is-typedarray": "~1.0.0",
 | 
				
			||||||
        "isstream": "~0.1.2",
 | 
					        "isstream": "~0.1.2",
 | 
				
			||||||
@@ -9842,7 +9843,7 @@
 | 
				
			|||||||
        "performance-now": "^2.1.0",
 | 
					        "performance-now": "^2.1.0",
 | 
				
			||||||
        "qs": "~6.5.2",
 | 
					        "qs": "~6.5.2",
 | 
				
			||||||
        "safe-buffer": "^5.1.2",
 | 
					        "safe-buffer": "^5.1.2",
 | 
				
			||||||
        "tough-cookie": "~2.4.3",
 | 
					        "tough-cookie": "~2.5.0",
 | 
				
			||||||
        "tunnel-agent": "^0.6.0",
 | 
					        "tunnel-agent": "^0.6.0",
 | 
				
			||||||
        "uuid": "^3.3.2"
 | 
					        "uuid": "^3.3.2"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@@ -11394,12 +11395,19 @@
 | 
				
			|||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "tough-cookie": {
 | 
					    "tough-cookie": {
 | 
				
			||||||
      "version": "2.4.3",
 | 
					      "version": "2.5.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
 | 
					      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
 | 
				
			||||||
      "requires": {
 | 
					      "requires": {
 | 
				
			||||||
        "psl": "^1.1.24",
 | 
					        "psl": "^1.1.28",
 | 
				
			||||||
        "punycode": "^1.4.1"
 | 
					        "punycode": "^2.1.1"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "punycode": {
 | 
				
			||||||
 | 
					          "version": "2.1.1",
 | 
				
			||||||
 | 
					          "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
 | 
				
			||||||
 | 
					          "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "trim-newlines": {
 | 
					    "trim-newlines": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@
 | 
				
			|||||||
    "passport-local-mongoose": "^6.0.1",
 | 
					    "passport-local-mongoose": "^6.0.1",
 | 
				
			||||||
    "qrcode": "^1.4.4",
 | 
					    "qrcode": "^1.4.4",
 | 
				
			||||||
    "referrer-policy": "^1.2.0",
 | 
					    "referrer-policy": "^1.2.0",
 | 
				
			||||||
 | 
					    "request": "^2.88.2",
 | 
				
			||||||
    "socket.io": "^2.3.0",
 | 
					    "socket.io": "^2.3.0",
 | 
				
			||||||
    "socket.io-client": "^2.3.0",
 | 
					    "socket.io-client": "^2.3.0",
 | 
				
			||||||
    "vue": "~2.6",
 | 
					    "vue": "~2.6",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,11 @@ const VirtualWinner = new Schema({
 | 
				
			|||||||
  green: Number,
 | 
					  green: Number,
 | 
				
			||||||
  blue: Number,
 | 
					  blue: Number,
 | 
				
			||||||
  red: Number,
 | 
					  red: Number,
 | 
				
			||||||
  yellow: Number
 | 
					  yellow: Number,
 | 
				
			||||||
 | 
					  id: String,
 | 
				
			||||||
 | 
					  timestamp_drawn: Number,
 | 
				
			||||||
 | 
					  timestamp_sent: Number,
 | 
				
			||||||
 | 
					  timestamp_limit: Number
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = mongoose.model("VirtualWinner", VirtualWinner);
 | 
					module.exports = mongoose.model("VirtualWinner", VirtualWinner);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,9 @@ const subscriptionApi = require(path.join(__dirname + "/api/subscriptions"));
 | 
				
			|||||||
const loginApi = require(path.join(__dirname + "/api/login"));
 | 
					const loginApi = require(path.join(__dirname + "/api/login"));
 | 
				
			||||||
const wineinfoApi = require(path.join(__dirname + "/api/wineinfo"));
 | 
					const wineinfoApi = require(path.join(__dirname + "/api/wineinfo"));
 | 
				
			||||||
const virtualApi = require(path.join(__dirname + "/api/virtualLottery"));
 | 
					const virtualApi = require(path.join(__dirname + "/api/virtualLottery"));
 | 
				
			||||||
 | 
					const virtualRegistrationApi = require(path.join(
 | 
				
			||||||
 | 
					  __dirname + "/api/virtualRegistration"
 | 
				
			||||||
 | 
					));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//This is required for the chat to work
 | 
					//This is required for the chat to work
 | 
				
			||||||
const chat = require(path.join(__dirname + "/api/chat"))(io);
 | 
					const chat = require(path.join(__dirname + "/api/chat"))(io);
 | 
				
			||||||
@@ -92,6 +95,7 @@ app.use("/api/", retrieveApi);
 | 
				
			|||||||
app.use("/api/", wineinfoApi);
 | 
					app.use("/api/", wineinfoApi);
 | 
				
			||||||
app.use("/api/", chatHistory);
 | 
					app.use("/api/", chatHistory);
 | 
				
			||||||
app.use("/api/virtual/", virtualApi(io));
 | 
					app.use("/api/virtual/", virtualApi(io));
 | 
				
			||||||
 | 
					app.use("/api/virtual-registration/", virtualRegistrationApi);
 | 
				
			||||||
app.use("/subscription", subscriptionApi);
 | 
					app.use("/subscription", subscriptionApi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.get("/dagens", function(req, res) {
 | 
					app.get("/dagens", function(req, res) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										207
									
								
								src/api.js
									
									
									
									
									
								
							
							
						
						
									
										207
									
								
								src/api.js
									
									
									
									
									
								
							@@ -1,58 +1,49 @@
 | 
				
			|||||||
const BASE_URL = __APIURL__ || window.location.origin;
 | 
					const BASE_URL = __APIURL__ || window.location.origin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const statistics = () => {
 | 
					const statistics = () => {
 | 
				
			||||||
  const url = new URL('/api/purchase/statistics', BASE_URL)
 | 
					  const url = new URL("/api/purchase/statistics", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const colorStatistics = () => {
 | 
					const colorStatistics = () => {
 | 
				
			||||||
  const url = new URL("/api/purchase/statistics/color", BASE_URL)
 | 
					  const url = new URL("/api/purchase/statistics/color", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const highscoreStatistics = () => {
 | 
					const highscoreStatistics = () => {
 | 
				
			||||||
  const url = new URL("/api/highscore/statistics", BASE_URL)
 | 
					  const url = new URL("/api/highscore/statistics", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const overallWineStatistics = () => {
 | 
					const overallWineStatistics = () => {
 | 
				
			||||||
  const url = new URL("/api/wines/statistics/overall", BASE_URL)
 | 
					  const url = new URL("/api/wines/statistics/overall", BASE_URL);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  return fetch(url.href)
 | 
					 | 
				
			||||||
    .then(resp => resp.json())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const chartWinsByColor = () => {
 | 
					const chartWinsByColor = () => {
 | 
				
			||||||
  const url = new URL("/api/purchase/statistics/color", BASE_URL)
 | 
					  const url = new URL("/api/purchase/statistics/color", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const chartPurchaseByColor = () => {
 | 
					const chartPurchaseByColor = () => {
 | 
				
			||||||
  const url = new URL("/api/purchase/statistics", BASE_URL)
 | 
					  const url = new URL("/api/purchase/statistics", BASE_URL);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  return fetch(url.href)
 | 
					 | 
				
			||||||
    .then(resp => resp.json())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const prelottery = () => {
 | 
					const prelottery = () => {
 | 
				
			||||||
  const url = new URL("/api/wines/prelottery", BASE_URL)
 | 
					  const url = new URL("/api/wines/prelottery", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const log = (sendObject) => {
 | 
					const log = sendObject => {
 | 
				
			||||||
  const url = new URL("/api/log", BASE_URL)
 | 
					  const url = new URL("/api/log", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const options = {
 | 
					  const options = {
 | 
				
			||||||
    headers: {
 | 
					    headers: {
 | 
				
			||||||
@@ -60,11 +51,10 @@ const log = (sendObject) => {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    method: "POST",
 | 
					    method: "POST",
 | 
				
			||||||
    body: JSON.stringify(sendObject)
 | 
					    body: JSON.stringify(sendObject)
 | 
				
			||||||
  } 
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href, options)
 | 
					  return fetch(url.href, options).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const addAttendee = sendObject => {
 | 
					const addAttendee = sendObject => {
 | 
				
			||||||
  const url = new URL("/api/virtual/attendee", BASE_URL);
 | 
					  const url = new URL("/api/virtual/attendee", BASE_URL);
 | 
				
			||||||
@@ -115,7 +105,7 @@ const deleteWinners = () => {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href, options).then(resp => resp.json());
 | 
					  return fetch(url.href, options).then(resp => resp.json());
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const deleteAttendees = () => {
 | 
					const deleteAttendees = () => {
 | 
				
			||||||
  const url = new URL("/api/virtual/attendees", BASE_URL);
 | 
					  const url = new URL("/api/virtual/attendees", BASE_URL);
 | 
				
			||||||
@@ -134,10 +124,10 @@ const attendees = () => {
 | 
				
			|||||||
  const url = new URL("/api/virtual/attendees", BASE_URL);
 | 
					  const url = new URL("/api/virtual/attendees", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href).then(resp => resp.json());
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logWines = (wines) => {
 | 
					const logWines = wines => {
 | 
				
			||||||
  const url = new URL("/api/log/wines", BASE_URL)
 | 
					  const url = new URL("/api/log/wines", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const options = {
 | 
					  const options = {
 | 
				
			||||||
    headers: {
 | 
					    headers: {
 | 
				
			||||||
@@ -145,95 +135,115 @@ const logWines = (wines) => {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    method: "POST",
 | 
					    method: "POST",
 | 
				
			||||||
    body: JSON.stringify(wines)
 | 
					    body: JSON.stringify(wines)
 | 
				
			||||||
  }
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href, options)
 | 
					  return fetch(url.href, options).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const wineSchema = () => {
 | 
					const wineSchema = () => {
 | 
				
			||||||
  const url = new URL("/api/log/schema", BASE_URL)
 | 
					  const url = new URL("/api/log/schema", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const barcodeToVinmonopolet = (id) => {
 | 
					const barcodeToVinmonopolet = id => {
 | 
				
			||||||
  const url = new URL("/api/wineinfo/" + id, BASE_URL)
 | 
					  const url = new URL("/api/wineinfo/" + id, BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(async resp => {
 | 
				
			||||||
    .then(async (resp) => {
 | 
					    if (!resp.ok) {
 | 
				
			||||||
      if (!resp.ok) {
 | 
					      if (resp.status == 404) {
 | 
				
			||||||
        if (resp.status == 404) {
 | 
					        throw await resp.json();
 | 
				
			||||||
          throw await resp.json()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        return resp.json()
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    } else {
 | 
				
			||||||
}
 | 
					      return resp.json();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const handleErrors = async (resp) => {
 | 
					const handleErrors = async resp => {
 | 
				
			||||||
  if ([400, 409].includes(resp.status)) {
 | 
					  if ([400, 409].includes(resp.status)) {
 | 
				
			||||||
    throw await resp.json()
 | 
					    throw await resp.json();
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    console.error("Unexpected error occured when login/register user:", resp)
 | 
					    console.error("Unexpected error occured when login/register user:", resp);
 | 
				
			||||||
    throw await resp.json()
 | 
					    throw await resp.json();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const login = (username, password) => {
 | 
					const login = (username, password) => {
 | 
				
			||||||
  const url = new URL("/login", BASE_URL)
 | 
					  const url = new URL("/login", BASE_URL);
 | 
				
			||||||
  const options = {
 | 
					  const options = {
 | 
				
			||||||
    headers: {
 | 
					    headers: {
 | 
				
			||||||
      "Content-Type": "application/json"
 | 
					      "Content-Type": "application/json"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    method: "POST",
 | 
					    method: "POST",
 | 
				
			||||||
    body: JSON.stringify({ username, password })
 | 
					    body: JSON.stringify({ username, password })
 | 
				
			||||||
  }
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href, options)
 | 
					  return fetch(url.href, options).then(resp => {
 | 
				
			||||||
    .then(resp => {
 | 
					    if (resp.ok) {
 | 
				
			||||||
      if (resp.ok) {        
 | 
					      return resp.json();
 | 
				
			||||||
        return resp.json()
 | 
					    } else {
 | 
				
			||||||
      } else {
 | 
					      return handleErrors(resp);
 | 
				
			||||||
        return handleErrors(resp)
 | 
					    }
 | 
				
			||||||
      }
 | 
					  });
 | 
				
			||||||
    })
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const register = (username, password) => {
 | 
					const register = (username, password) => {
 | 
				
			||||||
  const url = new URL("/register", BASE_URL)
 | 
					  const url = new URL("/register", BASE_URL);
 | 
				
			||||||
  const options = {
 | 
					  const options = {
 | 
				
			||||||
    headers: {
 | 
					    headers: {
 | 
				
			||||||
      "Content-Type": "application/json"
 | 
					      "Content-Type": "application/json"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    method: "POST",
 | 
					    method: "POST",
 | 
				
			||||||
    body: JSON.stringify({ username, password })
 | 
					    body: JSON.stringify({ username, password })
 | 
				
			||||||
  }
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href, options)
 | 
					  return fetch(url.href, options).then(resp => {
 | 
				
			||||||
    .then(resp => {
 | 
					    if (resp.ok) {
 | 
				
			||||||
      if (resp.ok) {
 | 
					      return resp.json();
 | 
				
			||||||
        return resp.json()
 | 
					    } else {
 | 
				
			||||||
      } else {
 | 
					      return handleErrors(resp);
 | 
				
			||||||
        return handleErrors(resp)
 | 
					    }
 | 
				
			||||||
      }
 | 
					  });
 | 
				
			||||||
    })
 | 
					};
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getChatHistory = (skip = null, take = null) => {
 | 
				
			||||||
const getChatHistory = (skip=null, take=null) => {
 | 
					 | 
				
			||||||
  const url = new URL("/api/chat/history", BASE_URL);
 | 
					  const url = new URL("/api/chat/history", BASE_URL);
 | 
				
			||||||
  if (!isNaN(skip))
 | 
					  if (!isNaN(skip)) url.searchParams.append("skip", skip);
 | 
				
			||||||
    url.searchParams.append("skip", skip);
 | 
					  if (!isNaN(take)) url.searchParams.append("take", take);
 | 
				
			||||||
  if (!isNaN(take))
 | 
					 | 
				
			||||||
    url.searchParams.append("take", take);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fetch(url.href)
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
    .then(resp => resp.json())
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					const finishedDraw = () => {
 | 
				
			||||||
 | 
					  const url = new URL("/api/virtual/finish", BASE_URL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getAmIWinner = id => {
 | 
				
			||||||
 | 
					  const url = new URL(`/api/virtual-registration/${id}`, BASE_URL);
 | 
				
			||||||
 | 
					  return fetch(url.href).then(resp => resp.json());
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const postWineChosen = (id, wineName) => {
 | 
				
			||||||
 | 
					  const url = new URL(`/api/virtual-registration/${id}`, BASE_URL);
 | 
				
			||||||
 | 
					  const options = {
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      "Content-Type": "application/json"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    method: "POST",
 | 
				
			||||||
 | 
					    body: JSON.stringify({ wineName: wineName })
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetch(url.href, options).then(resp => {
 | 
				
			||||||
 | 
					    if (resp.ok) {
 | 
				
			||||||
 | 
					      return resp.json();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return handleErrors(resp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {
 | 
					export {
 | 
				
			||||||
  statistics,
 | 
					  statistics,
 | 
				
			||||||
@@ -257,5 +267,8 @@ export {
 | 
				
			|||||||
  winnersSecure,
 | 
					  winnersSecure,
 | 
				
			||||||
  deleteWinners,
 | 
					  deleteWinners,
 | 
				
			||||||
  deleteAttendees,
 | 
					  deleteAttendees,
 | 
				
			||||||
  getChatHistory
 | 
					  getChatHistory,
 | 
				
			||||||
 | 
					  finishedDraw,
 | 
				
			||||||
 | 
					  getAmIWinner,
 | 
				
			||||||
 | 
					  postWineChosen
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -118,7 +118,8 @@ import {
 | 
				
			|||||||
  attendees,
 | 
					  attendees,
 | 
				
			||||||
  winnersSecure,
 | 
					  winnersSecure,
 | 
				
			||||||
  deleteWinners,
 | 
					  deleteWinners,
 | 
				
			||||||
  deleteAttendees
 | 
					  deleteAttendees,
 | 
				
			||||||
 | 
					  finishedDraw
 | 
				
			||||||
} from "@/api";
 | 
					} from "@/api";
 | 
				
			||||||
import RaffleGenerator from "@/ui/RaffleGenerator";
 | 
					import RaffleGenerator from "@/ui/RaffleGenerator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -165,6 +166,9 @@ export default {
 | 
				
			|||||||
    this.socket.on("new_attendee", async msg => {
 | 
					    this.socket.on("new_attendee", async msg => {
 | 
				
			||||||
      this.getAttendees();
 | 
					      this.getAttendees();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window.finishedDraw = finishedDraw;
 | 
				
			||||||
 | 
					    console.log("here");
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    setWithRandomColors(colors) {
 | 
					    setWithRandomColors(colors) {
 | 
				
			||||||
@@ -209,6 +213,7 @@ export default {
 | 
				
			|||||||
            this.countdown();
 | 
					            this.countdown();
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            this.drawingWinner = false;
 | 
					            this.drawingWinner = false;
 | 
				
			||||||
 | 
					            finishedDraw();
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          this.getWinners();
 | 
					          this.getWinners();
 | 
				
			||||||
          this.getAttendees();
 | 
					          this.getAttendees();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										102
									
								
								src/components/WinnerPage.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/components/WinnerPage.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="container">
 | 
				
			||||||
 | 
					    <div v-if="!posted">
 | 
				
			||||||
 | 
					      <h1 v-if="name">Gratulerer {{name}}!</h1>
 | 
				
			||||||
 | 
					      <p
 | 
				
			||||||
 | 
					        v-if="name"
 | 
				
			||||||
 | 
					      >Her er valgene for dagens lotteri, du har 10 minutter å velge etter du fikk SMS-en.</p>
 | 
				
			||||||
 | 
					      <h1 v-else-if="!turn && !existing" class="sent-container">Finner ikke noen vinner her..</h1>
 | 
				
			||||||
 | 
					      <h1 v-else-if="!turn" class="sent-container">Du må vente på tur..</h1>
 | 
				
			||||||
 | 
					      <div class="wines-container" v-if="name">
 | 
				
			||||||
 | 
					        <br />
 | 
				
			||||||
 | 
					        <br />
 | 
				
			||||||
 | 
					        <Wine
 | 
				
			||||||
 | 
					          :wine="wine"
 | 
				
			||||||
 | 
					          v-for="wine in wines"
 | 
				
			||||||
 | 
					          :key="wine"
 | 
				
			||||||
 | 
					          :winner="true"
 | 
				
			||||||
 | 
					          :fullscreen="true"
 | 
				
			||||||
 | 
					          :inlineSlot="true"
 | 
				
			||||||
 | 
					          v-on:chosen="chosenWine"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div v-else-if="posted" class="sent-container">
 | 
				
			||||||
 | 
					      <h1>Valget ditt er sendt inn!</h1>
 | 
				
			||||||
 | 
					      <p>Du får mer info om henting snarest!</p>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import { getAmIWinner, postWineChosen } from "@/api";
 | 
				
			||||||
 | 
					import Wine from "@/ui/Wine";
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  components: { Wine },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      id: null,
 | 
				
			||||||
 | 
					      existing: false,
 | 
				
			||||||
 | 
					      fetched: false,
 | 
				
			||||||
 | 
					      turn: false,
 | 
				
			||||||
 | 
					      name: null,
 | 
				
			||||||
 | 
					      wines: [],
 | 
				
			||||||
 | 
					      posted: false
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  async mounted() {
 | 
				
			||||||
 | 
					    this.id = this.$router.currentRoute.params.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let winnerObject = await getAmIWinner(this.id);
 | 
				
			||||||
 | 
					    this.fetched = true;
 | 
				
			||||||
 | 
					    if (!winnerObject || !winnerObject.existing) {
 | 
				
			||||||
 | 
					      console.error("non existing", winnerObject);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.existing = true;
 | 
				
			||||||
 | 
					    if (winnerObject.existing && !winnerObject.turn) {
 | 
				
			||||||
 | 
					      console.error("not your turn yet", winnerObject);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.turn = true;
 | 
				
			||||||
 | 
					    this.name = winnerObject.name;
 | 
				
			||||||
 | 
					    const _wines = await fetch("/api/wines/prelottery");
 | 
				
			||||||
 | 
					    this.wines = await _wines.json();
 | 
				
			||||||
 | 
					    console.log(this.wines);
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    chosenWine: async function(name) {
 | 
				
			||||||
 | 
					      console.log("chosen a wine");
 | 
				
			||||||
 | 
					      let posted = await postWineChosen(this.id, name);
 | 
				
			||||||
 | 
					      console.log("response", posted);
 | 
				
			||||||
 | 
					      if (posted.success) {
 | 
				
			||||||
 | 
					        this.posted = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
 | 
					@import "./src/styles/global";
 | 
				
			||||||
 | 
					.container {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  margin-top: 4rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.sent-container {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 90vh;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.wines-container {
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -8,7 +8,7 @@ import CreatePage from "@/components/CreatePage";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import AdminPage from "@/components/AdminPage";
 | 
					import AdminPage from "@/components/AdminPage";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import VirtualLotteryPage from "@/components/VirtualLotteryPage";
 | 
					import WinnerPage from "@/components/WinnerPage";
 | 
				
			||||||
import LotteryPage from "@/components/LotteryPage";
 | 
					import LotteryPage from "@/components/LotteryPage";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const routes = [
 | 
					const routes = [
 | 
				
			||||||
@@ -43,6 +43,10 @@ const routes = [
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    path: "/lottery/:tab",
 | 
					    path: "/lottery/:tab",
 | 
				
			||||||
    component: LotteryPage
 | 
					    component: LotteryPage
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: "/winner/:id",
 | 
				
			||||||
 | 
					    component: WinnerPage
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,32 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="wine-container" :class="{ 'big': fullscreen }">
 | 
					  <div class="wine-container" :class="{ 'big': fullscreen }">
 | 
				
			||||||
    <div class="left">
 | 
					    <div class="left">
 | 
				
			||||||
      <img v-if="wine.image" :src="wine.image" class="wine-image" :class="{ 'fullscreen': fullscreen }" />
 | 
					      <img
 | 
				
			||||||
 | 
					        v-if="wine.image"
 | 
				
			||||||
 | 
					        :src="wine.image"
 | 
				
			||||||
 | 
					        class="wine-image"
 | 
				
			||||||
 | 
					        :class="{ 'fullscreen': fullscreen }"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
      <img v-else class="wine-placeholder" alt="Wine image" />
 | 
					      <img v-else class="wine-placeholder" alt="Wine image" />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="right">
 | 
					    <div class="right">
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <h2 v-if="wine.name">{{ wine.name }}</h2><h2 v-else>(no name)</h2>
 | 
					        <h2 v-if="wine.name">{{ wine.name }}</h2>
 | 
				
			||||||
 | 
					        <h2 v-else>(no name)</h2>
 | 
				
			||||||
        <span v-if="wine.rating">{{ wine.rating }} rating</span>
 | 
					        <span v-if="wine.rating">{{ wine.rating }} rating</span>
 | 
				
			||||||
        <span v-if="wine.price">{{ wine.price }} NOK</span>
 | 
					        <span v-if="wine.price">{{ wine.price }} NOK</span>
 | 
				
			||||||
        <span v-if="wine.country">{{ wine.country }}</span>
 | 
					        <span v-if="wine.country">{{ wine.country }}</span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <a v-if="wine.vivinoLink" :href="wine.vivinoLink" class="wine-link">Les mer på {{ hostname(wine.vivinoLink) }}</a>
 | 
					        <a
 | 
				
			||||||
 | 
					          v-if="wine.vivinoLink"
 | 
				
			||||||
 | 
					          :href="wine.vivinoLink"
 | 
				
			||||||
 | 
					          class="wine-link"
 | 
				
			||||||
 | 
					        >Les mer på {{ hostname(wine.vivinoLink) }}</a>
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          v-if="winner"
 | 
				
			||||||
 | 
					          @click="choseWine(wine.name)"
 | 
				
			||||||
 | 
					          class="vin-button"
 | 
				
			||||||
 | 
					        >Velg dette som din vin</button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <slot v-if="shouldUseInlineSlot()"></slot>
 | 
					      <slot v-if="shouldUseInlineSlot()"></slot>
 | 
				
			||||||
@@ -36,15 +51,27 @@ export default {
 | 
				
			|||||||
      type: Boolean,
 | 
					      type: Boolean,
 | 
				
			||||||
      required: false,
 | 
					      required: false,
 | 
				
			||||||
      default: false
 | 
					      default: false
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    winner: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      required: false,
 | 
				
			||||||
 | 
					      default: false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    shouldUseInlineSlot() {
 | 
					    shouldUseInlineSlot() {
 | 
				
			||||||
      return this.inlineSlot && window.innerWidth > 768
 | 
					      return this.inlineSlot && window.innerWidth > 768;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    hostname(url) {
 | 
					    hostname(url) {
 | 
				
			||||||
      const urlHostname = new URL(url).hostname
 | 
					      const urlHostname = new URL(url).hostname;
 | 
				
			||||||
      return urlHostname.split(".")[(urlHostname.match(/\./g) || []).length - 1]
 | 
					      return urlHostname.split(".")[
 | 
				
			||||||
 | 
					        (urlHostname.match(/\./g) || []).length - 1
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    choseWine(name) {
 | 
				
			||||||
 | 
					      if (window.confirm(`Er du sikker på at du vil ha ${name}?`)) {
 | 
				
			||||||
 | 
					        this.$emit("chosen", name);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -52,6 +79,7 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<style lang="scss" scoped>
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
@import "./src/styles/media-queries";
 | 
					@import "./src/styles/media-queries";
 | 
				
			||||||
 | 
					@import "./src/styles/global";
 | 
				
			||||||
@import "./src/styles/variables";
 | 
					@import "./src/styles/variables";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.wine-image {
 | 
					.wine-image {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user