├── .gitignore ├── src ├── uploads │ └── 1621432900783-2b.jpg ├── routes │ ├── likes.js │ ├── friends.js │ ├── comments.js │ ├── posts.js │ └── users.js ├── models │ ├── userFriend.js │ ├── comment.js │ ├── like.js │ ├── post.js │ ├── index.js │ └── user.js ├── middlewares │ ├── headers.js │ └── auth.js ├── config │ ├── db.js │ └── cloudinary.js ├── utils │ └── imageUpload.js └── controllers │ ├── likes.js │ ├── comments.js │ ├── posts.js │ ├── user_post.js │ ├── friends.js │ └── users.js ├── .env.example ├── package.json ├── README.md ├── app.js └── bin └── www.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .env -------------------------------------------------------------------------------- /src/uploads/1621432900783-2b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grandbusta/SocialMedia-Api/HEAD/src/uploads/1621432900783-2b.jpg -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | CLOUD_NAME= 2 | CLOUDINARY_API_KEY= 3 | CLOUDINARY_API_SECRET= 4 | DB_NAME= 5 | DB_USER= 6 | DB_PASS= 7 | DB_HOST= 8 | JWT_KEY= -------------------------------------------------------------------------------- /src/routes/likes.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const { adminAuth, userAuth } = require('../middlewares/auth') 4 | const { like } = require('../controllers/likes') 5 | 6 | router.post('/', userAuth, like) 7 | 8 | module.exports = router 9 | -------------------------------------------------------------------------------- /src/models/userFriend.js: -------------------------------------------------------------------------------- 1 | const { sq } = require('../config/db') 2 | const { DataTypes } = require('sequelize') 3 | 4 | const User_Friend = sq.define('user_friend', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | autoIncrement: true, 8 | allowNull: false, 9 | primaryKey: true, 10 | }, 11 | }) 12 | 13 | module.exports = User_Friend 14 | -------------------------------------------------------------------------------- /src/routes/friends.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const { adminAuth, userAuth } = require('../middlewares/auth') 4 | const { addFriend, removeFriend } = require('../controllers/friends') 5 | 6 | router.post('/add', userAuth, addFriend) 7 | router.post('/remove', userAuth, removeFriend) 8 | 9 | module.exports = router 10 | -------------------------------------------------------------------------------- /src/routes/comments.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const { adminAuth, userAuth } = require('../middlewares/auth') 4 | const { createComment, editComment } = require('../controllers/comments') 5 | 6 | router.post('/', userAuth, createComment) 7 | router.patch('/:commentId', userAuth, editComment) 8 | 9 | module.exports = router 10 | -------------------------------------------------------------------------------- /src/models/comment.js: -------------------------------------------------------------------------------- 1 | const { sq } = require('../config/db') 2 | const { DataTypes } = require('sequelize') 3 | 4 | const Comment = sq.define('comments', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | autoIncrement: true, 8 | allowNull: false, 9 | primaryKey: true, 10 | }, 11 | text: { type: DataTypes.STRING, allowNull: false }, 12 | }) 13 | 14 | module.exports = Comment 15 | -------------------------------------------------------------------------------- /src/models/like.js: -------------------------------------------------------------------------------- 1 | const { sq } = require('../config/db') 2 | const { DataTypes } = require('sequelize') 3 | 4 | const Like = sq.define('like', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | autoIncrement: true, 8 | allowNull: false, 9 | primaryKey: true, 10 | }, 11 | state: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true }, 12 | }) 13 | 14 | module.exports = Like 15 | -------------------------------------------------------------------------------- /src/middlewares/headers.js: -------------------------------------------------------------------------------- 1 | exports.headers = (req, res, next) => { 2 | res.header('Access-Control-Allow-Origin', '*') 3 | res.header( 4 | 'Access-Control-Allow-Headers', 5 | 'Origin,X-Requested-With,Content-Type,Accept,Authorization', 6 | ) 7 | if (req.method === 'OPTIONS') { 8 | res.header('Access-Control-Allow-Methods', 'PUT,POST,PATCH,DELETE') 9 | return res.status(200).json({}) 10 | } 11 | next() 12 | } 13 | -------------------------------------------------------------------------------- /src/models/post.js: -------------------------------------------------------------------------------- 1 | const { sq } = require('../config/db') 2 | const { DataTypes } = require('sequelize') 3 | 4 | const Post = sq.define('post', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | autoIncrement: true, 8 | allowNull: false, 9 | primaryKey: true, 10 | }, 11 | img_url: { type: DataTypes.STRING, allowNull: false }, 12 | caption: { type: DataTypes.STRING, allowNull: true }, 13 | }) 14 | 15 | module.exports = Post 16 | -------------------------------------------------------------------------------- /src/models/index.js: -------------------------------------------------------------------------------- 1 | const User = require('./user') 2 | const UserFriend = require('./userFriend') 3 | const Post = require('./post') 4 | const Like = require('./like') 5 | const Comment = require('./comment') 6 | 7 | User.hasMany(Post) 8 | User.hasMany(Comment) 9 | User.hasMany(Like) 10 | User.belongsToMany(User, { through: UserFriend, as: 'friend' }) 11 | Post.hasMany(Like) 12 | Post.hasMany(Comment) 13 | 14 | module.exports = { User, Post, Like, Comment, UserFriend } 15 | -------------------------------------------------------------------------------- /src/config/db.js: -------------------------------------------------------------------------------- 1 | const { Sequelize } = require('sequelize') 2 | const { DB_NAME, DB_USER, DB_PASS, DB_HOST } = process.env 3 | 4 | const sequelize = new Sequelize(DB_NAME, DB_USER, DB_PASS, { 5 | host: DB_HOST, 6 | dialect: 'mysql', 7 | }) 8 | 9 | testDbConnection = async () => { 10 | try { 11 | await sequelize.authenticate() 12 | // sequelize.sync({ force: true }) 13 | } catch (error) { 14 | console.error(error) 15 | } 16 | } 17 | 18 | module.exports = { sq: sequelize, testDbConnection } 19 | -------------------------------------------------------------------------------- /src/routes/posts.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const { adminAuth, userAuth } = require('../middlewares/auth') 4 | const upload = require('../utils/imageUpload') 5 | const { 6 | createNewPost, 7 | deletePost, 8 | updatePost, 9 | getSinglePost, 10 | } = require('../controllers/posts') 11 | 12 | router.post('/', userAuth, upload.single('postImg'), createNewPost) 13 | router.patch('/:postId', userAuth, updatePost) 14 | router.delete('/:postId', userAuth, deletePost) 15 | router.get('/:postId', getSinglePost) 16 | 17 | module.exports = router 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "social-media", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "set DEBUG=* & node ./bin/www.js" 7 | }, 8 | "dependencies": { 9 | "bcrypt": "^5.0.1", 10 | "cloudinary": "^1.25.1", 11 | "cookie-parser": "~1.4.4", 12 | "debug": "~2.6.9", 13 | "dotenv": "^9.0.0", 14 | "email-validator": "^2.0.4", 15 | "express": "^4.16.4", 16 | "http-errors": "~1.6.3", 17 | "jsonwebtoken": "^8.5.1", 18 | "morgan": "~1.9.1", 19 | "multer": "^1.4.2", 20 | "mysql2": "^2.2.5", 21 | "sequelize": "^6.6.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/imageUpload.js: -------------------------------------------------------------------------------- 1 | const multer = require('multer') 2 | 3 | const storage = multer.diskStorage({ 4 | destination: (req, file, cb) => { 5 | cb(null, './src/uploads/') 6 | }, 7 | filename: (req, file, cb) => { 8 | cb(null, `${Date.now()}-${file.originalname}`) 9 | }, 10 | }) 11 | 12 | const fileFilter = (req, file, cb) => { 13 | if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') { 14 | cb(null, true) 15 | } else { 16 | cb(null, false) 17 | } 18 | } 19 | 20 | const upload = multer({ 21 | storage: storage, 22 | limits: { fieldSize: 1024 * 1024 }, 23 | fileFilter: fileFilter, 24 | }) 25 | 26 | module.exports = upload 27 | -------------------------------------------------------------------------------- /src/routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const { adminAuth, userAuth } = require('../middlewares/auth') 4 | const { signUp, login, remove, update } = require('../controllers/users') 5 | const { getAllPosts, getFeeds } = require('../controllers/user_post') 6 | const { getAllUserFriends } = require('../controllers/friends') 7 | 8 | router.post('/signup', signUp) 9 | router.post('/login', login) 10 | router.delete('/delete', userAuth, remove) 11 | router.patch('/update', userAuth, update) 12 | 13 | //User posts routes 14 | router.get('/:userId/posts', getAllPosts) 15 | router.get('/:userId/friends', getAllUserFriends) 16 | router.get('/:userId/feeds', getFeeds) 17 | 18 | module.exports = router 19 | -------------------------------------------------------------------------------- /src/config/cloudinary.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require('cloudinary').v2 2 | const { CLOUD_NAME, CLOUDINARY_API_SECRET, CLOUDINARY_API_KEY } = process.env 3 | 4 | cloudinary.config({ 5 | cloud_name: CLOUD_NAME, 6 | api_key: CLOUDINARY_API_KEY, 7 | api_secret: CLOUDINARY_API_SECRET, 8 | }) 9 | 10 | exports.uploads = (file, folder) => { 11 | return new Promise((resolve, reject) => { 12 | cloudinary.uploader.upload( 13 | file, 14 | { 15 | resource_type: 'auto', 16 | folder: folder, 17 | }, 18 | (err, result) => { 19 | if (err) { 20 | reject(err) 21 | } else { 22 | resolve({ 23 | id: result.public_id, 24 | url: result.url, 25 | }) 26 | } 27 | }, 28 | ) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/models/user.js: -------------------------------------------------------------------------------- 1 | const { sq } = require('../config/db') 2 | const { DataTypes } = require('sequelize') 3 | 4 | const User = sq.define('user', { 5 | id: { 6 | type: DataTypes.INTEGER, 7 | autoIncrement: true, 8 | allowNull: false, 9 | primaryKey: true, 10 | }, 11 | email: { type: DataTypes.STRING, allowNull: false, unique: true }, 12 | first_name: { type: DataTypes.STRING, allowNull: false }, 13 | last_name: { type: DataTypes.STRING, allowNull: false }, 14 | user_status: { 15 | type: DataTypes.ENUM, 16 | values: ['active', 'disabled'], 17 | defaultValue: 'active', 18 | }, 19 | user_type: { 20 | type: DataTypes.ENUM, 21 | values: ['user', 'admin'], 22 | defaultValue: 'user', 23 | }, 24 | password: { type: DataTypes.STRING, allowNull: false }, 25 | }) 26 | 27 | module.exports = User 28 | -------------------------------------------------------------------------------- /src/controllers/likes.js: -------------------------------------------------------------------------------- 1 | const { Post, User, Like } = require('../models') 2 | 3 | const like = async (req, res, next) => { 4 | try { 5 | const { postId } = req.body 6 | const userId = req.userData.id 7 | if (postId) { 8 | const checkPost = await Post.findOne({ where: { id: postId } }) 9 | if (checkPost) { 10 | const checkLike = await Like.findOne({ 11 | where: { userId: userId, postId: postId }, 12 | }) 13 | if (checkLike) { 14 | await Like.destroy({ where: { userId: userId, postId: postId } }) 15 | res.status(200).json({ 16 | response: 'success', 17 | likeState: !checkLike.state, 18 | }) 19 | } else { 20 | await Like.create({ state: true, userId: userId, postId: postId }) 21 | res.status(200).json({ 22 | response: 'success', 23 | likeState: true, 24 | }) 25 | } 26 | } else { 27 | res.status(404).json({ 28 | response: 'post not found', 29 | }) 30 | } 31 | } else { 32 | res.status(422).json({ 33 | response: 'postId not present', 34 | }) 35 | } 36 | } catch (error) { 37 | console.error(error) 38 | res.status(500).json({ 39 | response: 'error occured', 40 | }) 41 | } 42 | } 43 | 44 | module.exports = { 45 | like, 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Social Media API - NodeJS🔥 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![Twitter Follow](https://img.shields.io/twitter/follow/iamgrandbusta?style=social)](https://twitter.com/iamgrandbusta) 2 | 3 | A Social media Api built with NodeJS and Express framework. Improving day by day. 4 | 5 | [Here](https://ohsocial.herokuapp.com/) is the link to the API base URL 6 | 7 | Star⭐ the repo if you like what you see😉. 8 | 9 | ## 📖Table of contents 10 | 11 | - [Techologies](#technologies) 12 | - [Getting Started](#getting-started) 13 | - [Features](#features) 14 | - [Making requests](#making-requests) 15 | - [Docs](https://documenter.getpostman.com/view/11680593/TzXtJfzY) 16 | 17 | ## 🛠️Technologies 18 | 19 | - NodeJS 20 | - ExpressJS 21 | - Mysql 22 | - Sequelize ORM 23 | 24 | ## ⛷️Getting Started 25 | 26 | ### ✨Features 27 | 28 | - [x] User Signin 29 | - [x] User Signup 30 | - [x] Add friend 31 | - [x] Remove friend 32 | - [x] Create post 33 | - [x] Delete post 34 | - [x] Edit post 35 | - [x] Like/Unlike post 36 | - [x] Comment on post 37 | - [x] Edit Comment 38 | 39 | ### 📮Making requests 40 | 41 | Always attach the token on the request header 42 | 43 | ## 🤓 Author(s) 44 | 45 | **Olaifa Boluwatife Jonathan** [![Twitter Follow](https://img.shields.io/twitter/follow/iamgrandbusta?style=social)](https://twitter.com/iamgrandbusta) 46 | 47 | ## 🔖 LICENCE 48 | 49 | [WTFPL](http://www.wtfpl.net/about/) 50 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const createError = require('http-errors') 2 | const express = require('express') 3 | const path = require('path') 4 | const cookieParser = require('cookie-parser') 5 | const logger = require('morgan') 6 | const { testDbConnection } = require('./src/config/db') 7 | const { headers } = require('./src/middlewares/headers') 8 | 9 | const users = require('./src/routes/users') 10 | const posts = require('./src/routes/posts') 11 | const comments = require('./src/routes/comments') 12 | const likes = require('./src/routes/likes') 13 | const friends = require('./src/routes/friends') 14 | 15 | const app = express() 16 | 17 | app.use(headers) 18 | testDbConnection() 19 | app.use(logger('dev')) 20 | app.use(express.json()) 21 | app.use(express.urlencoded({ extended: false })) 22 | app.use(express.static('./src/uploads')) 23 | app.use(cookieParser()) 24 | 25 | app.use('/users', users) 26 | app.use('/posts', posts) 27 | app.use('/comments', comments) 28 | app.use('/likes', likes) 29 | app.use('/friends', friends) 30 | 31 | // catch 404 and forward to error handler 32 | app.use(function (req, res, next) { 33 | next(createError(404)) 34 | }) 35 | 36 | // error handler 37 | app.use(function (err, req, res, next) { 38 | // set locals, only providing error in development 39 | res.locals.message = err.message 40 | res.locals.error = req.app.get('env') === 'development' ? err : {} 41 | 42 | res.status(err.status || 500).json({ 43 | status: err.status || 500, 44 | response: err.message, 45 | }) 46 | }) 47 | 48 | module.exports = app 49 | -------------------------------------------------------------------------------- /src/controllers/comments.js: -------------------------------------------------------------------------------- 1 | const { Post, User, Comment } = require('../models') 2 | 3 | const createComment = async (req, res, next) => { 4 | const { postId, text } = req.body 5 | try { 6 | const findPost = await Post.findOne({ where: { id: postId } }) 7 | if (findPost) { 8 | if (text) { 9 | const comment = await Comment.create({ 10 | text: text, 11 | postId: postId, 12 | userId: req.userData.id, 13 | }) 14 | res.status(200).json({ 15 | response: 'success', 16 | comment: comment, 17 | }) 18 | } else { 19 | res.status(422).json({ 20 | response: 'text not present', 21 | }) 22 | } 23 | } else { 24 | res.status(404).json({ 25 | response: 'Not found', 26 | }) 27 | } 28 | } catch (error) { 29 | console.error(error) 30 | res.status(500).json({ 31 | response: 'error occured', 32 | }) 33 | } 34 | } 35 | 36 | const editComment = async (req, res, next) => { 37 | try { 38 | const [updateComment] = await Comment.update( 39 | { text: req.body.text }, 40 | { where: { id: req.params.commentId, userId: req.userData.id } }, 41 | ) 42 | if (updateComment) { 43 | res.status(200).json({ 44 | response: 'success', 45 | }) 46 | } else { 47 | res.status(404).json({ 48 | response: 'Not found', 49 | }) 50 | } 51 | } catch (error) { 52 | console.error(error) 53 | res.status(500).json({ 54 | response: 'error occured', 55 | }) 56 | } 57 | } 58 | 59 | module.exports = { 60 | createComment, 61 | editComment, 62 | } 63 | -------------------------------------------------------------------------------- /src/middlewares/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken') 2 | const { JWT_KEY } = process.env 3 | const { User } = require('../models') 4 | 5 | exports.userAuth = async (req, res, next) => { 6 | try { 7 | if (req.headers.authorization) { 8 | const token = req.headers.authorization.split(' ')[1] 9 | const decoded = jwt.verify(token, JWT_KEY) 10 | const userData = await User.findOne({ 11 | where: { email: decoded.email }, 12 | attributes: ['id', 'email', 'first_name', 'last_name', 'user_status'], 13 | }) 14 | if (userData.email && userData.user_status === 'active') { 15 | req.userData = userData 16 | next() 17 | } else { 18 | return res.status(401).json({ 19 | message: 'Auth failed', 20 | }) 21 | } 22 | } else { 23 | res.status(422).json({ message: 'token not present in header' }) 24 | } 25 | } catch (error) { 26 | console.error(error) 27 | return res.status(401).json({ 28 | message: 'Auth failed', 29 | }) 30 | } 31 | } 32 | 33 | exports.adminAuth = async (req, res, next) => { 34 | try { 35 | if (req.headers.authorization) { 36 | const token = req.headers.authorization.split(' ')[1] 37 | const decoded = jwt.verify(token, JWT_KEY) 38 | const userData = await User.findOne({ 39 | where: { email: decoded.email }, 40 | attributes: ['email', 'user_type'], 41 | }) 42 | if (userData.user_type === 'admin') { 43 | req.userData = userData 44 | next() 45 | } else { 46 | return res.status(401).json({ 47 | message: 'Auth failed', 48 | }) 49 | } 50 | } else { 51 | res.status(422).json({ message: 'Token not present in header' }) 52 | } 53 | } catch (error) { 54 | console.error(error) 55 | return res.status(401).json({ 56 | message: 'Auth failed', 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bin/www.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | require('dotenv').config() 7 | var app = require('../app'); 8 | var debug = require('debug')('social-media:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /src/controllers/posts.js: -------------------------------------------------------------------------------- 1 | const { User, Post, Comment } = require('../models') 2 | const cloudinary = require('../config/cloudinary') 3 | const fs = require('fs') 4 | 5 | const createNewPost = async (req, res, next) => { 6 | try { 7 | const { path } = req.file 8 | if (path) { 9 | const { caption } = req.body 10 | const { url } = await cloudinary.uploads(path, 'SocialMedia') 11 | fs.unlinkSync(path) 12 | const newPost = await Post.create({ 13 | img_url: url, 14 | caption: caption, 15 | userId: req.userData.id, 16 | }) 17 | console.log(url, caption) 18 | res.status(200).json({ post: newPost }) 19 | } else { 20 | res.status(422).json({ response: 'image not present in body' }) 21 | } 22 | } catch (error) { 23 | console.error(error) 24 | res.status(500).json({ response: 'error occured' }) 25 | } 26 | } 27 | 28 | const deletePost = async (req, res, next) => { 29 | try { 30 | const post = await Post.destroy({ 31 | where: { id: req.params.postId, userId: req.userData.id }, 32 | }) 33 | if (post) { 34 | res.status(200).json({ 35 | response: 'success', 36 | }) 37 | } else { 38 | res.status(404).json({ 39 | response: 'Not found', 40 | }) 41 | } 42 | } catch (error) { 43 | console.error(error) 44 | res.status(500).json({ 45 | response: 'error occured', 46 | }) 47 | } 48 | } 49 | 50 | const updatePost = (req, res, next) => {} 51 | 52 | const getSinglePost = async (req, res, next) => { 53 | try { 54 | const post = await Post.findOne({ 55 | where: { id: req.params.postId }, 56 | include: [ 57 | { 58 | model: Comment, 59 | attributes: ['id', 'text', 'createdAt', 'updatedAt'], 60 | }, 61 | ], 62 | }) 63 | if (post) { 64 | res.status(200).json({ 65 | response: 'success', 66 | post: post, 67 | }) 68 | } else { 69 | res.status(404).json({ 70 | response: 'Not found', 71 | }) 72 | } 73 | } catch (error) { 74 | console.error(error) 75 | res.status(500).json({ 76 | response: 'error occured', 77 | }) 78 | } 79 | } 80 | 81 | module.exports = { 82 | createNewPost, 83 | deletePost, 84 | updatePost, 85 | getSinglePost, 86 | } 87 | -------------------------------------------------------------------------------- /src/controllers/user_post.js: -------------------------------------------------------------------------------- 1 | const { User, Post, Comment, Like } = require('../models') 2 | 3 | const getAllPosts = async (req, res, next) => { 4 | try { 5 | const userPost = await User.findOne({ 6 | where: { id: req.params.userId }, 7 | attributes: ['id'], 8 | include: [{ model: Post, attributes: { exclude: ['userId'] } }], 9 | }) 10 | if (userPost) { 11 | res.status(200).json({ 12 | userId: userPost.id, 13 | posts: userPost.posts, 14 | }) 15 | } else { 16 | res.status(404).json({ 17 | response: 'User does not exist', 18 | }) 19 | } 20 | } catch (error) { 21 | console.error(error) 22 | res.status(500).json({ 23 | response: 'an error occured', 24 | }) 25 | } 26 | } 27 | 28 | const getFeeds = async (req, res, next) => { 29 | const userId = req.params.userId 30 | try { 31 | const allFriends = await User.findOne({ 32 | where: { id: userId }, 33 | attributes: ['id', 'first_name', 'last_name'], 34 | include: [ 35 | { 36 | model: User, 37 | attributes: ['id', 'first_name', 'last_name'], 38 | as: 'friend', 39 | include: [ 40 | { 41 | model: Post, 42 | include: [ 43 | { model: Comment, attributes: { exclude: ['postId'] } }, 44 | { 45 | model: Like, 46 | attributes: { exclude: ['postId'] }, 47 | }, 48 | ], 49 | attributes: { exclude: ['userId'] }, 50 | }, 51 | ], 52 | through: { attributes: [] }, 53 | }, 54 | ], 55 | }) 56 | let postArr = [] 57 | const allFriend = JSON.parse(JSON.stringify(allFriends)) 58 | if (allFriend) { 59 | allFriend.friend.forEach(friend => { 60 | friend.posts.forEach(post => { 61 | postArr.push({ 62 | user: { 63 | first_name: friend.first_name, 64 | last_name: friend.last_name, 65 | id: friend.id, 66 | }, 67 | ...post, 68 | }) 69 | }) 70 | }) 71 | const sortedArr = postArr.sort( 72 | (a, b) => 73 | new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(), 74 | ) 75 | res.status(200).json({ feed: sortedArr }) 76 | } else { 77 | res.status(404).json({ response: 'no friend added yet' }) 78 | } 79 | } catch (error) { 80 | console.error(error) 81 | res.status(500).json({ response: 'error occured' }) 82 | } 83 | } 84 | 85 | module.exports = { 86 | getAllPosts, 87 | getFeeds, 88 | } 89 | -------------------------------------------------------------------------------- /src/controllers/friends.js: -------------------------------------------------------------------------------- 1 | const { User, UserFriend, Post } = require('../models') 2 | 3 | const addFriend = async (req, res, next) => { 4 | const { friendId } = req.body 5 | const userId = req.userData.id 6 | try { 7 | if (friendId) { 8 | if (friendId === userId) { 9 | res.status(400).json({ response: 'user cannot follow self' }) 10 | } else { 11 | const checkFriend = await User.findOne({ where: { id: friendId } }) 12 | if (checkFriend) { 13 | const isAdded = await UserFriend.findOne({ 14 | where: { userId: userId, friendId: friendId }, 15 | }) 16 | if (isAdded) { 17 | res.status(200).json({ response: 'already added', isFriend: true }) 18 | } else { 19 | const newFriend = await UserFriend.create({ 20 | userId: userId, 21 | friendId: friendId, 22 | }) 23 | res.status(200).json({ response: 'added', isFriend: true }) 24 | } 25 | } else { 26 | res.status(404).json({ response: 'friend not found' }) 27 | } 28 | } 29 | } else { 30 | res.status(422).json({ response: 'friendId not preent' }) 31 | } 32 | } catch (error) { 33 | console.error(error) 34 | res.status(500).json({ response: 'error occured' }) 35 | } 36 | } 37 | 38 | const removeFriend = async (req, res, next) => { 39 | const { friendId } = req.body 40 | const userId = req.userData.id 41 | try { 42 | if (friendId) { 43 | const isAdded = await UserFriend.findOne({ 44 | where: { userId: userId, friendId: friendId }, 45 | }) 46 | if (isAdded) { 47 | await UserFriend.destroy({ 48 | where: { userId: userId, friendId: friendId }, 49 | }) 50 | res.status(200).json({ response: 'removed', isFriend: false }) 51 | } else { 52 | res.status(404).json({ response: 'not friend', isFriend: false }) 53 | } 54 | } else { 55 | res.status(422).json({ response: 'friendId not preent' }) 56 | } 57 | } catch (error) { 58 | console.error(error) 59 | res.status(500).json({ response: 'error occured' }) 60 | } 61 | } 62 | 63 | const getAllUserFriends = async (req, res, next) => { 64 | const userId = req.params.userId 65 | try { 66 | const allFriends = await User.findOne({ 67 | where: { id: userId }, 68 | attributes: ['id', 'first_name', 'last_name'], 69 | include: [ 70 | { 71 | model: User, 72 | attributes: ['id', 'first_name', 'last_name'], 73 | as: 'friend', 74 | through: { attributes: [] }, 75 | }, 76 | ], 77 | }) 78 | res.status(200).json({ user: allFriends }) 79 | } catch (error) { 80 | console.error(error) 81 | res.status(500).json({ response: 'error occured' }) 82 | } 83 | } 84 | 85 | module.exports = { 86 | addFriend, 87 | removeFriend, 88 | getAllUserFriends, 89 | } 90 | -------------------------------------------------------------------------------- /src/controllers/users.js: -------------------------------------------------------------------------------- 1 | const { User } = require('../models') 2 | const bcrypt = require('bcrypt') 3 | const jwt = require('jsonwebtoken') 4 | const validator = require('email-validator') 5 | const { JWT_KEY } = process.env 6 | 7 | const signUp = async (req, res, next) => { 8 | try { 9 | const { email, firstname, lastname, password } = req.body 10 | if (email && firstname && lastname && password) { 11 | if (validator.validate(email)) { 12 | let check = await User.findOne({ 13 | where: { email: email }, 14 | attributes: ['email'], 15 | }) 16 | if (check !== null) { 17 | res.status(409).json({ response: 'user already exist' }) 18 | } else { 19 | let createUser = await User.create({ 20 | email: email, 21 | first_name: firstname, 22 | last_name: lastname, 23 | password: await bcrypt.hash(password, 10), 24 | }) 25 | if (createUser) { 26 | res.status(201).json({ response: 'user created successfully' }) 27 | } 28 | } 29 | } else { 30 | res.status(401).json({ response: 'email is not valid' }) 31 | } 32 | } else { 33 | res.status(401).json({ response: 'one or more values is missing' }) 34 | } 35 | } catch (error) { 36 | res.status(500).json({ response: 'an error occured' }) 37 | } 38 | } 39 | 40 | const login = async (req, res, next) => { 41 | try { 42 | const { email, password } = req.body 43 | if (email && password) { 44 | if (validator.validate(email)) { 45 | let check = await User.findOne({ 46 | where: { email: email }, 47 | attributes: ['email', 'password'], 48 | }) 49 | if (check !== null) { 50 | let comparePassword = await bcrypt.compare(password, check.password) 51 | if (comparePassword) { 52 | res.status(200).json({ 53 | response: 'Auth successful', 54 | token: jwt.sign({ email: check.email }, JWT_KEY), 55 | }) 56 | } else { 57 | res.status(401).json({ response: 'Auth failed' }) 58 | } 59 | } else { 60 | res.status(404).json({ response: 'user not found' }) 61 | } 62 | } else { 63 | res.status(422).json({ response: 'not a valid email' }) 64 | } 65 | } else { 66 | res.status(422).json({ response: 'one or more values are missing' }) 67 | } 68 | } catch (error) { 69 | console.error(error) 70 | res.status(500).json({ response: 'An error occured' }) 71 | } 72 | } 73 | 74 | const remove = async (req, res, next) => { 75 | try { 76 | await User.destroy({ where: { id: req.userData.id } }) 77 | res.status(200).json({ response: 'user deleted sucessfully' }) 78 | } catch (error) { 79 | res.status(500).json({ message: 'An error occured' }) 80 | } 81 | } 82 | 83 | const update = async (req, res, next) => { 84 | try { 85 | await User.update({ ...req.body }, { where: { id: req.userData.id } }) 86 | res.status(200).json({ message: 'update successful' }) 87 | } catch (error) { 88 | res.status(500).json({ message: 'An error occured' }) 89 | } 90 | } 91 | 92 | module.exports = { 93 | signUp, 94 | login, 95 | remove, 96 | update, 97 | } 98 | --------------------------------------------------------------------------------