Merge branch 'dev'
This commit is contained in:
commit
32699e61f6
20 changed files with 287 additions and 242 deletions
|
|
@ -19,23 +19,19 @@ import AppRegistrationIcon from '@mui/icons-material/AppRegistration';
|
|||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import { useState } from 'react';
|
||||
import Gravatar from 'react-gravatar';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Logo from 'assets/bdpa-logo.svg';
|
||||
import { useMode } from 'contexts';
|
||||
|
||||
export default function MobileNavbar({ logout, userData }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { mode, setMode } = useMode();
|
||||
|
||||
const handleClick = () => setOpen((initial) => !initial);
|
||||
|
||||
const onClick = () => {
|
||||
setMode(() => (mode === 'light' ? 'dark' : 'light'));
|
||||
};
|
||||
|
||||
export default function MobileNavbar({
|
||||
logout,
|
||||
userData,
|
||||
open,
|
||||
setOpen,
|
||||
mode,
|
||||
setMode,
|
||||
}) {
|
||||
function NavbarControls() {
|
||||
return userData.username ? (
|
||||
<>
|
||||
|
|
@ -121,11 +117,11 @@ export default function MobileNavbar({ logout, userData }) {
|
|||
</Typography>
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
|
||||
<Button color='inherit' onClick={handleClick}>
|
||||
<Button color='inherit' onClick={setOpen}>
|
||||
Menu
|
||||
</Button>
|
||||
</Toolbar>
|
||||
<Drawer anchor='right' open={open} onClose={handleClick}>
|
||||
<Drawer anchor='right' open={open} onClose={setOpen}>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemButton component={Link} to='/questions/search'>
|
||||
|
|
@ -137,7 +133,7 @@ export default function MobileNavbar({ logout, userData }) {
|
|||
</ListItem>
|
||||
<NavbarControls />
|
||||
<ListItem>
|
||||
<ListItemButton onClick={onClick}>
|
||||
<ListItemButton onClick={setMode}>
|
||||
<ListItemIcon>
|
||||
{mode === 'light' ? (
|
||||
<DarkModeIcon />
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
import { IconButton } from '@mui/material';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import { useMode } from 'contexts';
|
||||
|
||||
export default function ModeToggle() {
|
||||
const { mode, setMode } = useMode();
|
||||
|
||||
const onClick = () => {
|
||||
setMode(() => (mode === 'light' ? 'dark' : 'light'));
|
||||
};
|
||||
|
||||
if (mode === 'light') {
|
||||
return (
|
||||
<IconButton
|
||||
color='inherit'
|
||||
onClick={onClick}
|
||||
sx={{ margin: '0 1vh' }}
|
||||
>
|
||||
<LightModeIcon />
|
||||
</IconButton>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<IconButton
|
||||
color='inherit'
|
||||
onClick={onClick}
|
||||
sx={{ margin: '0 1vh' }}
|
||||
>
|
||||
<DarkModeIcon />
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,12 @@ import {
|
|||
import MailIcon from '@mui/icons-material/Mail';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Logo from 'assets/bdpa-logo.svg';
|
||||
import { ModeToggle, Profile } from 'components';
|
||||
import { Profile } from 'components';
|
||||
import { SearchBar } from 'controllers';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
|
||||
export default function Navbar({ logout, userData }) {
|
||||
export default function Navbar({ logout, userData, mode, setMode }) {
|
||||
function NavbarControls() {
|
||||
return userData.username ? (
|
||||
<>
|
||||
|
|
@ -61,7 +63,23 @@ export default function Navbar({ logout, userData }) {
|
|||
</Typography>
|
||||
<SearchBar />
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
<ModeToggle />
|
||||
{mode === 'dark' ? (
|
||||
<IconButton
|
||||
color='inherit'
|
||||
onClick={setMode}
|
||||
sx={{ margin: '0 1vh' }}
|
||||
>
|
||||
<LightModeIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton
|
||||
color='inherit'
|
||||
onClick={setMode}
|
||||
sx={{ margin: '0 1vh' }}
|
||||
>
|
||||
<DarkModeIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<NavbarControls />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
import { Button, Card, CardContent, Tooltip } from '@mui/material'
|
||||
import { Button, Card, CardContent, Tooltip } from '@mui/material';
|
||||
|
||||
import { AnswerForm } from 'controllers/FormControllers'
|
||||
import MdPreview from 'components/MdPreview'
|
||||
|
||||
export default function CreateAnswer({
|
||||
canAnswer,
|
||||
show,
|
||||
toggleShow,
|
||||
}) {
|
||||
import { AnswerForm } from 'controllers/FormControllers';
|
||||
import MdPreview from 'components/MdPreview';
|
||||
|
||||
export default function CreateAnswer({ canAnswer, show, toggleShow }) {
|
||||
return (
|
||||
<div>
|
||||
<Tooltip title={!canAnswer ? '' : 'You need to be authenticated to answer or level 5 if the question is protected'}>
|
||||
<Tooltip
|
||||
title={
|
||||
canAnswer
|
||||
? ''
|
||||
: 'You need to be authenticated to answer or level 5 if the question is protected'
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<Button
|
||||
disabled={canAnswer}
|
||||
disabled={!canAnswer}
|
||||
variant='contained'
|
||||
onClick={toggleShow}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import Form from './Form';
|
|||
import ListAnswer from './ListAnswer';
|
||||
import ListQuestion from './ListQuestion';
|
||||
import MailUnit from './MailUnit';
|
||||
import ModeToggle from './ModeToggle';
|
||||
import Navbar from './Navbar';
|
||||
import NoData from './NoData';
|
||||
import PaginatedList from './PaginatedList';
|
||||
|
|
@ -18,7 +17,6 @@ export {
|
|||
ListAnswer,
|
||||
ListQuestion,
|
||||
MailUnit,
|
||||
ModeToggle,
|
||||
Navbar,
|
||||
NoData,
|
||||
PaginatedList,
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
import { Form } from 'controllers/FormControllers';
|
||||
import { requestReset } from 'services/userServices';
|
||||
import { recoverFields } from 'services/fields';
|
||||
import { recoverSchema } from 'services/schemas';
|
||||
|
||||
export default function ForgotPasswordFormController() {
|
||||
const recover = ({ username }) => {
|
||||
console.log(
|
||||
`Visit http://localhost:3000/users/recover/${username} to reset your password`
|
||||
);
|
||||
}
|
||||
const recover = async ({ username }) => {
|
||||
await requestReset({ username });
|
||||
};
|
||||
|
||||
return (
|
||||
Form({
|
||||
fields: recoverFields,
|
||||
onSubmit: recover,
|
||||
validationSchema: recoverSchema
|
||||
})
|
||||
);
|
||||
return Form({
|
||||
fields: recoverFields,
|
||||
onSubmit: recover,
|
||||
validationSchema: recoverSchema,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default function ResetFormController() {
|
|||
const { id } = useParams();
|
||||
|
||||
const changePassword = async ({ password }) => {
|
||||
const { error } = await resetPassword(id, password);
|
||||
const { error } = await resetPassword(id, { password });
|
||||
|
||||
if (!error) {
|
||||
navigate('/users/login');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import AnswerForm from './AnwerFormController';
|
||||
import AnswerForm from './AnswerFormController';
|
||||
import AskForm from './AskFormController';
|
||||
import ForgotPasswordForm from './ForgotPasswordFormController';
|
||||
import Form from './FormController';
|
||||
|
|
|
|||
|
|
@ -1,14 +1,24 @@
|
|||
import { useUser, useError } from 'contexts';
|
||||
import { useUser, useError, useMode } from 'contexts';
|
||||
import { Navbar, MobileNavbar } from 'components';
|
||||
import { logout } from 'services/userServices';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
import Cookies from 'js-cookie';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function NavbarController() {
|
||||
const { userData, setUserData } = useUser();
|
||||
const { setError } = useError();
|
||||
const navigate = useNavigate();
|
||||
const { userData, setUserData } = useUser();
|
||||
const { mode, setMode } = useMode();
|
||||
const { setError } = useError();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const toggleOpen = () => setOpen((initial) => !initial);
|
||||
|
||||
const toggleMode = () => {
|
||||
setMode(() => (mode === 'light' ? 'dark' : 'light'));
|
||||
};
|
||||
|
||||
const sm = useMediaQuery((theme) => theme.breakpoints.only('sm'));
|
||||
const md = useMediaQuery((theme) => theme.breakpoints.only('md'));
|
||||
|
|
@ -24,6 +34,13 @@ export default function NavbarController() {
|
|||
};
|
||||
|
||||
return md || sm
|
||||
? Navbar({ logout: logoutUser, userData })
|
||||
: MobileNavbar({ logout: logoutUser, userData });
|
||||
? Navbar({ logout: logoutUser, userData, mode, setMode: toggleMode })
|
||||
: MobileNavbar({
|
||||
logout: logoutUser,
|
||||
userData,
|
||||
open,
|
||||
setOpen: toggleOpen,
|
||||
mode,
|
||||
toggleMode,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export default function SearchBarController() {
|
|||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
console.log(search);
|
||||
};
|
||||
|
||||
return <SearchBar value={search} onChange={onChange} onSubmit={onSubmit} />;
|
||||
|
|
|
|||
|
|
@ -10,28 +10,30 @@ const throttle = new Throttle({
|
|||
});
|
||||
|
||||
export const createEndpoint = (path) => (op, endpoint, data, auth) => {
|
||||
let request = superagent[op](`${process.env.REACT_APP_API_ROOT}${path}${endpoint}`)
|
||||
let request = superagent[op](
|
||||
`${process.env.REACT_APP_API_ROOT}${path}${endpoint}`
|
||||
)
|
||||
.use(throttle.plugin)
|
||||
.set('Content-Type', 'application/json');
|
||||
|
||||
if (auth)
|
||||
request = request.set('Authorization', auth);
|
||||
if (auth) request = request.set('Authorization', auth);
|
||||
else if (Cookies.get('token'))
|
||||
request = request.set('Authorization', `bearer ${Cookies.get('token')}`);
|
||||
request = request.set(
|
||||
'Authorization',
|
||||
`bearer ${Cookies.get('token')}`
|
||||
);
|
||||
|
||||
if (data)
|
||||
if (op === 'get' || op === 'delete')
|
||||
request = request.query(data);
|
||||
else if (op === 'post' || op === 'patch')
|
||||
request = request.send(data);
|
||||
if (op === 'get' || op === 'delete') request = request.query(data);
|
||||
else if (op === 'post' || op === 'patch') request = request.send(data);
|
||||
|
||||
return request
|
||||
.then(({ body, status }) => {
|
||||
console.log(op, path, endpoint, data, body, status);
|
||||
return { ...body, status };
|
||||
})
|
||||
.catch(({ response, status }) => {
|
||||
console.log(op, path, endpoint, data, response?.body, status);
|
||||
return response ? { ...response?.body, status } : { error: 'Error' };
|
||||
return response
|
||||
? { ...response?.body, status }
|
||||
: { error: 'Error' };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,13 +20,15 @@ app.use(express.json());
|
|||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(cookieParser());
|
||||
|
||||
app.use('/users', userRouter);
|
||||
app.use('/mail', mailRouter);
|
||||
app.use('/questions', questionRouter);
|
||||
app.use('/public', express.static(path.join(__dirname, 'public')));
|
||||
|
||||
app.use('/api/users', userRouter);
|
||||
app.use('/api/mail', mailRouter);
|
||||
app.use('/api/questions', questionRouter);
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public/build')));
|
||||
app.get('*', (_, res) => {
|
||||
res.sendFile(path.join(__dirname, 'public/build/index.html'));
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, './public/build/index.html'));
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const Question = mongoose.Schema(
|
|||
createdAt: { type: Date, required: true, default: Date.now },
|
||||
creator: { type: String, required: true },
|
||||
downvotes: { type: Number, required: true, default: 0 },
|
||||
hasAccepted: { type: Boolean, required: true, default: false },
|
||||
hasAcceptedAnswer: { type: Boolean, required: true, default: false },
|
||||
lastAnswerFetch: { type: Date, default: new Date(0) },
|
||||
lastCommentFetch: { type: Date, default: new Date(0) },
|
||||
protect: [String],
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ servers:
|
|||
- description: SwaggerHub API Auto Mocking
|
||||
url: https://virtserver.swaggerhub.com/C4theBomb/qOverflow/1.0.0
|
||||
- description: Localhost
|
||||
url: http://localhost:3001
|
||||
url: http://localhost:3001/api
|
||||
info:
|
||||
version: '1.0.0'
|
||||
title: qOverflow API
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
const config = require('server/config.json');
|
||||
const Answer = require('server/db/models/Answer');
|
||||
const Question = require('server/db/models/Question');
|
||||
const User = require('server/db/models/User');
|
||||
const { getQuestion, refreshQuestion } = require('server/utils/question');
|
||||
const createRequest = require('server/utils/api');
|
||||
|
||||
|
|
@ -12,10 +14,7 @@ async function EditAnswerAccepted(req, res) {
|
|||
if (!question) return res.status(404).send(config.errorNotFound);
|
||||
|
||||
// Verify user owns question and question does not already have an accepted answer
|
||||
if (
|
||||
question.creator !== user.username ||
|
||||
question.hasAccepted
|
||||
)
|
||||
if (question.creator !== user.username || question.hasAccepted)
|
||||
return res.status(403).send(config.errorForbidden);
|
||||
|
||||
// Patch question with BDPA server
|
||||
|
|
@ -30,16 +29,22 @@ async function EditAnswerAccepted(req, res) {
|
|||
}
|
||||
|
||||
const cachedAnswer = await Answer.findByIdAndDelete(answer_id);
|
||||
//update hasAcceptedAnswer
|
||||
|
||||
// Update hasAcceptedAnswer
|
||||
await createRequest('patch', `/questions/${question_id}`, {
|
||||
hasAcceptedAnswer: true
|
||||
hasAcceptedAnswer: true,
|
||||
});
|
||||
await Question.findByIdAndUpdate(question_id, { hasAcceptedAnswer: true });
|
||||
|
||||
// Increment points of answer creator
|
||||
await createRequest('patch', `/users/${cachedAnswer.creator}/points`, {
|
||||
operation: 'increment',
|
||||
amount: 15,
|
||||
});
|
||||
await User.findByOneAndUpdate(
|
||||
{ username: cachedAnswer.creator },
|
||||
{ $inc: { points: 15 } }
|
||||
);
|
||||
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,11 @@
|
|||
const config = require('server/config.json');
|
||||
const User = require('server/db/models/User');
|
||||
const Vote = require('server/db/models/Vote');
|
||||
const { getQuestion, refreshQuestion } = require('server/utils/question');
|
||||
const Question = require('server/db/models/Question');
|
||||
const createRequest = require('server/utils/api');
|
||||
const getUserLevel = require('server/utils/getUserLevel');
|
||||
|
||||
async function downvote(res, user) {
|
||||
{
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'increment',
|
||||
target: 'downvotes',
|
||||
});
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
await Vote.create({
|
||||
parent_id: question_id,
|
||||
creator: user.username,
|
||||
status: 'downvoted',
|
||||
docModel: 'Question',
|
||||
});
|
||||
}
|
||||
{
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${user.username}/points`,
|
||||
{
|
||||
operation: 'decrement',
|
||||
amount: 1,
|
||||
}
|
||||
);
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
await User.findByIdAndUpdate(user.id, { $inc: { points: -1 } });
|
||||
}
|
||||
}
|
||||
|
||||
async function upvote(res, user) {
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'increment',
|
||||
target: 'upvotes',
|
||||
});
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
await Vote.create({
|
||||
parent_id: question_id,
|
||||
creator: user.username,
|
||||
status: 'upvoted',
|
||||
docModel: 'Question',
|
||||
});
|
||||
}
|
||||
async function upvote(res, user) {}
|
||||
|
||||
async function EditQuestionVote(req, res) {
|
||||
const { user } = req;
|
||||
|
|
@ -54,19 +14,24 @@ async function EditQuestionVote(req, res) {
|
|||
|
||||
if (!operation) return res.status(400).send(config.errorIncomplete);
|
||||
|
||||
// Filter out requests that do not have the permissions
|
||||
const userLevel = getUserLevel(user.points);
|
||||
|
||||
if (operation === 'upvote' && userLevel < 2)
|
||||
if (operation === 'upvote' && userLevel < 2) {
|
||||
return res.status(403).send(config.errorForbidden);
|
||||
}
|
||||
|
||||
if (operation === 'downvote' && userLevel < 4)
|
||||
if (operation === 'downvote' && userLevel < 4) {
|
||||
return res.status(403).send(config.errorForbidden);
|
||||
}
|
||||
|
||||
const URL = `/questions/${question_id}/vote/${user.username}`;
|
||||
|
||||
const question = await getQuestion(question_id);
|
||||
// Verify that the question exists
|
||||
const question = await Question.findById(question_id);
|
||||
if (!question) return res.status(404).send(config.errorNotFound);
|
||||
|
||||
// Find the cached vote and record the status
|
||||
let cachedVote = await Vote.findOneAndDelete({
|
||||
parent_id: question_id,
|
||||
creator: user.username,
|
||||
|
|
@ -77,28 +42,54 @@ async function EditQuestionVote(req, res) {
|
|||
cachedVote = !error ? { status: vote } : { status: null };
|
||||
}
|
||||
|
||||
if (cachedVote === 'upvoted') {
|
||||
{
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'decrement',
|
||||
target: 'upvotes',
|
||||
});
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
}
|
||||
if (cachedVote.status === 'upvoted') {
|
||||
// Undo the previous upvote status
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'decrement',
|
||||
target: 'upvotes',
|
||||
});
|
||||
|
||||
if (!success) return res.status(500).send();
|
||||
|
||||
if (operation === 'downvote') {
|
||||
await downvote(res, user);
|
||||
{
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${question.creator}/points`,
|
||||
{
|
||||
operation: 'decrement',
|
||||
amount: 6,
|
||||
}
|
||||
);
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
// Change the vote status to downvoted
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'increment',
|
||||
target: 'downvotes',
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
return res.status(500).send(config.errorGeneric);
|
||||
}
|
||||
|
||||
// Cache the new vote
|
||||
await Vote.create({
|
||||
parent_id: question_id,
|
||||
creator: user.username,
|
||||
status: 'downvoted',
|
||||
docModel: 'Question',
|
||||
});
|
||||
|
||||
// Decrement the question creator's points
|
||||
await createRequest('patch', `/users/${question.creator}/points`, {
|
||||
operation: 'decrement',
|
||||
amount: 6,
|
||||
});
|
||||
await User.findOneAndUpdate(
|
||||
{ username: question.creator },
|
||||
{ $inc: { points: -6 } }
|
||||
);
|
||||
|
||||
// Decrement the user's points
|
||||
await createRequest('patch', `/users/${user.username}/points`, {
|
||||
operation: 'decrement',
|
||||
amount: 1,
|
||||
});
|
||||
await User.findByIdAndUpdate(user.id, { $inc: { points: -1 } });
|
||||
|
||||
return res.send({ vote: 'downvoted' });
|
||||
} else {
|
||||
// Decrement the question creator's points
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${question.creator}/points`,
|
||||
|
|
@ -107,84 +98,138 @@ async function EditQuestionVote(req, res) {
|
|||
amount: 5,
|
||||
}
|
||||
);
|
||||
|
||||
await User.findOneAndUpdate(
|
||||
{ username: question.creator },
|
||||
{ $inc: { points: -5 } }
|
||||
);
|
||||
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
return res.send({ vote: 'upvoted' });
|
||||
}
|
||||
} else if (cachedVote === 'downvoted') {
|
||||
{
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'decrement',
|
||||
target: 'downvotes',
|
||||
});
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
|
||||
return res.send({ vote: null });
|
||||
}
|
||||
} else if (cachedVote.status === 'downvoted') {
|
||||
// Undo the previous vote status
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'decrement',
|
||||
target: 'downvotes',
|
||||
});
|
||||
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
|
||||
if (operation === 'upvote') {
|
||||
await upvote(res, user);
|
||||
{
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${question.creator}/points`,
|
||||
{
|
||||
operation: 'increment',
|
||||
amount: 6,
|
||||
}
|
||||
);
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
}
|
||||
// Increment upvotes
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'increment',
|
||||
target: 'upvotes',
|
||||
});
|
||||
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
|
||||
// Cache the new vote
|
||||
await Vote.create({
|
||||
parent_id: question_id,
|
||||
creator: user.username,
|
||||
status: 'upvoted',
|
||||
docModel: 'Question',
|
||||
});
|
||||
|
||||
// Increment question creator's points
|
||||
await createRequest('patch', `/users/${question.creator}/points`, {
|
||||
operation: 'increment',
|
||||
amount: 6,
|
||||
});
|
||||
await User.findOneAndUpdate(
|
||||
{ username: question.creator },
|
||||
{ $inc: { points: 6 } }
|
||||
);
|
||||
|
||||
return res.send({ vote: 'upvoted' });
|
||||
} else {
|
||||
{
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${question.creator}/points`,
|
||||
{
|
||||
operation: 'increment',
|
||||
amount: 1,
|
||||
}
|
||||
);
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
}
|
||||
{
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${user.username}/points`,
|
||||
{
|
||||
operation: 'increment',
|
||||
amount: 1,
|
||||
}
|
||||
);
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
await User.findByIdAndUpdate(user.id, { $inc: { points: 1 } });
|
||||
}
|
||||
// Increment the question creator's points
|
||||
await createRequest('patch', `/users/${question.creator}/points`, {
|
||||
operation: 'increment',
|
||||
amount: 1,
|
||||
});
|
||||
await User.findOneAndUpdate(
|
||||
{ username: question.creator },
|
||||
{ $inc: { points: 1 } }
|
||||
);
|
||||
|
||||
// Increment the user's points
|
||||
await createRequest('patch', `/users/${user.username}/points`, {
|
||||
operation: 'increment',
|
||||
amount: 1,
|
||||
});
|
||||
await User.findByIdAndUpdate(user.id, { $inc: { points: 1 } });
|
||||
|
||||
return res.send({ vote: null });
|
||||
}
|
||||
} else {
|
||||
if (operation === 'upvote') {
|
||||
await upvote(res, user);
|
||||
{
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${question.creator}/points`,
|
||||
{
|
||||
operation: 'increment',
|
||||
amount: 5,
|
||||
}
|
||||
);
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
}
|
||||
// Increment upvotes
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'increment',
|
||||
target: 'upvotes',
|
||||
});
|
||||
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
|
||||
// Cache the new vote
|
||||
await Vote.create({
|
||||
parent_id: question_id,
|
||||
creator: user.username,
|
||||
status: 'upvoted',
|
||||
docModel: 'Question',
|
||||
});
|
||||
|
||||
// Increment question creator's points
|
||||
await createRequest('patch', `/users/${question.creator}/points`, {
|
||||
operation: 'increment',
|
||||
amount: 5,
|
||||
});
|
||||
await User.findOneAndUpdate(
|
||||
{ username: question.creator },
|
||||
{ $inc: { points: 5 } }
|
||||
);
|
||||
|
||||
return res.send({ vote: 'upvoted' });
|
||||
} else if (operation === 'downvote') {
|
||||
await downvote(res, user);
|
||||
{
|
||||
const { success } = await createRequest(
|
||||
'patch',
|
||||
`/users/${question.creator}/points`,
|
||||
{
|
||||
operation: 'decrement',
|
||||
amount: 1,
|
||||
}
|
||||
);
|
||||
if (!success) return res.status(500).send(config.errorGeneric);
|
||||
// Change the vote status to downvoted
|
||||
const { success } = await createRequest('patch', URL, {
|
||||
operation: 'increment',
|
||||
target: 'downvotes',
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
return res.status(500).send(config.errorGeneric);
|
||||
}
|
||||
|
||||
// Cache the new vote
|
||||
await Vote.create({
|
||||
parent_id: question_id,
|
||||
creator: user.username,
|
||||
status: 'downvoted',
|
||||
docModel: 'Question',
|
||||
});
|
||||
|
||||
// Decrement the question creator's points
|
||||
await createRequest('patch', `/users/${question.creator}/points`, {
|
||||
operation: 'decrement',
|
||||
amount: 1,
|
||||
});
|
||||
await User.findOneAndUpdate(
|
||||
{ username: question.creator },
|
||||
{ $inc: { points: -1 } }
|
||||
);
|
||||
|
||||
// Decrement the user's points
|
||||
await createRequest('patch', `/users/${user.username}/points`, {
|
||||
operation: 'decrement',
|
||||
amount: 1,
|
||||
});
|
||||
await User.findByIdAndUpdate(user.id, { $inc: { points: -1 } });
|
||||
|
||||
return res.send({ vote: 'downvoted' });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ router.get('/answers', tokenAuth, Answers);
|
|||
router.post('/login', basicAuth, Login);
|
||||
router.get('/remember', tokenAuth, Remember);
|
||||
router.delete('/logout', tokenAuth, Logout);
|
||||
router.get('/reset', RequestReset);
|
||||
router.post('/reset', RequestReset);
|
||||
router.post('/reset/:id', ResetPassword);
|
||||
router.get('/:username', GetUser);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
const ResetRequest = require('server/db/models/ResetRequest');
|
||||
|
||||
async function RequestReset(req, res) {
|
||||
const { username } = req.query;
|
||||
const { username } = req.body;
|
||||
|
||||
const request = await ResetRequest.create({ user: username });
|
||||
|
||||
console.log(
|
||||
`Your request to reset your password has been receiver. Visit http://localhost:3000/reset/${request.id} to reset your password.`
|
||||
`Your request to reset your password has been receiver. Visit http://localhost:3000/users/recover/${request.id} to reset your password.`
|
||||
);
|
||||
|
||||
return res.sendStatus(200);
|
||||
|
|
|
|||
|
|
@ -33,11 +33,9 @@ const createRequest = async (op, endpoint, data) => {
|
|||
|
||||
return request
|
||||
.then((res) => {
|
||||
console.log(op, endpoint, JSON.stringify(res.body).substr(0, 100));
|
||||
return res.body;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(op, endpoint, err.response?.body);
|
||||
return err.response?.body || { error: 'Error' };
|
||||
});
|
||||
};
|
||||
|
|
|
|||
Reference in a new issue