Affichage post

post
Noan07 2 years ago
parent b7982ff0f0
commit 29c32b2203

@ -0,0 +1 @@
/node_modules

@ -9,12 +9,14 @@
name="description"
content="Web site created using create-react-app"
/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" />
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> -->
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.

@ -3,7 +3,7 @@ import { UidContext } from "./components/AppContext";
import { useEffect, useState } from "react";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import { setPictureData } from "./feature/pictures.slice";
import { getUser } from "./actions/user.actions";
//useeffect controle le token de l'utilisateur
//les crochets dans le use effect permette de pas lancer la fonction à l'infini
@ -22,11 +22,12 @@ function App() {
.catch((err) => console.log("No Token"));
};
fetchToken();
if (uid){
if(uid) dispatch(getUser(uid));
/*if (uid){
axios
.get(`${process.env.REACT_APP_API_URL}api/user/${uid}`)
.then((res) => dispatch(setPictureData(res.data)))
};
};*/
}, [uid, dispatch]);

@ -0,0 +1,134 @@
import axios from "axios";
import { setPostError } from "../reducers/error.reducer";
import { setPostData, setPostLikeData, setPostUnLikeData } from "../reducers/post.reducer";
export const getPosts = () => {
return (dispatch) => {
return axios
.get(`${process.env.REACT_APP_API_URL}api/post/`)
.then((res) => {
dispatch(setPostData(res.data));
})
.catch((err) => console.log(err))
}
}
export const likePost = (postId, userId) => {
return (dispatch) => {
return axios({
method: 'patch',
url: `${process.env.REACT_APP_API_URL}api/post/like-post/` + postId,
data: { id: userId },
})
.then((res) => {
dispatch(setPostLikeData({payload:{ postId, userId }}));
})
.catch((err) => console.log(err))
}
}
export const unlikePost = (postId, userId) => {
return (dispatch) => {
return axios({
method: 'patch',
url: `${process.env.REACT_APP_API_URL}api/post/unlike-post/` + postId,
data: { id: userId },
})
.then((res) => {
//dispatch(setPostUnLikeData({ postId, userId }));
})
.catch((err) => console.log(err))
}
}
export const addPost = (data) => {
console.log(data,"test")
return (dispatch) => {
return axios
.post(`${process.env.REACT_APP_API_URL}api/post/`, data)
.then((res) => {
console.log(data,"test");
// if (res.data.errors) {
// dispatch(setPostError({payload: res.data.errors }));
// } else {
// dispatch(setPostError({payload: "" }));
// }
});
};
};
// // export const updatePost = (postId, message) => {
// // return (dispatch) => {
// // return axios({
// // method: "put",
// // url: `${process.env.REACT_APP_API_URL}api/post/${postId}`,
// // data: { message },
// // })
// // .then((res) => {
// // dispatch({ type: UPDATE_POST, payload: { message, postId } });
// // })
// // .catch((err) => console.log(err));
// // };
// // };
// export const deletePost = (postId) => {
// return (dispatch) => {
// return axios({
// method: "delete",
// url: `${process.env.REACT_APP_API_URL}api/post/${postId}`,
// })
// .then((res) => {
// dispatch({ type: DELETE_POST, payload: { postId } });
// })
// .catch((err) => console.log(err));
// };
// };
// export const addComment = (postId, commenterId, text, commenterPseudo) => {
// return (dispatch) => {
// return axios({
// method: "patch",
// url: `${process.env.REACT_APP_API_URL}api/post/comment-post/${postId}`,
// data: { commenterId, text, commenterPseudo },
// })
// .then((res) => {
// dispatch({ type: ADD_COMMENT, payload: { postId } });
// })
// .catch((err) => console.log(err));
// };
// };
// export const editComment = (postId, commentId, text) => {
// return (dispatch) => {
// return axios({
// method: "patch",
// url: `${process.env.REACT_APP_API_URL}api/post/edit-comment-post/${postId}`,
// data: { commentId, text },
// })
// .then((res) => {
// dispatch({ type: EDIT_COMMENT, payload: { postId, commentId, text } });
// })
// .catch((err) => console.log(err));
// };
// };
// export const deleteComment = (postId, commentId) => {
// return (dispatch) => {
// return axios({
// method: "patch",
// url: `${process.env.REACT_APP_API_URL}api/post/delete-comment-post/${postId}`,
// data: { commentId },
// })
// .then((res) => {
// dispatch({ type: DELETE_COMMENT, payload: { postId, commentId } });
// })
// .catch((err) => console.log(err));
// };
// };
// export const getTrends = (sortedArray) => {
// return (dispatch) => {
// dispatch({ type: GET_TRENDS, payload: sortedArray });
// };
// };

@ -0,0 +1,61 @@
import axios from "axios";
import { setUserData, setUserDataImg, setUserToFollowData, setUserToUnFollowData } from "../reducers/user.reducer";
export const getUser = (uid) => {
return (dispatch)=> {
return axios
.get(`${process.env.REACT_APP_API_URL}api/user/${uid}`)
.then((res) => {
dispatch(setUserData(res.data))
})
.catch((err) => console.log(err));
};
};
export const uploadPicture = (data, id) => {
return (dispatch) => {
return axios
.post(`${process.env.REACT_APP_API_URL}api/user/upload`,data)
.then((res) => {
return axios
.get(`${process.env.REACT_APP_API_URL}api/user/${id}`)
.then((res) => {
dispatch(setUserDataImg(res.data.picture));
})
})
.catch((err) => console.log(err));
};
};
export const followUser = (followerId, idToFollow) => {
return async (dispatch) => {
try {
const res = await axios({
method: "patch",
url: `${process.env.REACT_APP_API_URL}api/user/follow/` + followerId,
data: { idToFollow },
});
dispatch(setUserToFollowData({payload: { idToFollow }}));
} catch (err) {
return console.log(err);
}
};
};
export const unFollowUser = (followerId, idToUnFollow) => {
return (dispatch) => {
return axios({
method: "patch",
url: `${process.env.REACT_APP_API_URL}api/user/unfollow/` + followerId,
data: { idToUnFollow },
})
.then((res) => {
dispatch(setUserToUnFollowData({payload: {idToUnFollow}}));
})
.catch((err) => console.log(err));
};
};

@ -0,0 +1,14 @@
import axios from "axios";
import { setUsersData } from "../reducers/users.reducer";
export const getUsers = () => {
return (dispatch) => {
return axios
.get(`${process.env.REACT_APP_API_URL}api/user`)
.then((res) => {
dispatch(setUsersData(res.data));
})
.catch((err) => console.log(err));
};
};

@ -1,18 +1,115 @@
import React, {useState} from 'react';
import NewPoste from "./NewPoste";
import axios from 'axios';
import React, {useContext, useState} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { addPost, getPosts } from '../actions/post.actions';
import { UidContext } from './AppContext';
const AjoutLien = () => {
const uid = useContext(UidContext);
const userData = useSelector((state) => state.user.user);
const [displayAdd, setDisplayAdd] = useState(false);
const [lien, setLien] = useState("");
const [description, setDescription] = useState("");
const dispatch = useDispatch();
const handleModals = (e) => {
<NewPoste/>
const handlePost = async () => {
if(isValidUrl(lien))
if ((description || lien) ){
putData();
dispatch(getPosts());
cancelPost();
setDisplayAdd(false);
window.location.reload();
}else {
alert("Veuillez compléter tous les champs.")
}
else{
alert("Ce n'est pas lien!")
}
};
const cancelPost = () => {
setDescription("");
setLien("");
};
const putData = async() => {
axios
.post(`${process.env.REACT_APP_API_URL}api/post/`, { postedId: userData._id, message: description, lien: lien}
)
.then((res) => {
// if (res.data.errors) {
// dispatch(setPostError({payload: res.data.errors }));
// } else {
// dispatch(setPostError({payload: "" }));
// }
})};
const isValidUrl = (url) => {
try {
new URL(url);
return true;
}catch {
return false;
}
}
return (
<div className='ajoutLien'>
<button className="bouttonAjoutLien" id="display" type='bouton' onClick={handleModals}>
Poster un lien
</button>
</div>
<>
<div className='ajoutLien'>
{uid === null &&
<button className="bouttonAjoutLien" id="display" type='bouton'>
Se connecter
</button>
}
{uid !== null &&
<button className='button-add-link' onClick={() => setDisplayAdd(true)} >
Poster un lien
</button>
}
</div>
{displayAdd && (
<div className="popup-profil-container">
<div className="modal">
<h3>Ajout d'un lien</h3>
<span className="cross" onClick={() => setDisplayAdd(false)}>
&#10005;
</span>
<ul>
<form className="newPoste-form-container">
<div className="newPoste-input">
<input type="text"
placeholder="Votre lien!"
onChange={(e) => setLien(e.target.value)}
value={lien}
required />
</div>
<div className="newPoste-input">
<textarea type="text"
row="250"
placeholder="Description de votre post"
onChange={(e) => setDescription(e.target.value)}
value={description}
required
/>
</div>
<div id="newPoste-buttonForm">
<div>
<button onClick={() => setDisplayAdd(false)} className="newPoste-btn-cancel" >retour</button>
</div>
<div>
<button onClick={handlePost} className="newPoste-btn">Poster</button>
</div>
</div>
</form>
</ul>
</div>
</div>
)}
</>
);
};

@ -1,9 +1,11 @@
import React from 'react';
import PP from "../../assets/img/unknown.png";
import { NavLink } from "react-router-dom";
import { useSelector } from 'react-redux';
const ConfigurationDuProfil = ()=>{
const userData = useSelector((state) => state.userReducer)
return(
<div>

@ -5,18 +5,17 @@ import { NavLink } from "react-router-dom";
const PolitiqueDeConfidentialite =()=>{
return(
<div>
Cette politique de confidentialité sapplique au site : Designed By GG.
La présente politique de confidentialité a pour but dexposer aux utilisateurs du site :
Cette politique de confidentialité sapplique au site : Designed By GG.
La présente politique de confidentialité a pour but dexposer aux utilisateurs du site :
La manière dont sont collectées et traitées leurs données à caractère
personnel. Doivent être considérées comme données personnelles toutes les données étant susceptibles didentifier un utilisateur. Il sagit notamment du prénom et du nom, de lâge, de ladresse postale, ladresse mail, la localisation de lutilisateur ou encore son adresse IP ;
Quels sont les droits des utilisateurs concernant ces données ;
Qui est responsable du traitement des données à caractère personnes
collectées et traitées ;
A qui ces données sont transmises ;
Eventuellement, la politique du site en matière de fichiers cookies
La manière dont sont collectées et traitées leurs données à caractère
personnel. Doivent être considérées comme données personnelles toutes les données étant susceptibles didentifier un utilisateur. Il sagit notamment du prénom et du nom, de lâge, de ladresse postale, ladresse mail, la localisation de lutilisateur ou encore son adresse IP ;
Quels sont les droits des utilisateurs concernant ces données ;
Qui est responsable du traitement des données à caractère personnes
collectées et traitées ;
A qui ces données sont transmises ;
Eventuellement, la politique du site en matière de fichiers cookies
</div>
)
}

@ -1,18 +1,66 @@
import { NavLink } from "react-router-dom";
import { UidContext } from "./AppContext";
import React, { useContext } from 'react';
import { useSelector } from "react-redux";
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PP from "../assets/img/unknown.png";
import cookie from 'js-cookie';
import axios from "axios";
import Logo from "../assets/img/logo.png";
import Configuration from "./../pages/Configuration";
import { addPost, getPosts } from "../actions/post.actions";
const Navbar = () => {
const uid = useContext( UidContext );
const userData = useSelector((state) => state.pictures.pictures);
const userData = useSelector((state) => state.user.user);
const [displayAdd, setDisplayAdd] = useState(false);
const [lien, setLien] = useState("");
const [description, setDescription] = useState("");
const dispatch = useDispatch();
const handlePost = () => {
if ((description || lien) ){
const data = new FormData();
data.append('postedId', userData._id);
data.append('message', description);
data.append('lien', lien);
console.log(data);
putData(data);
console.log(data);
cancelPost();
setDisplayAdd(false)
}else {
alert("Veuillez compléter tous les champs.")
}
};
const putData = async(data) => {
axios
.post(`${process.env.REACT_APP_API_URL}api/post/`, { postedId: userData._id, message: description, lien: lien}
)
.then((res) => {
console.log(data,"test");
// if (res.data.errors) {
// dispatch(setPostError({payload: res.data.errors }));
// } else {
// dispatch(setPostError({payload: "" }));
// }
})};
const cancelPost = () => {
setDescription("");
setLien("");
};
const isValidUrl = (url) => {
try {
new URL(url);
return true;
}catch {
return false;
}
}
const removeCookie = (key) => {
if(window !== "undefined"){
cookie.remove(key, {expires: 1} );
@ -34,68 +82,116 @@ const Navbar = () => {
return (
<div>
{uid ? (
<header>
<nav>
<ul className="partie-gauche-nav ul-navBar">
<li className="logo">
<img src={Logo} height="30px" alt='Logo'/>
</li>
</ul>
<ul className="ul-navBar">
<li>
<NavLink to="/Discover" className={((nav) => (nav.isActive ? "nav-active b nav-active-tendances-decouvrir" : "b"))}>
<span>Découvrir</span>
</NavLink>
</li>
<nav className="navbar">
<ul className="navbar-menu">
<li className="navbar-item logo">
<NavLink to="/Home" className="navbar-link">
<span className="navbar-title">Favor</span>
<i className="fas fa-chevron-right navbar-icon"></i>
</NavLink>
</li>
<li className="navbar-item">
<NavLink to="/Home" className="navbar-link">
<i className="fas fa-home navbar-icon"></i>
<span className="navbar-title">Home</span>
</NavLink>
</li>
<li className="navbar-item">
<NavLink to="/Discover" className="navbar-link">
<i className="fas fa-bullseye navbar-icon"></i>
<span className="navbar-title">Découvrir</span>
</NavLink>
</li>
<li className="navbar-item">
<NavLink to="/Trends" className="navbar-link">
<i className="fas fa-bell navbar-icon"></i>
<span className="navbar-title">Notifications</span>
</NavLink>
</li>
<li className="navbar-item">
<NavLink to="/Profil" className="navbar-link">
<i className="fas fa-user navbar-icon"></i>
<span className="navbar-title">Me</span>
</NavLink>
</li>
<li className="navbar-item">
<a onClick={() => setDisplayAdd(true)} className="navbar-link">
<i className="fas fa-cog navbar-icon"></i>
<span className="navbar-title">Paramètre</span>
</a>
</li>
<li className="navbar-item">
<a onClick={logout} className="navbar-link">
<i className="fas fa-sign-out-alt navbar-icon"></i>
<span className="navbar-title">Déconnexion</span>
</a>
</li>
</ul>
</nav>
// <header>
// <nav>
// <ul className="partie-gauche-nav ul-navBar">
// <li className="logo">
// <img src={Logo} height="30px" alt='Logo'/>
// </li>
// </ul>
// <ul className="ul-navBar">
// <li>
// <NavLink to="/Discover" className={((nav) => (nav.isActive ? "nav-active b nav-active-tendances-decouvrir" : "b"))}>
// <span>Découvrir</span>
// </NavLink>
// </li>
<li>
<NavLink to="/Home" className={((nav) => (nav.isActive ? "nav-active b nav-active-menu" : "b"))}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" className="svg-home">
<path stroke="black" fill='black' d="M256 73.825a182.18 182.18 0 0 0-182.18 182.18c0 100.617 81.567 182.17 182.18 182.17a182.175 182.175 0 1 0 0-364.35zm76.636 161.579h-12.037v91.503a18.908 18.908 0 0 1-18.896 18.904h-26.78v-53.56a6.299 6.299 0 0 0-6.297-6.294H232.4a6.3 6.3 0 0 0-6.302 6.294v53.56h-26.771a18.91 18.91 0 0 1-18.906-18.904v-91.503h-11.97a7.879 7.879 0 0 1-5.071-13.905l82.055-69.039a7.89 7.89 0 0 1 10.142 0l81.479 68.547a7.88 7.88 0 0 1-4.421 14.396z" data-name="Home"/>
</svg>
</NavLink>
</li>
// <li>
// <NavLink to="/Home" className={((nav) => (nav.isActive ? "nav-active b nav-active-menu" : "b"))}>
// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" className="svg-home">
// <path stroke="black" fill='black' d="M256 73.825a182.18 182.18 0 0 0-182.18 182.18c0 100.617 81.567 182.17 182.18 182.17a182.175 182.175 0 1 0 0-364.35zm76.636 161.579h-12.037v91.503a18.908 18.908 0 0 1-18.896 18.904h-26.78v-53.56a6.299 6.299 0 0 0-6.297-6.294H232.4a6.3 6.3 0 0 0-6.302 6.294v53.56h-26.771a18.91 18.91 0 0 1-18.906-18.904v-91.503h-11.97a7.879 7.879 0 0 1-5.071-13.905l82.055-69.039a7.89 7.89 0 0 1 10.142 0l81.479 68.547a7.88 7.88 0 0 1-4.421 14.396z" data-name="Home"/>
// </svg>
// </NavLink>
// </li>
<li>
<NavLink to="/Trends" className={((nav) => (nav.isActive ? "nav-active b nav-active-tendances-decouvrir" : "b"))}>
<span>Tendances</span>
</NavLink>
</li>
</ul>
<ul className="partie-droit-nav ul-navBar">
<li >
<NavLink to="/Profil" className='b'>
{ (userData==null) ? (<span></span>):(<span>{userData.pseudo}</span>)}
</NavLink>
</li>
// <li>
// <NavLink to="/Trends" className={((nav) => (nav.isActive ? "nav-active b nav-active-tendances-decouvrir" : "b"))}>
// <span>Tendances</span>
// </NavLink>
// </li>
// </ul>
// <ul className="partie-droit-nav ul-navBar">
// <li >
// <NavLink to="/Profil" className='b'>
// { (userData==null) ? (<span></span>):(<span>{userData.pseudo}</span>)}
// </NavLink>
// </li>
<li>
<NavLink to="/Profil" className='b'>
<img src={PP} alt='Logo' className="img-profile"/>
</NavLink>
</li>
// <li>
// <NavLink to="/Profil" className='b'>
// <img src={PP} alt='Logo' className="img-profile"/>
// </NavLink>
// </li>
<li>
<div className='b more'>
<div className="menu-deroulant">
<svg xmlns="http://www.w3.org/2000/svg" className="svg-param" viewBox="0 0 256 256">
<path d="M128,24A104,104,0,1,0,232,128,104.11791,104.11791,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.09957,88.09957,0,0,1,128,216Zm12-88a12,12,0,1,1-12-12A12.01375,12.01375,0,0,1,140,128Zm48,0a12,12,0,1,1-12-12A12.01375,12.01375,0,0,1,188,128Zm-96,0a12,12,0,1,1-12-12A12.01375,12.01375,0,0,1,92,128Z"/>
</svg>
</div>
<div className="more-menu">
<NavLink className='element-menu-deroulant' to="/Configuration">Configurations</NavLink>
<NavLink className='element-menu-deroulant'>Élement 2</NavLink>
<NavLink className='element-menu-deroulant' onClick={logout}>Élement 3</NavLink>
</div>
</div>
</li>
// <li>
// <div className='b more'>
// <div className="menu-deroulant">
// <svg xmlns="http://www.w3.org/2000/svg" className="svg-param" viewBox="0 0 256 256">
// <path d="M128,24A104,104,0,1,0,232,128,104.11791,104.11791,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.09957,88.09957,0,0,1,128,216Zm12-88a12,12,0,1,1-12-12A12.01375,12.01375,0,0,1,140,128Zm48,0a12,12,0,1,1-12-12A12.01375,12.01375,0,0,1,188,128Zm-96,0a12,12,0,1,1-12-12A12.01375,12.01375,0,0,1,92,128Z"/>
// </svg>
// </div>
// <div className="more-menu">
// <NavLink className='element-menu-deroulant' to="/Configuration">Configurations</NavLink>
// <a onClick={() => setDisplayAdd(true)} className='element-menu-deroulant'>Élement 2</a>
// <NavLink className='element-menu-deroulant' onClick={logout}>Élement 3</NavLink>
// </div>
// </div>
// </li>
</ul>
</nav>
</header>
// </ul>
// </nav>
// </header>
) : (<div> </div>
)}
</div>
);
};

@ -3,26 +3,28 @@ import React, { useState } from 'react';
//e.preventDefaul(); pour ne pas recharcher la page
const NewPoste = (props) => {
const NewPoste = ({ post }) => {
return (
<div>
<div class="newPoste-form-popup" id="newPoste-popupFormulaireCreationPoste">
<form action="/action_page.php" class="newPoste-form-container">
<div class="newPoste-input">
<label for="lien">Lien:</label>
<div className="newPoste-form-popup" id="newPoste-popupFormulaireCreationPoste">
<form action="/action_page.php" className="newPoste-form-container">
<div className="newPoste-input">
{/* <label for="lien">Lien:</label> */}
<input type="text" name="lien" required />
</div>
<div class="newPoste-input">
<label for="description">Description:</label>
<div className="newPoste-input">
{/* <label for="description">Description:</label> */}
<textarea name="description" row="250"></textarea>
</div>
<div id="newPoste-buttonForm">
<div>
<button type="button" class="newPoste-btn-cancel" onclick="closeForm()">retour</button>
{/* <button type="button" className="newPoste-btn-cancel" onclick="closeForm()">retour</button> */}
<button type="button" className="newPoste-btn-cancel" >retour</button>
</div>
<div>
<button type="submit" class="newPoste-btn">Poster</button>
<button type="submit" className="newPoste-btn">Poster</button>
</div>
</div>
</form>

@ -0,0 +1,40 @@
import React, { useContext, useEffect, useState } from 'react';
import { UidContext } from "../AppContext";
import { useDispatch } from 'react-redux';
import Coeur from '../../assets/img/coeurs.png';
import CoeurPlein from '../../assets/img/coeursPlein.png';
import { likePost, unlikePost } from '../../actions/post.actions';
const ButtonLike = ( { post } ) => {
const [liked, setLiked] = useState(false);
const uid = useContext(UidContext);
const dispatch = useDispatch();
const like = () => {
dispatch(likePost(post._id, uid))
setLiked(true);
};
const unlike = () => {
dispatch(unlikePost(post._id, uid))
setLiked(false);
};
useEffect(() => {
if (post.likers.includes(uid)) setLiked(true);
else setLiked(false);
}, [uid, post.likers, liked]);
return (
<div>
{uid && liked === false && (
<img src={Coeur} onClick={like} alt="like" />
)}
{uid && liked && (
<img src={CoeurPlein} onClick={unlike} alt="unlike" />
)}
</div>
);
};
export default ButtonLike;

@ -0,0 +1,33 @@
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getPosts } from '../../actions/post.actions';
import NewPoste from '../NewPoste';
import { isEmpty } from "../Utils";
import Post from './Post';
const DisplayPosts = () => {
const [loadPost, setLoadPost] = useState(true);
const [count , setCount] = useState(5);
const dispatch = useDispatch();
const postsData = useSelector((state) => state.post.post);
useEffect(() => {
if (loadPost) {
dispatch(getPosts());
setLoadPost(false);
}
}, [loadPost,dispatch])
return (
<div>
<ul>
{!isEmpty(postsData[0]) &&
postsData.map((post) => {
return <Post post={post} key={post._id}/>
})}
</ul>
</div>
);
};
export default DisplayPosts;

@ -0,0 +1,76 @@
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import FollowHandler from '../UserProfil/FollowHandler';
import { dateParser, isEmpty } from '../Utils';
import ButtonLike from './ButtonLike';
//e.preventDefaul(); pour ne pas recharcher la page
const Post = ( { post } ) => {
const [isLoading, setIsLoading] = useState(true);
const usersData = useSelector((state) => state.users.users);
const userData = useSelector((state) => state.user.user);
useEffect(() => {
!isEmpty(usersData[0]) && setIsLoading(false)
})
return (
<li className='conteneur_postes' key={post._id}>
{isLoading ? (
<i className='fas fa-spinner fa-spin'></i>
):(
<div id="cadrePoste">
<div id="postContenu">
<div id="hautPoste">
<div id="cadreInfoPoste">
<img id="PhotoProfile" alt="" src={
!isEmpty(usersData[0]) &&
usersData.map((user) => {
if (user._id === post.postedId) return user.picture;
else return null;
}).join('')
}/>
<h6 id="NomProfile">
{
!isEmpty(usersData[0]) &&
usersData.map((user) => {
if(user._id === post.postedId) return user.pseudo;
else return null;
}).join('')
}
</h6>
{/* {post.postedId !== userData._id &&
(<FollowHandler idToFollow={post.postedId} type={'suggest'}/>)} */}
</div>
<div>{dateParser(post.createdAt)}</div>
</div>
<div id="contenuePoste">
/*utliser leakpreview*/
</div>
<div id="contenuePoste">
<p>{post.message}</p>
</div>
<div id="basPoste">
<div id="like">
<ButtonLike post={post}/>
{/* <img src="coeurs.png"/> */}
<div>{post.likers.length}</div>
</div>
<div id="commentaire">
<img src="commentaire.png"/>
<div>{post.comments.length}</div>
</div>
</div>
</div>
</div>) }
</li>
);
};
//ce qui écrit dans le input est récuperé par le state
export default Post;

@ -1,36 +0,0 @@
import axios from 'axios';
import React, { useState } from 'react';
//e.preventDefaul(); pour ne pas recharcher la page
const Poste = (props) => {
return (
<div id="cadrePoste">
<div id="hautPoste">
<div id="cadreInfoPoste">
<img id="PhotoProfile" src="https://i.pinimg.com/originals/5c/a0/cf/5ca0cf624647dced23ec5329ed0cde6f.png"/>
<h6 id="NomProfile">Lena 1er</h6>
</div>
<div>6 jours</div>
</div>
<div id="contenuePoste">
/*utliser leakpreview*/
</div>
<div id="basPoste">
<div id="like">
<img src="coeurs.png"/>
<div>10k</div>
</div>
<div id="commentaire">
<img src="commentaire.png"/>
<div>10k</div>
</div>
</div>
</div>
);
};
//ce qui écrit dans le input est récuperé par le state
export default Poste;

@ -12,7 +12,7 @@ const index = () => {
<BrowserRouter>
<Routes>
<Route path="/" element={<HomeNavigation/>} />
<Route path="/home" element={<Home/>} />
<Route path="/h" element={<Home/>} />
<Route path="/trends" element={<Trends/>} />
<Route path="/discover" element={<Discover/>} />
<Route path="*" element={<HomeNavigation/>} />

@ -0,0 +1,71 @@
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { followUser, unFollowUser } from '../../actions/user.actions';
import { setUserToFollowData, setUserToUnFollowData } from '../../reducers/user.reducer';
import { isEmpty } from '../Utils';
const FollowHandler = ( { idToFollow , type } ) => {
const userData = useSelector((state) => state.user.user);
const [isFollowed, setIsFollowed] = useState(false);
const dispatch = useDispatch();
const handleFollow = () => {
axios.patch(`${process.env.REACT_APP_API_URL}api/user/follow/` + userData._id, {params: {idToFollow: idToFollow}} )
.then((res) => {
//dispatch(setUserToFollowData({payload: {idToFollow}}));
})
.catch((err) => console.log(err));
setIsFollowed(false);
};
const handleUnFollow = () => {
axios.patch(`${process.env.REACT_APP_API_URL}api/user/unfollow/` + userData._id, {params: {idToFollow: idToFollow}} )
.then((res) => {
// dispatch(setUserToUnFollowData({payload: {idToFollow}}));
})
.catch((err) => console.log(err));
setIsFollowed(false);
};
// const handleFollow = () => {
// dispatch(followUser(userData._id, idToFollow))
// setIsFollowed(true);
// }
// const handleUnFollow = () => {
// dispatch(unFollowUser(userData._id, idToFollow))
// setIsFollowed(false);
// }
useEffect(() => {
if(!isEmpty(userData.following)) {
if(userData.following.includes(idToFollow)) {
setIsFollowed(true);
} else setIsFollowed(false);
}
}, [userData,idToFollow])
return (
<>
{isFollowed && !isEmpty(userData) && (
<span onClick={handleUnFollow}>
{type === "card" && <button className="unfollow-btn">Abonné</button>}
{type === "suggest" && <i className="fas fa-check-circle navbar-icon"></i>}
</span>
)}
{isFollowed === false && !isEmpty(userData) && (
<span onClick={handleFollow}>
{type === "card" && <button className="follow-btn">Suivre</button>}
{type === "suggest" && <i className="far fa-check-circle navbar-icon"></i>}
</span>
)}
</>
);
};
export default FollowHandler;

@ -0,0 +1,29 @@
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
const UploadImage = () => {
const [file,setFile] =useState();
//pour envoyer l'image
const dispatch = useDispatch();
const userData = useSelector((state) => state.user.user)
const handlePicture = (e) => {
e.preventDefault();
const data = new FormData();
data.append("name", userData.pseudo);
data.append("userId", userData._id);
data.append("file",file);
dispatch(uploadPicture(data, userData._id));
}
return (
<form action="" onSubmit={handlePicture} className="">
<label htmlFor='file'>Changer d'image</label>
<input type="file" id="file" accept='.jpg, .jpeg, .png' onChange={(e) => setFile(e.target.files[0])}/>
<br/>
<input type="submit" value="Send"/>
</form>
);
};
export default UploadImage;

@ -0,0 +1,39 @@
export const isEmpty = (value) => {
return ( value === 'string ' || value === null ||
(typeof value === "object" && Object.keys(value).length === 0) ||
(typeof value === "string" && value.trim().length === 0) );
};
export const dateParser = (num) => {
let options = {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
weekday: "long",
year: "numeric",
month: "short",
day: "numeric",
};
let timestamp = Date.parse(num);
let date = new Date(timestamp).toLocaleDateString("fr-FR", options);
return date.toString();
};
export const timestampParser = (num) => {
let options = {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
weekday: "long",
year: "numeric",
month: "short",
day: "numeric",
};
let date = new Date(num).toLocaleDateString("fr-FR", options);
return date.toString();
}

@ -1,18 +0,0 @@
import { createSlice } from "@reduxjs/toolkit";
export const picturesSlice = createSlice({
name: "pictures",
initialState: {
pictures: null,
},
reducers: {
setPictureData: (state,action) => {
state.pictures = action.payload;
},
},
});
export const {setPictureData} = picturesSlice.actions;
export default picturesSlice.reducer;

@ -3,8 +3,13 @@ import ReactDOM from 'react-dom/client';
import App from './App';
import "./styles/index.scss";
import { Provider } from 'react-redux';
import store from './app/store';
//import store from './app/store';
import store from './reducers/index';
import { getPosts } from './actions/post.actions';
import { getUsers } from './actions/users.actions';
store.dispatch(getUsers());
store.dispatch(getPosts());
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(

@ -1,9 +1,21 @@
import React from 'react';
import AjoutLien from '../components/AjoutLien';
import Navbar from '../components/Navbar';
import DisplayPosts from '../components/Post/DisplayPosts';
const Discover = () => {
return (
<Navbar />
<>
<Navbar />
<main >
<div className='postInMain'>
<DisplayPosts/>
</div>
<div className="RightBar">
<AjoutLien/>
</div>
</main>
</>
);
};

@ -1,12 +1,20 @@
import React from 'react';
import AjoutLien from '../components/AjoutLien';
import DisplayPosts from '../components/Post/DisplayPosts';
import Navbar from '../components/Navbar';
const Home = () => {
return (
<>
<Navbar />
<AjoutLien/>
<main >
<div className='postInMain'>
<DisplayPosts/>
</div>
<div className="RightBar">
<AjoutLien/>
</div>
</main>
</>
);
};

@ -20,8 +20,6 @@ const HomeNavigation = () => {
<Log signin={true} signup={false}/>
</div>
)}
<AjoutLien></AjoutLien>
<NewPoste></NewPoste>
</div>
);
};

@ -1,13 +1,22 @@
import React from 'react';
import { useSelector } from 'react-redux';
import {React, useState} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Navbar from '../components/Navbar';
import { dateParser } from '../components/Utils';
import FollowHandler from '../components/UserProfil/FollowHandler';
const Profil = () => {
const userData = useSelector((state) => state.pictures.pictures);
const userData = useSelector((state) => state.user.user);
const usersData = useSelector((state) => state.users.users);
const dispatch = useDispatch();
const [followingPopup, setFollowingPopup] = useState(false);
const [followerPopup, setFollowerPopup] = useState(false);
return (
<>
<Navbar />
<main>
<div id="bandeauProfil">
<h1 className='affichage-date'>Compte créé le : {dateParser(userData.createdAt)}</h1>
<div id="image">
<img id="PhotoProfile" alt="Profil" src={userData.picture}/>
</div>
@ -16,18 +25,76 @@ const Profil = () => {
<h2 className="subdo">@{userData.pseudo}</h2>
<div>
<div id="blocAbonnement">
<div>
<div onClick={() => setFollowingPopup(true)} className="bloc-aboonnements-abonner">
<div className="nombre">{userData.following.length}</div>
<div className="texteNombre">Abonnement</div>
</div>
<div>
<div onClick={() => setFollowerPopup(true)} className="bloc-aboonnements-abonner">
<div className="nombre">{userData.followers.length}</div>
<div className="texteNombre">Abonnée</div>
</div>
{followingPopup && (
<div className="popup-profil-container">
<div className="modal">
<h3>Abonnements</h3>
<span className="cross" onClick={() => setFollowingPopup(false)}>
&#10005;
</span>
<ul>
{usersData.map((user) => {
for (let i = 0; i < userData.following.length; i++) {
if (user._id === userData.following[i]) {
return (
<li key={user._id}>
<img src={user.picture} alt="user-pic" />
<h4>{user.pseudo}</h4>
<div className="follow-handler">
<FollowHandler idToFollow={user._id} type={'card'}/>
</div>
</li>
);
}
}
return null;
})}
</ul>
</div>
</div>
)}
{followerPopup && (
<div className="popup-profil-container">
<div className="modal">
<h3>Abonnés</h3>
<span className="cross" onClick={() => setFollowerPopup(false)}>
&#10005;
</span>
<ul>
{usersData.map((user) => {
for (let i = 0; i < userData.followers.length; i++) {
if (user._id === userData.followers[i]) {
return (
<li key={user._id}>
<img src={user.picture} alt="user-pic" />
<h4>{user.pseudo}</h4>
<div className="follow-handler">
<FollowHandler idToFollow={user._id} type={'card'} />
</div>
</li>
);
}
}
return null;
})}
</ul>
</div>
</div>
)}
</div>
</div>
</div>
</div>
</main>
</>
)
};

@ -0,0 +1,23 @@
import { createSlice } from "@reduxjs/toolkit";
export const errorSlice = createSlice({
name: "error",
initialState: {
postError: [],
userError: []
},
reducers: {
setPostError: (state,action) => {
state.postError = action.payload;
state.postUser = [];
},
setUserError: (state,action) => {
state.postUser = action.payload;
state.userError = [];
},
}
});
export const {setPostError} = errorSlice.actions;
export const {setUserError} = errorSlice.actions;
export default errorSlice.reducer;

@ -0,0 +1,21 @@
//import { combineReducers } from "redux";
import { configureStore } from "@reduxjs/toolkit";
import errorReducer from "./error.reducer";
import postReducer from "./post.reducer";
import userReducer from "./user.reducer";
import usersReducer from "./users.reducer";
/*export default combineReducers({
userReducer,
})*/
//import picturesReducer from "../feature/pictures.slice";
export default configureStore({
reducer: {
user: userReducer,
users: usersReducer,
post: postReducer,
error: errorReducer,
},
});

@ -0,0 +1,33 @@
import { createSlice } from "@reduxjs/toolkit";
export const postSlice = createSlice({
name: "post",
initialState: {
post : null,
},
reducers: {
setPostData: (state,action) => {
state.post = action.payload;
console.log(state.post)
},
setPostLikeData: (state,action) => {
console.log(...state.post);
if (state.post._id === action.payload.postId) {
return {
...state.post,
likers: [action.payload.userId, ...state.post.likers],
};
}
return state.post;
},
setPostUnLikeData: (state,action) => {
console.log(state);
console.log(action);
}
},
});
export const {setPostUnLikeData} = postSlice.actions;
export const {setPostLikeData} = postSlice.actions;
export const {setPostData} = postSlice.actions;
export default postSlice.reducer;

@ -0,0 +1,35 @@
import { createSlice } from "@reduxjs/toolkit";
export const userSlice = createSlice({
name: "user",
initialState: {
user: null,
},
reducers: {
setUserData: (state,action) => {
state.user = action.payload;
},
setUserDataImg: (state,action) => {
state.user = {...state, picture: action.payload};
},
setUserToFollowData: (state,action) => {
state.user = {...state,
following: [action.payload.idToFollow, ...state.following],}
},
setUserToUnFollowData: (state,action) => {
state.user = {...state,
following: state.following.filter(
(id) => id !== action.payload.idToUnfollow
),};
},
},
});
export const { setUserToFollowData} = userSlice.actions;
export const {setUserToUnFollowData} = userSlice.actions;
export const {setUserData} = userSlice.actions;
export const {setUserDataImg} = userSlice.actions;
export default userSlice.reducer;

@ -0,0 +1,16 @@
import { createSlice } from "@reduxjs/toolkit";
export const usersSlice = createSlice({
name: "users",
initialState: {
users: null,
},
reducers: {
setUsersData: (state,action) => {
state.users = action.payload;
},
},
});
export const {setUsersData} = usersSlice.actions;
export default usersSlice.reducer;

@ -1,10 +1,114 @@
.ajoutLien{
position: absolute;
top: 100px;
right: 50px;
.button-add-link{
border: solid black 2px;
background-color: beige;
border-radius: 10px;
width: 140px;
height: 50px;
text-align: center;
}
.bouttonAjoutLien{
height: 100px;
width: 100px;
}
.popup-profil-container {
z-index: 100;
width: 100%;
top: 0;
left: 0;
height: 100%;
position: fixed;
animation: popup 1s forwards;
@keyframes popup {
to {
backdrop-filter: blur(2px);
}
}
.modal {
position: absolute;
top: 10%;
left: 50%;
transform: translate(-100%);
background: $color-2;
padding: 25px;
border-radius: 20px;
box-shadow: 0 0 2px rgba(131, 130, 130, 0.356);
overflow: auto;
transform: scaleY(0);
transform-origin: center;
animation: modal .5s forwards;
h3 {
min-width: 200px;
}
@keyframes modal {
to {
transform: scale(1) translate(-50%);
}
}
.cross {
position: absolute;
top: 25px;
right: 25px;
transition: 0.1s;
cursor: pointer;
&:hover {
transform: scale(1.07);
}
}
ul {
margin: 20px 0 0;
max-height: 500px;
overflow-y: scroll;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 2px;
}
&::-webkit-scrollbar-track {
background: $color-4;
}
&::-webkit-scrollbar-thumb {
background: $color-1;
}
&::-webkit-scrollbar-thumb:hover {
background: #555;
}
li {
display: grid;
grid-template-columns: 64px 1fr 114px;
align-items: center;
margin: 10px 0;
img {
height: 50px;
width: 50px;
border-radius: 20px;
object-fit: cover;
box-shadow: 0 0 2px rgba(51, 51, 51, 0.376);
}
h4 {
text-align: left;
min-width: 210px;
}
.follow-handler {
text-align: left;
button {
background: $color-4;
border-radius: 20px;
margin-right: 6px;
&:hover {
color: $color-3;
transform: translateX(2px);
}
}
}
}
}
}
}

@ -1,142 +1,157 @@
header {
height: 50px;
background-color: $color-2;
top: 0;
left: 0;
right: 0;
text-transform: uppercase;
color: #b9b9b9;
position: fixed;
:root {
--navbar-background-color: #f2f2f2;
--navbar-item-hovered-color:white;
--navbar-separator-color:#ececec;
--navbar-icon-color:#646c79;
--navbar-text-color:black;
--navbar-transition-speed: .2s;
--primary-color:#7C4DFF;
--navbar-logo-background-color:#e2dfe4;
}
nav{
html, body {
margin:0;
font-family: 'Source Sans Pro';
font-size:16px;
}
main {
padding: 1rem 4rem;
margin-left: 5rem;
display: flex;
justify-content: center;
flex-direction: row;
}
.postInMain{
width: 80%;
}
.RightBar{
width: 20%;
height: 100px;
}
.navbar {
width:5rem;
position:fixed;
top:0px;
left:0px;
bottom:0px;
background:var(--navbar-background-color);
box-shadow: rgba(12,43, 30, 0.2) 0 4px 14px;
transition: width var(--navbar-transition-speed) ease;
}
.navbar:hover {
width:15rem;
}
.navbar-menu {
list-style: none;
padding:0;
margin:0;
display:flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
height:100%;
}
.logo{
//margin-right: 250px;
.navbar-item {
width: 100%;
border-bottom:solid 1px var(--navbar-separator-color);
transition: background-color var(--navbar-transition-speed);
}
.navbar-item:hover {
background-color:var(--navbar-item-hovered-color);
}
.navbar-item:last-child {
margin-top:auto;
}
.partie-droit-nav,.partie-gauche-nav{
width: 20%;
.navbar-link {
display:flex;
align-items: center;
justify-content: flex-start;
height:5rem;
width:100%;
text-decoration: none;
}
.navbar-icon {
text-align: center;
font-size: 1.5rem;
min-width: 2rem;
margin: 0 1.5rem;
color:var(--navbar-icon-color);
transition:var(--navbar-transition-speed);
}
.svg-home{
height: 45px;
width: 45px;
.navbar-item:hover .navbar-icon {
color:var(--primary-color);
}
.svg-param{
height: 42px;
width: 42px;
.navbar-title {
color:var(--navbar-text-color);
}
.navbar:not(:hover) .navbar-title {
display:none;
}
.logo {
background:var(--navbar-logo-background-color);
}
.ul-navBar{
display: flex;
li{
list-style-type: none;
padding-left: 1rem;
align-self: center;
}
.logo .navbar-title {
font-size:1.5rem;
font-weight:bold;
margin-left:1.5rem;
}
.nav-active-menu {
padding-top: 50px;
}
.navbar:hover .logo .navbar-icon {
transform:rotate(180deg);
}
.nav-active-tendances-decouvrir {
padding-bottom: 0.75rem;
}
/*
** RESPONSIVE
*/
.nav-active {
position: relative;
&::after {
content: "";
height: 4px;
background: violet;
position: absolute;
border-radius: 10px;
bottom: 0px;
left: 50%;
transform: translateX(-50%);
-webkit-animation: anim 0.3s ease forwards;
animation: anim 0.6s ease forwards;
@-webkit-keyframes anim {
to {
width: 100%;
}
}
@keyframes anim {
to {
width: 100%;
}
}
}
@media only screen and (max-width:1024px) {
.navbar {
top:auto;
width:100%;
bottom:0px;
}
}
.img-profile{
height: 35px;
width: 35px;
border-radius: 20px;
}
.navbar:hover {
width:100%;
}
.b{
text-decoration: none;
cursor: pointer;
}
.navbar-menu {
flex-direction: row;
}
.n{
height: 0.95rem;
width: 0.95rem;
padding-top: 0.15rem;
overflow: hidden;
fill: #b9b9b9;
}
.logo {
display:none;
}
main {
margin-left:0;
margin-bottom:5rem;
}
.more .menu-deroulant {
font-size: 20px;
outline: none;
color: white;
background-color: inherit;
font-family: inherit;
margin-left: 60px;
}
.navbar:hover .navbar-title {
display:none;
}
.more-menu {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 200px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.more-menu .element-menu-deroulant {
float: none;
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}
.navbar-icon {
min-width:auto;
margin:0;
}
.dropdown-menu .element-menu-deroulant:hover {
background-color: #cccccc;
color: black;
}
.more:hover .more-menu {
display: block;
.navbar-link {
justify-content: center;
}
}

@ -2,15 +2,25 @@
background: white;
border: 2px solid grey;
border-radius:5px;
width: 40%;
margin-left: auto;
margin-right: auto;
width: 70%;
margin-bottom: 20px;
}
#postContenu{
padding: 15px;
}
#hautPoste{
display: flex;
align-items:center;
justify-content: space-between;
margin-right: 15px;
}
.conteneur_postes{
text-decoration: none;
list-style-type: none;
display: flex;
justify-content: center;
}
#cadreInfoPoste{
@ -18,7 +28,7 @@
align-items:center;
}
#PhotoProfile{
margin: 10px 15px 0px 20px;
margin: 0px 15px 0px 0px;
width: 40px;
height:40px;
border-radius: 20px;

@ -14,7 +14,7 @@
}
#blocAbonnement div{
#blocAbonnement .bloc-aboonnements-abonner{
margin-left: 80px;
margin-right: 80px;
}
@ -23,6 +23,10 @@
text-align: center;
}
.affichage-date{
font-size: 15px;
}
.nombre{
color:black;
}
@ -36,3 +40,108 @@
margin-top: 50px;
}
.popup-profil-container {
z-index: 100;
width: 100%;
top: 0;
left: 0;
height: 100%;
position: fixed;
animation: popup 1s forwards;
@keyframes popup {
to {
backdrop-filter: blur(2px);
}
}
.modal {
position: absolute;
top: 10%;
left: 50%;
transform: translate(-100%);
background: $color-2;
padding: 25px;
border-radius: 20px;
box-shadow: 0 0 2px rgba(131, 130, 130, 0.356);
overflow: auto;
transform: scaleY(0);
transform-origin: center;
animation: modal .5s forwards;
h3 {
min-width: 200px;
}
@keyframes modal {
to {
transform: scale(1) translate(-50%);
}
}
.cross {
position: absolute;
top: 25px;
right: 25px;
transition: 0.1s;
cursor: pointer;
&:hover {
transform: scale(1.07);
}
}
ul {
margin: 20px 0 0;
max-height: 500px;
overflow-y: scroll;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 2px;
}
&::-webkit-scrollbar-track {
background: $color-4;
}
&::-webkit-scrollbar-thumb {
background: $color-1;
}
&::-webkit-scrollbar-thumb:hover {
background: #555;
}
li {
display: grid;
grid-template-columns: 64px 1fr 114px;
align-items: center;
margin: 10px 0;
img {
height: 50px;
width: 50px;
border-radius: 20px;
object-fit: cover;
box-shadow: 0 0 2px rgba(51, 51, 51, 0.376);
}
h4 {
text-align: left;
min-width: 210px;
}
.follow-handler {
text-align: left;
button {
background: $color-4;
border-radius: 20px;
margin-right: 6px;
&:hover {
color: $color-3;
transform: translateX(2px);
}
}
}
}
}
}
}

@ -0,0 +1,8 @@
.ligneVertical{
border-right: thick solid #000;
height:85%;
}
.ligneHorizontal{
border-top: thick solid #000;
width: 78%;
}

@ -16,7 +16,7 @@ module.exports.readPost = (req, res) => {
module.exports.createPost = async (req, res) => {
const newPost = new postModel({
postedId: req.body.posterId,
postedId: req.body.postedId,
message: req.body.message,
lien: req.body.lien,
likers: [],
@ -70,20 +70,24 @@ module.exports.likePost = async (req, res) => {
{
$addToSet: { likers: req.body.id },
},
{ new: true })
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err }));
{ new: true },
(err, docs) => {
if (err) return res.status(200).send(err);
}
);
await UserModel.findByIdAndUpdate(
req.body.id,
{
$addToSet: { likes: req.params.id },
},
{ new: true })
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err }));
{ new: true },
(err, docs) => {
if (!err) return res.send(docs);
else return res.status(200).send(err);
}
);
} catch (err) {
return res.status(400).send(err);
return res.status(200).send(err);
}
};
@ -97,18 +101,22 @@ module.exports.unlikePost = async (req, res) => {
{
$pull: { likers: req.body.id },
},
{ new: true })
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err }));
{ new: true },
(err, docs) => {
if (err) return res.status(400).send(err);
}
);
await UserModel.findByIdAndUpdate(
req.body.id,
{
$pull: { likes: req.params.id },
},
{ new: true })
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err }));
{ new: true },
(err, docs) => {
if (!err) return res.send(docs);
else return res.status(400).send(err);
}
);
} catch (err) {
return res.status(400).send(err);
}

@ -59,22 +59,27 @@ module.exports.follow = async (req, res) => {
try {
// add to the follower list
await UserModel.findByIdAndUpdate(
await UserModel.findByIdAndUpdate(
req.params.id,
{ $addToSet: { following: req.body.idToFollow } },
{ new: true, upsert: true }
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err }))),
{ $addToSet: { following: req.body.idToFollow }, },
{ new: true , upsert: true },
(err, docs) => {
if (!err) return res.send(docs);
else return res.status(200).send(err);
}
)
// ajouter à la liste des followers
await UserModel.findByIdAndUpdate(
req.body.idToFollow,
{ $addToSet: { followers: req.params.id } },
{ new: true, upsert: true }
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err })))
{ $addToSet: { followers: req.params.id }, },
{ new: true , upsert: true},
(err, docs) => {
if (!err) return res.send(docs);
else return res.status(200).send(err);
}
);
} catch (err) {
return res.status(500).json({ message: err });
return res.status(400).send(err);
}
};
@ -88,19 +93,24 @@ module.exports.unfollow = async (req, res) => {
try {
await userModel.findByIdAndUpdate(
req.params.id,
{ $pull: { following: req.body.idToUnfollow } },
{ new: true, upsert: true }
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err }))),
{ $pull: { following: req.body.idToUnfollow ,} },
{ new: true , upsert: true },
(err, docs) => {
if (err) return res.status(400).send(err);
}
);
// Retirer de la liste des followers
await userModel.findByIdAndUpdate(
req.body.idToUnfollow,
{ $pull: { followers: req.params.id } },
{ new: true, upsert: true }
.then((data) => res.send(data))
.catch((err) => res.status(500).send({ message: err })))
{ new: true , upsert: true},
(err, docs) => {
if (!err) return res.send(docs);
else return res.status(400).send(err);
}
);
} catch (err) {
return res.status(500).json({ message: err });
return res.status(401).send(err);
}
}

@ -17,7 +17,7 @@ const app = express();
const corsOptions = {
origin: process.env.CLIENT_URL,
credentials: true,
'allowedHeaders': ['sessionId', 'Content-Type'],
'allowedHeaders': ['sessionId', 'Content-Type','Authorization'],
'exposedHeaders': ['sessionId'],
'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
'preflightContinue': false

360
package-lock.json generated

@ -0,0 +1,360 @@
{
"name": "Favor",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"multer": "^1.4.5-lts.1"
}
},
"node_modules/append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"engines": [
"node >= 0.8"
],
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^2.2.2",
"typedarray": "^0.0.6"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimist": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/multer": {
"version": "1.4.5-lts.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
"dependencies": {
"append-field": "^1.0.0",
"busboy": "^1.0.0",
"concat-stream": "^1.5.2",
"mkdirp": "^0.5.4",
"object-assign": "^4.1.1",
"type-is": "^1.6.4",
"xtend": "^4.0.0"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
}
},
"dependencies": {
"append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"requires": {
"streamsearch": "^1.1.0"
}
},
"concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^2.2.2",
"typedarray": "^0.0.6"
}
},
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.52.0"
}
},
"minimist": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
},
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"requires": {
"minimist": "^1.2.6"
}
},
"multer": {
"version": "1.4.5-lts.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
"requires": {
"append-field": "^1.0.0",
"busboy": "^1.0.0",
"concat-stream": "^1.5.2",
"mkdirp": "^0.5.4",
"object-assign": "^4.1.1",
"type-is": "^1.6.4",
"xtend": "^4.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
}
}
}

@ -0,0 +1,5 @@
{
"dependencies": {
"multer": "^1.4.5-lts.1"
}
}
Loading…
Cancel
Save