292 lines
8.8 KiB
JavaScript
292 lines
8.8 KiB
JavaScript
const express = require("express");
|
|
const sqlite = require("better-sqlite3");
|
|
const bcrypt = require("bcrypt");
|
|
const cors = require("cors");
|
|
const jwt = require("jsonwebtoken")
|
|
const dotenv = require("dotenv");
|
|
const Database = require("better-sqlite3");
|
|
|
|
const serverPort = 3006;
|
|
|
|
const dev = process.argv.length > 2 && process.argv[2] == "-dev";
|
|
|
|
const app = express();
|
|
app.use(express.json());
|
|
app.use((req, res, next) =>{
|
|
if (req.path.endsWith(".html")) {
|
|
res.redirect(req.path.replace(".html", ""));
|
|
return;
|
|
}
|
|
next();
|
|
})
|
|
app.use(express.static("public"))
|
|
app.use(cors({
|
|
origin: [
|
|
"https://project.rochesterx.dev",
|
|
"https://localhost:3001"
|
|
]
|
|
}));
|
|
|
|
const db = new Database("project.db");
|
|
db.pragma("journal_mode = WAL")
|
|
|
|
dotenv.config();
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET;
|
|
if (!JWT_SECRET) {
|
|
throw new Error("JWT_SECRET not set in environment.");
|
|
}
|
|
|
|
app.post("/register", async (req, res) => {
|
|
const { username, password, role, salt } = req.body;
|
|
try {
|
|
const hash = await bcrypt.hash(password, 10);
|
|
|
|
const inputs = {
|
|
username: username,
|
|
hash: hash,
|
|
role: role,
|
|
salt: salt
|
|
}
|
|
const query = db.prepare("INSERT INTO Users (Username, PasswordHash, Role, CreatedAt, Salt) VALUES (@username, @hash, @role, datetime('now'), @salt)");
|
|
query.run(inputs);
|
|
|
|
res.send({ success: true, message: "User registered" })
|
|
|
|
} catch (err) {
|
|
if (err.message.includes("UNIQUE")) {
|
|
res.status(500).send({ success: false, message: `Username "${username}" is already taken.` });
|
|
return;
|
|
}
|
|
res.status(500).send({ success: false, message: err.message });
|
|
}
|
|
});
|
|
|
|
app.post("/login", async (req, res) => {
|
|
const { username, password } = req.body;
|
|
//try {
|
|
const query = db.prepare("SELECT * FROM Users WHERE Username = @username");
|
|
const results = query.all({ username: username });
|
|
|
|
if (results.length == 0) return res.status(400).send({ message: "User not found" });
|
|
|
|
const hash = results[0].PasswordHash;
|
|
const isDeleted = results[0].IsDeleted;
|
|
|
|
if (isDeleted === true) {
|
|
return res.status(400).json({ message: "User not found (deleted)" })
|
|
}
|
|
|
|
const match = await bcrypt.compare(password, hash);
|
|
|
|
if (match){
|
|
const token = jwt.sign(results[0], JWT_SECRET, { expiresIn: "1h" });
|
|
res.send({
|
|
success: true,
|
|
message: "Login successful",
|
|
token,
|
|
salt: results[0].Salt
|
|
});
|
|
const update = db.prepare("UPDATE Users SET LastLogin = datetime('now') WHERE Username = @username");
|
|
update.run({ username: username });
|
|
|
|
console.log("Issued token: " + JSON.stringify(token))
|
|
}
|
|
else res.status(401).send({ success: false, message: "Invalid credentials" });
|
|
//} catch (err) {
|
|
// res.status(500).send({ success: false, message: err.message });
|
|
//}
|
|
});
|
|
|
|
app.post("/getSalt", async (req, res) => {
|
|
const { username } = req.body;
|
|
try {
|
|
const query = db.prepare("SELECT * FROM Users WHERE Username = @username");
|
|
const results = query.all({ username: username });
|
|
|
|
if (results.length == 0) return res.status(400).send({ message: "User not found" });
|
|
|
|
const isDeleted = results[0].IsDeleted;
|
|
|
|
if (isDeleted === true) {
|
|
return res.status(400).json({ message: "User not found (deleted)" })
|
|
}
|
|
|
|
res.send({
|
|
salt: results[0].Salt
|
|
});
|
|
} catch (err) {
|
|
res.status(500).send({ success: false, message: err.message });
|
|
}
|
|
});
|
|
|
|
app.post("/setPassword", authenticate, async (req, res) => {
|
|
try {
|
|
const { name, iv, password } = req.body;
|
|
|
|
const inputs = {
|
|
name: name,
|
|
userID: req.user.Id,
|
|
iv: iv,
|
|
password: password
|
|
}
|
|
|
|
const query = db.prepare("INSERT INTO Passwords (Name, UserID, Password, IV) VALUES (@name, @userID, @password, @iv)");
|
|
query.run(inputs);
|
|
|
|
res.status(200).json({ success: true, message: "Success" });
|
|
} catch (err) {
|
|
res.status(500).json({ success: false, message: err.message });
|
|
}
|
|
});
|
|
|
|
app.post("/updatePassword", authenticate, async (req, res) => {
|
|
try {
|
|
const { name, iv, password } = req.body;
|
|
|
|
const inputs = {
|
|
name: name,
|
|
userID: req.user.Id,
|
|
iv: iv,
|
|
password: password
|
|
}
|
|
|
|
const query = db.prepare("UPDATE Passwords SET Password = @password, IV = @iv WHERE UserID = @userID AND Name = @name");
|
|
query.run(inputs);
|
|
|
|
res.status(200).json({ success: true, message: "Success" });
|
|
} catch (err) {
|
|
res.status(500).json({ success: false, message: err.message });
|
|
}
|
|
});
|
|
|
|
app.post("/getPassword", authenticate, async (req, res) => {
|
|
try {
|
|
const { name } = req.body;
|
|
|
|
const query = db.prepare("SELECT * FROM Passwords WHERE UserID = @userID AND Name = @name");
|
|
const results = query.all({ userID: req.user.Id, name: name});
|
|
|
|
if (results.length == 0) {
|
|
res.status(500).json({ success: false, message: `No password with name ${name}` });
|
|
return;
|
|
}
|
|
|
|
if (results.length > 1) {
|
|
res.status(500).json({ success: false, message: `More than one password with name ${name}` });
|
|
return;
|
|
}
|
|
|
|
res.status(200).json({ password: results[0].Password, iv: results[0].IV, success: true, message: "Success" });
|
|
} catch (err) {
|
|
res.status(500).json({ success: false, message: err.message });
|
|
}
|
|
});
|
|
|
|
app.post("/getPasswords", authenticate, async (req, res) => {
|
|
try {
|
|
const query = db.prepare("SELECT * FROM Passwords WHERE UserID = @userID");
|
|
const results = query.all({ userID: req.user.Id});
|
|
|
|
if (results.length == 0) {
|
|
res.status(500).json({ success: false, message: `No passwords` });
|
|
return;
|
|
}
|
|
|
|
res.status(200).json({ results: results, success: true, message: "Success" });
|
|
} catch (err) {
|
|
res.status(500).json({ success: false, message: err.message });
|
|
}
|
|
});
|
|
|
|
app.post("/getWatchlist", authenticate, async (req, res) => {
|
|
const { id } = req.body;
|
|
|
|
const query = db.prepare(`SELECT Player.PlayerName, Player.Team, Player.Position, Player.PlayerID FROM Watch JOIN Player ON Watch.PlayerID = Player.PlayerID WHERE UserID = @userID ORDER BY Player.PlayerName`);
|
|
const watchlist = query.all({
|
|
userID: req.user.Id,
|
|
id: id
|
|
});
|
|
|
|
|
|
res.status(200).json({ watchlist: watchlist });
|
|
});
|
|
|
|
app.post("/getInfo", authenticate, async (req, res) => {
|
|
const userData = req.user;
|
|
|
|
res.status(200).json(userData);
|
|
});
|
|
|
|
app.post("/delete", authenticate, async (req, res) => {
|
|
let { username, actor } = req.body;
|
|
|
|
if (username === true) {
|
|
username = req.user.Username;
|
|
}
|
|
if (actor === true) {
|
|
actor = req.user.Username;
|
|
}
|
|
|
|
console.log(`Deleting user ${username}`);
|
|
|
|
const query = ("UPDATE Users SET IsDeleted = 1, DeletedAt = datetime('now'), DeletedBy = @actor WHERE Username = @username");
|
|
query.run({
|
|
username: username,
|
|
actor: actor
|
|
});
|
|
|
|
console.log(`User ${username} deleted`);
|
|
res.status(200).json({ message: `User "${username}" deleted.` });
|
|
});
|
|
|
|
async function authenticate(req, res, next) {
|
|
try {
|
|
const authenticationHeader = req.headers["authorization"];
|
|
console.log("authenticationheader: " + authenticationHeader);
|
|
const token = authenticationHeader.split(" ")[1];
|
|
|
|
console.log(JSON.stringify(authenticationHeader));
|
|
const decoded = jwt.verify(token, JWT_SECRET);
|
|
|
|
req.user = decoded;
|
|
|
|
console.log(req.user);
|
|
|
|
console.log(decoded.Username + " authenticated");
|
|
} catch (error) {
|
|
console.log("Authentication header missing");
|
|
console.log(error);
|
|
res.status(401).json({ message: "You are not logged in", error: error, logout: true });
|
|
return;
|
|
}
|
|
next();
|
|
}
|
|
|
|
app.get("/player/:id", (req, res) => {
|
|
res.sendFile(__dirname + "/public/player.html");
|
|
})
|
|
|
|
app.get("/search", (req, res) => {
|
|
res.sendFile(__dirname + "/public/search.html");
|
|
})
|
|
|
|
app.get("/home", (req, res) => {
|
|
res.sendFile(__dirname + "/public/home.html");
|
|
})
|
|
|
|
app.get("/info", (req, res) => {
|
|
res.sendFile(__dirname + "/public/info.html");
|
|
})
|
|
|
|
app.get("/register", (req, res) => {
|
|
res.sendFile(__dirname + "/public/register.html");
|
|
})
|
|
|
|
app.get("/login", (req, res) => {
|
|
res.sendFile(__dirname + "/public/login.html");
|
|
})
|
|
|
|
app.listen(serverPort, "0.0.0.0", () => console.log(`Running ${dev ? "dev " : ""}server on port ${serverPort}`));
|
|
|