244 lines
6.5 KiB
TypeScript
244 lines
6.5 KiB
TypeScript
import { env, exit } from "process";
|
|
import { randomBytes, pbkdf2Sync } from "crypto";
|
|
import * as assert from "assert";
|
|
|
|
import * as express from "express";
|
|
import * as cors from "cors";
|
|
|
|
import * as bodyparser from "body-parser";
|
|
|
|
import { isAuthenticated, performLogin } from "./security/auth";
|
|
|
|
import { LRequest } from "./types/express";
|
|
|
|
import UserRouter from "./api/user";
|
|
import LevelRouter from "./api/level";
|
|
|
|
import { ITrackerDBModel } from "./models/tracker";
|
|
|
|
const baseRouter = express.Router();
|
|
const authRouter = express.Router();
|
|
|
|
import { MongoClient } from "mongodb";
|
|
|
|
const user = encodeURIComponent("backend");
|
|
const password = encodeURIComponent(env["LATEINICUS_USER_PW"]);
|
|
|
|
(async function() {
|
|
// Database Name
|
|
const dbName = 'lateinicus';
|
|
// Connection URL
|
|
const url = `mongodb://${user}:${password}@128.1.0.2:27017/?authMechanism=SCRAM-SHA-1&authSource=${dbName}`;
|
|
let client: MongoClient;
|
|
|
|
try {
|
|
// Use connect method to connect to the Server
|
|
client = await MongoClient.connect(url);
|
|
console.log("Connected to MongoDB");
|
|
} catch (err) {
|
|
console.log(err.stack);
|
|
assert(false);
|
|
}
|
|
|
|
const db = client.db(dbName);
|
|
console.log("Connected to the database");
|
|
|
|
|
|
const app = express();
|
|
app.use(bodyparser.json());
|
|
app.options("*", cors());
|
|
|
|
app.use((req: LRequest, res, next) => {
|
|
// Every route should have access to the database so that
|
|
// we can easily make calls to it
|
|
req.db = db;
|
|
next();
|
|
});
|
|
app.use("/api/level", LevelRouter);
|
|
app.use("/api/user", UserRouter);
|
|
app.post("/api/tracker", async (req, res) => {
|
|
// Did we get any data
|
|
if (!req.body) {
|
|
res.send({
|
|
error: "403",
|
|
data: {
|
|
msg: "No request body provided",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Did we get all arguments?
|
|
if (!("session" in req.body) || !("event" in req.body)) {
|
|
res.send({
|
|
error: "403",
|
|
data: {
|
|
msg: "Invalid request",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Insert it into the database
|
|
const tracker: ITrackerDBModel = Object.assign({}, req.body, {
|
|
timestamp: Date.now(),
|
|
});
|
|
await db.collection("tracker").insertOne(tracker);
|
|
|
|
res.send({
|
|
error: "200",
|
|
data: {},
|
|
});
|
|
});
|
|
app.get("/api/levels", async (req, res) => {
|
|
// TODO: if (levels)
|
|
const levels = (await db.collection("levels").find({}, {
|
|
// The order in which we send the levels is important, so better
|
|
// sort them
|
|
sort: {
|
|
level: 1,
|
|
},
|
|
})
|
|
.toArray())
|
|
.map((el) => {
|
|
let tmp = Object.assign({}, el);
|
|
delete tmp.vocab;
|
|
delete tmp._id;
|
|
|
|
return tmp;
|
|
});
|
|
|
|
res.send({
|
|
error: "200",
|
|
data: {
|
|
levels,
|
|
},
|
|
});
|
|
});
|
|
app.post("/api/register", async (req, res) => {
|
|
// Check if any data was sent
|
|
if (!req.body) {
|
|
res.send({
|
|
error: "403",
|
|
data: {
|
|
msg: `No data sent`,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Check if we have all we need
|
|
const params = ["username", "password", "classId"];
|
|
for (let param of params) {
|
|
if (!(param in req.body)) {
|
|
res.send({
|
|
error: "403",
|
|
data: {
|
|
msg: `${param} not specified!`,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
const { username, password, classId } = req.body;
|
|
|
|
// Check if the registration is open for the class Id
|
|
// NOTE: This to prevent people from spamming the database
|
|
const classes = env["LATEINICUS_CLASSES"].split(",");
|
|
if (classes.indexOf(classId) === -1) {
|
|
res.send({
|
|
error: "403",
|
|
data: {
|
|
msg: "Class does not exist",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Check if the user already exists
|
|
const checkUser = await db.collection("users").findOne({
|
|
username,
|
|
});
|
|
if (checkUser) {
|
|
res.send({
|
|
error: "403",
|
|
data: {
|
|
msg: "User already exists",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const salt = randomBytes(30).toString("hex");
|
|
const hash = pbkdf2Sync(password, salt, 50000, 512, "sha512").toString("hex");
|
|
const user = {
|
|
username,
|
|
salt,
|
|
hash,
|
|
classId,
|
|
score: 0,
|
|
showWelcome: true,
|
|
|
|
lastReview: {
|
|
correct: 0,
|
|
wrong: 0,
|
|
},
|
|
|
|
lastLevel: 0,
|
|
levels: [] as number[],
|
|
vocabMetadata: {},
|
|
};
|
|
await db.collection("users").insertOne(user);
|
|
|
|
res.send({
|
|
error: "200",
|
|
data: {},
|
|
});
|
|
});
|
|
app.get("/api/health", (req, res) => {
|
|
res.send({
|
|
error: "200",
|
|
data: {
|
|
msg: "lol",
|
|
},
|
|
});
|
|
});
|
|
app.post("/api/login", async (req, res) => {
|
|
// Check if all arguments were sent
|
|
const { body } = req;
|
|
if (!body || !("username" in body) || !("password" in body)) {
|
|
res.send({
|
|
error: "400",
|
|
data: {
|
|
msg: "Username or password not specified",
|
|
},
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
// Try to log the user in
|
|
try {
|
|
const userData = await performLogin(body.username, body.password, db);
|
|
res.send({
|
|
error: "200",
|
|
data: userData,
|
|
});
|
|
} catch (err) {
|
|
console.log("Could not resolve login promise!", err);
|
|
|
|
// If anything was wrong, just tell the client
|
|
res.send({
|
|
error: "1",
|
|
data: {
|
|
msg: "Username or password is wrong",
|
|
},
|
|
});
|
|
}
|
|
});
|
|
const server = app.listen(8080, () => {
|
|
console.log("Starting on port 8080");
|
|
});
|
|
})();
|