This repository has been archived on 2021-07-19. You can view files and clone it, but cannot push or open issues or pull requests.
eltelive/eltelive-new/server.js

347 lines
12 KiB
JavaScript

const express = require('express');
const serveStatic = require('serve-static')
const path = require('path')
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const cors = require('cors');
shortid = require('shortid');
const md5 = require('md5')
const dotenv = require('dotenv');
const mongoose = require('./db_connections/db');
streaming_config = require('./config/config');
const node_media_server = require('./config/media_server');
const User = require('./model/user');
dotenv.config();
node_media_server.run();
const app = express();
app.use(cors());
app.use(bodyParser.json());
// GET functions
app.get('/api/get_user', async (req, res) => {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(!token || typeof token !== 'string') {
return res.status(401).json({ status: 'error', title: 'JWT Token not provided' })
}
try {
const user_data = jwt.verify(token, process.env.JWT_SECRET)
const email = user_data.email
const user = await User.findOne({ email: email }).lean()
if (!user) {
return res.status(404).json({ status: 'error', title: 'User with this token does not exist' })
}
res.status(200).json({
status: 'ok',
title: 'User details are retrieved successfully',
user: {
givenName: user.givenName,
familyName: user.familyName,
email: user.email,
stream_key: user.stream_key
}
})
} catch (error) {
// console.log(error)
res.status(400).json({ status: 'error', title: 'Unexpected error' })
}
})
app.get('/api/get_users', async (req, res) => {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(!token || typeof token !== 'string') {
return res.status(401).json({ status: 'error', title: 'JWT Token not provided' })
}
try {
const user_data = jwt.verify(token, process.env.JWT_SECRET)
const email = user_data.email
const user = await User.findOne({ email }).lean()
if (!user) {
return res.status(404).json({ status: 'error', title: 'User with this token does not exist' })
}
// If the user is not the admin, then he's not authorised to access the full list of users
if(user.email.localeCompare('admin@admin.com')){
return res.status(403).json({ status: 'error', title: 'Only the admin can get the list of users' })
}
return res.status(200).json({
status: 'ok',
title: 'Users details are retrieved successfully',
users: await User.find({}).select('givenName familyName email stream_key')
})
} catch (error) {
// console.log(error)
res.status(400).json({ status: 'error', title: 'Unexpected error' })
}
})
app.get('/api/get_key', async(req, res) => {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(!token || typeof token !== 'string') {
return res.status(401).json({ status: 'error', title: 'JWT Token not provided' })
}
try {
const user_data = jwt.verify(token, process.env.JWT_SECRET)
const email = user_data.email
const user = await User.findOne({ email }).lean()
if (!user) {
return res.status(404).json({ status: 'error', title: 'User with this token does not exist' })
}
res.status(200).json({
status: 'ok',
title: 'Stream key was retrieved successfully',
stream_key: user.stream_key
})
} catch (error) {
// console.log(error)
res.status(400).json({ status: 'error', title: 'Unexpected error' })
}
})
// POST functions
app.post('/api/register', async (req, res) => {
const { givenName, familyName, email, password: plainTextPassword } = req.body
if (!givenName || typeof givenName !== 'string') {
return res.status(400).json({ status: 'error', title: 'Missing Given Name' })
}
if (!familyName || typeof familyName !== 'string') {
return res.stats(400).json({ status: 'error', title: 'Missing Family Name' })
}
if (!email || typeof email !== 'string') {
return res.status(400).json({ status: 'error', title: 'Missing Email Address' })
}
if (!plainTextPassword || typeof plainTextPassword !== 'string') {
return res.status(400).json({ status: 'error', title: 'Missing password' })
}
// regular expression for matching email addresses
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if ( !re.test(email.toLowerCase()) ) {
return res.status(400).json({ status: 'error', title: 'Email Address is invalid' })
}
if (plainTextPassword.length < 5) {
return res.status(403).json({
status: 'error',
title: 'Password is too small. It should be at least 5 characters'
})
}
const password = await bcrypt.hash(plainTextPassword, 10)
try {
const response = await User.create({
givenName,
familyName,
email,
password
})
// console.log('User created successfully: ', response)
} catch (err) {
if (err.code === 11000) {
// duplicate key
return res.status(409).json({ status: 'error', title: 'Email already in use' })
}
throw err
}
res.status(200).json({ status: 'ok', title: 'A new user was created successfully' })
})
app.post('/api/login', async (req, res) => {
const { email, password: plainTextPassword } = req.body
if (!email || typeof email !== 'string') {
return res.status(400).json({ status: 'error', title: 'Missing Email Address' })
}
if (!plainTextPassword || typeof plainTextPassword !== 'string') {
return res.status(400).json({ status: 'error', title: 'Missing password' })
}
// regular expression for matching email addresses
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if ( !re.test(email.toLowerCase()) ) {
return res.status(400).json({ status: 'error', title: 'Email Address is invalid' })
}
if (plainTextPassword.length < 5) {
return res.status(403).json({
status: 'error',
title: 'Password is too small. It should be at least 5 characters'
})
}
const user = await User.findOne({ email }).lean()
if (!user) {
return res.status(401).json({ status: 'error', title: 'Invalid email/password' })
}
if (await bcrypt.compare(plainTextPassword, user.password)) {
// the username, password combination is successful
const token = jwt.sign(
{
id: user._id,
email: user.email
},
process.env.JWT_SECRET
)
// console.log('User logged in successfully!')
// console.log('Token: ', token)
// console.log('Username: ', `${user.givenName} ${user.familyName}`)
return res.status(200).json({
status: 'ok',
title: "User logged in successfully",
token: token,
username: `${user.givenName} ${user.familyName}`
})
}
res.status(401).json({ status: 'error', error: 'Invalid email/password' })
})
// PUT functions
app.put('/api/generate_key', async(req, res) => {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(!token || typeof token !== 'string') {
return res.status(401).json({ status: 'error', title: 'JWT Token not provided' })
}
try {
const user_data = jwt.verify(token, process.env.JWT_SECRET)
const email = user_data.email
const user = await User.findOne({ email }).lean()
if (!user) {
return res.status(404).json({ status: 'error', title: 'User with this token does not exist' })
}
const stream_key = shortid.generate();
await User.updateOne(
{ email },
{
$set: { stream_key }
}
)
// Hasing part for generating the server address
const day_in_epoch = 86400
const current_time_in_epoch = Math.floor(new Date().getTime() / 1000)
const stream_expiration_time = current_time_in_epoch + day_in_epoch // After one day
const hashValue = md5("/live-" + stream_expiration_time + "-" + streaming_config.auth.secret)
const stream_address = "rtmp://" + process.env.HOST + "/live?sign=" + stream_expiration_time + "-" + hashValue
res.status(201).json({
status: 'ok',
title: 'Stream key generated successfully',
stream_key: stream_key,
stream_display_url: "http://" + process.env.HOST + ":" + streaming_config.http.port + "/live/" + stream_key + ".flv",
web_admin_panel: "http://" + process.env.HOST + ":" + streaming_config.http.port,
stream_address: "rtmp://" + process.env.HOST + "/live",
stream_address_authenticated: stream_address
})
} catch (error) {
// console.log(error)
res.status(400).json({ status: 'error', title: 'Unexpected error' })
}
})
// PATCH functions
app.patch('/api/change_password', async (req, res) => {
const { newPassword: newPlainTextPassword } = req.body
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(!token || typeof token !== 'string') {
return res.status(401).json({ status: 'error', title: 'JWT Token not provided' })
}
if (!newPlainTextPassword || typeof newPlainTextPassword !== 'string') {
return res.status(400).json({ status: 'error', title: 'Missing password' })
}
if (newPlainTextPassword.length < 5) {
return res.status(400).json({
status: 'error',
title: 'Password too small. It should be at least 5 characters'
})
}
try {
const user = jwt.verify(token, process.env.JWT_SECRET)
const email = user.email
const password = await bcrypt.hash(newPlainTextPassword, 10)
await User.updateOne(
{ email },
{
$set: { password }
}
)
res.status(200).json({ status: 'ok', title: 'Password was changed successfully' })
} catch (error) {
// console.log(error)
res.status(400).json({ status: 'error', title: 'Unexpected error' })
}
})
// DELETE functions
app.delete('/api/delete_user', async (req, res) => {
const { email_to_be_deleted } = req.body
if(!email_to_be_deleted || typeof email_to_be_deleted !== 'string') {
return res.status(401).json({ status: 'error', title: 'The email of the user to be deleted is not provided' })
}
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(!token || typeof token !== 'string') {
return res.status(401).json({ status: 'error', title: 'JWT Token not provided' })
}
try {
const user_data = jwt.verify(token, process.env.JWT_SECRET)
const email = user_data.email
const user = await User.findOne({ email }).lean()
if (!user) {
return res.status(404).json({ status: 'error', title: 'User with this token does not exist' })
}
// If the user is not the admin or the email address holder,
// then he's not authorised to delete any other user from the database
if(user.email.localeCompare('admin@admin.com') && user.email.localeCompare(email_to_be_deleted)){
return res.status(403).json({ status: 'error', title: 'Only the admin or the email holder can delete the account registered with this email' })
}
deletion_result = await User.deleteOne({email: email_to_be_deleted})
// Check if there was found a user with this email address in the database
if(deletion_result.n == 0){
return res.status(200).json({
status: 'ok',
title: 'A user with this email address does not exist in the database'
})
}
await User.deleteOne({email: email_to_be_deleted})
res.status(200).json({
status: 'ok',
title: 'The user details with the email address provided were deleted from the database'
})
} catch (error) {
// console.log(error)
res.status(400).json({ status: 'error', title: 'Unexpected error' })
}
})
app.delete('/api/delete_key', async (req, res) => {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(!token || typeof token !== 'string') {
return res.status(401).json({ status: 'error', title: 'JWT Token not provided' })
}
try {
const user_data = jwt.verify(token, process.env.JWT_SECRET)
const email = user_data.email
const user = await User.findOne({ email }).lean()
if (!user) {
return res.status(404).json({ status: 'error', title: 'User with this token does not exist' })
}
await User.updateOne(
{ email },
{
$unset: { stream_key: 1 }
}
)
res.status(200).json({ status: 'ok', title: 'Stream key deleted successfully'})
} catch (error) {
// console.log(error)
res.status(400).json({ status: 'error', title: 'Unexpected error' })
}
})
const port = process.env.NODE_JS_PORT || 4000;
app.listen(port, (err) => {
if (err) return console.log(err);
console.log('server running on port ' + port);
})
module.exports = app;