Fixed API issues and added a test file
This commit is contained in:
@@ -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
2
.gitignore
vendored
@@ -136,4 +136,4 @@ logs/
|
|||||||
*.log
|
*.log
|
||||||
|
|
||||||
# token
|
# token
|
||||||
token/
|
tokens/
|
||||||
13
database.sql
13
database.sql
@@ -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 (
|
||||||
|
|||||||
2
index.js
2
index.js
@@ -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));
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
@@ -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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
34
test.js
Normal 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();
|
||||||
Reference in New Issue
Block a user