Skip to content

Commit ee5cecc

Browse files
committed
basic requests pages
1 parent b308913 commit ee5cecc

9 files changed

Lines changed: 709 additions & 4 deletions

File tree

api/src/controllers/request.js

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import Request from '../schema/request'
2+
import Comment from '../schema/comment'
3+
4+
export const createRequest = async (req, res) => {
5+
if (req.body.title && req.body.body) {
6+
try {
7+
const existing = await Request.countDocuments()
8+
9+
const index = existing + 1
10+
11+
const request = new Request({
12+
index,
13+
title: req.body.title,
14+
body: req.body.body,
15+
createdBy: req.userId,
16+
created: Date.now(),
17+
candidates: [],
18+
})
19+
20+
await request.save()
21+
res.send({ index })
22+
} catch (e) {
23+
res.status(500).send(e.message)
24+
}
25+
} else {
26+
res.status(400).send('Request must include title and body')
27+
}
28+
}
29+
30+
export const getRequests = async (req, res) => {
31+
const pageSize = 25
32+
try {
33+
let { page } = req.query
34+
page = parseInt(page) || 0
35+
36+
const requests = await Request.aggregate([
37+
{
38+
$sort: { created: -1 },
39+
},
40+
{
41+
$skip: page * pageSize,
42+
},
43+
{
44+
$limit: pageSize,
45+
},
46+
{
47+
$lookup: {
48+
from: 'users',
49+
as: 'createdBy',
50+
let: { userId: '$createdBy' },
51+
pipeline: [
52+
{
53+
$match: { $expr: { $eq: ['$_id', '$$userId'] } },
54+
},
55+
{
56+
$project: {
57+
username: 1,
58+
},
59+
},
60+
],
61+
},
62+
},
63+
{
64+
$project: {
65+
body: 0,
66+
},
67+
},
68+
{
69+
$unwind: {
70+
path: '$createdBy',
71+
preserveNullAndEmptyArrays: true,
72+
},
73+
},
74+
])
75+
res.json(requests)
76+
} catch (e) {
77+
res.status(500).send(e.message)
78+
}
79+
}
80+
81+
export const fetchRequest = async (req, res) => {
82+
try {
83+
const [request] = await Request.aggregate([
84+
{
85+
$match: { index: parseInt(req.params.index) },
86+
},
87+
{
88+
$lookup: {
89+
from: 'users',
90+
as: 'createdBy',
91+
let: { userId: '$createdBy' },
92+
pipeline: [
93+
{
94+
$match: { $expr: { $eq: ['$_id', '$$userId'] } },
95+
},
96+
{
97+
$project: {
98+
username: 1,
99+
},
100+
},
101+
],
102+
},
103+
},
104+
{
105+
$unwind: {
106+
path: '$createdBy',
107+
preserveNullAndEmptyArrays: true,
108+
},
109+
},
110+
{
111+
$lookup: {
112+
from: 'comments',
113+
as: 'comments',
114+
let: { parentId: '$_id' },
115+
pipeline: [
116+
{
117+
$match: {
118+
type: 'request',
119+
$expr: { $eq: ['$parentId', '$$parentId'] },
120+
},
121+
},
122+
{
123+
$lookup: {
124+
from: 'users',
125+
as: 'user',
126+
let: { userId: '$userId' },
127+
pipeline: [
128+
{
129+
$match: {
130+
$expr: { $eq: ['$_id', '$$userId'] },
131+
},
132+
},
133+
{
134+
$project: {
135+
username: 1,
136+
},
137+
},
138+
],
139+
},
140+
},
141+
{
142+
$unwind: {
143+
path: '$user',
144+
preserveNullAndEmptyArrays: true,
145+
},
146+
},
147+
{ $sort: { created: -1 } },
148+
],
149+
},
150+
},
151+
])
152+
if (!request) {
153+
res.status(404).send('Request could not be found')
154+
return
155+
}
156+
res.send(request)
157+
} catch (e) {
158+
res.status(500).send(e.message)
159+
}
160+
}
161+
162+
export const deleteRequest = async (req, res) => {
163+
try {
164+
const request = await Request.findOne({
165+
index: parseInt(req.params.index),
166+
}).lean()
167+
168+
if (req.userId.toString() !== request.createdBy.toString()) {
169+
res.status(401).send('You do not have permission to delete that request')
170+
return
171+
}
172+
173+
await Request.deleteOne({ index: parseInt(req.params.index) })
174+
res.sendStatus(200)
175+
} catch (e) {
176+
res.status(500).send(e.message)
177+
}
178+
}
179+
180+
export const addComment = async (req, res) => {
181+
if (req.body.comment) {
182+
try {
183+
const request = await Request.findOne({
184+
_id: req.params.requestId,
185+
}).lean()
186+
187+
if (!request) {
188+
res.status(404).send('Request does not exist')
189+
return
190+
}
191+
192+
const comment = new Comment({
193+
type: 'request',
194+
parentId: request._id,
195+
userId: req.userId,
196+
comment: req.body.comment,
197+
created: Date.now(),
198+
})
199+
await comment.save()
200+
201+
res.sendStatus(200)
202+
} catch (err) {
203+
res.status(500).send(err.message)
204+
}
205+
} else {
206+
res.status(400).send('Request must include comment')
207+
}
208+
}

api/src/controllers/user.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,34 @@ export const fetchUser = async (req, res) => {
495495
},
496496
},
497497
],
498+
request: [
499+
{
500+
$match: {
501+
type: 'request',
502+
},
503+
},
504+
{
505+
$lookup: {
506+
from: 'requests',
507+
as: 'request',
508+
let: { requestId: '$parentId' },
509+
pipeline: [
510+
{
511+
$match: {
512+
$expr: { $eq: ['$_id', '$$requestId'] },
513+
},
514+
},
515+
{ $project: { title: 1, index: 1 } },
516+
],
517+
},
518+
},
519+
{
520+
$unwind: {
521+
path: '$request',
522+
preserveNullAndEmptyArrays: true,
523+
},
524+
},
525+
],
498526
},
499527
},
500528
],
@@ -509,7 +537,11 @@ export const fetchUser = async (req, res) => {
509537
{
510538
$addFields: {
511539
comments: {
512-
$concatArrays: ['$comments.torrent', '$comments.announcement'],
540+
$concatArrays: [
541+
'$comments.torrent',
542+
'$comments.announcement',
543+
'$comments.request',
544+
],
513545
},
514546
},
515547
},

api/src/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ import {
5555
getStats,
5656
} from './controllers/moderation'
5757
import { rssFeed } from './controllers/rss'
58+
import {
59+
createRequest,
60+
getRequests,
61+
fetchRequest,
62+
deleteRequest,
63+
addComment as addCommentRequest,
64+
} from './controllers/request'
5865
import validateConfig from './utils/validateConfig'
5966
import createAdminUser from './setup/createAdminUser'
6067

@@ -189,6 +196,13 @@ app.post('/reports/resolve/:reportId', setReportResolved)
189196
app.get('/reports/:reportId', fetchReport)
190197
app.get('/admin/stats', getStats)
191198

199+
// request routes
200+
app.post('/requests/new', createRequest)
201+
app.get('/requests/page/:page', getRequests)
202+
app.get('/requests/:index', fetchRequest)
203+
app.delete('/requests/:index', deleteRequest)
204+
app.post('/requests/comment/:requestId', addCommentRequest)
205+
192206
const port = process.env.SQ_PORT || 3001
193207
app.listen(port, () => {
194208
console.log(`[sq] ■ sqtracker running http://localhost:${port}`)

api/src/schema/request.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import mongoose from 'mongoose'
2+
3+
const Request = new mongoose.Schema({
4+
index: Number,
5+
title: String,
6+
body: String,
7+
createdBy: mongoose.Schema.ObjectId,
8+
created: Number,
9+
candidates: Array,
10+
fulfilledBy: mongoose.Schema.ObjectId,
11+
})
12+
13+
export default mongoose.model('request', Request)

client/components/Comment.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import moment from 'moment'
44
import { Comment as CommentIcon } from '@styled-icons/boxicons-regular/Comment'
55
import { File } from '@styled-icons/boxicons-regular/File'
66
import { News } from '@styled-icons/boxicons-regular/News'
7+
import { CommentAdd } from '@styled-icons/boxicons-regular/CommentAdd'
78
import Box from './Box'
89
import Text from './Text'
910

1011
const Comment = ({ comment }) => {
11-
const isTorrent = !comment.announcement
12-
const isAnnouncement = !comment.torrent
13-
console.log({ isTorrent, isAnnouncement })
12+
const isTorrent = !comment.announcement && !comment.request
13+
const isAnnouncement = !comment.torrent && !comment.request
14+
const isRequest = !comment.torrent && !comment.announcement
15+
1416
return (
1517
<Box
1618
p={4}
@@ -79,6 +81,26 @@ const Comment = ({ comment }) => {
7981
)}
8082
</>
8183
)}
84+
{isRequest && (
85+
<>
86+
{comment.request ? (
87+
<Link href={`/requests/${comment.request.index}`} passHref>
88+
<Text
89+
as="a"
90+
icon={CommentAdd}
91+
iconColor="primary"
92+
iconTextWrapperProps={{
93+
style: { verticalAlign: 'text-bottom' },
94+
}}
95+
>
96+
{comment.request.title}
97+
</Text>
98+
</Link>
99+
) : (
100+
'deleted request'
101+
)}
102+
</>
103+
)}
82104
</Text>
83105
) : (
84106
<Text>Comment by deleted user</Text>

client/components/Navigation.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ListUl } from '@styled-icons/boxicons-regular/ListUl'
1111
import { Search } from '@styled-icons/boxicons-regular/Search'
1212
import { Upload } from '@styled-icons/boxicons-regular/Upload'
1313
import { News } from '@styled-icons/boxicons-regular/News'
14+
import { MessageAdd } from '@styled-icons/boxicons-regular/MessageAdd'
1415
import { Rss } from '@styled-icons/boxicons-regular/Rss'
1516
import { User } from '@styled-icons/boxicons-regular/User'
1617
import { Error } from '@styled-icons/boxicons-regular/Error'
@@ -167,6 +168,12 @@ const Navigation = ({ isMobile, menuIsOpen, setMenuIsOpen }) => {
167168
<News size={24} />
168169
</NavLink>
169170
</Link>
171+
<Link href="/requests" passHref>
172+
<NavLink>
173+
<Text>Requests</Text>
174+
<MessageAdd size={24} />
175+
</NavLink>
176+
</Link>
170177
<Link href="/rss" passHref>
171178
<NavLink>
172179
<Text>RSS</Text>

0 commit comments

Comments
 (0)