aboutsummaryrefslogtreecommitdiff
path: root/backend/handlers/discussions.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--backend/handlers/discussions.go291
1 files changed, 291 insertions, 0 deletions
diff --git a/backend/handlers/discussions.go b/backend/handlers/discussions.go
new file mode 100644
index 0000000..605c7c3
--- /dev/null
+++ b/backend/handlers/discussions.go
@@ -0,0 +1,291 @@
1package handlers
2
3import (
4 "net/http"
5 "strconv"
6 "time"
7
8 "github.com/gin-gonic/gin"
9 "github.com/pektezol/leastportalshub/backend/database"
10 "github.com/pektezol/leastportalshub/backend/models"
11)
12
13type MapDiscussionResponse struct {
14 Discussion MapDiscussion `json:"discussion"`
15}
16
17type MapDiscussionsResponse struct {
18 Discussions []MapDiscussionOnlyTitle `json:"discussions"`
19}
20
21type MapDiscussion struct {
22 ID int `json:"id"`
23 Creator models.UserShortWithAvatar `json:"creator"`
24 Title string `json:"title"`
25 Content string `json:"content"`
26 // Upvotes int `json:"upvotes"`
27 UpdatedAt time.Time `json:"updated_at"`
28 Comments []MapDiscussionComment `json:"comments"`
29}
30
31type MapDiscussionOnlyTitle struct {
32 ID int `json:"id"`
33 Creator models.UserShortWithAvatar `json:"creator"`
34 Title string `json:"title"`
35 // Upvotes int `json:"upvotes"`
36 UpdatedAt time.Time `json:"updated_at"`
37 Comments []MapDiscussionComment `json:"comments"`
38}
39
40type MapDiscussionComment struct {
41 User models.UserShortWithAvatar `json:"user"`
42 Comment string `json:"comment"`
43 Date time.Time `json:"date"`
44}
45
46type CreateMapDiscussionRequest struct {
47 Title string `json:"title" binding:"required"`
48 Content string `json:"content" binding:"required"`
49}
50
51type EditMapDiscussionRequest struct {
52 Title string `json:"title" binding:"required"`
53 Content string `json:"content" binding:"required"`
54}
55
56// GET Map Discussions
57//
58// @Description Get map discussions with specified map id.
59// @Tags maps
60// @Produce json
61// @Param mapid path int true "Map ID"
62// @Success 200 {object} models.Response{data=MapDiscussionsResponse}
63// @Router /maps/{mapid}/discussions [get]
64func FetchMapDiscussions(c *gin.Context) {
65 // TODO: get upvotes
66 response := MapDiscussionsResponse{}
67 mapID, err := strconv.Atoi(c.Param("mapid"))
68 if err != nil {
69 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
70 return
71 }
72 sql := `SELECT md.id, u.steam_id, u.user_name, u.avatar_link, md.title, md.updated_at FROM map_discussions md
73 INNER JOIN users u ON md.user_id = u.steam_id WHERE md.map_id = $1
74 ORDER BY md.updated_at DESC`
75 rows, err := database.DB.Query(sql, mapID)
76 if err != nil {
77 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
78 return
79 }
80 // Get discussion data
81 for rows.Next() {
82 discussion := MapDiscussionOnlyTitle{}
83 err := rows.Scan(&discussion.ID, &discussion.Creator.SteamID, &discussion.Creator.UserName, &discussion.Creator.AvatarLink, &discussion.Title, &discussion.UpdatedAt)
84 if err != nil {
85 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
86 return
87 }
88 response.Discussions = append(response.Discussions, discussion)
89 }
90 c.JSON(http.StatusOK, models.Response{
91 Success: true,
92 Message: "Successfully retrieved map discussions.",
93 Data: response,
94 })
95}
96
97// GET Map Discussion
98//
99// @Description Get map discussion with specified map and discussion id.
100// @Tags maps
101// @Produce json
102// @Param mapid path int true "Map ID"
103// @Param discussionid path int true "Discussion ID"
104// @Success 200 {object} models.Response{data=MapDiscussionResponse}
105// @Router /maps/{mapid}/discussions/{discussionid} [get]
106func FetchMapDiscussion(c *gin.Context) {
107 // TODO: get upvotes
108 response := MapDiscussionResponse{}
109 mapID, err := strconv.Atoi(c.Param("mapid"))
110 if err != nil {
111 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
112 return
113 }
114 discussionID, err := strconv.Atoi(c.Param("discussionid"))
115 if err != nil {
116 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
117 return
118 }
119 sql := `SELECT md.id, u.steam_id, u.user_name, u.avatar_link, md.title, md.content, md.updated_at FROM map_discussions md
120 INNER JOIN users u ON md.user_id = u.steam_id WHERE md.map_id = $1 AND md.id = $2`
121 err = database.DB.QueryRow(sql, mapID, discussionID).Scan(&response.Discussion.ID, &response.Discussion.Creator.SteamID, &response.Discussion.Creator.UserName, &response.Discussion.Creator.AvatarLink, &response.Discussion.Title, &response.Discussion.Content, &response.Discussion.UpdatedAt)
122 if err != nil {
123 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
124 return
125 }
126 // Get commments
127 sql = `SELECT u.steam_id, u.user_name, u.avatar_link, mdc.comment, mdc.created_at FROM map_discussions_comments mdc
128 INNER JOIN users u ON mdc.user_id = u.steam_id WHERE mdc.discussion_id = $1`
129 comments, err := database.DB.Query(sql, response.Discussion.ID)
130 if err != nil {
131 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
132 return
133 }
134 for comments.Next() {
135 comment := MapDiscussionComment{}
136 err = comments.Scan(&comment.User.SteamID, &comment.User.UserName, &comment.User.AvatarLink, &comment.Comment, &comment.Date)
137 if err != nil {
138 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
139 return
140 }
141 response.Discussion.Comments = append(response.Discussion.Comments, comment)
142 }
143 c.JSON(http.StatusOK, models.Response{
144 Success: true,
145 Message: "Successfully retrieved map discussion.",
146 Data: response,
147 })
148}
149
150// POST Map Discussion
151//
152// @Description Create map discussion with specified map id.
153// @Tags maps
154// @Produce json
155// @Param Authorization header string true "JWT Token"
156// @Param mapid path int true "Map ID"
157// @Param discussionid path int true "Discussion ID"
158// @Param request body CreateMapDiscussionRequest true "Body"
159// @Success 200 {object} models.Response{data=CreateMapDiscussionRequest}
160// @Router /maps/{mapid}/discussions [post]
161func CreateMapDiscussion(c *gin.Context) {
162 mapID, err := strconv.Atoi(c.Param("mapid"))
163 if err != nil {
164 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
165 return
166 }
167 user, exists := c.Get("user")
168 if !exists {
169 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
170 return
171 }
172 var request CreateMapDiscussionRequest
173 if err := c.BindJSON(&request); err != nil {
174 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
175 return
176 }
177 sql := `INSERT INTO map_discussions (map_id,user_id,title,"content")
178 VALUES($1,$2,$3,$4);`
179 _, err = database.DB.Exec(sql, mapID, user.(models.User).SteamID, request.Title, request.Content)
180 if err != nil {
181 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
182 return
183 }
184 c.JSON(http.StatusOK, models.Response{
185 Success: true,
186 Message: "Successfully created map discussion.",
187 Data: request,
188 })
189}
190
191// PUT Map Discussion
192//
193// @Description Edit map discussion with specified map id.
194// @Tags maps
195// @Produce json
196// @Param Authorization header string true "JWT Token"
197// @Param mapid path int true "Map ID"
198// @Param discussionid path int true "Discussion ID"
199// @Param request body EditMapDiscussionRequest true "Body"
200// @Success 200 {object} models.Response{data=EditMapDiscussionRequest}
201// @Router /maps/{mapid}/discussions/{discussionid} [put]
202func EditMapDiscussion(c *gin.Context) {
203 mapID, err := strconv.Atoi(c.Param("mapid"))
204 if err != nil {
205 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
206 return
207 }
208 discussionID, err := strconv.Atoi(c.Param("discussionid"))
209 if err != nil {
210 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
211 return
212 }
213 user, exists := c.Get("user")
214 if !exists {
215 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
216 return
217 }
218 var request EditMapDiscussionRequest
219 if err := c.BindJSON(&request); err != nil {
220 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
221 return
222 }
223 sql := `UPDATE map_discussions SET title = $4, content = $5, updated_at = $6 WHERE id = $1 AND map_id = $2 AND user_id = $3`
224 result, err := database.DB.Exec(sql, discussionID, mapID, user.(models.User).SteamID, request.Title, request.Content, time.Now().UTC())
225 if err != nil {
226 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
227 return
228 }
229 affectedRows, err := result.RowsAffected()
230 if err != nil {
231 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
232 return
233 }
234 if affectedRows == 0 {
235 c.JSON(http.StatusOK, models.ErrorResponse("You can only edit your own post."))
236 return
237 }
238 c.JSON(http.StatusOK, models.Response{
239 Success: true,
240 Message: "Successfully edited map discussion.",
241 Data: request,
242 })
243}
244
245// DELETE Map Summary
246//
247// @Description Delete map summary with specified map id.
248// @Tags maps
249// @Produce json
250// @Param Authorization header string true "JWT Token"
251// @Param mapid path int true "Map ID"
252// @Param discussionid path int true "Discussion ID"
253// @Success 200 {object} models.Response
254// @Router /maps/{mapid}/discussions/{discussionid} [delete]
255func DeleteMapDiscussion(c *gin.Context) {
256 mapID, err := strconv.Atoi(c.Param("mapid"))
257 if err != nil {
258 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
259 return
260 }
261 discussionID, err := strconv.Atoi(c.Param("discussionid"))
262 if err != nil {
263 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
264 return
265 }
266 user, exists := c.Get("user")
267 if !exists {
268 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
269 return
270 }
271 sql := `DELETE FROM map_discussions WHERE id = $1 AND map_id = $2 AND user_id = $3`
272 result, err := database.DB.Exec(sql, discussionID, mapID, user.(models.User).SteamID)
273 if err != nil {
274 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
275 return
276 }
277 affectedRows, err := result.RowsAffected()
278 if err != nil {
279 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
280 return
281 }
282 if affectedRows == 0 {
283 c.JSON(http.StatusOK, models.ErrorResponse("You can only delete your own post."))
284 return
285 }
286 c.JSON(http.StatusOK, models.Response{
287 Success: true,
288 Message: "Successfully deleted map discussion.",
289 Data: nil,
290 })
291}