<repo_name>JanhaviMakwana/Twitter-react-app<file_sep>/twitter-frontend/src/store/reducer/auth.js
import * as actionTypes from '../actionTypes';

export const initialState = {
    user: null,
    error: null,
    isUpdated: true
};

const reducer = (state, action) => {
    switch(action.type) {
        case actionTypes.AUTH_SUCCESS:
            return {
                ...state,
                user: action.user,
                error: null
            }

        case actionTypes.AUTH_FAIL:
            return {
                ...state,
                user: null,
                error: action.error
            }

        case actionTypes.LOGOUT:
            return {
                ...state,
                user: null,
                error: null
            }

        case actionTypes.SET_ERROR:
            return {
                ...state,
                error: action.error
            }

        case actionTypes.REMOVE_ERROR: {
            return {
                ...state,
                error: null
            }
        }
        case actionTypes.SET_ISUPDATING: {
            return {
                ...state,
                isUpdated: true
            };
        }
        case actionTypes.SET_ISUPDATED: {
            return {
                ...state,
                isUpdated: false
            };
        }
        default : {
            return {
                ...state
            }
        }
    }
};

export default reducer;<file_sep>/twitter-backend/app/models/tweets.model.js
const moment = require('moment');
const appConfig = require('../config/app');

module.exports = (sequelize, Sequelize) => {
    const Tweet = sequelize.define("tweet", {
        id: {
            type: Sequelize.INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        description: {
            type: Sequelize.STRING
        },
        imageUrl: {
            type: Sequelize.STRING,
            defaultValue: '',
            get() {
                const image = this.getDataValue('imageUrl');
                return image ? `${appConfig.appUrl}:${appConfig.appPort}/${image}` : '';
            }
        },
        totalLikes: {
            type: Sequelize.INTEGER,
            defaultValue: 0
        },
        createdAt: {
            type: Sequelize.DATE,
            get() {
                return moment(this.getDataValue('createdAt')).format('DD/MM/YYYY h:mm:ss');
            }
        },
        updatedAt: {
            type: Sequelize.DATE,
            get() {
                return moment(this.getDataValue('updatedAt')).format('DD/MM/YYYY h:mm:ss');
            }
        }
    });
    return Tweet;
};
<file_sep>/twitter-frontend/src/twitter-context.js
import React, { createContext, useReducer } from 'react';
import reducer, { initialState } from './store/reducer/auth';

const TwitterContext = createContext();

const AuthProvider = (props) => {

    const [state, dispatch] = useReducer(reducer, initialState);

    return (
        <TwitterContext.Provider
            value={{
                state,
                dispatch
            }}>
            {props.children}
        </TwitterContext.Provider>
    );
};

const withAuth = (Child) => (props) => (
    <TwitterContext.Consumer>
        {(context) => <Child {...props} {...context} />}
    </TwitterContext.Consumer>
);

export { AuthProvider, withAuth };<file_sep>/twitter-frontend/src/store/actionTypes.js
export const AUTH_SUCCESS = 'AUTH_SUCCESS';
export const AUTH_FAIL = 'AUTH_FAIL';
export const LOGOUT = 'LOGOUT';
export const REMOVE_ERROR = 'REMOVE_ERROR';
export const SET_ERROR = 'SET_ERROR';
export const SET_AUTH_DATA = 'SET_AUTH_DATA';
export const SET_ISUPDATING = 'SET_ISUPDATING';
export const SET_ISUPDATED = 'SET_ISUPDATED';   <file_sep>/twitter-backend/app/models/likes.controller.js
const moment = require('moment');
module.exports = (sequelize, Sequelize) => {
    const Like = sequelize.define("like", {
        id: {
            type: Sequelize.INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        createdAt: {
            type: Sequelize.DATE,
            get() {
                return moment(this.getDataValue('createdAt')).format('DD/MM/YYYY h:mm:ss');
            }
        },
        updatedAt: {
            type: Sequelize.DATE,
            get() {
                return moment(this.getDataValue('updatedAt')).format('DD/MM/YYYY h:mm:ss');
            }
        }
    });
    return Like;
};<file_sep>/twitter-backend/app/controllers/auth.controller.js
const db = require('../models');
const User = db.user;
const { validationResult } = require('express-validator');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const config = require('../config/app');
const Op = db.Sequelize.Op;

exports.login = async (req, res) => {
    const { email, password } = req.body;
    try {
        const user = await User.findOne({
            "email": email
        });
        if (!user) return res.status(404).json({ message: 'User not found' });

        if (!(bcrypt.compare(password, user.password))) return res.status(401).json({ message: 'Incorrect password !' });

        const userWithToken = generateToken(user.get({ raw: true }));

        return res.send(userWithToken);
    } catch (e) {
        return res.status(500).json({ message: e.message });
    }
}

exports.signup = async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() })
    };

    try {
        const user = await User.create(req.body);

        const userWithToken = generateToken(user.get({ raw: true }));

        return res.send(userWithToken);
    } catch (e) {
        return res.status(500).json({ message: e.message });
    }
}

exports.editProfile = async (req, res) => {

    const { userId } = req.params;
    const { username, name } = req.body;

    const user = await User.findByPk(userId);
    if (user) {
        try {
            const updatedUser = await user.update({ username: username, name: name });
            return res.send(updatedUser);
        } catch (e) {
            return res.status(500).json({ message: e.message });
        }

    } else {
        res.status(400).send({
            message: 'Invalid userId!'
        })
    }
};

exports.uploadProfileImage = async (req, res) => {
    const { userId } = req.params;
    if (req.file) {
        const updatedUser = await User.update({ profileImageUrl: req.file.filename }, { where: { id: userId } });
        if (updatedUser) {
            return res.send({message: 'successfully updated user profile...'});
        } else {
            return res.status(404).json({ message: 'Tweet not found' });
        }
    }
    return res.status(500).json('No image uploaded')
};

exports.getUserProfile = async (req, res) => {
    try {
        res.send(req.user);
    } catch (e) {
        return res.status(500).json({ message: e.message });
    }
};

const generateToken = (user) => {
    delete user.password;

    const token = jwt.sign(user, config.appKey, { expiresIn: '86400' });

    return { ...{ user }, ...{ token } }
};


<file_sep>/twitter-frontend/src/components/Auth/Profile/Profile.js
import React, { useState, useEffect } from 'react';
import CreateIcon from '@material-ui/icons/Create';
import './Profile.css';
import { Button } from '@material-ui/core';
import AuthService from '../../../Services/AuthService';
import { withAuth } from '../../../twitter-context';
import FormData from 'form-data';

const Profile = (props) => {
    console.log(props.state.user);
    const [user, setUser] = useState([]);
    const [username, setUserName] = useState('');
    const [image, setImage] = useState(null);
    const [name, setName] = useState('');

    useEffect(() => {
        const userId = props.state.user.id;
        const fetchUserProfile = async () => {
            const user = await AuthService.getUserProfile(userId)
                .then(res => {
                    setUser(res);
                    setUserName(res.username);
                    setImage(res.profileImageUrl);
                    setName(res.name);
                })
                .catch(err => {
                    console.log(err);
                })
        }
        fetchUserProfile();
        // eslint-disable-next-line
    }, [])

    const nameHandler = (event) => {
        setName(event.target.value);
    }

    const usernameHandler = (event) => {
        setUserName(event.target.value);
    }

    const imageHandler = (event) => {
        setImage(event.target.files[0]);
    }

    const formSubmitHandler = async (event) => {
        event.preventDefault();
        const data = {
            username: username,
            name: name
        }
        AuthService.editProfile(props.state.user.id, data)
            .then(res => {
                if (image != null && typeof image === 'object') {
                    const imageData = new FormData();
                    imageData.append('image', image);
                    AuthService.updateProfileImage(props.state.user.id, imageData)
                        .then(res => {
                            props.click('Home');
                        }).catch(err => {
                            console.log(err);
                        });
                } else {
                    props.click('Home');
                }

            })
            .catch(err => {
                console.log(err.message);
                alert("Failed")
            });
    }

    return (
        <div className="profile">
            <div className="profileBox">
                <div className="profileBox__header">
                    <img
                        src={user.profileImageUrl !== null ? user.profileImageUrl : null}
                        alt="user"
                        style={{ height: '70px', width: '70px', borderRadius: '50px', margin: '5px auto' }}
                    />
                    <div className="profileBox__imageSelect">
                        <CreateIcon fontSize="small" />
                        <input
                            type="file"
                            onChange={imageHandler}
                            style={{ width: '180px' }}
                        />
                    </div>
                </div>
                <div className="profileBox__body">
                    <input
                        type="text"
                        placeholder="<NAME>"
                        value={name}
                        onChange={nameHandler}
                    />
                    <input
                        type="text"
                        placeholder="Username"
                        value={username}
                        onChange={usernameHandler}
                    />
                    <Button
                        style={{ fontWeight: 'bold' }}
                        variant="contained"
                        color="primary"
                        onClick={formSubmitHandler}
                    >
                        Save
                    </Button>
                </div>

            </div>
        </div>
    );
};

export default withAuth(Profile);<file_sep>/twitter-frontend/src/components/SideBar/SideBar.js
import React from 'react';
import './SideBar.css';
import TwitterIcon from "@material-ui/icons/Twitter";
import SidebarOptions from '../SidebarOptions/SidebarOptions';
import HomeIcon from "@material-ui/icons/Home";
import PermIdentityIcon from "@material-ui/icons/PermIdentity";

const SideBar = (props) => {
    return (
        <div className="sidebar">
            <TwitterIcon className="sidebar__twitterIcon" />
            <SidebarOptions active={props.type === 'Home' ? true : false} Icon={HomeIcon} text="Home" click={() => props.click('Home')} />
            <SidebarOptions active={props.type === 'Profile' ? true : false} Icon={PermIdentityIcon} text="Profile" click={() => props.click('Profile')} />
        </div>
    );

};

export default SideBar;<file_sep>/twitter-backend/app/models/index.js
const dbConfig = require('../config/db.config');

const Sequelize = require('sequelize');

const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
    host: dbConfig.HOST,
    dialect: dbConfig.dialect,
    operatorAliases: false,
    pool: {
        max: dbConfig.pool.max,
        min: dbConfig.pool.min,
        acquire: dbConfig.pool.acquire,
        idle: dbConfig.pool.idle
    }
});

const db = {};

db.Sequelize = Sequelize;
db.sequelize = sequelize;

db.tweet = require('./tweets.model')(sequelize, Sequelize);
db.comment = require('./comments.model')(sequelize, Sequelize);
db.user = require('./auth.model')(sequelize, Sequelize);
db.like = require('./likes.controller')(sequelize, Sequelize);


db.user.hasMany(db.tweet);
db.tweet.belongsTo(db.user, { constraints: true, onDelete: 'CASCADE' });

db.tweet.hasMany(db.like);
db.tweet.hasMany(db.comment);

db.like.belongsTo(db.tweet, { constraints: true, onDelete: 'CASCADE' });
db.comment.belongsTo(db.tweet, { constraints: true, onDelete: 'CASCADE' });

db.user.hasMany(db.like);
db.user.hasMany(db.comment);

db.like.belongsTo(db.user, { constraints: true, onDelete: 'CASCADE' });
db.comment.belongsTo(db.user, { constraints: true, onDelete: 'CASCADE' });

module.exports = db;<file_sep>/twitter-frontend/src/components/Feed/Feed.js
import React, { useState, useEffect } from 'react';
import './Feed.css';
import Tweet from '../Tweet/Tweet';
import TweetBox from '../TweetBox/TweetBox';
import TweetService from '../../Services/TweetService';
import { SET_ISUPDATING } from '../../store/actionTypes';
import { withAuth } from '../../twitter-context';

const Feed = (props) => {
    const [tweets, setTweets] = useState([]);

    useEffect(() => {
        async function fetchTweets() {
            const res = await TweetService.getAllTweets();
            setTweets(res);
        }
        fetchTweets();
    }, [setTweets])

    const getTweets = async () => {
        const res = await TweetService.getAllTweets();
        setTweets(res);
        props.dispatch({ type: SET_ISUPDATING });
    }

    const iconClickHandler = async (type, id) => {
        if (type === 'Like') {
            TweetService.likeTweet(props.state.user.id, id)
                .then(res => {
                    getTweets();
                })
                .catch(err => {
                    console.log(err);
                })
        } else if (type === 'Retweet') {
            if (window.confirm('Share it?')) {
                const res = await TweetService.getTweetById(id);
                const filename = res.imageUrl.split('/')[3]
                const userId = props.state.user.id;
                const data = {
                    description: res.description,
                    imageUrl: res.imageUrl === null ? '' : filename
                }
                TweetService.retweet(data, userId)
                    .then(res => {
                        console.log(res);
                    })
                    .catch(err => {
                        console.log(err);
                    })
            }

        } else {

        }

    }

    const arr = tweets.map((tweet, index) => {
        const like = tweet.likes.filter(ob => ob.userId === props.state.user.id);
        return <Tweet key={index} tweet={tweet} onIconClick={(type) => iconClickHandler(type, tweet.id)} liked={like.length !== 0 ? true : false} />
    })
    return (

        <div className="feed">
            <div className="feed_header">
                <h2>Home</h2>
            </div>
            <TweetBox getTweets={getTweets} />
            {arr}
        </div >
    );
};

export default withAuth(Feed);<file_sep>/twitter-frontend/src/components/Home/Home.js
import React, { useState } from 'react';
import SideBar from '../SideBar/SideBar';
import Feed from '../Feed/Feed';
import Widgets from '../Widgets/Widgets';
import Profile from '../Auth/Profile/Profile';
import './Home.css';
const Home = (props) => {
    const [type, setType] = useState('Home');

    const sidebarClickHandler = (type) => {
        setType(type);
    };

    return (
        <div className="home">
            <SideBar type={type} click={(type) => sidebarClickHandler(type)} />
            { type === 'Home' && <Feed />}
            { type === 'Profile' && <Profile click={(type) => sidebarClickHandler(type)}/>}
            <Widgets />
        </div>
    );
};

export default Home;<file_sep>/twitter-frontend/src/components/Auth/Auth.js
import React from 'react';
import Login from '../Login/Login';
import { CardActionArea, CardMedia, Box } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles'
const useStyles = makeStyles({
    auth: {
        maxWidth: '50vw',
        maxHeight: '48vw'
    },
    media: {
        height: '48vw',
        width: '50vw'
    },
    login: {
        width: '50vw'
    }
});

const Auth = (props) => {

    const classes = useStyles();
    const src = require('../../assets/twitter1.jpg').default;
    return (
        <Box className={classes.auth} display="flex" flexDirection="row">
            <CardActionArea>
                <CardMedia
                    image={src}
                    className={classes.media}
                    title="Twitter Image"
                />
            </CardActionArea>
            <Box className={classes.login}>
                <Login />
            </Box>
        </Box>
    );
};

export default Auth;<file_sep>/twitter-frontend/src/Services/TweetService.js
import axios from '../axios';

const TweetService = {
    postTweet: (tweetData, userId) => {
        return axios.post(`/post-tweet/${userId}`, tweetData)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },
    getAllTweets: () => {
        return axios.get('/get-tweets')
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },
    likeTweet: (userId, tweetId) => {
        return axios.get(`/like-tweet/${userId}/${tweetId}`)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },

    uploadTweetImage: (imageData, tweetId) => {
        return axios.post(`/upload-image/${tweetId}`, imageData)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },
    retweet: (tweetData, userId) => {
        return axios.post(`/retweet/${userId}`, tweetData)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },

    getTweetById: (tweetId) => {
        return axios.get(`/tweet/${tweetId}`)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },
    postComment: (data, userId, tweetId) => {
        return axios.post(`/post-comment/${userId}/${tweetId}`, data)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    }
};

export default TweetService;<file_sep>/twitter-frontend/src/Services/AuthService.js
import axios from '../axios';

const AuthService = {
    login: (authData) => {
        return axios.post("/login", authData)
            .then(({ data }) => {
                setHeaderAndStorage(data);
                return data.user;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });

    },

    signup: (authData) => {
        return axios.post("/signup", authData)
            .then(({ data }) => {
                setHeaderAndStorage(data);
                return data.user;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },

    editProfile: (userId, profileData) => {
        return axios.post(`/edit-profile/${userId}`, profileData)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },
    updateProfileImage: (userId, profileImageData) => {
        return axios.post(`/upload-profile-image/${userId}`, profileImageData)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    },
    getUserProfile: (userId) => {
        return axios.get(`/get-user/${userId}`)
            .then(({ data }) => {
                return data;
            })
            .catch(err => {
                throw new Error(err.response.data.message)
            });
    }
};

const setHeaderAndStorage = ({ user, token }) => {
    axios.defaults.headers['Authorization'] = `Bearer ${token}`;
    localStorage.setItem('user', JSON.stringify(user));
    localStorage.setItem('token', token);
}

export default AuthService;<file_sep>/twitter-backend/index.js
const express = require('express');
const cors = require('cors');
const PORT = require('./app/config/app').appPort;

const app = express();

app.use(cors({
    'allowedHeaders': ['sessionId', 'Content-Type', 'Origin', 'Authorization'],
    'credentials': true,
    'origin': 'http://localhost:3000',
    'methods': 'GET, HEAD, PUT,POST,DELETE'
}));

app.use(express.json());

app.use(express.urlencoded({ extended: true }));

app.use(express.static(__dirname + '/uploads'));

require("./app/routes/routes")(app);

const server = require('http').createServer(app);

const db = require('./app/models');

db.sequelize.sync({ force: false}).then(() => {
    server.listen(PORT, () => {
        console.log(`Server is running on port ${PORT}`);
    });
})


<file_sep>/twitter-frontend/src/components/Login/Login.js
import React, { useState } from 'react';
import { withRouter } from 'react-router';
import { Typography, Avatar, TextField, Button, Box } from '@material-ui/core';
import { withAuth } from '../../twitter-context';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import { makeStyles } from '@material-ui/styles';
import AuthService from '../../Services/AuthService';
import { AUTH_SUCCESS, AUTH_FAIL } from '../../store/actionTypes';

const styles = makeStyles((theme) => ({
    paper: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        backgroundColor: 'white',
        width: '50vw',
        paddingBottom: '250px',
        paddingTop: '100px'
    },
    avatar: {
        fontWeight: 'bold'
    },
    form: {
        width: '90%',
    }
}));


const Auth = (props) => {

    const classes = styles();

    const [email, setEmail] = useState('<EMAIL>');
    const [password, setPassword] = useState('<PASSWORD>');
    const [isSignup, setIsSignUp] = useState(false);

    const formSubmitHandler = (event) => {
        event.preventDefault();
        const data = {
            email: email,
            password: <PASSWORD>
        }
        if (isSignup) {
            AuthService.signup(data)
                .then(res => {
                    props.dispatch({ type: AUTH_SUCCESS, user: res });
                    props.history.push('/home');
                })
                .catch(err => {
                    console.log(err);
                    props.dispatch({type: AUTH_FAIL, error: err.message});
                    alert('Access Denied !!!!');
                })
        } else {
            AuthService.login(data)
                .then(res => {
                    props.dispatch({ type: AUTH_SUCCESS, user: res });
                    props.history.push('/home');
                })
                .catch(err => {
                    console.log(err);
                    props.dispatch({type: AUTH_FAIL, error: err.message});
                    alert('Access Denied !!!!');
                })
        }
    };

    const emailChangeHandler = (event) => {
        setEmail(event.target.value);
    };

    const passwordChangeHandler = (event) => {
        setPassword(event.target.value);
    };

    const authChangeHandler = () => {
        setIsSignUp(!isSignup);
    };

    return (
        <Box className={classes.paper}>
            <Avatar>
                <LockOutlinedIcon />
            </Avatar>
            <Typography style={{ fontWeight: 'bold' }}>
                {isSignup ? "Sign Up" : "Sign In"}
            </Typography>
            <form className={classes.form}>
                <TextField
                    variant="outlined"
                    fullWidth
                    margin="normal"
                    placeholder="Enter Email"
                    value={email}
                    onChange={emailChangeHandler}
                />
                <TextField
                    variant="outlined"
                    fullWidth
                    margin="normal"
                    placeholder="Enter Password"
                    value={password}
                    onChange={passwordChangeHandler}
                />
                <Button
                    type="submit"
                    onClick={formSubmitHandler}
                    fullWidth
                    variant="contained"
                    color="primary"
                    style={{ fontWeight: 'bold', backgroundColor: '#0DD8F9' }}
                >
                    {isSignup ? "Sign Up" : "Sign In"}
                </Button>
                <Button
                    onClick={authChangeHandler}
                    fullWidth
                    variant="contained"
                    style={{ marginTop: '10px', fontWeight: 'bold' }}
                >
                    {isSignup ? "Already have an account ? Sign In" : "Don't have an account? Sign Up"}
                </Button>
            </form>

        </Box>
    );
};

export default withRouter(withAuth(Auth));<file_sep>/twitter-backend/app/controllers/comment.controller.js
const db = require('../models');
const Comment = db.comment;
const Tweet = db.tweet;

exports.postComment = async (req, res) => {
    const { tweetId } = req.params;
    const { desc } = req.body;

    try {
        const postedComment = await req.user.createComment({ description: desc, tweetId: tweetId });
        res.send(postedComment);

    } catch (e) {
        res.status(500).json({ message: e.message });
    }
};

<file_sep>/twitter-backend/app/routes/routes.js
const express = require('express');
module.exports = app => {
    var router = require("express").Router();
    const { login, signup, editProfile, uploadProfileImage, getUserProfile } = require('../controllers/auth.controller');
    const { findTweetById, postTweet, getTweets, uploadImage, reTweet } = require('../controllers/tweet.controller');
    const { likeTweet } = require('../controllers/likes.controller');
    const { postComment } = require(`../controllers/comment.controller`);
    const { authenticate, getCurrentUser } = require('../middleware/auth');
    const { imageUpload } = require('../middleware/imageUpload');

    router.post('/login', login);
    router.post('/signup', signup);
    router.post('/post-tweet/:userId', [authenticate, getCurrentUser], postTweet);
    router.get('/get-tweets', [authenticate], getTweets);
    router.get('/like-tweet/:userId/:tweetId', [authenticate, getCurrentUser], likeTweet);
    router.post('/upload-profile-image/:userId', [authenticate, imageUpload], uploadProfileImage);
    router.post('/upload-image/:tweetId', [authenticate, imageUpload], uploadImage);
    router.get('/get-user/:userId', [authenticate, getCurrentUser], getUserProfile);
    router.post('/retweet/:userId', [authenticate, getCurrentUser], reTweet);
    router.get('/tweet/:tweetId', [authenticate], findTweetById);
    router.post('/post-comment/:userId/:tweetId', [authenticate, getCurrentUser], postComment);
    router.post('/edit-profile/:userId', [authenticate], editProfile);

    app.use('/', router);

};
