github.com/decred/politeia@v1.4.0/politeiawww/legacy/pi/pi.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 pi 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/pi" 15 v1 "github.com/decred/politeia/politeiawww/api/pi/v1" 16 "github.com/decred/politeia/politeiawww/config" 17 "github.com/decred/politeia/politeiawww/legacy/events" 18 "github.com/decred/politeia/politeiawww/legacy/mail" 19 "github.com/decred/politeia/politeiawww/legacy/sessions" 20 "github.com/decred/politeia/politeiawww/legacy/user" 21 "github.com/decred/politeia/util" 22 "github.com/pkg/errors" 23 ) 24 25 // Pi is the context for the pi API. 26 type Pi struct { 27 cfg *config.Config 28 politeiad *pdclient.Client 29 userdb user.Database 30 mail mail.Mailer 31 sessions *sessions.Sessions 32 events *events.Manager 33 policy *v1.PolicyReply 34 } 35 36 // HandlePolicy is the request handler for the pi v1 Policy route. 37 func (p *Pi) HandlePolicy(w http.ResponseWriter, r *http.Request) { 38 log.Tracef("HandlePolicy") 39 40 util.RespondWithJSON(w, http.StatusOK, p.policy) 41 } 42 43 // HandleSetBillingStatus is the request handler for the pi v1 BillingStatus 44 // route. 45 func (p *Pi) HandleSetBillingStatus(w http.ResponseWriter, r *http.Request) { 46 log.Tracef("HandleSetBillingStatus") 47 48 var sbs v1.SetBillingStatus 49 decoder := json.NewDecoder(r.Body) 50 if err := decoder.Decode(&sbs); err != nil { 51 respondWithError(w, r, "HandleSetBillingStatus: unmarshal", 52 v1.UserErrorReply{ 53 ErrorCode: v1.ErrorCodeInputInvalid, 54 }) 55 return 56 } 57 58 u, err := p.sessions.GetSessionUser(w, r) 59 if err != nil { 60 respondWithError(w, r, 61 "HandleSetBillingStatus: GetSessionUser: %v", err) 62 return 63 } 64 65 bsr, err := p.processSetBillingStatus(r.Context(), sbs, *u) 66 if err != nil { 67 respondWithError(w, r, 68 "HandleSetBillingStatus: processSetBillingStatus: %v", err) 69 return 70 } 71 72 util.RespondWithJSON(w, http.StatusOK, bsr) 73 } 74 75 // HandleBillingStatusChanges is the request handler for the pi v1 76 // BillingStatusChanges route. 77 func (p *Pi) HandleBillingStatusChanges(w http.ResponseWriter, r *http.Request) { 78 log.Tracef("HandleBillingStatusChanges") 79 80 var bscs v1.BillingStatusChanges 81 decoder := json.NewDecoder(r.Body) 82 if err := decoder.Decode(&bscs); err != nil { 83 respondWithError(w, r, "HandleBillingStatusChanges: unmarshal", 84 v1.UserErrorReply{ 85 ErrorCode: v1.ErrorCodeInputInvalid, 86 }) 87 return 88 } 89 90 bsr, err := p.processBillingStatusChanges(r.Context(), bscs) 91 if err != nil { 92 respondWithError(w, r, 93 "HandleBillingStatusChanges: processBillingStatusChanges: %v", err) 94 return 95 } 96 97 util.RespondWithJSON(w, http.StatusOK, bsr) 98 99 } 100 101 // HandleSummaries is the request handler for the pi v1 Summaries route. 102 func (p *Pi) HandleSummaries(w http.ResponseWriter, r *http.Request) { 103 log.Tracef("HandleSummaries") 104 105 var s v1.Summaries 106 decoder := json.NewDecoder(r.Body) 107 if err := decoder.Decode(&s); err != nil { 108 respondWithError(w, r, "HandleSummaries: unmarshal", 109 v1.UserErrorReply{ 110 ErrorCode: v1.ErrorCodeInputInvalid, 111 }) 112 return 113 } 114 115 bsr, err := p.processSummaries(r.Context(), s) 116 if err != nil { 117 respondWithError(w, r, 118 "HandleSummaries: processSummaries: %v", err) 119 return 120 } 121 122 util.RespondWithJSON(w, http.StatusOK, bsr) 123 } 124 125 // New returns a new Pi context. 126 func New(cfg *config.Config, pdc *pdclient.Client, udb user.Database, m mail.Mailer, s *sessions.Sessions, e *events.Manager, plugins []pdv2.Plugin) (*Pi, error) { 127 // Parse plugin settings 128 var ( 129 textFileSizeMax uint32 130 imageFileCountMax uint32 131 imageFileSizeMax uint32 132 titleLengthMin uint32 133 titleLengthMax uint32 134 titleSupportedChars []string 135 amountMin uint64 136 amountMax uint64 137 startDateMin int64 138 endDateMax int64 139 domains []string 140 billingStatusChangesMax uint32 141 summariesPageSize uint32 142 billingStatusChangesPageSize uint32 143 ) 144 for _, p := range plugins { 145 if p.ID != pi.PluginID { 146 // Not the pi plugin; skip 147 continue 148 } 149 for _, v := range p.Settings { 150 switch v.Key { 151 case pi.SettingKeyTextFileSizeMax: 152 u, err := strconv.ParseUint(v.Value, 10, 64) 153 if err != nil { 154 return nil, err 155 } 156 textFileSizeMax = uint32(u) 157 158 case pi.SettingKeyImageFileCountMax: 159 u, err := strconv.ParseUint(v.Value, 10, 64) 160 if err != nil { 161 return nil, err 162 } 163 imageFileCountMax = uint32(u) 164 165 case pi.SettingKeyImageFileSizeMax: 166 u, err := strconv.ParseUint(v.Value, 10, 64) 167 if err != nil { 168 return nil, err 169 } 170 imageFileSizeMax = uint32(u) 171 172 case pi.SettingKeyTitleLengthMin: 173 u, err := strconv.ParseUint(v.Value, 10, 64) 174 if err != nil { 175 return nil, err 176 } 177 titleLengthMin = uint32(u) 178 179 case pi.SettingKeyTitleLengthMax: 180 u, err := strconv.ParseUint(v.Value, 10, 64) 181 if err != nil { 182 return nil, err 183 } 184 titleLengthMax = uint32(u) 185 186 case pi.SettingKeyTitleSupportedChars: 187 err := json.Unmarshal([]byte(v.Value), &titleSupportedChars) 188 if err != nil { 189 return nil, err 190 } 191 192 case pi.SettingKeyProposalAmountMin: 193 u, err := strconv.ParseUint(v.Value, 10, 64) 194 if err != nil { 195 return nil, err 196 } 197 amountMin = u 198 199 case pi.SettingKeyProposalAmountMax: 200 u, err := strconv.ParseUint(v.Value, 10, 64) 201 if err != nil { 202 return nil, err 203 } 204 amountMax = u 205 206 case pi.SettingKeyProposalStartDateMin: 207 u, err := strconv.ParseInt(v.Value, 10, 64) 208 if err != nil { 209 return nil, err 210 } 211 startDateMin = u 212 213 case pi.SettingKeyProposalEndDateMax: 214 u, err := strconv.ParseInt(v.Value, 10, 64) 215 if err != nil { 216 return nil, err 217 } 218 endDateMax = u 219 220 case pi.SettingKeyProposalDomains: 221 err := json.Unmarshal([]byte(v.Value), &domains) 222 if err != nil { 223 return nil, err 224 } 225 // Ensure no empty strings. 226 for _, d := range domains { 227 if d == "" { 228 return nil, errors.Errorf("proposal domain can not be an empty " + 229 "string") 230 } 231 } 232 233 case pi.SettingKeyBillingStatusChangesMax: 234 u, err := strconv.ParseUint(v.Value, 10, 64) 235 if err != nil { 236 return nil, err 237 } 238 billingStatusChangesMax = uint32(u) 239 240 case pi.SettingKeySummariesPageSize: 241 u, err := strconv.ParseUint(v.Value, 10, 64) 242 if err != nil { 243 return nil, err 244 } 245 summariesPageSize = uint32(u) 246 247 case pi.SettingKeyBillingStatusChangesPageSize: 248 u, err := strconv.ParseUint(v.Value, 10, 64) 249 if err != nil { 250 return nil, err 251 } 252 billingStatusChangesPageSize = uint32(u) 253 254 default: 255 // Skip unknown settings 256 log.Warnf("Unknown plugin setting %v; Skipping...", v.Key) 257 } 258 } 259 } 260 261 // Verify all plugin settings have been provided 262 switch { 263 case textFileSizeMax == 0: 264 return nil, errors.Errorf("plugin setting not found: %v", 265 pi.SettingKeyTextFileSizeMax) 266 case imageFileCountMax == 0: 267 return nil, errors.Errorf("plugin setting not found: %v", 268 pi.SettingKeyImageFileCountMax) 269 case imageFileSizeMax == 0: 270 return nil, errors.Errorf("plugin setting not found: %v", 271 pi.SettingKeyImageFileSizeMax) 272 case titleLengthMin == 0: 273 return nil, errors.Errorf("plugin setting not found: %v", 274 pi.SettingKeyTitleLengthMin) 275 case titleLengthMax == 0: 276 return nil, errors.Errorf("plugin setting not found: %v", 277 pi.SettingKeyTitleLengthMax) 278 case len(titleSupportedChars) == 0: 279 return nil, errors.Errorf("plugin setting not found: %v", 280 pi.SettingKeyTitleSupportedChars) 281 case amountMin == 0: 282 return nil, errors.Errorf("plugin setting not found: %v", 283 pi.SettingKeyProposalAmountMin) 284 case amountMax == 0: 285 return nil, errors.Errorf("plugin setting not found: %v", 286 pi.SettingKeyProposalAmountMax) 287 case endDateMax == 0: 288 return nil, errors.Errorf("plugin setting not found: %v", 289 pi.SettingKeyProposalEndDateMax) 290 case len(domains) == 0: 291 return nil, errors.Errorf("plugin setting not found: %v", 292 pi.SettingKeyProposalDomains) 293 case summariesPageSize == 0: 294 return nil, errors.Errorf("plugin setting not found: %v", 295 pi.SettingKeySummariesPageSize) 296 case billingStatusChangesPageSize == 0: 297 return nil, errors.Errorf("plugin setting not found: %v", 298 pi.SettingKeyBillingStatusChangesPageSize) 299 } 300 301 // Setup pi context 302 p := Pi{ 303 cfg: cfg, 304 politeiad: pdc, 305 userdb: udb, 306 sessions: s, 307 events: e, 308 mail: m, 309 policy: &v1.PolicyReply{ 310 TextFileSizeMax: textFileSizeMax, 311 ImageFileCountMax: imageFileCountMax, 312 ImageFileSizeMax: imageFileSizeMax, 313 NameLengthMin: titleLengthMin, 314 NameLengthMax: titleLengthMax, 315 NameSupportedChars: titleSupportedChars, 316 AmountMin: amountMin, 317 AmountMax: amountMax, 318 StartDateMin: startDateMin, 319 EndDateMax: endDateMax, 320 Domains: domains, 321 SummariesPageSize: summariesPageSize, 322 BillingStatusChangesPageSize: billingStatusChangesPageSize, 323 BillingStatusChangesMax: billingStatusChangesMax, 324 }, 325 } 326 327 // Setup event listeners 328 p.setupEventListeners() 329 330 return &p, nil 331 }