-
Notifications
You must be signed in to change notification settings - Fork 0
/
v1.go
1227 lines (1075 loc) · 56.6 KB
/
v1.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package v1
import (
"fmt"
)
type ErrorStatusT int
type PropStateT int
type PropStatusT int
type PropVoteStatusT int
type UserManageActionT int
type EmailNotificationT int
const (
PoliteiaWWWAPIVersion = 1 // API version this backend understands
CsrfToken = "X-CSRF-Token" // CSRF token for replies
Forward = "X-Forwarded-For" // Proxy header
RouteVersion = "/version"
RoutePolicy = "/policy"
RouteSecret = "/secret"
RouteLogin = "/login"
RouteLogout = "/logout"
RouteUserMe = "/user/me"
RouteUserDetails = "/user/{userid:[0-9a-zA-Z-]{36}}"
RouteNewUser = "/user/new"
RouteResendVerification = "/user/new/resend"
RouteVerifyNewUser = "/user/verify"
RouteUpdateUserKey = "/user/key"
RouteVerifyUpdateUserKey = "/user/key/verify"
RouteChangeUsername = "/user/username/change"
RouteChangePassword = "/user/password/change"
RouteResetPassword = "/user/password/reset"
RouteUserProposals = "/user/proposals"
RouteUserProposalCredits = "/user/proposals/credits"
RouteUserCommentsLikes = "/user/proposals/{token:[A-z0-9]{64}}/commentslikes"
RouteVerifyUserPayment = "/user/verifypayment"
RouteUserPaymentsRescan = "/user/payments/rescan"
RouteManageUser = "/user/manage"
RouteEditUser = "/user/edit"
RouteUsers = "/users"
RouteTokenInventory = "/proposals/tokeninventory"
RouteAllVetted = "/proposals/vetted"
RouteAllUnvetted = "/proposals/unvetted"
RouteNewProposal = "/proposals/new"
RouteEditProposal = "/proposals/edit"
RouteAuthorizeVote = "/proposals/authorizevote"
RouteStartVote = "/proposals/startvote"
RouteActiveVote = "/proposals/activevote" // XXX rename to ActiveVotes
RouteCastVotes = "/proposals/castvotes"
RouteAllVoteStatus = "/proposals/votestatus"
RoutePropsStats = "/proposals/stats"
RouteProposalPaywallDetails = "/proposals/paywall"
RouteProposalPaywallPayment = "/proposals/paywallpayment"
RouteProposalDetails = "/proposals/{token:[A-z0-9]{64}}"
RouteSetProposalStatus = "/proposals/{token:[A-z0-9]{64}}/status"
RouteCommentsGet = "/proposals/{token:[A-z0-9]{64}}/comments"
RouteVoteResults = "/proposals/{token:[A-z0-9]{64}}/votes"
RouteVoteStatus = "/proposals/{token:[A-z0-9]{64}}/votestatus"
RouteNewComment = "/comments/new"
RouteLikeComment = "/comments/like"
RouteCensorComment = "/comments/censor"
RouteUnauthenticatedWebSocket = "/ws"
RouteAuthenticatedWebSocket = "/aws"
// VerificationTokenSize is the size of verification token in bytes
VerificationTokenSize = 32
// VerificationExpiryHours is the number of hours before the
// verification token expires
VerificationExpiryHours = 24
// PolicyMaxImages is the maximum number of images accepted
// when creating a new proposal
PolicyMaxImages = 5
// PolicyMaxImageSize is the maximum image file size (in bytes)
// accepted when creating a new proposal
PolicyMaxImageSize = 512 * 1024
// PolicyMaxMDs is the maximum number of markdown files accepted
// when creating a new proposal
PolicyMaxMDs = 1
// PolicyMaxMDSize is the maximum markdown file size (in bytes)
// accepted when creating a new proposal
PolicyMaxMDSize = 512 * 1024
// PolicyMinPasswordLength is the minimum number of characters
// accepted for user passwords
PolicyMinPasswordLength = 8
// PolicyMaxUsernameLength is the max length of a username
PolicyMaxUsernameLength = 30
// PolicyMinUsernameLength is the min length of a username
PolicyMinUsernameLength = 3
// PolicyMaxProposalNameLength is the max length of a proposal name
PolicyMaxProposalNameLength = 80
// PolicyMinProposalNameLength is the min length of a proposal name
PolicyMinProposalNameLength = 8
// PolicyMaxCommentLength is the maximum number of characters
// accepted for comments
PolicyMaxCommentLength = 8000
// ProposalListPageSize is the maximum number of proposals returned
// for the routes that return lists of proposals
ProposalListPageSize = 20
// UserListPageSize is the maximum number of users returned
// for the routes that return lists of users
UserListPageSize = 20
// Error status codes
ErrorStatusInvalid ErrorStatusT = 0
ErrorStatusInvalidEmailOrPassword ErrorStatusT = 1
ErrorStatusMalformedEmail ErrorStatusT = 2
ErrorStatusVerificationTokenInvalid ErrorStatusT = 3
ErrorStatusVerificationTokenExpired ErrorStatusT = 4
ErrorStatusProposalMissingFiles ErrorStatusT = 5
ErrorStatusProposalNotFound ErrorStatusT = 6
ErrorStatusProposalDuplicateFilenames ErrorStatusT = 7
ErrorStatusProposalInvalidTitle ErrorStatusT = 8
ErrorStatusMaxMDsExceededPolicy ErrorStatusT = 9
ErrorStatusMaxImagesExceededPolicy ErrorStatusT = 10
ErrorStatusMaxMDSizeExceededPolicy ErrorStatusT = 11
ErrorStatusMaxImageSizeExceededPolicy ErrorStatusT = 12
ErrorStatusMalformedPassword ErrorStatusT = 13
ErrorStatusCommentNotFound ErrorStatusT = 14
ErrorStatusInvalidFilename ErrorStatusT = 15
ErrorStatusInvalidFileDigest ErrorStatusT = 16
ErrorStatusInvalidBase64 ErrorStatusT = 17
ErrorStatusInvalidMIMEType ErrorStatusT = 18
ErrorStatusUnsupportedMIMEType ErrorStatusT = 19
ErrorStatusInvalidPropStatusTransition ErrorStatusT = 20
ErrorStatusInvalidPublicKey ErrorStatusT = 21
ErrorStatusNoPublicKey ErrorStatusT = 22
ErrorStatusInvalidSignature ErrorStatusT = 23
ErrorStatusInvalidInput ErrorStatusT = 24
ErrorStatusInvalidSigningKey ErrorStatusT = 25
ErrorStatusCommentLengthExceededPolicy ErrorStatusT = 26
ErrorStatusUserNotFound ErrorStatusT = 27
ErrorStatusWrongStatus ErrorStatusT = 28
ErrorStatusNotLoggedIn ErrorStatusT = 29
ErrorStatusUserNotPaid ErrorStatusT = 30
ErrorStatusReviewerAdminEqualsAuthor ErrorStatusT = 31
ErrorStatusMalformedUsername ErrorStatusT = 32
ErrorStatusDuplicateUsername ErrorStatusT = 33
ErrorStatusVerificationTokenUnexpired ErrorStatusT = 34
ErrorStatusCannotVerifyPayment ErrorStatusT = 35
ErrorStatusDuplicatePublicKey ErrorStatusT = 36
ErrorStatusInvalidPropVoteStatus ErrorStatusT = 37
ErrorStatusUserLocked ErrorStatusT = 38
ErrorStatusNoProposalCredits ErrorStatusT = 39
ErrorStatusInvalidUserManageAction ErrorStatusT = 40
ErrorStatusUserActionNotAllowed ErrorStatusT = 41
ErrorStatusWrongVoteStatus ErrorStatusT = 42
ErrorStatusCannotCommentOnProp ErrorStatusT = 43
ErrorStatusCannotVoteOnPropComment ErrorStatusT = 44
ErrorStatusChangeMessageCannotBeBlank ErrorStatusT = 45
ErrorStatusCensorReasonCannotBeBlank ErrorStatusT = 46
ErrorStatusCannotCensorComment ErrorStatusT = 47
ErrorStatusUserNotAuthor ErrorStatusT = 48
ErrorStatusVoteNotAuthorized ErrorStatusT = 49
ErrorStatusVoteAlreadyAuthorized ErrorStatusT = 50
ErrorStatusInvalidAuthVoteAction ErrorStatusT = 51
ErrorStatusUserDeactivated ErrorStatusT = 52
ErrorStatusInvalidPropVoteBits ErrorStatusT = 53
ErrorStatusInvalidPropVoteParams ErrorStatusT = 54
ErrorStatusEmailNotVerified ErrorStatusT = 55
ErrorStatusInvalidUUID ErrorStatusT = 56
ErrorStatusInvalidLikeCommentAction ErrorStatusT = 57
ErrorStatusInvalidCensorshipToken ErrorStatusT = 58
ErrorStatusEmailAlreadyVerified ErrorStatusT = 59
ErrorStatusInvalidPassword ErrorStatusT = 85
// CMS Errors
ErrorStatusMalformedName ErrorStatusT = 60
ErrorStatusMalformedLocation ErrorStatusT = 61
ErrorStatusInvoiceNotFound ErrorStatusT = 62
ErrorStatusInvalidMonthYearRequest ErrorStatusT = 63
ErrorStatusMalformedInvoiceFile ErrorStatusT = 64
ErrorStatusInvalidInvoiceStatusTransition ErrorStatusT = 65
ErrorStatusReasonNotProvided ErrorStatusT = 66
ErrorStatusInvoiceDuplicate ErrorStatusT = 67
ErrorStatusInvalidPaymentAddress ErrorStatusT = 68
ErrorStatusMalformedLineItem ErrorStatusT = 69
ErrorStatusInvoiceMissingName ErrorStatusT = 70
ErrorStatusInvoiceMissingLocation ErrorStatusT = 71
ErrorStatusInvoiceMissingContact ErrorStatusT = 72
ErrorStatusInvoiceMissingRate ErrorStatusT = 73
ErrorStatusInvoiceInvalidRate ErrorStatusT = 74
ErrorStatusInvoiceMalformedContact ErrorStatusT = 75
ErrorStatusMalformedProposalToken ErrorStatusT = 76
ErrorStatusMalformedDomain ErrorStatusT = 77
ErrorStatusMalformedSubdomain ErrorStatusT = 78
ErrorStatusMalformedDescription ErrorStatusT = 79
ErrorStatusWrongInvoiceStatus ErrorStatusT = 80
ErrorStatusInvoiceRequireLineItems ErrorStatusT = 81
ErrorStatusMultipleInvoiceMonthYear ErrorStatusT = 82
ErrorStatusInvalidInvoiceMonthYear ErrorStatusT = 83
ErrorStatusInvalidExchangeRate ErrorStatusT = 84
// Proposal state codes
//
// PropStateUnvetted includes proposals with a status of:
// * PropStatusNotReviewed
// * PropStatusUnreviewedChanges
// * PropStatusCensored
// PropStateVetted includes proposals with a status of:
// * PropStatusPublic
// * PropStatusAbandoned
//
// Proposal states correspond to the unvetted and vetted politeiad
// repositories.
PropStateInvalid PropStateT = 0 // Invalid state
PropStateUnvetted PropStateT = 1 // Unvetted proposal
PropStateVetted PropStateT = 2 // Vetted proposal
// Proposal status codes (set and get)
PropStatusInvalid PropStatusT = 0 // Invalid status
PropStatusNotFound PropStatusT = 1 // Proposal not found
PropStatusNotReviewed PropStatusT = 2 // Proposal has not been reviewed
PropStatusCensored PropStatusT = 3 // Proposal has been censored
PropStatusPublic PropStatusT = 4 // Proposal is publicly visible
PropStatusUnreviewedChanges PropStatusT = 5 // Proposal is not public and has unreviewed changes
PropStatusAbandoned PropStatusT = 6 // Proposal has been declared abandoned by an admin
// Proposal vote status codes
PropVoteStatusInvalid PropVoteStatusT = 0 // Invalid vote status
PropVoteStatusNotAuthorized PropVoteStatusT = 1 // Vote has not been authorized by author
PropVoteStatusAuthorized PropVoteStatusT = 2 // Vote has been authorized by author
PropVoteStatusStarted PropVoteStatusT = 3 // Proposal vote has been started
PropVoteStatusFinished PropVoteStatusT = 4 // Proposal vote has been finished
PropVoteStatusDoesntExist PropVoteStatusT = 5 // Proposal doesn't exist
// User manage actions
UserManageInvalid UserManageActionT = 0 // Invalid action type
UserManageExpireNewUserVerification UserManageActionT = 1
UserManageExpireUpdateKeyVerification UserManageActionT = 2
UserManageExpireResetPasswordVerification UserManageActionT = 3
UserManageClearUserPaywall UserManageActionT = 4
UserManageUnlock UserManageActionT = 5
UserManageDeactivate UserManageActionT = 6
UserManageReactivate UserManageActionT = 7
// Authorize vote actions
// XXX these should be in bitumplugin
AuthVoteActionAuthorize = "authorize" // Authorize a proposal vote
AuthVoteActionRevoke = "revoke" // Revoke a proposal vote authorization
// Email notification types
NotificationEmailMyProposalStatusChange EmailNotificationT = 1 << 0
NotificationEmailMyProposalVoteStarted EmailNotificationT = 1 << 1
NotificationEmailRegularProposalVetted EmailNotificationT = 1 << 2
NotificationEmailRegularProposalEdited EmailNotificationT = 1 << 3
NotificationEmailRegularProposalVoteStarted EmailNotificationT = 1 << 4
NotificationEmailAdminProposalNew EmailNotificationT = 1 << 5
NotificationEmailAdminProposalVoteAuthorized EmailNotificationT = 1 << 6
NotificationEmailCommentOnMyProposal EmailNotificationT = 1 << 7
NotificationEmailCommentOnMyComment EmailNotificationT = 1 << 8
)
var (
// PolicyProposalNameSupportedChars is the regular expression of a valid
// proposal name
PolicyProposalNameSupportedChars = []string{
"A-z", "0-9", "&", ".", ",", ":", ";", "-", " ", "@", "+", "#", "/",
"(", ")", "!", "?", "\"", "'"}
// PolicyUsernameSupportedChars is the regular expression of a valid
// username
PolicyUsernameSupportedChars = []string{
"a-z", "0-9", ".", ",", ":", ";", "-", "@", "+", "(", ")", "_"}
// PoliteiaWWWAPIRoute is the prefix to the API route
PoliteiaWWWAPIRoute = fmt.Sprintf("/v%v", PoliteiaWWWAPIVersion)
// CookieSession is the cookie name that indicates that a user is
// logged in.
CookieSession = "session"
// ErrorStatus converts error status codes to human readable text.
ErrorStatus = map[ErrorStatusT]string{
ErrorStatusInvalid: "invalid error status",
ErrorStatusInvalidEmailOrPassword: "invalid email or password",
ErrorStatusMalformedEmail: "malformed email",
ErrorStatusVerificationTokenInvalid: "invalid verification token",
ErrorStatusVerificationTokenExpired: "expired verification token",
ErrorStatusProposalMissingFiles: "missing proposal files",
ErrorStatusProposalNotFound: "proposal not found",
ErrorStatusProposalDuplicateFilenames: "duplicate proposal files",
ErrorStatusProposalInvalidTitle: "invalid proposal title",
ErrorStatusMaxMDsExceededPolicy: "maximum markdown files exceeded",
ErrorStatusMaxImagesExceededPolicy: "maximum image files exceeded",
ErrorStatusMaxMDSizeExceededPolicy: "maximum markdown file size exceeded",
ErrorStatusMaxImageSizeExceededPolicy: "maximum image file size exceeded",
ErrorStatusMalformedPassword: "malformed password",
ErrorStatusCommentNotFound: "comment not found",
ErrorStatusInvalidFilename: "invalid filename",
ErrorStatusInvalidFileDigest: "invalid file digest",
ErrorStatusInvalidBase64: "invalid base64 file content",
ErrorStatusInvalidMIMEType: "invalid MIME type detected for file",
ErrorStatusUnsupportedMIMEType: "unsupported MIME type for file",
ErrorStatusInvalidPropStatusTransition: "invalid proposal status",
ErrorStatusInvalidPublicKey: "invalid public key",
ErrorStatusNoPublicKey: "no active public key",
ErrorStatusInvalidSignature: "invalid signature",
ErrorStatusInvalidInput: "invalid input",
ErrorStatusInvalidSigningKey: "invalid signing key",
ErrorStatusCommentLengthExceededPolicy: "maximum comment length exceeded",
ErrorStatusUserNotFound: "user not found",
ErrorStatusWrongStatus: "wrong status",
ErrorStatusNotLoggedIn: "user not logged in",
ErrorStatusUserNotPaid: "user hasn't paid paywall",
ErrorStatusReviewerAdminEqualsAuthor: "user cannot change the status of his own proposal",
ErrorStatusMalformedUsername: "malformed username",
ErrorStatusDuplicateUsername: "duplicate username",
ErrorStatusVerificationTokenUnexpired: "verification token not yet expired",
ErrorStatusCannotVerifyPayment: "cannot verify payment at this time",
ErrorStatusDuplicatePublicKey: "public key already taken by another user",
ErrorStatusInvalidPropVoteStatus: "invalid proposal vote status",
ErrorStatusUserLocked: "user locked due to too many login attempts",
ErrorStatusNoProposalCredits: "no proposal credits",
ErrorStatusInvalidUserManageAction: "invalid user edit action",
ErrorStatusUserActionNotAllowed: "user action is not allowed",
ErrorStatusWrongVoteStatus: "wrong proposal vote status",
ErrorStatusCannotCommentOnProp: "cannot comment on proposal",
ErrorStatusCannotVoteOnPropComment: "cannot vote on proposal comment",
ErrorStatusChangeMessageCannotBeBlank: "status change message cannot be blank",
ErrorStatusCensorReasonCannotBeBlank: "censor comment reason cannot be blank",
ErrorStatusCannotCensorComment: "cannot censor comment",
ErrorStatusUserNotAuthor: "user is not the proposal author",
ErrorStatusVoteNotAuthorized: "vote has not been authorized",
ErrorStatusVoteAlreadyAuthorized: "vote has already been authorized",
ErrorStatusInvalidAuthVoteAction: "invalid authorize vote action",
ErrorStatusUserDeactivated: "user account is deactivated",
ErrorStatusInvalidPropVoteBits: "invalid proposal vote option bits",
ErrorStatusInvalidPropVoteParams: "invalid proposal vote parameters",
ErrorStatusEmailNotVerified: "email address is not verified",
ErrorStatusInvalidUUID: "invalid user UUID",
ErrorStatusInvalidLikeCommentAction: "invalid like comment action",
ErrorStatusInvalidCensorshipToken: "invalid proposal censorship token",
ErrorStatusEmailAlreadyVerified: "email address is already verified",
ErrorStatusMalformedName: "malformed name",
ErrorStatusMalformedLocation: "malformed location",
ErrorStatusInvoiceNotFound: "invoice cannot be found",
ErrorStatusInvalidMonthYearRequest: "month or year was set, while the other was not",
ErrorStatusInvalidInvoiceStatusTransition: "invalid invoice status transition",
ErrorStatusReasonNotProvided: "reason for action not provided",
ErrorStatusMalformedInvoiceFile: "submitted invoice file is malformed",
ErrorStatusInvoiceDuplicate: "submitted invoice is a duplicate of an existing invoice",
ErrorStatusInvalidPaymentAddress: "invalid payment address",
ErrorStatusMalformedLineItem: "malformed line item submitted",
ErrorStatusInvoiceMissingName: "invoice missing contractor name",
ErrorStatusInvoiceMissingLocation: "invoice missing contractor location",
ErrorStatusInvoiceMissingContact: "invoice missing contractor contact",
ErrorStatusInvoiceMalformedContact: "invoice has malformed contractor contact",
ErrorStatusInvoiceMissingRate: "invoice missing contractor rate",
ErrorStatusInvoiceInvalidRate: "invoice has invalid contractor rate",
ErrorStatusMalformedProposalToken: "line item has malformed proposal token",
ErrorStatusMalformedDomain: "line item has malformed domain",
ErrorStatusMalformedSubdomain: "line item has malformed subdomain",
ErrorStatusMalformedDescription: "line item has malformed description",
ErrorStatusWrongInvoiceStatus: "invoice is an wrong status to be editted (approved, rejected or paid)",
ErrorStatusInvoiceRequireLineItems: "invoices require at least 1 line item",
ErrorStatusMultipleInvoiceMonthYear: "only one invoice per month/year is allowed to be submitted",
ErrorStatusInvalidInvoiceMonthYear: "an invalid month/year was submitted on an invoice",
ErrorStatusInvalidExchangeRate: "exchange rate was invalid or didn't match expected result",
ErrorStatusInvalidPassword: "invalid password",
}
// PropStatus converts propsal status codes to human readable text
PropStatus = map[PropStatusT]string{
PropStatusInvalid: "invalid proposal status",
PropStatusNotFound: "not found",
PropStatusNotReviewed: "unreviewed",
PropStatusCensored: "censored",
PropStatusPublic: "public",
PropStatusAbandoned: "abandoned",
}
// PropVoteStatus converts votes status codes to human readable text
PropVoteStatus = map[PropVoteStatusT]string{
PropVoteStatusInvalid: "invalid vote status",
PropVoteStatusNotAuthorized: "voting has not been authorized by author",
PropVoteStatusAuthorized: "voting has been authorized by author",
PropVoteStatusStarted: "voting active",
PropVoteStatusFinished: "voting finished",
PropVoteStatusDoesntExist: "proposal does not exist",
}
// UserManageAction converts user edit actions to human readable text
UserManageAction = map[UserManageActionT]string{
UserManageInvalid: "invalid action",
UserManageExpireNewUserVerification: "expire new user verification",
UserManageExpireUpdateKeyVerification: "expire update key verification",
UserManageExpireResetPasswordVerification: "expire reset password verification",
UserManageClearUserPaywall: "clear user paywall",
UserManageUnlock: "unlock user",
UserManageDeactivate: "deactivate user",
UserManageReactivate: "reactivate user",
}
)
// File describes an individual file that is part of the proposal. The
// directory structure must be flattened. The server side SHALL verify MIME
// and Digest.
type File struct {
// Meta-data
Name string `json:"name"` // Suggested filename
MIME string `json:"mime"` // Mime type
Digest string `json:"digest"` // Digest of unencoded payload
// Data
Payload string `json:"payload"` // File content, base64 encoded
}
// CensorshipRecord contains the proof that a proposal was accepted for review.
// The proof is verifiable on the client side.
//
// The Merkle field contains the ordered merkle root of all files in the proposal.
// The Token field contains a random censorship token that is signed by the
// server private key. The token can be used on the client to verify the
// authenticity of the CensorshipRecord.
type CensorshipRecord struct {
Token string `json:"token"` // Censorship token
Merkle string `json:"merkle"` // Merkle root of proposal
Signature string `json:"signature"` // Server side signature of []byte(Merkle+Token)
}
// ProposalRecord is an entire proposal and it's content.
type ProposalRecord struct {
Name string `json:"name"` // Suggested short proposal name
State PropStateT `json:"state"` // Current state of proposal
Status PropStatusT `json:"status"` // Current status of proposal
Timestamp int64 `json:"timestamp"` // Last update of proposal
UserId string `json:"userid"` // ID of user who submitted proposal
Username string `json:"username"` // Username of user who submitted proposal
PublicKey string `json:"publickey"` // Key used for signature.
Signature string `json:"signature"` // Signature of merkle root
Files []File `json:"files"` // Files that make up the proposal
NumComments uint `json:"numcomments"` // Number of comments on the proposal
Version string `json:"version"` // Record version
StatusChangeMessage string `json:"statuschangemessage,omitempty"` // Message associated to the status change
PublishedAt int64 `json:"publishedat,omitempty"` // The timestamp of when the proposal has been published
CensoredAt int64 `json:"censoredat,omitempty"` // The timestamp of when the proposal has been censored
AbandonedAt int64 `json:"abandonedat,omitempty"` // The timestamp of when the proposal has been abandoned
CensorshipRecord CensorshipRecord `json:"censorshiprecord"`
}
// ProposalCredit contains the details of a proposal credit that has been
// purchased by the user.
type ProposalCredit struct {
PaywallID uint64 `json:"paywallid"` // paywall that created this credit
Price uint64 `json:"price"` // Price credit was purchased at in atoms
DatePurchased int64 `json:"datepurchased"` // Unix timestamp of the purchase date
TxID string `json:"txid"` // Bitum tx that purchased this credit
}
// UserError represents an error that is caused by something that the user
// did (malformed input, bad timing, etc).
type UserError struct {
ErrorCode ErrorStatusT
ErrorContext []string
}
// Error satisfies the error interface.
func (e UserError) Error() string {
return fmt.Sprintf("user error code: %v", e.ErrorCode)
}
// PDError is emitted when an HTTP error response is returned from Politeiad
// for a request. It contains the HTTP status code and the JSON response body.
type PDError struct {
HTTPCode int
ErrorReply PDErrorReply
}
// Error satisfies the error interface.
func (e PDError) Error() string {
return fmt.Sprintf("error from politeiad: %v %v", e.HTTPCode,
e.ErrorReply.ErrorCode)
}
// PDErrorReply is an error reply returned from Politeiad whenever an
// error occurs.
type PDErrorReply struct {
ErrorCode int
ErrorContext []string
}
// ErrorReply are replies that the server returns a when it encounters an
// unrecoverable problem while executing a command. The HTTP Error Code
// shall be 500 if it's an internal server error or 4xx if it's a user error.
type ErrorReply struct {
ErrorCode int64 `json:"errorcode,omitempty"`
ErrorContext []string `json:"errorcontext,omitempty"`
}
// Version command is used to determine the version of the API this backend
// understands and additionally it provides the route to said API. This call
// is required in order to establish CSRF for the session. The client should
// verify compatibility with the server version.
type Version struct{}
// VersionReply returns information that indicates what version of the server
// is running and additionally the route to the API and the public signing key of
// the server.
type VersionReply struct {
Version uint `json:"version"` // politeia WWW API version
Route string `json:"route"` // prefix to API calls
PubKey string `json:"pubkey"` // Server public key
TestNet bool `json:"testnet"` // Network indicator
Mode string `json:"mode"` // current politeiawww mode running (piwww or cmswww)
ActiveUserSession bool `json:"activeusersession"` // indicates if there is an active user session
}
// NewUser is used to request that a new user be created within the db.
// If successful, the user will require verification before being able to login.
type NewUser struct {
Email string `json:"email"`
Password string `json:"password"`
PublicKey string `json:"publickey"`
Username string `json:"username"`
}
// NewUserReply is used to reply to the NewUser command with an error
// if the command is unsuccessful.
type NewUserReply struct {
VerificationToken string `json:"verificationtoken"` // Server verification token
}
// VerifyNewUser is used to perform verification for the user created through
// the NewUser command using the token provided in NewUserReply.
type VerifyNewUser struct {
Email string `schema:"email"` // User email address
VerificationToken string `schema:"verificationtoken"` // Server provided verification token
Signature string `schema:"signature"` // Verification token signature
}
// VerifyNewUserReply
type VerifyNewUserReply struct{}
// ResendVerification is used to resent a new user verification email.
type ResendVerification struct {
Email string `json:"email"`
PublicKey string `json:"publickey"`
}
// ResendVerificationReply is used to reply to the ResendVerification command.
type ResendVerificationReply struct {
VerificationToken string `json:"verificationtoken"` // Server verification token
}
// UpdateUserKey is used to request a new active key.
type UpdateUserKey struct {
PublicKey string `json:"publickey"`
}
// UpdateUserKeyReply replies to the UpdateUserKey command.
type UpdateUserKeyReply struct {
VerificationToken string `json:"verificationtoken"` // Server verification token
}
// VerifyUpdateUserKey is used to request a new active key.
type VerifyUpdateUserKey struct {
VerificationToken string `json:"verificationtoken"` // Server provided verification token
Signature string `json:"signature"` // Verification token signature
}
// VerifyUpdateUserKeyReply replies to the VerifyUpdateUserKey command.
type VerifyUpdateUserKeyReply struct{}
// ChangeUsername is used to perform a username change while the user
// is logged in.
type ChangeUsername struct {
Password string `json:"password"`
NewUsername string `json:"newusername"`
}
// ChangeUsernameReply is used to perform a username change while the user
// is logged in.
type ChangeUsernameReply struct{}
// ChangePassword is used to perform a password change while the user
// is logged in.
type ChangePassword struct {
CurrentPassword string `json:"currentpassword"`
NewPassword string `json:"newpassword"`
}
// ChangePasswordReply is used to perform a password change while the user
// is logged in.
type ChangePasswordReply struct{}
// ResetPassword is used to perform a password change when the
// user is not logged in.
type ResetPassword struct {
Email string `json:"email"`
VerificationToken string `json:"verificationtoken"`
NewPassword string `json:"newpassword"`
}
// ResetPasswordReply is used to reply to the ResetPassword command
// with an error if the command is unsuccessful.
type ResetPasswordReply struct {
VerificationToken string `json:"verificationtoken"`
}
// UserProposalCredits is used to request a list of all the user's unspent
// proposal credits and a list of all of the user's spent proposal credits.
// A spent credit means that the credit was used to submit a proposal. Spent
// credits have a proposal censorship token associated with them to signify
// that they have been spent.
type UserProposalCredits struct{}
// UserProposalCredits is used to reply to the UserProposalCredits command.
type UserProposalCreditsReply struct {
UnspentCredits []ProposalCredit `json:"unspentcredits"` // credits that the user has purchased, but have not yet been used to submit proposals (credit price in atoms)
SpentCredits []ProposalCredit `json:"spentcredits"` // credits that the user has purchased and that have already been used to submit proposals (credit price in atoms)
}
// UserPaymentsRescan allows an admin to rescan a user's paywall address to
// check for any payments that may have been missed by paywall polling. Any
// proposal credits that are created as a result of the rescan are returned in
// the UserPaymentsRescanReply. This call isn't RESTful, but a PUT request is
// used since it's idempotent.
type UserPaymentsRescan struct {
UserID string `json:"userid"` // ID of user to rescan
}
// UserPaymentsRescanReply is used to reply to the UserPaymentsRescan command.
type UserPaymentsRescanReply struct {
NewCredits []ProposalCredit `json:"newcredits"` // Credits that were created by the rescan
}
// UserProposals is used to request a list of proposals that the
// user has submitted. This command optionally takes either a Before
// or After parameter, which specify a proposal's censorship token.
// If After is specified, the "page" returned starts after the proposal
// whose censorship token is provided. If Before is specified, the "page"
// returned starts before the proposal whose censorship token is provided.
type UserProposals struct {
UserId string `schema:"userid"`
Before string `schema:"before"`
After string `schema:"after"`
}
// UserProposalsReply replies to the UserProposals command with
// a list of proposals that the user has submitted and the total
// amount of proposals
type UserProposalsReply struct {
Proposals []ProposalRecord `json:"proposals"` // user proposals
NumOfProposals int `json:"numofproposals"` // number of proposals submitted by the user
}
// VerifyUserPayment is used to request the server to check for the
// provided transaction on the Bitum blockchain and verify that it
// satisfies the requirements for a user to pay his registration fee.
type VerifyUserPayment struct {
}
type VerifyUserPaymentReply struct {
HasPaid bool `json:"haspaid"`
PaywallAddress string `json:"paywalladdress"` // Registration paywall address
PaywallAmount uint64 `json:"paywallamount"` // Registration paywall amount in atoms
PaywallTxNotBefore int64 `json:"paywalltxnotbefore"` // Minimum timestamp for paywall tx
}
// Users is used to request a list of users given a filter.
type Users struct {
Username string `json:"username"` // String which should match or partially match a username
Email string `json:"email"` // String which should match or partially match an email
PublicKey string `json:"publickey"` // Active or inactive user pubkey
}
// UsersReply is a reply to the Users command, replying with a list of users.
type UsersReply struct {
TotalUsers uint64 `json:"totalusers,omitempty"` // Total number of all users in the database
TotalMatches uint64 `json:"totalmatches"` // Total number of users that match the filters
Users []AbridgedUser `json:"users"` // List of users that match the filters
}
// AbridgedUser is a shortened version of User that's used for the admin list.
type AbridgedUser struct {
ID string `json:"id"`
Email string `json:"email,omitempty"`
Username string `json:"username"`
}
// Login attempts to login the user. Note that by necessity the password
// travels in the clear.
type Login struct {
Email string `json:"email"`
Password string `json:"password"`
}
// LoginReply is used to reply to the Login command.
type LoginReply struct {
IsAdmin bool `json:"isadmin"` // Set if user is an admin
UserID string `json:"userid"` // User id
Email string `json:"email"` // User email
Username string `json:"username"` // Username
PublicKey string `json:"publickey"` // Active public key
PaywallAddress string `json:"paywalladdress"` // Registration paywall address
PaywallAmount uint64 `json:"paywallamount"` // Registration paywall amount in atoms
PaywallTxNotBefore int64 `json:"paywalltxnotbefore"` // Minimum timestamp for paywall tx
PaywallTxID string `json:"paywalltxid"` // Paywall payment tx ID
ProposalCredits uint64 `json:"proposalcredits"` // Number of the proposal credits the user has available to spend
LastLoginTime int64 `json:"lastlogintime"` // Unix timestamp of last login date
SessionMaxAge int64 `json:"sessionmaxage"` // Unix timestamp of session max age
}
//Logout attempts to log the user out.
type Logout struct{}
// LogoutReply indicates whether the Logout command was success or not.
type LogoutReply struct{}
// Me asks the server to return pertinent user information.
//
// Note that MeReply is not present because LoginReply is reused
// for this endpoint.
type Me struct{}
// ProposalPaywallDetails is used to request proposal paywall details from the
// server that the user needs in order to purchase paywall credits.
type ProposalPaywallDetails struct{}
// ProposalPaywallDetailsReply is used to reply to the ProposalPaywallDetails
// command.
type ProposalPaywallDetailsReply struct {
CreditPrice uint64 `json:"creditprice"` // Cost per proposal credit in atoms
PaywallAddress string `json:"paywalladdress"` // Proposal paywall address
PaywallTxNotBefore int64 `json:"paywalltxnotbefore"` // Minimum timestamp for paywall tx
}
// ProposalPaywallPayment is used to request payment details for a pending
// proposal paywall payment.
type ProposalPaywallPayment struct{}
// ProposalPaywallPaymentReply is used to reply to the ProposalPaywallPayment
// command.
type ProposalPaywallPaymentReply struct {
TxID string `json:"txid"` // Transaction ID
TxAmount uint64 `json:"amount"` // Transaction amount in atoms
Confirmations uint64 `json:"confirmations"` // Number of block confirmations
}
// NewProposal attempts to submit a new proposal.
type NewProposal struct {
Files []File `json:"files"` // Proposal files
PublicKey string `json:"publickey"` // Key used for signature.
Signature string `json:"signature"` // Signature of merkle root
}
// NewProposalReply is used to reply to the NewProposal command
type NewProposalReply struct {
CensorshipRecord CensorshipRecord `json:"censorshiprecord"`
}
// ProposalsDetails is used to retrieve a proposal by it's token
// and by the proposal version (optional). If the version isn't specified
// the latest proposal version will be returned by default.
type ProposalsDetails struct {
Token string `json:"token"` // Censorship token
Version string `json:"version,omitempty"` // Proposal version
}
// ProposalDetailsReply is used to reply to a proposal details command.
type ProposalDetailsReply struct {
Proposal ProposalRecord `json:"proposal"`
}
// SetProposalStatus is used to publish or censor an unreviewed proposal.
type SetProposalStatus struct {
Token string `json:"token"`
ProposalStatus PropStatusT `json:"proposalstatus"`
StatusChangeMessage string `json:"statuschangemessage,omitempty"` // Message associated to the status change
Signature string `json:"signature"` // Signature of Token+string(ProposalStatus)+StatusChangeMessage
PublicKey string `json:"publickey"`
}
// SetProposalStatusReply is used to reply to a SetProposalStatus command.
type SetProposalStatusReply struct {
Proposal ProposalRecord `json:"proposal"`
}
// GetAllUnvetted retrieves all unvetted proposals; the maximum number returned
// is dictated by ProposalListPageSize. This command optionally takes either
// a Before or After parameter, which specify a proposal's censorship token.
// If After is specified, the "page" returned starts after the proposal whose
// censorship token is provided. If Before is specified, the "page" returned
// starts before the proposal whose censorship token is provided.
//
// Note: This call requires admin privileges.
type GetAllUnvetted struct {
Before string `schema:"before"`
After string `schema:"after"`
}
// GetAllUnvettedReply is used to reply with a list of all unvetted proposals.
type GetAllUnvettedReply struct {
Proposals []ProposalRecord `json:"proposals"`
}
// GetAllVetted retrieves vetted proposals; the maximum number returned is dictated
// by ProposalListPageSize. This command optionally takes either a Before or After
// parameter, which specify a proposal's censorship token. If After is specified,
// the "page" returned starts after the proposal whose censorship token is provided.
// If Before is specified, the "page" returned starts before the proposal whose
// censorship token is provided.
type GetAllVetted struct {
Before string `schema:"before"`
After string `schema:"after"`
}
// GetAllVettedReply is used to reply with a list of vetted proposals.
type GetAllVettedReply struct {
Proposals []ProposalRecord `json:"proposals"`
}
// Policy returns a struct with various maxima. The client shall observe the
// maxima.
type Policy struct{}
// PolicyReply is used to reply to the policy command. It returns
// the file upload restrictions set for Politeia.
type PolicyReply struct {
MinPasswordLength uint `json:"minpasswordlength"`
MinUsernameLength uint `json:"minusernamelength"`
MaxUsernameLength uint `json:"maxusernamelength"`
UsernameSupportedChars []string `json:"usernamesupportedchars"`
ProposalListPageSize uint `json:"proposallistpagesize"`
UserListPageSize uint `json:"userlistpagesize"`
MaxImages uint `json:"maximages"`
MaxImageSize uint `json:"maximagesize"`
MaxMDs uint `json:"maxmds"`
MaxMDSize uint `json:"maxmdsize"`
ValidMIMETypes []string `json:"validmimetypes"`
MinProposalNameLength uint `json:"minproposalnamelength"`
MaxProposalNameLength uint `json:"maxproposalnamelength"`
ProposalNameSupportedChars []string `json:"proposalnamesupportedchars"`
MaxCommentLength uint `json:"maxcommentlength"`
BackendPublicKey string `json:"backendpublickey"`
}
// VoteOption describes a single vote option.
type VoteOption struct {
Id string `json:"id"` // Single unique word identifying vote (e.g. yes)
Description string `json:"description"` // Longer description of the vote.
Bits uint64 `json:"bits"` // Bits used for this option
}
// Vote represents the vote options for vote that is identified by its token.
type Vote struct {
Token string `json:"token"` // Token that identifies vote
Mask uint64 `json:"mask"` // Valid votebits
Duration uint32 `json:"duration"` // Duration in blocks
QuorumPercentage uint32 `json:"quorumpercentage"` // Percent of eligible votes required for quorum
PassPercentage uint32 `json:"passpercentage"` // Percent of total votes required to pass
Options []VoteOption `json:"options"` // Vote options
}
// ActiveVote obtains all proposals that have active votes.
type ActiveVote struct{}
// ProposalVoteTuple is the proposal, vote and vote details.
type ProposalVoteTuple struct {
Proposal ProposalRecord `json:"proposal"` // Proposal
StartVote StartVote `json:"startvote"` // Vote bits and mask
StartVoteReply StartVoteReply `json:"startvotereply"` // Eligible tickets and other details
}
// ActiveVoteReply returns all proposals that have active votes.
type ActiveVoteReply struct {
Votes []ProposalVoteTuple `json:"votes"` // Active votes
}
// plugin commands
// AuthorizeVote is used to indicate that a proposal has been finalized and
// is ready to be voted on. The signature and public key are from the
// proposal author. The author can revoke a previously sent vote authorization
// by setting the Action field to revoke.
type AuthorizeVote struct {
Action string `json:"action"` // Authorize or revoke
Token string `json:"token"` // Proposal token
Signature string `json:"signature"` // Signature of token+version+action
PublicKey string `json:"publickey"` // Key used for signature
}
// AuthorizeVoteReply returns a receipt if the action was successfully
// executed.
type AuthorizeVoteReply struct {
Action string `json:"action"` // Authorize or revoke
Receipt string `json:"receipt"` // Server signature of client signature
}
// StartVote starts the voting process for a proposal.
type StartVote struct {
PublicKey string `json:"publickey"` // Key used for signature.
Vote Vote `json:"vote"` // Vote
Signature string `json:"signature"` // Signature of Votehash
}
// StartVoteReply returns the eligible ticket pool.
type StartVoteReply struct {
StartBlockHeight string `json:"startblockheight"` // Block height
StartBlockHash string `json:"startblockhash"` // Block hash
EndHeight string `json:"endheight"` // Height of vote end
EligibleTickets []string `json:"eligibletickets"` // Valid voting tickets
}
// CastVote is a signed vote.
type CastVote struct {
Token string `json:"token"` // Proposal ID
Ticket string `json:"ticket"` // Ticket ID
VoteBit string `json:"votebit"` // Vote bit that was selected, this is encode in hex
Signature string `json:"signature"` // Signature of Token+Ticket+VoteBit
}
// Ballot is a batch of votes that are sent to the server.
type Ballot struct {
Votes []CastVote `json:"votes"`
}
// CastVoteReply is the answer to the CastVote command.
type CastVoteReply struct {
ClientSignature string `json:"clientsignature"` // Signature that was sent in
Signature string `json:"signature"` // Signature of the ClientSignature
Error string `json:"error"` // Error if something went wrong during casting a vote
}
// CastVotesReply is a reply to a batched list of votes.
type BallotReply struct {
Receipts []CastVoteReply `json:"receipts"`
}
// VoteResults retrieves a single proposal vote results from the server.
type VoteResults struct{}
// VoteResultsReply returns the original proposal vote and the associated cast
// votes.
type VoteResultsReply struct {
StartVote StartVote `json:"startvote"` // Original vote
CastVotes []CastVote `json:"castvotes"` // Vote results
StartVoteReply StartVoteReply `json:"startvotereply"` // Eligible tickets and other details
}
// Comment is the structure that describes the full server side content. It
// includes server side meta-data as well.
type Comment struct {
// Data generated by client
Token string `json:"token"` // Censorship token
ParentID string `json:"parentid"` // Parent comment ID
Comment string `json:"comment"` // Comment
Signature string `json:"signature"` // Client Signature of Token+ParentID+Comment
PublicKey string `json:"publickey"` // Pubkey used for Signature
// Metadata generated by bitum plugin
CommentID string `json:"commentid"` // Comment ID
Receipt string `json:"receipt"` // Server signature of the client Signature
Timestamp int64 `json:"timestamp"` // Received UNIX timestamp
TotalVotes uint64 `json:"totalvotes"` // Total number of up/down votes
ResultVotes int64 `json:"resultvotes"` // Vote score
Censored bool `json:"censored"` // Has this comment been censored
// Metadata generated by www
UserID string `json:"userid"` // User id
Username string `json:"username"` // Username
}
// NewComment sends a comment from a user to a specific proposal. Note that
// the user is implied by the session. A parent ID of 0 indicates that the
// comment does not have a parent. A non-zero parent ID indicates that the
// comment is a reply to an existing comment.
type NewComment struct {
Token string `json:"token"` // Censorship token
ParentID string `json:"parentid"` // Parent comment ID
Comment string `json:"comment"` // Comment
Signature string `json:"signature"` // Client Signature of Token+ParentID+Comment
PublicKey string `json:"publickey"` // Pubkey used for Signature
}
// NewCommentReply returns the site generated Comment ID or an error if
// something went wrong.
type NewCommentReply struct {
Comment Comment `json:"comment"` // Comment + receipt
}
// GetComments retrieve all comments for a given proposal.
type GetComments struct {