Skip to content
This repository was archived by the owner on Sep 12, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
805e4f5
(SERVER) Add integration tests
miguelgimenezgimenez Sep 12, 2023
5bf24f6
(SERVER) update Dependencies
miguelgimenezgimenez Sep 12, 2023
bbf08ad
(SERVER) project configuration (add eslint, tsconfig, jest config..)
miguelgimenezgimenez Sep 12, 2023
ee145b3
(SERVER) refactor to typescript )
miguelgimenezgimenez Sep 12, 2023
912011f
(SERVER) refactor app structure
miguelgimenezgimenez Sep 12, 2023
386ebea
(SERVER) add error handler
miguelgimenezgimenez Sep 12, 2023
4a76b5c
(SERVER) Add Authentication dependencies
miguelgimenezgimenez Sep 13, 2023
b285b26
Add config from env
miguelgimenezgimenez Sep 13, 2023
69c40bf
(SERVER) Add user integration tests
miguelgimenezgimenez Sep 13, 2023
c9b607a
(SERVER) Add user modules
miguelgimenezgimenez Sep 13, 2023
32aa4c8
(SERVER) fix post integration tests
miguelgimenezgimenez Sep 13, 2023
ba3ded4
(SERVER) Auth strategy and middlewares
miguelgimenezgimenez Sep 13, 2023
c8917a8
(SERVER) fix error
miguelgimenezgimenez Sep 13, 2023
c85462b
(SERVER) Add express async middleware and aync handle errors through …
miguelgimenezgimenez Sep 13, 2023
777d278
(SERVER) Add post permission tests
miguelgimenezgimenez Sep 13, 2023
9eec5af
(SERVER) Add post authorizations
miguelgimenezgimenez Sep 13, 2023
a01a5e0
(SERVER) Improve error handling
miguelgimenezgimenez Sep 14, 2023
511d877
(SERVER) Add express user @types
miguelgimenezgimenez Sep 14, 2023
a433409
(SERVER) fix config
miguelgimenezgimenez Sep 14, 2023
3c61157
(SERVER) fix .gitignore
miguelgimenezgimenez Sep 14, 2023
53f0f70
(CLIENT) Add package-lock and .nvmrc file
miguelgimenezgimenez Sep 14, 2023
14da85c
(SERVER) fix tests(authentication header)
miguelgimenezgimenez Sep 16, 2023
c9dcc72
(CLIENT) add user actions and reducers
miguelgimenezgimenez Sep 16, 2023
f09e510
(CLIENT) Add login button to NavBar
miguelgimenezgimenez Sep 16, 2023
7671a94
(CLIENT) Add Authorization Headers to apiCall
miguelgimenezgimenez Sep 16, 2023
077b589
(CLIENT) Fix Post reducer and post list errors
miguelgimenezgimenez Sep 16, 2023
3d640ee
(CLIENT) Add login button to postListPage
miguelgimenezgimenez Sep 16, 2023
c99edba
(CLIENT) Add Acccess Page
miguelgimenezgimenez Sep 16, 2023
32584f1
(CLIENT) fix delete Post action
miguelgimenezgimenez Sep 16, 2023
169e278
(SERVER) Add cloudinary and multer packages
miguelgimenezgimenez Sep 17, 2023
0d6878a
(SERVER) Add multer cloduinary middleware
miguelgimenezgimenez Sep 17, 2023
f63194c
(SERVER) Add cloudinary config
miguelgimenezgimenez Sep 17, 2023
3ad30f2
(CLIENT) refactor actions and api caller
miguelgimenezgimenez Sep 17, 2023
1ceec9d
(CLIENT) Post create widget handle file
miguelgimenezgimenez Sep 17, 2023
c3ec2ff
(SERVER) Add image to post
miguelgimenezgimenez Sep 17, 2023
0d76720
(SERVER) fix post tests
miguelgimenezgimenez Sep 17, 2023
5589109
clean up
miguelgimenezgimenez Sep 17, 2023
fc7e745
(CLIENT) Add alert component and error reducer
miguelgimenezgimenez Sep 18, 2023
788367c
(CLIENT) Add loader component
miguelgimenezgimenez Sep 19, 2023
d0b1dcc
(CLIENT) Refactor to use axios instead of fetch;
miguelgimenezgimenez Sep 19, 2023
5b80732
(CLIENT) Add (dev) .env
miguelgimenezgimenez Sep 19, 2023
0d806db
(CLIENT) fix error reducer
miguelgimenezgimenez Sep 19, 2023
8b2ce5a
(CLIENT) Add background image
miguelgimenezgimenez Sep 19, 2023
c3a40ff
(CLIENT) fix axios interceptors
miguelgimenezgimenez Sep 19, 2023
f745f6c
(CLIENT) Refactor Client
miguelgimenezgimenez Sep 19, 2023
0c6c051
(SERVER) Add zod validation
miguelgimenezgimenez Sep 19, 2023
5fb8a4d
(SERVER) refactor tests
miguelgimenezgimenez Sep 20, 2023
2009afe
(SERVER) Add post add file test
miguelgimenezgimenez Sep 20, 2023
f8daa6e
(SERVER) Refactor to use DI
miguelgimenezgimenez Sep 23, 2023
6e75712
(SERVER) Fix tests
miguelgimenezgimenez Sep 23, 2023
1ae2985
(CLIENT) fix routes
miguelgimenezgimenez Sep 23, 2023
4587673
(SERVER ) add cloudinary keys
miguelgimenezgimenez Sep 23, 2023
c077af0
(SERVER ) add async encryption
miguelgimenezgimenez Sep 24, 2023
13d58a6
(SERVER ) add database and crudservices
miguelgimenezgimenez Sep 24, 2023
2156a19
(SERVER ) refactor tests and remove docker for testing
miguelgimenezgimenez Sep 24, 2023
0207595
update README
miguelgimenezgimenez Sep 24, 2023
ce24865
(SERVER ) Add service tests
miguelgimenezgimenez Sep 25, 2023
8ad24d9
(SERVER ) Use user reference in model instead of string and fix tests
miguelgimenezgimenez Sep 25, 2023
a82e291
update README
miguelgimenezgimenez Sep 25, 2023
b7dc1ce
(SERVER) Refactor folder naming and router to get route path from fol…
miguelgimenezgimenez Sep 25, 2023
e8ac3b6
(SERVER) Remove old docker scripts
miguelgimenezgimenez Sep 26, 2023
c4d2b7d
(SERVER) Reorder app imports
miguelgimenezgimenez Sep 26, 2023
0c8793c
(SERVER) Add rate-limit
miguelgimenezgimenez Sep 26, 2023
6719216
(SERVER) remane test file
miguelgimenezgimenez Sep 28, 2023
96ff699
(SERVER) refactor database services (Mongo typing improved)
miguelgimenezgimenez Sep 30, 2023
5dde12a
(SERVER) add logger
miguelgimenezgimenez Oct 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,30 @@ To start the server be sure to have installed mongoDB locally as a service then
```$xslt
cd server
npm i
node index.js
npm run build
npm start
```
If you want to restart the server at any change you can also install nodemon and start the server like this
```
cd server
npm i
npm i -g nodemon
nodemon index.js
npm run dev

```
To run the test on the backend
```
cd server
npm i
npm test

```
- In the client repository you have the Front-end code of the blog that uses React and Redux.
To start the Front-end
To start the Front-end.
(Since this project needs external libraries to work with node versions above v16 i added an .nvmrc file to explicitly show the node version required, that's why i wrote the script to use the correct version below )
```
cd client
npm i
nvm use
npm start
```

Expand Down
1 change: 1 addition & 0 deletions client/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REACT_APP_API_URL= 'http://localhost:3000/api'
1 change: 1 addition & 0 deletions client/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v16.13.0
19,616 changes: 19,354 additions & 262 deletions client/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
"private": true,
"dependencies": {
"@material-ui/core": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.61",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.19.2",
"bootstrap": "^4.4.1",
"fetch": "^1.1.0",
"isomorphic-fetch": "^2.2.1",
"jwt-decode": "^3.1.2",
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-loader-spinner": "^5.4.5",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.1",
Expand Down
67 changes: 38 additions & 29 deletions client/src/App.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
import './App.css';
import React, { useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { createTheme, ThemeProvider } from '@material-ui/core/styles';
import { useDispatch, useSelector } from 'react-redux';
import { Route, BrowserRouter, Switch } from 'react-router-dom';
import apiCaller from './util/apiCaller'
import './App.css';
import PostListPage from './Post/pages/PostListPage/PostListPage';
import PostDetailPage from './Post/pages/PostDetailPage/PostDetailPage';
import { Provider } from 'react-redux';

import 'bootstrap/dist/css/bootstrap.min.css';
import Navbar from './Nav/components/Navbar';
import AccessPage from './User/pages/AccessPage/AccessPage';
import { checkAuthentication } from './User/UserActions';
import AlertComponent from './shared/components/Alert/AlertComponent';


const theme = createMuiTheme({
const theme = createTheme({
palette: {
primary: {
main: '#1ecde2',
Expand All @@ -19,27 +22,33 @@ const theme = createMuiTheme({
});

function App(props) {
return (
<ThemeProvider theme={theme}>
<div className="w-100">
<Navbar />
<div className="w-100 pt-5 mt-5">
<Provider store={props.store}>
<BrowserRouter>
<Switch>
<Route path="/" exact component={PostListPage} />
<Route path="/posts/:cuid/:slug" exact component={PostDetailPage} />
</Switch>
</BrowserRouter>
</Provider>
</div>
</div>
</ThemeProvider>
);
}
const dispatch = useDispatch();
const accessToken = useSelector(state => state.user.accessToken)

useEffect(() => {
dispatch(checkAuthentication())
apiCaller.innerClient.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
}, [accessToken])


App.propTypes = {
store: PropTypes.object.isRequired,
};
return (
<ThemeProvider theme={theme}>
<div className="w-100">
<BrowserRouter>
<AlertComponent />
<Navbar />
<div className="w-100 pt-5 mt-5">
<Switch>
<Route path="/" exact component={PostListPage} />
<Route path="/posts/:cuid/:slug" exact component={PostDetailPage} />
<Route path="/access" exact component={AccessPage} />
</Switch>

</div>
</BrowserRouter>
</div>
</ThemeProvider >
);
}

export default App;
19 changes: 18 additions & 1 deletion client/src/Nav/components/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,31 @@ import Toolbar from '@material-ui/core/Toolbar';
import AppBar from '@material-ui/core/AppBar';
import Typography from '@material-ui/core/Typography';
import Link from '@material-ui/core/Link';
import { useDispatch, useSelector } from 'react-redux';
import { Button } from '@material-ui/core';
import { useHistory } from "react-router-dom";
import { logoutRequest } from '../../User/UserActions';

function Navbar() {
const dispatch = useDispatch()
const history = useHistory()
const isAuthenticated = useSelector(state => state.user?.isAuthenticated);
const login = () => history.push('/access');
const logout = () => dispatch(logoutRequest())

return (
<AppBar position="fixed">
<Toolbar>
<Toolbar className='justify-content-between'>
<Typography variant="h6" >
<Link href="/" className="text-white">Home</Link>
</Typography>
{history.location.pathname !== '/access' && <Typography variant="h6" >
<Button
onClick={isAuthenticated ? logout : login}
className="text-white">
{isAuthenticated ? "Logout" : "Login"}
</Button>
</Typography>}
</Toolbar>
</AppBar>
);
Expand Down
34 changes: 21 additions & 13 deletions client/src/Post/PostActions.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import callApi from '../util/apiCaller';
import apiCaller from '../util/apiCaller';


// Export Constants
export const ADD_POST = 'ADD_POST';
export const ADD_POSTS = 'ADD_POSTS';
export const DELETE_POST = 'DELETE_POST';
export const SET_LOADING = 'SET_LOADING';

// Export Actions

export function addPost(post) {
return {
type: ADD_POST,
Expand All @@ -15,13 +18,14 @@ export function addPost(post) {

export function addPostRequest(post) {
return (dispatch) => {
return callApi('posts', 'post', {
post: {
name: post.name,
title: post.title,
content: post.content,
},
}).then(res => dispatch(addPost(res.post)));
dispatch({ type: SET_LOADING, loading: true })
return apiCaller.callApi('posts', 'post',
post
, {})
.then(res => dispatch(addPost(res.post)))
.catch(error => {
dispatch({ type: SET_LOADING, loading: false })
})
};
}

Expand All @@ -34,15 +38,15 @@ export function addPosts(posts) {

export function fetchPosts() {
return (dispatch) => {
return callApi('posts').then(res => {
dispatch(addPosts(res.posts));
});
return apiCaller.callApi('posts').then(res => {
dispatch(addPosts(res.posts))
})
};
}

export function fetchPost(cuid) {
return (dispatch) => {
return callApi(`posts/${cuid}`).then(res => dispatch(addPost(res.post)));
return apiCaller.callApi(`posts/${cuid}`).then(res => dispatch(addPost(res.post)));
};
}

Expand All @@ -55,6 +59,10 @@ export function deletePost(cuid) {

export function deletePostRequest(cuid) {
return (dispatch) => {
return callApi(`posts/${cuid}`, 'delete').then(() => dispatch(deletePost(cuid)));
return apiCaller.callApi(`posts/${cuid}`, "delete").then((res) => {
if (res.ok) {
dispatch(deletePost(cuid));
}
});
};
}
29 changes: 20 additions & 9 deletions client/src/Post/PostReducer.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
import { ADD_POST, ADD_POSTS, DELETE_POST } from './PostActions';
import { ADD_POST, ADD_POSTS, DELETE_POST, SET_LOADING } from './PostActions';

// Initial State
const initialState = { data: [] };
const initialState = { data: [], loading: false };

const PostReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_POST :
return {
data: [action.post, ...state.data],
};
case ADD_POST:
if (action.post) {
return {
data: [action.post, ...state.data],
loading: false
};
}
return state


case ADD_POSTS :
case ADD_POSTS:
return {
data: action.posts,
loading: false
};

case DELETE_POST :
case DELETE_POST:
return {
data: state.data.filter(post => post.cuid !== action.cuid),
loading: false
};

case SET_LOADING: {
return {
...state, loading: action.loading
}
}
default:
return state;
}
Expand Down
63 changes: 44 additions & 19 deletions client/src/Post/components/PostCreateWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,72 @@ import React, { useState } from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import { InputLabel } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
// Import Style
import { useSelector } from 'react-redux';

import Loader from '../../shared/components/Loader/Loader';



const useStyles = makeStyles(theme => ({
root: {
'& > *': {
margin: theme.spacing(1),
},
root: {
'& > *': {
margin: theme.spacing(1),
},
},
}));

const PostCreateWidget = ({ addPost }) => {

const [state, setState] = useState({});
const classes = useStyles();

const [state, setState] = useState({});
const classes = useStyles();
const [imageFile, setImageFile] = useState(null);
const loading = useSelector(state => state.posts.loading);

const handleImageChange = (e) => {
setImageFile(e.target.files[0]);
};

const submit = () => {
const formData = new FormData();
if (imageFile) {
formData.append('image', imageFile);
}
if (state.name && state.title && state.content) {
addPost(state);
formData.append('name', state.name);
formData.append('title', state.title);
formData.append('content', state.content);

addPost(formData);
}

setState({
name: undefined,
title: undefined,
content: undefined
})
};

const handleChange = (evt) => {
const value = evt.target.value;
setState({
...state,
[evt.target.name]: value
...state,
[evt.target.name]: value
});
};

if (loading) return <Loader />
return (
<div className={`${classes.root} d-flex flex-column my-4 w-100`}>
<h3>Create new post</h3>
<TextField variant="filled" label="Author name" name="name" onChange={handleChange} />
<TextField variant="filled" label="Post title" name="title" onChange={handleChange} />
<TextField variant="filled" multiline rows="4" label="Post content" name="content" onChange={handleChange} />
<Button className="mt-4" variant="contained" color="primary" onClick={() => submit()} disabled={!state.name || !state.title || !state.content}>
Submit
</Button>
<h3>Create new post</h3>
<TextField variant="filled" required label="Author name" name="name" onChange={handleChange} />
<TextField variant="filled" required label="Post title" name="title" onChange={handleChange} />
<TextField variant="filled" required multiline minRows="4" label="Post content" name="content" onChange={handleChange} />
<InputLabel htmlFor="background-image-input">Select background image</InputLabel>
<input type="file" placeholder="Add Background image" onChange={handleImageChange} />
<Button className="mt-4" variant="contained" color="primary" onClick={() => submit()} disabled={!state.name || !state.title || !state.content}>
Submit
</Button>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion client/src/Post/components/PostList.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function PostList(props) {
<div className="d-flex flex-column w-100">
<h3 className="mt-4">Posts</h3>
{
props.posts.map(post => (
(props.posts || []).map(post => (
<PostListItem
post={post}
key={post.cuid}
Expand Down
Loading