Add lots of things

This commit is contained in:
AndrewTrieu
2023-03-09 13:37:03 +02:00
parent 5767d0cfdb
commit 96af150813
10 changed files with 225 additions and 3 deletions

View File

@@ -0,0 +1,66 @@
import * as authServices from "../services/authServices.js";
import { bcrypt, validasaur } from "../../deps";
const validationRules = {
email: [validasaur.isEmail, validasaur.required],
password: [validasaur.minLength(4), validasaur.required],
};
const showLogin = ({ render }) => {
render("login.eta");
};
const showRegister = ({ render }) => {
render("register.eta");
};
const createUser = async ({ request, response, render }) => {
const body = request.body({ type: "form-data" });
const params = await body.value;
const userData = {
email: params.get("email"),
password: params.get("password"),
};
const [passes, errors] = await validasaur.validate(userData, validationRules);
if (!passes) {
response.status = 422;
userData.errors = errors;
render("register.eta", userData);
return;
} else {
const hashedPassword = await bcrypt.hash(userData.password);
const user = await authServices.createUser(userData.email, hashedPassword);
response.direct("/auth/login");
}
};
const login = async ({ request, response, state, render }) => {
const body = request.body({ type: "form" });
const params = await body.value;
const userDatabase = await authServices.findUser(params.get("email"));
if (userDatabase.length < 1) {
response.status = 422;
render("login.eta", { error: "User not found!" });
return;
}
const user = userDatabase[0];
const passwordCorrect = await bcrypt.compare(
params.get("password"),
user.password
);
if (!passwordCorrect) {
response.status = 422;
render("login.eta", { error: "Incorrect password!" });
return;
}
await state.session.set("authenticated", user);
response.direct("/topics");
};
export { showLogin, showRegister, createUser, login };

View File

@@ -0,0 +1,53 @@
import * as topicService from "../../services/topicService.js";
import { validasaur } from "../../deps";
const validationRules = {
name: [validasaur.required, validasaur.minLength(1)],
};
const addTopic = async ({ request, response, render, state }) => {
const userId = (await state.session.get("user")).id;
const admin = (await state.session.get("user")).admin;
const body = request.body({ type: "form-data" });
const params = await body.value;
const topicData = {
admin: admin,
name: params.get("name"),
};
const [passes, errors] = await validasaur.validate(
topicData,
validationRules
);
if (!passes || !admin) {
response.status = 422;
topicData.errors = errors;
if (!admin) {
topicData.errors = { admin: { error: "You are not an admin!" } };
}
topicData.allTopics = await topicService.getAllTopics();
render("topics.eta", topicData);
} else {
await topicService.addTopic(userId, topicData.name);
response.redirect("/topics");
}
};
const deleteTopic = async ({ params, response, state }) => {
const id = params.id;
const admin = (await state.session.get("user")).admin;
if (admin) {
await topicService.deleteTopic(id);
}
response.redirect("/topics");
};
const listTopics = async ({ render, state }) => {
const user = await state.session.get("user");
render("topics.eta", {
admin: user.admin,
allTopics: await topicService.getAllTopics(),
});
};
export { addTopic, deleteTopic, listTopics };

View File

@@ -1,8 +1,12 @@
import { Router } from "../deps.js";
import * as mainController from "./controllers/mainController.js";
import * as authController from "./controllers/authController.js";
const router = new Router();
router.get("/", mainController.showMain);
router.get("/auth/login", authController.showLogin);
router.get("/auth/register", authController.showRegister);
router.post("/auth/login", authController.login);
export { router };

View File

@@ -0,0 +1,11 @@
import { sql } from "../database/database.js";
const findUser = async (email) => {
return await sql`SELECT * FROM users WHERE email = ${email}`;
};
const createUser = async (email, password) => {
return await sql`INSERT INTO users (email, password) VALUES (${email}, ${password})`;
};
export { findUser, createUser };

View File

@@ -0,0 +1,8 @@
import { sql } from "../database/database.js";
const countQuestions = async () => {
const result = await sql`SELECT COUNT(id) FROM questions`;
return result.rows[0].count;
};
const getQuestionsByTopicId = async (topicId) => {

View File

@@ -0,0 +1,39 @@
import { sql } from "../database/database.js";
const addTopic = async (userId, name) => {
await sql`INSERT INTO topics (user_id, name) VALUES (${userId}, ${name})`;
};
const countTopics = async () => {
const result = await sql`SELECT COUNT(id) FROM topics`;
return result.rows[0].count;
};
const getAllTopics = async () => {
await sql`SELECT * FROM topics ORDER BY name ASC`;
return result.rows;
};
const getTopicsByUserId = async (userId) => {
const result =
await sql`SELECT * FROM topics WHERE user_id = ${userId} ORDER BY name ASC`;
return result.rows;
};
const getTopicByTopicId = async (topicId) => {
const result = await sql`SELECT * FROM topics WHERE id = ${topicId}`;
return result.rows[0];
};
const deleteTopic = async (topicId) => {
await sql`DELETE FROM topics WHERE id = ${topicId}`;
};
export {
addTopic,
countTopics,
getAllTopics,
getTopicsByUserId,
getTopicByTopicId,
deleteTopic,
};

View File

@@ -0,0 +1,18 @@
<% layout("./layouts/layout.eta") %>
<h1>Login</h1>
<form method="POST" action="/auth/login">
Email:<br/>
<input type="email" name="email" /><br/>
Password:<br/>
<input type="password" name="password" /><br/>
<input type="submit" value="Login" />
</form>
<% if(it.error){ %>
<b><%= it.error %></b>
<br/>
<% } %>
<a href="/auth/register">Are you a new user?</a>

View File

@@ -2,11 +2,13 @@
<h1>MCQ Application</h1>
<P>This is a web application that is used for repeated practice of learned content. The application provides a list of topics and allows creating multiple-choice questions into those topics that are then answered by self and others. The application also shows basic statistics: the total number of available questions and the total number of question answers. In addition, the application also provides an API for retrieving and answering random questions.</p>
<P>This is a web application that is used for repeated practice of learned content. The application provides a list of topics and allows creating multiple-choice questions into those topics that are then answered by self and others.</p>
<p>Are you already registered? <a href="/login">Login</a></p>
<p>The application also shows basic statistics: the total number of available questions and the total number of question answers. In addition, the application also provides an API for retrieving and answering random questions.</p>
<p>Not registered yet? <a href="/register">Register</a></p>
<p>Are you already registered? <a href="/auth/login">Login</a></p>
<p>Not registered yet? <a href="/auth/register">Register</a></p>
<h2>Statistics</h2>
<p>Total number of:</p>

View File

@@ -0,0 +1,21 @@
<% layout("./layouts/layout.eta") %>
<h1>New user registration</h1>
<form method="POST" action="/auth/register">
Email:<br/>
<input type="email" name="email" /><br/>
Password:<br/>
<input type="password" name="password" /><br/>
<input type="submit" value="Register" />
</form>
<% if (it.errors) { %>
<% Object.keys(it.errors).forEach((error) => { %>
<% Object.values(it.errors[error]).forEach((err) => { %>
<%= err %>
<% }); %>
<% }); %>
<% } %>
<br/>
<a href="/auth/login">Are you already registered?</a>