Skip to content

Commit 5edd086

Browse files
authored
Merge pull request #9 from paywteam/feature/sharing
Complete implementation of the feature: sharing
2 parents efb7c42 + ae8b547 commit 5edd086

File tree

5 files changed

+187
-2
lines changed

5 files changed

+187
-2
lines changed

src/app/http/controllers/GlueBoardController.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface IndexResponseBody {
1313
name: string
1414
color: string
1515
}
16+
sharing: boolean
1617
}>
1718
}
1819

@@ -22,6 +23,7 @@ interface GetResponseBody {
2223
name: string
2324
color: string
2425
}
26+
sharing: boolean
2527
}
2628

2729
export default class GlueBoardController {
@@ -51,7 +53,8 @@ export default class GlueBoardController {
5153
for (const glueBoard of glueBoards) {
5254
responseBody.glueBoards.push({
5355
id: glueBoard.id,
54-
category: glueBoard.category
56+
category: glueBoard.category,
57+
sharing: glueBoard.sharing
5558
})
5659
}
5760

@@ -143,7 +146,8 @@ export default class GlueBoardController {
143146
category: {
144147
name: glueBoard.category.name,
145148
color: glueBoard.category.color
146-
}
149+
},
150+
sharing: glueBoard.sharing
147151
}
148152

149153
return res.status(200).json(responseBody)
@@ -198,6 +202,12 @@ export default class GlueBoardController {
198202
},
199203
errorMessage: '`color` must be a hex color.'
200204
},
205+
sharing: {
206+
optional: true,
207+
in: 'body',
208+
isBoolean: true,
209+
errorMessage: '`sharing` must be a boolean.'
210+
},
201211
position: {
202212
optional: true,
203213
in: 'body',
@@ -237,6 +247,11 @@ export default class GlueBoardController {
237247
glueBoard.category.color = req.body.color
238248
}
239249

250+
// update sharing option
251+
if (req.body.sharing !== undefined) {
252+
glueBoard.sharing = req.body.sharing
253+
}
254+
240255
await glueBoard.save()
241256

242257
// update relative position in the GlueBoard list
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Response, SimpleHandler } from '@/http/RequestHandler'
2+
import { GlueBoardDoc } from '@@/migrate/schemas/glue-board'
3+
import GlueBoard from '@@/migrate/models/glue-board'
4+
import { FragmentDoc } from '@@/migrate/schemas/fragment'
5+
6+
interface GetHashResponseBody {
7+
hash: string
8+
}
9+
10+
interface GetResponseBody {
11+
category: {
12+
name: string
13+
color: string
14+
}
15+
fragments: Array<{
16+
url: string
17+
selector: string
18+
xPos: number
19+
yPos: number
20+
scale: number
21+
}>
22+
}
23+
24+
export default class SharingController {
25+
/**
26+
* Get the url hash of the shared GlueBoard
27+
*/
28+
public static getHash(): SimpleHandler {
29+
return (req, res): Response => {
30+
const glueBoard = res.locals.glueBoard as GlueBoardDoc
31+
32+
// if sharing option is off, reject this request.
33+
if (glueBoard.sharing === false) {
34+
return res.status(403).json({
35+
err: {
36+
msg: 'Sharing option for this GlueBoard is off.'
37+
}
38+
})
39+
}
40+
41+
const responseBody: GetHashResponseBody = {
42+
hash: glueBoard.id
43+
}
44+
45+
return res.status(200).json(responseBody)
46+
}
47+
}
48+
49+
/**
50+
* Access to the shared GlueBoard
51+
*/
52+
public static get(): SimpleHandler {
53+
return async (req, res): Promise<Response> => {
54+
const glueBoard = (await GlueBoard.findById(res.locals.glueBoard._id, {
55+
category: 1,
56+
fragments: 1
57+
})
58+
.lean()
59+
.populate({
60+
path: 'fragments',
61+
select: '-_id -id'
62+
})) as GlueBoardDoc
63+
64+
const responseBody: GetResponseBody = {
65+
category: {
66+
name: glueBoard.category.name,
67+
color: glueBoard.category.color
68+
},
69+
fragments: []
70+
}
71+
72+
// compose response body
73+
for (const fragment of glueBoard.fragments as FragmentDoc[]) {
74+
responseBody.fragments.push({
75+
url: fragment.url,
76+
selector: fragment.selector,
77+
xPos: fragment.xPos,
78+
yPos: fragment.yPos,
79+
scale: fragment.scale
80+
})
81+
}
82+
83+
return res.status(200).json(responseBody)
84+
}
85+
}
86+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Response, NextHandler } from '@/http/RequestHandler'
2+
import GlueBoard from '@@/migrate/models/glue-board'
3+
import { GlueBoardDoc } from '@@/migrate/schemas/glue-board'
4+
import { checkSchema, ValidationChain } from 'express-validator'
5+
6+
export default class CheckSharing {
7+
public static validate(): ValidationChain[] {
8+
return checkSchema({
9+
hash: {
10+
exists: true,
11+
in: 'params',
12+
isString: true,
13+
trim: true,
14+
errorMessage: '`hash` must be a string.'
15+
}
16+
})
17+
}
18+
19+
public static handler(): NextHandler {
20+
return async (req, res, next): Promise<Response | void> => {
21+
const glueBoardID = req.params.hash
22+
const glueBoard = (await GlueBoard.findOne({
23+
id: glueBoardID
24+
})) as GlueBoardDoc
25+
26+
if (!glueBoard) {
27+
return res.status(404).json({
28+
err: {
29+
msg: 'Glueboard not found.'
30+
}
31+
})
32+
}
33+
34+
if (!glueBoard.sharing) {
35+
return res.status(403).json({
36+
err: {
37+
msg: 'Unshared GlueBoard.'
38+
}
39+
})
40+
}
41+
42+
res.locals.glueBoard = glueBoard
43+
44+
return next()
45+
}
46+
}
47+
}

src/routes/api.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,42 @@ import express from 'express'
22
import oauth2Router from '@@/routes/oauth2'
33
import meRouter from '@@/routes/me'
44
import mirroringRouter from '@@/routes/mirroring'
5+
import RequestValidationError from '@/http/middleware/RequestValidationError'
6+
import CheckSharing from '@/http/middleware/CheckSharing'
7+
import SharingController from '@/http/controllers/SharingController'
8+
import Handle405Error from '@/http/middleware/Handle405Error'
59

610
const mainRouter = express.Router()
711

12+
/**
13+
* Middleware
14+
*/
15+
16+
mainRouter.use(
17+
'/sharing/:hash',
18+
CheckSharing.validate(),
19+
RequestValidationError.handler(),
20+
CheckSharing.handler()
21+
)
22+
823
/**
924
* Sub router
1025
*/
26+
1127
mainRouter.use('/oauth2', oauth2Router)
1228
mainRouter.use('/me', meRouter)
1329
mainRouter.use('/mirroring', mirroringRouter)
1430

31+
/**
32+
* Controller
33+
*/
34+
35+
/**
36+
* GET: access to the shared GlueBoard
37+
*/
38+
mainRouter
39+
.route('/sharing/:hash')
40+
.get(SharingController.get())
41+
.all(Handle405Error.handler())
42+
1543
export default mainRouter

src/routes/me.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import GlueBoardController from '@/http/controllers/GlueBoardController'
77
import CheckGlueBoard from '@/http/middleware/CheckGlueBoard'
88
import FragmentController from '@/http/controllers/FragmentController'
99
import CheckFragment from '@/http/middleware/CheckFragment'
10+
import SharingController from '@/http/controllers/SharingController'
1011

1112
const meRouter = express.Router({ mergeParams: true })
1213

@@ -109,4 +110,12 @@ meRouter
109110
.delete(FragmentController.delete())
110111
.all(Handle405Error.handler())
111112

113+
/**
114+
* GET: get the url hash of shared GlueBoard
115+
*/
116+
meRouter
117+
.route('/glueboards/:glueboard/sharing')
118+
.get(SharingController.getHash())
119+
.all(Handle405Error.handler())
120+
112121
export default meRouter

0 commit comments

Comments
 (0)