Merge pull request #67 from KevinMidboe/refactor/project-structure

Refactor/Project structure
This commit was merged in pull request #67.
This commit is contained in:
2020-12-10 23:20:38 +01:00
committed by GitHub
102 changed files with 4237 additions and 4682 deletions

View File

@@ -1,22 +1,46 @@
const path = require("path");
const { addMessage } = require(path.join(__dirname + "/redis.js"));
const validateUsername = (username) => {
let error = undefined;
const illegalChars = /\W/;
const minLength = 3;
const maxLength = 15;
if (typeof username !== 'string') {
error = 'Ugyldig brukernavn.';
} else if (username.length === 0) {
error = 'Vennligst oppgi brukernavn.';
} else if (username.length < minLength || username.length > maxLength) {
error = `Brukernavn må være mellom ${minLength}-${maxLength} karaktere.`
} else if (illegalChars.test(username)) {
error = 'Brukernavn kan bare inneholde tall og bokstaver.'
}
return error;
}
const io = (io) => {
io.on("connection", socket => {
let username = null;
socket.on("username", msg => {
if (msg.username == null) {
const usernameValidationError = validateUsername(msg.username);
if (usernameValidationError) {
username = null;
socket.emit("accept_username", false);
return;
}
if (msg.username.length > 3 && msg.username.length < 30) {
socket.emit("accept_username", {
reason: usernameValidationError,
success: false,
username: undefined
});
} else {
username = msg.username;
socket.emit("accept_username", true);
return;
socket.emit("accept_username", {
reason: undefined,
success: true,
username: msg.username
});
}
socket.emit("accept_username", false);
});
socket.on("chat", msg => {

View File

@@ -1,33 +1,29 @@
const express = require("express");
const path = require("path");
const router = express.Router();
const { history, clearHistory } = require(path.join(__dirname + "/../api/redis"));
router.use((req, res, next) => {
next();
});
const getAllHistory = (req, res) => {
let { page, limit } = req.query;
page = !isNaN(page) ? Number(page) : undefined;
limit = !isNaN(limit) ? Number(limit) : undefined;
router.route("/chat/history").get(async (req, res) => {
let { skip, take } = req.query;
skip = !isNaN(skip) ? Number(skip) : undefined;
take = !isNaN(take) ? Number(take) : undefined;
return history(page, limit)
.then(messages => res.json(messages))
.catch(error => res.status(500).json({
message: error.message,
success: false
}));
};
try {
const messages = await history(skip, take);
res.json(messages)
} catch(error) {
res.status(500).send(error);
}
});
const deleteHistory = (req, res) => {
return clearHistory()
.then(message => res.json(message))
.catch(error => res.status(500).json({
message: error.message,
success: false
}));
};
router.route("/chat/history").delete(async (req, res) => {
try {
const messages = await clearHistory();
res.json(messages)
} catch(error) {
res.status(500).send(error);
}
});
module.exports = router;
module.exports = {
getAllHistory,
deleteHistory
};

View File

@@ -1,59 +0,0 @@
const passport = require("passport");
const path = require("path");
const User = require(path.join(__dirname + "/../schemas/User"));
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) {
// User.register(
// new User({ username: req.body.username }),
// req.body.password,
// function(err) {
// if (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);
// }
// return res.status(200).send({ message: "Bruker registrert. Velkommen " + req.body.username, success: true })
// }
// );
// });
router.get("/login", function(req, res) {
res.sendFile(path.join(__dirname + "/../public/index.html"));
});
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({ message: err.message, success: false })
return next(err);
}
if (!user) return res.status(404).send({ message: "Incorrect username or password", success: false })
req.logIn(user, (err) => {
if (err) { return next(err) }
return res.status(200).send({ message: "Velkommen " + user.username, success: true })
})
})(req, res, next);
});
router.get("/logout", function(req, res) {
req.logout();
res.redirect("/");
});
module.exports = router;

View File

@@ -1,7 +1,7 @@
const path = require('path');
const Highscore = require(path.join(__dirname + '/../schemas/Highscore'));
const Wine = require(path.join(__dirname + '/../schemas/Wine'));
const Highscore = require(path.join(__dirname, '/schemas/Highscore'));
const Wine = require(path.join(__dirname, '/schemas/Wine'));
// Utils
const epochToDateString = date => new Date(parseInt(date)).toDateString();

View File

@@ -0,0 +1,18 @@
const mustBeAuthenticated = (req, res, next) => {
if (process.env.NODE_ENV == "development") {
console.info(`Restricted endpoint ${req.originalUrl}, allowing with environment development.`)
req.isAuthenticated = () => true;
return next();
}
if (!req.isAuthenticated()) {
return res.status(401).send({
success: false,
message: "Du må være logget inn."
});
}
return next();
};
module.exports = mustBeAuthenticated;

View File

@@ -0,0 +1,6 @@
const setAdminHeaderIfAuthenticated = (req, res, next) => {
res.set("Vinlottis-Admin", req.isAuthenticated());
return next();
};
module.exports = setAdminHeaderIfAuthenticated;

View File

@@ -0,0 +1,6 @@
const openCORS = (req, res, next) => {
res.set("Access-Control-Allow-Origin", "*")
return next();
};
module.exports = openCORS;

View File

@@ -0,0 +1,37 @@
const camelToKebabCase = str => str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
const mapFeaturePolicyToString = (features) => {
return Object.entries(features).map(([key, value]) => {
key = camelToKebabCase(key)
value = value == "*" ? value : `'${ value }'`
return `${key} ${value}`
}).join("; ")
}
const setupHeaders = (req, res, next) => {
res.set("Access-Control-Allow-Headers", "Content-Type")
// Security
res.set("X-Content-Type-Options", "nosniff");
res.set("X-XSS-Protection", "1; mode=block");
res.set("X-Frame-Options", "SAMEORIGIN");
res.set("X-DNS-Prefetch-Control", "off");
res.set("X-Download-Options", "noopen");
res.set("Strict-Transport-Security", "max-age=15552000; includeSubDomains")
// Feature policy
const features = {
fullscreen: "*",
payment: "none",
microphone: "none",
camera: "self",
speaker: "*",
syncXhr: "self"
}
const featureString = mapFeaturePolicyToString(features);
res.set("Feature-Policy", featureString)
return next();
}
module.exports = setupHeaders;

View File

@@ -1,5 +1,5 @@
const path = require("path");
const Highscore = require(path.join(__dirname + "/../schemas/Highscore"));
const Highscore = require(path.join(__dirname, "/schemas/Highscore"));
async function findSavePerson(foundWinner, wonWine, date) {
let person = await Highscore.findOne({

View File

@@ -1,29 +1,40 @@
const { promisify } = require("util"); // from node
let client;
let llenAsync;
let lrangeAsync;
try {
const redis = require("redis");
console.log("trying to create redis");
console.log("Trying to connect with redis..");
client = redis.createClient();
client.zcount = promisify(client.zcount).bind(client);
client.zadd = promisify(client.zadd).bind(client);
client.zrevrange = promisify(client.zrevrange).bind(client);
client.del = promisify(client.del).bind(client);
client.on("connect", () => console.log("Redis connection established!"));
client.on("error", function(err) {
client.quit();
console.error("Missing redis-configurations..");
console.error("Unable to connect to redis, setting up redis-mock.");
client = {
rpush: function() {
console.log("redis-dummy lpush", arguments);
if (typeof arguments[arguments.length - 1] == "function") {
arguments[arguments.length - 1](null);
}
zcount: function() {
console.log("redis-dummy zcount", arguments);
return Promise.resolve()
},
lrange: function() {
console.log("redis-dummy lrange", arguments);
if (typeof arguments[arguments.length - 1] == "function") {
arguments[arguments.length - 1](null);
}
zadd: function() {
console.log("redis-dummy zadd", arguments);
return Promise.resolve();
},
zrevrange: function() {
console.log("redis-dummy zrevrange", arguments);
return Promise.resolve(null);
},
del: function() {
console.log("redis-dummy del", arguments);
if (typeof arguments[arguments.length - 1] == "function") {
arguments[arguments.length - 1](null);
}
return Promise.resolve();
}
};
});
@@ -31,36 +42,46 @@ try {
const addMessage = message => {
const json = JSON.stringify(message);
client.rpush("messages", json);
return message;
return client.zadd("messages", message.timestamp, json)
.then(position => {
return {
success: true
}
})
};
const history = (skip = 0, take = 20) => {
skip = (1 + skip) * -1; // negate to get FIFO
return new Promise((resolve, reject) =>
client.lrange("messages", skip * take, skip, (err, data) => {
if (err) {
console.log(err);
reject(err);
}
const history = (page=1, limit=10) => {
const start = (page - 1) * limit;
const stop = (limit * page) - 1;
data = data.map(data => JSON.parse(data));
resolve(data);
const getTotalCount = client.zcount("messages", '-inf', '+inf');
const getMessages = client.zrevrange("messages", start, stop);
return Promise.all([getTotalCount, getMessages])
.then(([totalCount, messages]) => {
if (messages) {
return {
messages: messages.map(entry => JSON.parse(entry)).reverse(),
count: messages.length,
total: totalCount
}
} else {
return {
messages: [],
count: 0,
total: 0
}
}
})
);
};
const clearHistory = () => {
return new Promise((resolve, reject) =>
client.del("messages", (err, success) => {
if (err) {
console.log(err);
reject(err);
return client.del("messages")
.then(success => {
return {
success: success == 1 ? true : false
}
resolve(success == 1 ? true : false);
})
);
};
module.exports = {

View File

@@ -1,10 +1,10 @@
const express = require("express");
const path = require("path");
const RequestedWine = require(path.join(
__dirname + "/../schemas/RequestedWine"
__dirname, "/schemas/RequestedWine"
));
const Wine = require(path.join(
__dirname + "/../schemas/Wine"
__dirname, "/schemas/Wine"
));
const deleteRequestedWineById = async (req, res) => {

View File

@@ -1,10 +1,10 @@
const path = require("path");
const Purchase = require(path.join(__dirname + "/../schemas/Purchase"));
const Wine = require(path.join(__dirname + "/../schemas/Wine"));
const Highscore = require(path.join(__dirname + "/../schemas/Highscore"));
const Purchase = require(path.join(__dirname, "/schemas/Purchase"));
const Wine = require(path.join(__dirname, "/schemas/Wine"));
const Highscore = require(path.join(__dirname, "/schemas/Highscore"));
const PreLotteryWine = require(path.join(
__dirname + "/../schemas/PreLotteryWine"
__dirname, "/schemas/PreLotteryWine"
));
const prelotteryWines = async (req, res) => {

View File

@@ -1,22 +1,21 @@
const express = require("express");
const path = require("path");
// Middleware
const mustBeAuthenticated = require(__dirname + "/../middleware/mustBeAuthenticated");
const setAdminHeaderIfAuthenticated = require(__dirname + "/../middleware/setAdminHeaderIfAuthenticated");
const mustBeAuthenticated = require(path.join(__dirname, "/middleware/mustBeAuthenticated"));
const setAdminHeaderIfAuthenticated = require(path.join(__dirname, "/middleware/setAdminHeaderIfAuthenticated"));
const update = require(path.join(__dirname + "/update"));
const retrieve = require(path.join(__dirname + "/retrieve"));
const request = require(path.join(__dirname + "/request"));
const subscriptionApi = require(path.join(__dirname + "/subscriptions"));
const loginApi = require(path.join(__dirname + "/login"));
const wineinfo = require(path.join(__dirname + "/wineinfo"));
const virtualApi = require(path.join(__dirname + "/virtualLottery"));
const update = require(path.join(__dirname, "/update"));
const retrieve = require(path.join(__dirname, "/retrieve"));
const request = require(path.join(__dirname, "/request"));
const subscriptionApi = require(path.join(__dirname, "/subscriptions"));
const userApi = require(path.join(__dirname, "/user"));
const wineinfo = require(path.join(__dirname, "/wineinfo"));
const virtualApi = require(path.join(__dirname, "/virtualLottery"));
const virtualRegistrationApi = require(path.join(
__dirname + "/virtualRegistration"
__dirname, "/virtualRegistration"
));
const lottery = require(path.join(__dirname + "/lottery"));
const lottery = require(path.join(__dirname, "/lottery"));
const chatHistoryApi = require(path.join(__dirname, "/chatHistory"));
const router = express.Router();
@@ -61,10 +60,11 @@ router.post('/winner/notify/:id', virtualRegistrationApi.sendNotificationToWinne
router.get('/winner/:id', virtualRegistrationApi.getWinesToWinnerById);
router.post('/winner/:id', virtualRegistrationApi.registerWinnerSelection);
// router.use("/api/", updateApi);
// router.use("/api/", retrieveApi);
// router.use("/api/", wineinfoApi);
// router.use("/api/lottery", lottery);
// router.use("/virtual-registration/", virtualRegistrationApi);
router.get('/chat/history', chatHistoryApi.getAllHistory)
router.delete('/chat/history', mustBeAuthenticated, chatHistoryApi.deleteHistory)
router.post('/login', userApi.login);
router.post('/register', mustBeAuthenticated, userApi.register);
router.get('/logout', userApi.logout);
module.exports = router;

14
api/schemas/Attendee.js Normal file
View File

@@ -0,0 +1,14 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Attendee = new Schema({
name: String,
phoneNumber: String,
green: Number,
blue: Number,
red: Number,
yellow: Number,
winner: Boolean
});
module.exports = mongoose.model("Attendee", Attendee);

18
api/schemas/Highscore.js Normal file
View File

@@ -0,0 +1,18 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Highscore = new Schema({
name: String,
wins: [
{
color: String,
date: Date,
wine: {
type: Schema.Types.ObjectId,
ref: "Wine"
}
}
]
});
module.exports = mongoose.model("Highscore", Highscore);

View File

@@ -0,0 +1,14 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const PreLotteryWine = new Schema({
name: String,
vivinoLink: String,
rating: Number,
id: String,
image: String,
price: String,
country: String
});
module.exports = mongoose.model("PreLotteryWine", PreLotteryWine);

20
api/schemas/Purchase.js Normal file
View File

@@ -0,0 +1,20 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Purchase = new Schema({
date: Date,
blue: Number,
red: Number,
yellow: Number,
green: Number,
bought: Number,
stolen: Number,
wines: [
{
type: Schema.Types.ObjectId,
ref: "Wine"
}
]
});
module.exports = mongoose.model("Purchase", Purchase);

View File

@@ -0,0 +1,13 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const RequestedWine = new Schema({
count: Number,
wineId: String,
wine: {
type: Schema.Types.ObjectId,
ref: "Wine"
}
});
module.exports = mongoose.model("RequestedWine", RequestedWine);

View File

@@ -0,0 +1,13 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Subscription = new Schema({
endpoint: String,
expirationTime: Number,
keys: {
p256dh: String,
auth: String
}
});
module.exports = mongoose.model("Subscription", Subscription);

9
api/schemas/User.js Normal file
View File

@@ -0,0 +1,9 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const passportLocalMongoose = require("passport-local-mongoose");
const User = new Schema({});
User.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", User);

View File

@@ -0,0 +1,18 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const VirtualWinner = new Schema({
name: String,
phoneNumber: String,
color: String,
green: Number,
blue: Number,
red: Number,
yellow: Number,
id: String,
timestamp_drawn: Number,
timestamp_sent: Number,
timestamp_limit: Number
});
module.exports = mongoose.model("VirtualWinner", VirtualWinner);

15
api/schemas/Wine.js Normal file
View File

@@ -0,0 +1,15 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Wine = new Schema({
name: String,
vivinoLink: String,
rating: Number,
occurences: Number,
id: String,
image: String,
price: String,
country: String
});
module.exports = mongoose.model("Wine", Wine);

View File

@@ -5,11 +5,11 @@ const webpush = require("web-push"); //requiring the web-push module
const schedule = require("node-schedule");
const mustBeAuthenticated = require(path.join(
__dirname + "/../middleware/mustBeAuthenticated"
__dirname, "/middleware/mustBeAuthenticated"
));
const config = require(path.join(__dirname + "/../config/defaults/push"));
const Subscription = require(path.join(__dirname + "/../schemas/Subscription"));
const Subscription = require(path.join(__dirname, "/schemas/Subscription"));
const lotteryConfig = require(path.join(
__dirname + "/../config/defaults/lottery"
));

View File

@@ -1,14 +1,14 @@
const express = require("express");
const path = require("path");
const sub = require(path.join(__dirname + "/../api/subscriptions"));
const sub = require(path.join(__dirname, "/subscriptions"));
const _wineFunctions = require(path.join(__dirname + "/../api/wine"));
const _personFunctions = require(path.join(__dirname + "/../api/person"));
const Subscription = require(path.join(__dirname + "/../schemas/Subscription"));
const Lottery = require(path.join(__dirname + "/../schemas/Purchase"));
const _wineFunctions = require(path.join(__dirname, "/wine"));
const _personFunctions = require(path.join(__dirname, "/person"));
const Subscription = require(path.join(__dirname, "/schemas/Subscription"));
const Lottery = require(path.join(__dirname, "/schemas/Purchase"));
const PreLotteryWine = require(path.join(
__dirname + "/../schemas/PreLotteryWine"
__dirname, "/schemas/PreLotteryWine"
));
const submitWines = async (req, res) => {

51
api/user.js Normal file
View File

@@ -0,0 +1,51 @@
const passport = require("passport");
const path = require("path");
const User = require(path.join(__dirname, "/schemas/User"));
const router = require("express").Router();
const register = (req, res, next) => {
User.register(
new User({ username: req.body.username }),
req.body.password,
function(err) {
if (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);
}
return res.status(200).send({ message: "Bruker registrert. Velkommen " + req.body.username, success: true })
}
);
};
const login = (req, res, next) => {
passport.authenticate("local", function(err, user, info) {
if (err) {
if (err.name == "MissingUsernameError" || err.name == "MissingPasswordError")
return res.status(400).send({ message: err.message, success: false })
return next(err);
}
if (!user) return res.status(404).send({ message: "Incorrect username or password", success: false })
req.logIn(user, (err) => {
if (err) { return next(err) }
return res.status(200).send({ message: "Velkommen " + user.username, success: true })
})
})(req, res, next);
};
const logout = (req, res) => {
req.logout();
res.redirect("/");
};
module.exports = {
register,
login,
logout
};

View File

@@ -1,13 +1,13 @@
const path = require("path");
const crypto = require("crypto");
const config = require(path.join(__dirname + "/../config/defaults/lottery"));
const Message = require(path.join(__dirname + "/message"));
const { findAndNotifyNextWinner } = require(path.join(__dirname + "/virtualRegistration"));
const config = require(path.join(__dirname, "/../config/defaults/lottery"));
const Message = require(path.join(__dirname, "/message"));
const { findAndNotifyNextWinner } = require(path.join(__dirname, "/virtualRegistration"));
const Attendee = require(path.join(__dirname + "/../schemas/Attendee"));
const VirtualWinner = require(path.join(__dirname + "/../schemas/VirtualWinner"));
const PreLotteryWine = require(path.join(__dirname + "/../schemas/PreLotteryWine"));
const Attendee = require(path.join(__dirname, "/schemas/Attendee"));
const VirtualWinner = require(path.join(__dirname, "/schemas/VirtualWinner"));
const PreLotteryWine = require(path.join(__dirname, "/schemas/PreLotteryWine"));
const winners = async (req, res) => {

View File

@@ -1,13 +1,13 @@
const path = require("path");
const _wineFunctions = require(path.join(__dirname + "/../api/wine"));
const _personFunctions = require(path.join(__dirname + "/../api/person"));
const Message = require(path.join(__dirname + "/../api/message"));
const _wineFunctions = require(path.join(__dirname, "/wine"));
const _personFunctions = require(path.join(__dirname, "/person"));
const Message = require(path.join(__dirname, "/message"));
const VirtualWinner = require(path.join(
__dirname + "/../schemas/VirtualWinner"
__dirname, "/schemas/VirtualWinner"
));
const PreLotteryWine = require(path.join(
__dirname + "/../schemas/PreLotteryWine"
__dirname, "/schemas/PreLotteryWine"
));

View File

@@ -1,5 +1,5 @@
const path = require("path");
const Wine = require(path.join(__dirname + "/../schemas/Wine"));
const Wine = require(path.join(__dirname, "/schemas/Wine"));
async function findSaveWine(prelotteryWine) {
let wonWine = await Wine.findOne({ name: prelotteryWine.name });