github.com/decred/politeia@v1.4.0/politeiawww/legacy/comments/comments.go (about) 1 // Copyright (c) 2020-2021 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package comments 6 7 import ( 8 "encoding/json" 9 "net/http" 10 "strconv" 11 12 pdv2 "github.com/decred/politeia/politeiad/api/v2" 13 pdclient "github.com/decred/politeia/politeiad/client" 14 "github.com/decred/politeia/politeiad/plugins/comments" 15 v1 "github.com/decred/politeia/politeiawww/api/comments/v1" 16 "github.com/decred/politeia/politeiawww/config" 17 "github.com/decred/politeia/politeiawww/legacy/events" 18 "github.com/decred/politeia/politeiawww/legacy/sessions" 19 "github.com/decred/politeia/politeiawww/legacy/user" 20 "github.com/decred/politeia/util" 21 "github.com/pkg/errors" 22 ) 23 24 // Comments is the context for the comments API. 25 type Comments struct { 26 cfg *config.Config 27 politeiad *pdclient.Client 28 userdb user.Database 29 sessions *sessions.Sessions 30 events *events.Manager 31 policy *v1.PolicyReply 32 } 33 34 // HandlePolicy is the request handler for the comments v1 Policy route. 35 func (c *Comments) HandlePolicy(w http.ResponseWriter, r *http.Request) { 36 log.Tracef("HandlePolicy") 37 38 util.RespondWithJSON(w, http.StatusOK, c.policy) 39 } 40 41 // HandleNew is the request handler for the comments v1 New route. 42 func (c *Comments) HandleNew(w http.ResponseWriter, r *http.Request) { 43 log.Tracef("HandleNew") 44 45 var n v1.New 46 decoder := json.NewDecoder(r.Body) 47 if err := decoder.Decode(&n); err != nil { 48 respondWithError(w, r, "HandleNew: unmarshal", 49 v1.UserErrorReply{ 50 ErrorCode: v1.ErrorCodeInputInvalid, 51 }) 52 return 53 } 54 55 u, err := c.sessions.GetSessionUser(w, r) 56 if err != nil { 57 respondWithError(w, r, 58 "HandleNew: GetSessionUser: %v", err) 59 return 60 } 61 62 nr, err := c.processNew(r.Context(), n, *u) 63 if err != nil { 64 respondWithError(w, r, 65 "HandleNew: processNew: %v", err) 66 return 67 } 68 69 util.RespondWithJSON(w, http.StatusOK, nr) 70 } 71 72 // HandleEdit is the request handler for the comments v1 Edit route. 73 func (c *Comments) HandleEdit(w http.ResponseWriter, r *http.Request) { 74 log.Tracef("HandleEdit") 75 76 var v v1.Edit 77 decoder := json.NewDecoder(r.Body) 78 if err := decoder.Decode(&v); err != nil { 79 respondWithError(w, r, "HandleEdit: unmarshal", 80 v1.UserErrorReply{ 81 ErrorCode: v1.ErrorCodeInputInvalid, 82 }) 83 return 84 } 85 86 u, err := c.sessions.GetSessionUser(w, r) 87 if err != nil { 88 respondWithError(w, r, 89 "HandleEdit: GetSessionUser: %v", err) 90 return 91 } 92 93 vr, err := c.processEdit(r.Context(), v, *u) 94 if err != nil { 95 respondWithError(w, r, 96 "HandleEdit: processEdit: %v", err) 97 return 98 } 99 100 util.RespondWithJSON(w, http.StatusOK, vr) 101 } 102 103 // HandleVote is the request handler for the comments v1 Vote route. 104 func (c *Comments) HandleVote(w http.ResponseWriter, r *http.Request) { 105 log.Tracef("HandleVote") 106 107 var v v1.Vote 108 decoder := json.NewDecoder(r.Body) 109 if err := decoder.Decode(&v); err != nil { 110 respondWithError(w, r, "HandleVote: unmarshal", 111 v1.UserErrorReply{ 112 ErrorCode: v1.ErrorCodeInputInvalid, 113 }) 114 return 115 } 116 117 u, err := c.sessions.GetSessionUser(w, r) 118 if err != nil { 119 respondWithError(w, r, 120 "HandleVote: GetSessionUser: %v", err) 121 return 122 } 123 124 vr, err := c.processVote(r.Context(), v, *u) 125 if err != nil { 126 respondWithError(w, r, 127 "HandleVote: processVote: %v", err) 128 return 129 } 130 131 util.RespondWithJSON(w, http.StatusOK, vr) 132 } 133 134 // HandleDel is the request handler for the comments v1 Del route. 135 func (c *Comments) HandleDel(w http.ResponseWriter, r *http.Request) { 136 log.Tracef("HandleDel") 137 138 var d v1.Del 139 decoder := json.NewDecoder(r.Body) 140 if err := decoder.Decode(&d); err != nil { 141 respondWithError(w, r, "HandleDel: unmarshal", 142 v1.UserErrorReply{ 143 ErrorCode: v1.ErrorCodeInputInvalid, 144 }) 145 return 146 } 147 148 u, err := c.sessions.GetSessionUser(w, r) 149 if err != nil { 150 respondWithError(w, r, 151 "HandleDel: GetSessionUser: %v", err) 152 return 153 } 154 155 dr, err := c.processDel(r.Context(), d, *u) 156 if err != nil { 157 respondWithError(w, r, 158 "HandleDel: processDel: %v", err) 159 return 160 } 161 162 util.RespondWithJSON(w, http.StatusOK, dr) 163 } 164 165 // HandleCount is the request handler for the comments v1 Count route. 166 func (c *Comments) HandleCount(w http.ResponseWriter, r *http.Request) { 167 log.Tracef("HandleCount") 168 169 var ct v1.Count 170 decoder := json.NewDecoder(r.Body) 171 if err := decoder.Decode(&ct); err != nil { 172 respondWithError(w, r, "HandleCount: unmarshal", 173 v1.UserErrorReply{ 174 ErrorCode: v1.ErrorCodeInputInvalid, 175 }) 176 return 177 } 178 179 cr, err := c.processCount(r.Context(), ct) 180 if err != nil { 181 respondWithError(w, r, 182 "HandleCount: processCount: %v", err) 183 return 184 } 185 186 util.RespondWithJSON(w, http.StatusOK, cr) 187 } 188 189 // HandleComments is the request handler for the comments v1 Comments route. 190 func (c *Comments) HandleComments(w http.ResponseWriter, r *http.Request) { 191 log.Tracef("HandleComments") 192 193 var cs v1.Comments 194 decoder := json.NewDecoder(r.Body) 195 if err := decoder.Decode(&cs); err != nil { 196 respondWithError(w, r, "HandleComments: unmarshal", 197 v1.UserErrorReply{ 198 ErrorCode: v1.ErrorCodeInputInvalid, 199 }) 200 return 201 } 202 203 // Lookup session user. This is a public route so a session may not 204 // exist. Ignore any session not found errors. 205 u, err := c.sessions.GetSessionUser(w, r) 206 if err != nil && err != sessions.ErrSessionNotFound { 207 respondWithError(w, r, 208 "HandleComments: GetSessionUser: %v", err) 209 return 210 } 211 212 cr, err := c.processComments(r.Context(), cs, u) 213 if err != nil { 214 respondWithError(w, r, 215 "HandleComments: processComments: %v", err) 216 return 217 } 218 219 util.RespondWithJSON(w, http.StatusOK, cr) 220 } 221 222 // HandleVotes is the request handler for the comments v1 Votes route. 223 func (c *Comments) HandleVotes(w http.ResponseWriter, r *http.Request) { 224 log.Tracef("HandleVotes") 225 226 var v v1.Votes 227 decoder := json.NewDecoder(r.Body) 228 if err := decoder.Decode(&v); err != nil { 229 respondWithError(w, r, "HandleVotes: unmarshal", 230 v1.UserErrorReply{ 231 ErrorCode: v1.ErrorCodeInputInvalid, 232 }) 233 return 234 } 235 236 vr, err := c.processVotes(r.Context(), v) 237 if err != nil { 238 respondWithError(w, r, 239 "HandleVotes: processVotes: %v", err) 240 return 241 } 242 243 util.RespondWithJSON(w, http.StatusOK, vr) 244 } 245 246 // HandleTimestamps is the request handler for the comments v1 Timestamps 247 // route. 248 func (c *Comments) HandleTimestamps(w http.ResponseWriter, r *http.Request) { 249 log.Tracef("HandleTimestamps") 250 251 var t v1.Timestamps 252 decoder := json.NewDecoder(r.Body) 253 if err := decoder.Decode(&t); err != nil { 254 respondWithError(w, r, "HandleTimestamps: unmarshal", 255 v1.UserErrorReply{ 256 ErrorCode: v1.ErrorCodeInputInvalid, 257 }) 258 return 259 } 260 261 // Lookup session user. This is a public route so a session may not 262 // exist. Ignore any session not found errors. 263 u, err := c.sessions.GetSessionUser(w, r) 264 if err != nil && err != sessions.ErrSessionNotFound { 265 respondWithError(w, r, 266 "HandleTimestamps: GetSessionUser: %v", err) 267 return 268 } 269 270 isAdmin := u != nil && u.Admin 271 tr, err := c.processTimestamps(r.Context(), t, isAdmin) 272 if err != nil { 273 respondWithError(w, r, 274 "HandleTimestamps: processTimestamps: %v", err) 275 return 276 } 277 278 util.RespondWithJSON(w, http.StatusOK, tr) 279 } 280 281 // New returns a new Comments context. 282 func New(cfg *config.Config, pdc *pdclient.Client, udb user.Database, s *sessions.Sessions, e *events.Manager, plugins []pdv2.Plugin) (*Comments, error) { 283 // Parse plugin settings 284 var ( 285 lengthMax uint32 286 voteChangesMax uint32 287 allowExtraData bool 288 votesPageSize uint32 289 countPageSize uint32 290 timestampsPageSize uint32 291 allowEdits bool 292 editPeriod uint32 293 ) 294 for _, p := range plugins { 295 if p.ID != comments.PluginID { 296 // Not the comments plugin; skip 297 continue 298 } 299 for _, v := range p.Settings { 300 switch v.Key { 301 case comments.SettingKeyCommentLengthMax: 302 u, err := strconv.ParseUint(v.Value, 10, 64) 303 if err != nil { 304 return nil, err 305 } 306 lengthMax = uint32(u) 307 308 case comments.SettingKeyVoteChangesMax: 309 u, err := strconv.ParseUint(v.Value, 10, 64) 310 if err != nil { 311 return nil, err 312 } 313 voteChangesMax = uint32(u) 314 315 case comments.SettingKeyAllowExtraData: 316 b, err := strconv.ParseBool(v.Value) 317 if err != nil { 318 return nil, err 319 } 320 allowExtraData = b 321 322 case comments.SettingKeyVotesPageSize: 323 u, err := strconv.ParseUint(v.Value, 10, 64) 324 if err != nil { 325 return nil, err 326 } 327 votesPageSize = uint32(u) 328 329 case comments.SettingKeyCountPageSize: 330 u, err := strconv.ParseUint(v.Value, 10, 64) 331 if err != nil { 332 return nil, err 333 } 334 countPageSize = uint32(u) 335 336 case comments.SettingKeyTimestampsPageSize: 337 u, err := strconv.ParseUint(v.Value, 10, 64) 338 if err != nil { 339 return nil, err 340 } 341 timestampsPageSize = uint32(u) 342 343 case comments.SettingKeyAllowEdits: 344 b, err := strconv.ParseBool(v.Value) 345 if err != nil { 346 return nil, errors.Errorf("invalid plugin setting %v '%v': %v", 347 v.Key, v.Value, err) 348 } 349 allowEdits = b 350 351 case comments.SettingKeyEditPeriod: 352 u, err := strconv.ParseUint(v.Value, 10, 64) 353 if err != nil { 354 return nil, errors.Errorf("invalid plugin setting %v '%v': %v", 355 v.Key, v.Value, err) 356 } 357 editPeriod = uint32(u) 358 359 default: 360 // Skip unknown settings 361 log.Warnf("Unknown plugin setting %v; Skipping...", v.Key) 362 } 363 } 364 } 365 366 // Verify all plugin settings have been provided 367 switch { 368 case lengthMax == 0: 369 return nil, errors.Errorf("plugin setting not found: %v", 370 comments.SettingKeyCommentLengthMax) 371 case voteChangesMax == 0: 372 return nil, errors.Errorf("plugin setting not found: %v", 373 comments.SettingKeyVoteChangesMax) 374 case votesPageSize == 0: 375 return nil, errors.Errorf("plugin setting not found: %v", 376 comments.SettingKeyVotesPageSize) 377 case countPageSize == 0: 378 return nil, errors.Errorf("plugin setting not found: %v", 379 comments.SettingKeyCountPageSize) 380 case timestampsPageSize == 0: 381 return nil, errors.Errorf("plugin setting not found: %v", 382 comments.SettingKeyTimestampsPageSize) 383 case editPeriod == 0: 384 return nil, errors.Errorf("plugin setting not found: %v", 385 comments.SettingKeyEditPeriod) 386 } 387 388 return &Comments{ 389 cfg: cfg, 390 politeiad: pdc, 391 userdb: udb, 392 sessions: s, 393 events: e, 394 policy: &v1.PolicyReply{ 395 LengthMax: lengthMax, 396 VoteChangesMax: voteChangesMax, 397 AllowExtraData: allowExtraData, 398 VotesPageSize: votesPageSize, 399 CountPageSize: countPageSize, 400 TimestampsPageSize: timestampsPageSize, 401 AllowEdits: allowEdits, 402 EditPeriod: editPeriod, 403 }, 404 }, nil 405 }