Fixed API issues and added a test file

This commit is contained in:
2024-03-11 12:19:19 +01:00
parent 6d941b7bf1
commit 6452ec2dba
10 changed files with 71 additions and 31 deletions

View File

@@ -1,8 +1,8 @@
PORT=3000 PORT=3000
DOMAIN="http://localhost:3000" DOMAIN="http://localhost:3000"
DATABASE_HOST="127.0.0.1" DATABASE_HOST="127.0.0.1"
DATABASE_NAME=hsp-gdh DATABASE_NAME=hsp_gdh
DATABASE_USER=hsp-gdh DATABASE_USER=hsp_gdh
DATABASE_PASSWORD="" DATABASE_PASSWORD=""
JWT_SECRET="" JWT_SECRET=""
SMTP= SMTP=

2
.gitignore vendored
View File

@@ -136,4 +136,4 @@ logs/
*.log *.log
# token # token
token/ tokens/

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,14 +1,14 @@
SET default_storage_engine = InnoDB; SET default_storage_engine = InnoDB;
DROP DATABASE IF EXISTS `hsp-gdh`; DROP DATABASE IF EXISTS `hsp_gdh`;
CREATE DATABASE IF NOT EXISTS `hsp-gdh` CREATE DATABASE IF NOT EXISTS `hsp_gdh`
CHARACTER SET utf8mb4 CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci; COLLATE utf8mb4_unicode_ci;
DROP USER IF EXISTS 'hsp-gdh'; DROP USER IF EXISTS 'hsp_gdh';
CREATE USER 'hsp-gdh'@'%' IDENTIFIED BY 'PASSWORD'; CREATE USER 'hsp_gdh'@'%' IDENTIFIED BY 'PASSWORD';
GRANT ALL PRIVILEGES ON airjet.* TO 'hsp-gdh'@'%'; GRANT ALL PRIVILEGES ON hsp_gdh.* TO 'hsp_gdh'@'%';
USE `hsp-gdh`; USE `hsp_gdh`;
CREATE TABLE roles ( CREATE TABLE roles (
id INT UNSIGNED NOT NULL AUTO_INCREMENT, id INT UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -189,6 +189,7 @@ CREATE TABLE rooms (
REFERENCES hospitals(id) REFERENCES hospitals(id)
ON DELETE RESTRICT ON DELETE RESTRICT
ON UPDATE CASCADE, ON UPDATE CASCADE,
INDEX ch_hospital_id_idx (hospital_id)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
CREATE TABLE hospital_doctors ( CREATE TABLE hospital_doctors (

View File

@@ -49,4 +49,4 @@ app.listen(process.env.PORT, async () => {
// test // test
// import { post } from './modules/fetcher'; // import { post } from './modules/fetcher';
// post('http://127.0.0.1:1109/users/login', { 'usernameOrEmail':'foo', 'password':'bar' }).then(res => console.log(res)); // post('http://127.0.0.1:1109/users/login', { 'usernameOrEmail':'foo', 'password':'bar' }).then(res => console.log(res));

View File

@@ -1,44 +1,45 @@
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import { random } from './random'; import { random } from './random';
import { log } from './logManager';
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
host: process.env.SMTP, host: process.env.SMTP,
port: 465, port: 465,
secure: true, secure: true,
auth: { auth: {
user: process.env.MAIL, user: `${process.env.MAIL}`,
pass: process.env.MAIL_PASS, pass: `${process.env.PASS}`,
}, },
tls: { rejectUnauthorized: false },
}); });
function sendMail(email, head, body) { function sendMail(email, head, body) {
try { try {
// setup email data
const mailOptions = { const mailOptions = {
from: `"AirJet" <${process.env.MAIL}>`, from: `"HSP-GDH" <${process.env.MAIL}>`,
to: email, to: email,
subject: head, subject: head,
text: body, text: body,
}; };
// send mail with defined transport object
transporter.sendMail(mailOptions, (error, info) => { transporter.sendMail(mailOptions, (error, info) => {
if (error) { if (error) {
console.log(error); log('Retrying connection to SMTP');
if (sendMail(email, head, body)) return true;
} }
else { else {
console.log('Email sent: ' + info.response); log('Email sent: ' + info.response);
return true;
} }
}); });
return true;
} }
catch (err) { catch (err) {
return false; return false;
} }
} }
function sendVerification(email, userId, type = 'register', code = null) { async function sendVerification(email, userId, type = 'register', code = null) {
try { try {
code ? code : random(100000, 999999); code ? code : code = random(100000, 999999);
let title, body; let title, body;
switch (type) { switch (type) {
case 'email': case 'email':
@@ -56,8 +57,9 @@ function sendVerification(email, userId, type = 'register', code = null) {
default: default:
return false; return false;
} }
if (sendMail(email, title, body)) return code; await sendMail(email, title, body);
return false; return code;
} }
catch (err) { catch (err) {
return false; return false;

View File

@@ -18,7 +18,7 @@ export async function userExists(userId) {
export async function isBanned(userId) { export async function isBanned(userId) {
try { try {
const [bannedUser] = await pool.execute('SELECT * FROM bans WHERE user_id = ? LIMIT 1', [userId]); const [bannedUser] = await pool.execute('SELECT * FROM bans WHERE user_id = ? LIMIT 1', [userId]);
if (bannedUser.length > 0) return true; if (bannedUser.length) return true;
return false; return false;
} }
catch (err) { catch (err) {
@@ -60,24 +60,24 @@ export async function checkIfUserEmailIsVerified(userId) {
export async function checkUserExists(req, res, next) { export async function checkUserExists(req, res, next) {
const userId = req.userId; const userId = req.userId;
if (!userExists(userId)) return await respondWithStatus(res, 404, 'User not found'); if (!await userExists(userId)) return await respondWithStatus(res, 404, 'User not found');
next(); next();
} }
export async function checkBanned(req, res, next) { export async function checkBanned(req, res, next) {
const userId = req.userId; const userId = req.userId;
if (isBanned(userId)) return await respondWithStatus(res, 403, 'User is banned'); if (await isBanned(userId)) return await respondWithStatus(res, 403, 'User is banned');
next(); next();
} }
export const checkPermissions = (permissionName, permissionType) => async (req, res, next) => { export const checkPermissions = (permissionName, permissionType) => async (req, res, next) => {
const userId = req.userId; const userId = req.userId;
if (!verifyPermissions(userId, permissionName, permissionType)) return await respondWithStatus(res, 403, 'Missing permission'); if (!await verifyPermissions(userId, permissionName, permissionType)) return await respondWithStatus(res, 403, 'Missing permission');
next(); next();
}; };
export const checkEmailVerified = async (req, res, next) => { export const checkEmailVerified = async (req, res, next) => {
const userId = req.userId; const userId = req.userId;
if (!checkIfUserEmailIsVerified(userId)) return await respondWithStatus(res, 403, 'Email not verified'); if (!await checkIfUserEmailIsVerified(userId)) return await respondWithStatus(res, 403, 'Email not verified');
next(); next();
}; };

View File

@@ -1,6 +1,7 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Level } from 'level'; import { Level } from 'level';
import { pool } from './databaseManager'; import { pool } from './databaseManager';
import { error } from '../modules/logManager';
import { respondWithStatus } from './requestHandler'; import { respondWithStatus } from './requestHandler';
const db = new Level('tokens', { valueEncoding: 'json' }); const db = new Level('tokens', { valueEncoding: 'json' });
@@ -31,7 +32,8 @@ export async function verifyToken(req, res, next) {
} }
next(); next();
} }
catch (error) { catch (err) {
error(err);
return await respondWithStatus(res, 401, 'Invalid user'); return await respondWithStatus(res, 401, 'Invalid user');
} }
} }

View File

@@ -20,7 +20,7 @@ router.post('/register', requestLimiter, async (req, res) => {
const [existingEmail] = await pool.execute('SELECT * FROM users WHERE email = ? LIMIT 1', [email]); const [existingEmail] = await pool.execute('SELECT * FROM users WHERE email = ? LIMIT 1', [email]);
if (existingEmail.length) return await respondWithStatus(res, 400, 'Email is already taken'); if (existingEmail.length) return await respondWithStatus(res, 400, 'Email is already taken');
if (!isPhoneNumber(phone)) return await respondWithStatus(res, 400, 'Invalid phone number'); if (phone && !isPhoneNumber(phone)) return await respondWithStatus(res, 400, 'Invalid phone number');
const hashedPassword = await Bun.password.hash(password); const hashedPassword = await Bun.password.hash(password);
@@ -63,7 +63,7 @@ router.post('/login', requestLimiter, async (req, res) => {
const passwordMatch = await Bun.password.verify(password, user.password); const passwordMatch = await Bun.password.verify(password, user.password);
if (!passwordMatch) return await respondWithStatus(res, 401, 'Incorrect password'); if (!passwordMatch) return await respondWithStatus(res, 401, 'Incorrect password');
if (isBanned(user.id)) return await respondWithStatus(res, 403, 'User is banned or an issue occured'); if (await isBanned(user.id)) return await respondWithStatus(res, 403, 'User is banned or an issue occured');
const token = await generateToken(user.id, password); const token = await generateToken(user.id, password);
return await respondWithStatusJSON(res, 200, { return await respondWithStatusJSON(res, 200, {
message: 'Login successful', message: 'Login successful',
@@ -128,7 +128,7 @@ router.get('/email/request', verifyToken, checkBanned, async (req, res) => {
if (!rows.length) return await respondWithStatus(res, 404, 'User not found'); if (!rows.length) return await respondWithStatus(res, 404, 'User not found');
const user = rows[0]; const user = rows[0];
if (user.email_verified) return await respondWithStatus(res, 400, 'Email is already verified'); if (user.email_verified) return await respondWithStatus(res, 400, 'Email is already verified');
const code = sendVerification(user.email, userId, 'email'); const code = await sendVerification(user.email, userId, 'email');
pool.execute('INSERT INTO verification_codes (user_id, verification_code, type) VALUES (?, ?, ?)', [ userId, code, 'email' ]); pool.execute('INSERT INTO verification_codes (user_id, verification_code, type) VALUES (?, ?, ?)', [ userId, code, 'email' ]);
return await respondWithStatus(res, 200, 'Successfully sent verification email'); return await respondWithStatus(res, 200, 'Successfully sent verification email');
} }
@@ -151,6 +151,7 @@ router.get('/email/verify', verifyToken, checkBanned, async (req, res) => {
const [result] = await pool.execute('UPDATE users SET email_verified = ? WHERE id = ?', [ true, userId ]); const [result] = await pool.execute('UPDATE users SET email_verified = ? WHERE id = ?', [ true, userId ]);
if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating user'); if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating user');
await pool.execute('DELETE FROM verification_codes WHERE id = ?', [ rows[0].id ]);
return await respondWithStatus(res, 200, 'Successfully verified user'); return await respondWithStatus(res, 200, 'Successfully verified user');
} }

34
test.js Normal file
View File

@@ -0,0 +1,34 @@
import { get, post } from './modules/fetchManager';
let token = '';
async function main() {
let res = await post('http://127.0.0.1:1109/api/users/register', {
username:
'emerald',
email:
'light_emerald@aostia.com',
password:
'test1234',
first_name:
'Emerald',
last_name:
'Light',
});
console.log(res);
res = await post('http://127.0.0.1:1109/api/users/login', {
usernameOrEmail: 'emerald',
password: 'test1234',
});
console.log(res);
token = res.JSON.token;
res = await get('http://127.0.0.1:1109/api/users/email/request', token);
console.log(res);
res = await get('http://127.0.0.1:1109/api/users/email/verify?code=345708', token);
console.log(res);
}
main();