blink/backend/apis/nodejs/api_controller.js
2023-10-16 14:59:46 +02:00

322 lines
8.4 KiB
JavaScript

/*
This code is part of Blink
licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
require('dotenv').config();
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const knex = require('knex')({
client: 'pg',
connection: {
host: process.env.POSTGRES_SERVER,
user: process.env.POSTGRES_USERNAME,
password: process.env.POSTGRES_PASSWORD,
port: process.env.POSTGRES_PORT,
database: 'Blink'
}
});
const jwt = require('jsonwebtoken');
// ======== API ENDPOINTS ========
// POST
async function registerPerson(req, res){
if (process.env.ALLOW_USER_REGISTRATION === 'false'){
return res.status(403).json({error : "Users cannot register on this server"});
}
// Ensure that the required fields are present before proceeding
if (!req.body.display_name || !req.body.email || !req.body.password) {
return res.status(400).json("Invalid request.");
}
// Generate activation link token
const activationLink = crypto.randomBytes(16).toString('hex');
// Hash provided password
const hashPasswordPromise = bcrypt.hash(req.body.password, 10);
try{
// Begin transaction
await knex.transaction(async (tr) => {
const personIdResult = await tr('Person')
.insert({
email: req.body.email,
password: await hashPasswordPromise,
display_name: req.body.display_name,
date_of_birth: req.body.date_of_birth,
available: req.body.available,
enabled: true,
place_of_living: req.body.place_of_living
})
.returning("id");
await tr('ActivationLink')
.insert({
person_id: personIdResult[0].id,
identifier: activationLink
});
});
return res.status(200).json({ activationLink: activationLink });
}
catch (error){
console.error('Error inserting data:', error);
res.status(500).json("Internal server error");
}
}
// POST
async function login(req, res){
// Ensure that the required fields are present before proceeding
if (!req.body.email || !req.body.password) {
return res.status(400).json("Invalid request");
}
const person = await checkUserCredentials(req.body.email, req.body.password);
if (person){
const token = generateToken(person.id);
res.status(200).json({ token });
}
else{
res.status(401).json("Unauthorized");
}
}
// GET
async function getPerson(req, res){
try {
const user = await knex('Person')
.select('*')
.where({ id: req.params.id, enabled: true })
.first();
if(user){
if(user.id == req.jwt.person_id || user.enabled){
return res.status(200).send(user);
}
}
return res.status(403).json("Forbidden");
}
catch (error) {
console.log(error);
return res.status(500).json("Internal server error");
}
}
// POST
async function createOrganization(req, res){
// Ensure that the required fields are present before proceeding
if (!req.body.name) {
return res.status(400).json("Invalid request.");
}
try{
knex.transaction(async (trx) => {
const organizationResult = await trx('Organization')
.insert({
name: req.body.name,
location: req.body.location,
description: req.body.description,
is_hiring: req.body.is_hiring,
})
.returning('*');
// Inserting in the "OrganizationAdministrator" table
await trx('OrganizationAdministrator')
.insert({
id_person: req.jwt.person_id,
id_organization: organizationResult[0].id,
});
await trx.commit();
return res.status(200).json({ Organization: organizationResult[0] });
});
}
catch (error){
console.error('Error inserting data:', error);
res.status(500).json("Internal server error");
}
}
// DELETE
async function deleteOrganization(req, res){
const organizationIdToDelete = req.params.id;
try {
if(await isPersonOrganizationAdmin(req.jwt.person_id, organizationIdToDelete)){
await knex('Organization')
.where({ id: organizationIdToDelete })
.del();
return res.status(200).json("Ok");
}
return res.status(403).json("Forbidden");
}
catch (error) {
console.error(error);
return res.status(500).json("Internal server error");
}
}
// POST
async function createOrganizationPost(req, res){
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.content) {
return res.status(400).json("Invalid request.");
}
try {
if (await isPersonOrganizationAdmin(req.jwt.person_id, req.body.organization_id)){
const organizationPost = await knex('OrganizationPost')
.insert({
organization_id: req.body.organization_id,
content: req.body.content,
})
.returning('*');
return res.status(200).json(organizationPost[0]);
}
else{
return res.status(401).json("Forbidden");
}
}
catch (error) {
console.log(error);
return res.status(500).json("Internal server error");
}
}
// DELETE
async function deleteOrganizationPost(req, res){
const organizationPostIdToDelete = req.params.id;
try{
knex.transaction(async (trx) => {
// Check if user is allowed to delete the post
const result = await trx('OrganizationPost')
.join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization')
.where('OrganizationPost.id', organizationPostIdToDelete)
.where('OrganizationAdministrator.id_person', req.jwt.person_id)
.select('*')
.first();
if (result) {
await trx('OrganizationPost')
.where('id', organizationPostIdToDelete)
.del();
await trx.commit();
return res.status(200).json('Ok');
} else {
return res.status(401).json('Forbidden');
}
});
}
catch (error) {
console.log(error);
res.status(500).json("Internal server error");
}
}
// POST [NOT COMPLETE]
async function makeAdmin(req, res){
}
// ======== END API ENDPOINTS ========
async function checkUserCredentials(email, password){
try {
const user = await knex('Person')
.where('email', email)
.where('enabled', true)
.select('*')
.first();
if(user){
const passwordMatches = await bcrypt.compare(password, user.password);
if (passwordMatches) {
return user;
}
}
return null;
}
catch (error) {
console.log(error);
return null;
}
}
async function isPersonOrganizationAdmin(personId, organizationId){
try {
const organizationAdministrator = await knex('OrganizationAdministrator')
.where('id_person', personId)
.where('id_organization', organizationId)
.select('*')
.first();
if (organizationAdministrator) {
return true;
}
else {
return false;
}
}
catch (error) {
return false;
}
}
function generateToken(person_id) {
const payload = {
person_id: person_id
};
const token = jwt.sign(payload, 'your-secret-key', { expiresIn: '1h' });
return token;
}
// Middlware
function verifyToken(req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(403).send('No token provided');
}
jwt.verify(token, 'your-secret-key', (err, decoded) => {
if (err) {
return res.status(401).send('Failed to authenticate token');
}
// If the token is valid, store the decoded data in the request object
req.jwt = decoded;
next();
});
}
// Exporting a function
// means making a JavaScript function defined in one
// module available for use in another module.
module.exports = {
registerPerson,
login,
getPerson,
verifyToken,
createOrganization,
deleteOrganization,
createOrganizationPost,
deleteOrganizationPost,
makeAdmin
};