From f9eec7f36416513e57e5575253a4c730d05e38cc Mon Sep 17 00:00:00 2001 From: orthomind Date: Sat, 19 Jan 2019 17:19:10 +1100 Subject: [PATCH] database: change primary keys to use UUID's instead of integers Change primary keys to use UUID's as recommended by CockroachDB's documentation. Change includes: - Update primary key's to UUID's - Fix bug with dataloader that would sometimes cause it to fail - Change some output from fmt to log to allow using logging options --- .gitignore | 1 + cmswww/admin.go | 11 +--- cmswww/cmd/cmswwwcli/client/client.go | 7 ++- cmswww/cmd/cmswwwcli/commands/editinvoice.go | 3 +- .../cmd/cmswwwcli/commands/invitenewuser.go | 4 +- .../cmd/cmswwwcli/commands/invoicedetails.go | 10 ++-- cmswww/cmd/cmswwwcli/commands/invoices.go | 13 ++-- cmswww/cmd/cmswwwcli/commands/login.go | 6 +- cmswww/cmd/cmswwwcli/commands/logout.go | 4 +- cmswww/cmd/cmswwwcli/commands/logwork.go | 3 +- cmswww/cmd/cmswwwcli/commands/myinvoices.go | 15 ++--- cmswww/cmd/cmswwwcli/commands/payinvoices.go | 25 ++++---- .../cmd/cmswwwcli/commands/reviewinvoices.go | 37 ++++++------ .../cmswwwcli/commands/setinvoicestatus.go | 3 +- .../cmd/cmswwwcli/commands/submitinvoice.go | 3 +- .../commands/updateinvoicepayment.go | 4 +- cmswww/cmd/cmswwwcli/commands/userdetails.go | 18 +++--- cmswww/cmd/cmswwwcli/commands/users.go | 16 ++--- .../cmd/cmswwwcli/commands/verifyidentity.go | 4 +- cmswww/cmd/cmswwwcli/commands/version.go | 8 +-- cmswww/cmd/cmswwwcli/main.go | 3 + cmswww/cmd/cmswwwdataload/client/client.go | 60 +++++++++---------- cmswww/cmd/cmswwwdataload/config/config.go | 3 +- cmswww/cmd/cmswwwdataload/main.go | 19 +++--- cmswww/cmd/cmswwwdbutil/cmswwwdbutil.go | 15 +++-- cmswww/convert.go | 5 +- cmswww/database/cockroachdb/cockroachdb.go | 33 +++++----- cmswww/database/cockroachdb/encoding.go | 20 +++---- cmswww/database/cockroachdb/models.go | 23 ++++--- cmswww/database/database.go | 15 ++--- cmswww/invoice.go | 8 +-- cmswww/session.go | 3 +- cmswww/user.go | 5 +- cmswww/validate.go | 10 ++-- go.mod | 4 +- go.sum | 2 + 36 files changed, 224 insertions(+), 199 deletions(-) diff --git a/.gitignore b/.gitignore index 08e11d7..1b728e8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.dylib cmswww/cmswww cmswww/cmd/cmswwwcli/cmswwwcli +cmswww/cmd/cmswwwdataload/cmswwwdataload # Test binary, build with `go test -c` *.test diff --git a/cmswww/admin.go b/cmswww/admin.go index 3665afd..cfb7193 100644 --- a/cmswww/admin.go +++ b/cmswww/admin.go @@ -6,20 +6,15 @@ import ( "net/http" "os" "sort" - "strconv" "strings" "time" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/database" + "github.com/gofrs/uuid" ) -func (c *cmswww) getUserByIDStr(userIDStr string) (*database.User, error) { - userID, err := strconv.ParseUint(userIDStr, 10, 64) - if err != nil { - return nil, err - } - +func (c *cmswww) getUserByIDStr(userID uuid.UUID) (*database.User, error) { user, err := c.db.GetUserById(userID) if err != nil { return nil, err @@ -323,7 +318,7 @@ func (c *cmswww) HandleUsers( ur.Users = make([]v1.AbridgedUser, 0, len(users)) for _, user := range users { ur.Users = append(ur.Users, v1.AbridgedUser{ - ID: strconv.FormatUint(user.ID, 10), + ID: user.ID.String(), Email: user.Email, Username: user.Username, Admin: user.Admin, diff --git a/cmswww/cmd/cmswwwcli/client/client.go b/cmswww/cmd/cmswwwcli/client/client.go index 6c22f99..70ce745 100644 --- a/cmswww/cmd/cmswwwcli/client/client.go +++ b/cmswww/cmd/cmswwwcli/client/client.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "encoding/json" "fmt" + "log" "net/http" "net/http/cookiejar" "net/url" @@ -84,7 +85,7 @@ func (c *Ctx) makeRequest(method, route string, requestJSON interface{}, respons } if config.Verbose { - fmt.Printf("Request: %v %v ", method, fullRoute) + log.Printf("Request: %v %v ", method, fullRoute) if method != http.MethodGet { prettyPrintJSON(requestJSON) } else { @@ -129,7 +130,7 @@ func (c *Ctx) handleResponse(r *http.Response, responseJSON interface{}) error { if r.StatusCode != http.StatusOK { if config.Verbose { - fmt.Printf("Response: %v ", r.Status) + log.Printf("Response: %v ", r.Status) var errJSON interface{} err := json.Unmarshal(responseBody, &errJSON) if err == nil { @@ -163,7 +164,7 @@ func (c *Ctx) handleResponse(r *http.Response, responseJSON interface{}) error { } if config.Verbose { - fmt.Printf("Response: %v ", r.Status) + log.Printf("Response: %v ", r.Status) if responseJSON != nil { prettyPrintJSON(responseJSON) } else { diff --git a/cmswww/cmd/cmswwwcli/commands/editinvoice.go b/cmswww/cmd/cmswwwcli/commands/editinvoice.go index 4584eb7..f76c6d6 100644 --- a/cmswww/cmd/cmswwwcli/commands/editinvoice.go +++ b/cmswww/cmd/cmswwwcli/commands/editinvoice.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -105,7 +106,7 @@ func (cmd *EditInvoiceCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Invoice submitted successfully! The censorship record has"+ + log.Printf("Invoice submitted successfully! The censorship record has"+ " been stored in %v for your future reference.", revisionRecordFilename) } diff --git a/cmswww/cmd/cmswwwcli/commands/invitenewuser.go b/cmswww/cmd/cmswwwcli/commands/invitenewuser.go index 889d036..186e6f7 100644 --- a/cmswww/cmd/cmswwwcli/commands/invitenewuser.go +++ b/cmswww/cmd/cmswwwcli/commands/invitenewuser.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -30,7 +30,7 @@ func (cmd *InviteNewUserCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Invitation created and sent to %v. Their invitation will expire"+ + log.Printf("Invitation created and sent to %v. Their invitation will expire"+ " in %v.\n", cmd.Args.Email, v1.VerificationExpiryTime.String()) } return nil diff --git a/cmswww/cmd/cmswwwcli/commands/invoicedetails.go b/cmswww/cmd/cmswwwcli/commands/invoicedetails.go index f53fe9a..c965b40 100644 --- a/cmswww/cmd/cmswwwcli/commands/invoicedetails.go +++ b/cmswww/cmd/cmswwwcli/commands/invoicedetails.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "time" "github.com/decred/contractor-mgmt/cmswww/api/v1" @@ -33,11 +33,11 @@ func (cmd *InvoiceDetailsCmd) Execute(args []string) error { if !config.JSONOutput { date := time.Date(int(idr.Invoice.Year), time.Month(idr.Invoice.Month), 1, 0, 0, 0, 0, time.UTC) - fmt.Printf("%v %v\n", idr.Invoice.CensorshipRecord.Token[0:7], + log.Printf("%v %v\n", idr.Invoice.CensorshipRecord.Token[0:7], idr.Invoice.CensorshipRecord.Token) - fmt.Printf(" Submitted by: %v\n", idr.Invoice.Username) - fmt.Printf(" at: %v\n", time.Unix(idr.Invoice.Timestamp, 0)) - fmt.Printf(" For: %v\n", date.Format("January 2006")) + log.Printf(" Submitted by: %v\n", idr.Invoice.Username) + log.Printf(" at: %v\n", time.Unix(idr.Invoice.Timestamp, 0)) + log.Printf(" For: %v\n", date.Format("January 2006")) } return nil diff --git a/cmswww/cmd/cmswwwcli/commands/invoices.go b/cmswww/cmd/cmswwwcli/commands/invoices.go index 087a410..c8f6a7a 100644 --- a/cmswww/cmd/cmswwwcli/commands/invoices.go +++ b/cmswww/cmd/cmswwwcli/commands/invoices.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "log" "strings" "time" @@ -59,18 +60,18 @@ func (cmd *InvoicesCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Invoices: ") + log.Printf("Invoices: ") if len(ir.Invoices) == 0 { - fmt.Printf("none\n") + log.Printf("none\n") } else { fmt.Println() for _, v := range ir.Invoices { - fmt.Printf(" %v\n", v.CensorshipRecord.Token) - fmt.Printf(" Submitted by: %v\n", v.Username) - fmt.Printf(" at: %v\n", + log.Printf(" %v\n", v.CensorshipRecord.Token) + log.Printf(" Submitted by: %v\n", v.Username) + log.Printf(" at: %v\n", time.Unix(v.Timestamp, 0).String()) if cmd.Status == "" { - fmt.Printf(" Status: %v\n", + log.Printf(" Status: %v\n", v1.InvoiceStatus[v.Status]) } } diff --git a/cmswww/cmd/cmswwwcli/commands/login.go b/cmswww/cmd/cmswwwcli/commands/login.go index af12a67..c634a25 100644 --- a/cmswww/cmd/cmswwwcli/commands/login.go +++ b/cmswww/cmd/cmswwwcli/commands/login.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -33,13 +33,13 @@ func (cmd *LoginCmd) Execute(args []string) error { config.LoggedInUser = &lr if !config.JSONOutput { - fmt.Printf("You are now logged in as %v\n", lr.Username) + log.Printf("You are now logged in as %v\n", lr.Username) } // Load identity, if available. _, err = config.LoadUserIdentity(cmd.Args.Email) if err != nil && !config.JSONOutput { - fmt.Printf("WARNING: Your identity could not be loaded, please generate" + + log.Printf("WARNING: Your identity could not be loaded, please generate" + " a new one using the newidentity command\n") } diff --git a/cmswww/cmd/cmswwwcli/commands/logout.go b/cmswww/cmd/cmswwwcli/commands/logout.go index 02cd1bb..a508e8e 100644 --- a/cmswww/cmd/cmswwwcli/commands/logout.go +++ b/cmswww/cmd/cmswwwcli/commands/logout.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -21,7 +21,7 @@ func (cmd *LogoutCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("You are now logged out\n") + log.Printf("You are now logged out\n") } config.LoggedInUser = nil config.LoggedInUserIdentity = nil diff --git a/cmswww/cmd/cmswwwcli/commands/logwork.go b/cmswww/cmd/cmswwwcli/commands/logwork.go index bb174e4..22a5f98 100644 --- a/cmswww/cmd/cmswwwcli/commands/logwork.go +++ b/cmswww/cmd/cmswwwcli/commands/logwork.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/csv" "fmt" + "log" "os" "strconv" "strings" @@ -55,7 +56,7 @@ func promptForFieldValues() ([]string, error) { if !field.Required { optionalStr = " (optional)" } - fmt.Printf("%v%v: ", field.Name, optionalStr) + log.Printf("%v%v: ", field.Name, optionalStr) } valueStr, err := reader.ReadString('\n') if err != nil { diff --git a/cmswww/cmd/cmswwwcli/commands/myinvoices.go b/cmswww/cmd/cmswwwcli/commands/myinvoices.go index 9d434d9..2701a49 100644 --- a/cmswww/cmd/cmswwwcli/commands/myinvoices.go +++ b/cmswww/cmd/cmswwwcli/commands/myinvoices.go @@ -3,6 +3,7 @@ package commands import ( "encoding/json" "fmt" + "log" "os" "path/filepath" "sort" @@ -166,28 +167,28 @@ func (cmd *MyInvoicesCmd) Execute(args []string) error { return nil } - fmt.Printf("Invoices: ") + log.Printf("Invoices: ") if len(allInvoices) == 0 { - fmt.Printf("none\n") + log.Printf("none\n") } else { for _, v := range allInvoices { fmt.Println() date := time.Date(int(v.Year), time.Month(v.Month), 1, 0, 0, 0, 0, time.UTC) - fmt.Printf(" %v • ", date.Format("January 2006")) + log.Printf(" %v • ", date.Format("January 2006")) if v.Token != nil { - fmt.Printf("%v\n", *v.Token) + log.Printf("%v\n", *v.Token) } else { - fmt.Printf("Draft\n") + log.Printf("Draft\n") } if v.Timestamp != nil { - fmt.Printf(" Submitted: %v\n", + log.Printf(" Submitted: %v\n", time.Unix(*v.Timestamp, 0).String()) } - fmt.Printf(" Status: %v\n", v.Status) + log.Printf(" Status: %v\n", v.Status) } } diff --git a/cmswww/cmd/cmswwwcli/commands/payinvoices.go b/cmswww/cmd/cmswwwcli/commands/payinvoices.go index 7c0af25..60d8ae6 100644 --- a/cmswww/cmd/cmswwwcli/commands/payinvoices.go +++ b/cmswww/cmd/cmswwwcli/commands/payinvoices.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -39,9 +40,9 @@ func (cmd *PayInvoicesCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Invoices ready to be paid: ") + log.Printf("Invoices ready to be paid: ") if len(pir.Invoices) == 0 { - fmt.Printf("none\n") + log.Printf("none\n") } else { for _, invoice := range pir.Invoices { fmt.Println() @@ -49,16 +50,16 @@ func (cmd *PayInvoicesCmd) Execute(args []string) error { rate := float64(invoice.TotalCostUSD) / float64(invoice.TotalHours) - fmt.Printf(" User ID: %v\n", invoice.UserID) - fmt.Printf(" Username: %v\n", invoice.Username) - fmt.Printf(" Invoice token: %v\n", invoice.Token) - fmt.Printf(" ------------------------------------------\n") - fmt.Printf(" Hours: %v\n", invoice.TotalHours) - fmt.Printf(" Total cost: $%v\n", invoice.TotalCostUSD) - fmt.Printf(" Average Rate: $%.2f / hr\n", rate) - fmt.Printf(" ------------------------------------------\n") - fmt.Printf(" Total cost: %v DCR\n", invoice.TotalCostDCR) - fmt.Printf(" Payment Address: %v\n", invoice.PaymentAddress) + log.Printf(" User ID: %v\n", invoice.UserID) + log.Printf(" Username: %v\n", invoice.Username) + log.Printf(" Invoice token: %v\n", invoice.Token) + log.Printf(" ------------------------------------------\n") + log.Printf(" Hours: %v\n", invoice.TotalHours) + log.Printf(" Total cost: $%v\n", invoice.TotalCostUSD) + log.Printf(" Average Rate: $%.2f / hr\n", rate) + log.Printf(" ------------------------------------------\n") + log.Printf(" Total cost: %v DCR\n", invoice.TotalCostDCR) + log.Printf(" Payment Address: %v\n", invoice.PaymentAddress) } } } diff --git a/cmswww/cmd/cmswwwcli/commands/reviewinvoices.go b/cmswww/cmd/cmswwwcli/commands/reviewinvoices.go index 516a361..025d1e0 100644 --- a/cmswww/cmd/cmswwwcli/commands/reviewinvoices.go +++ b/cmswww/cmd/cmswwwcli/commands/reviewinvoices.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -37,9 +38,9 @@ func (cmd *ReviewInvoicesCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Invoices to review: ") + log.Printf("Invoices to review: ") if len(rir.Invoices) == 0 { - fmt.Printf("none\n") + log.Printf("none\n") } else { for _, invoice := range rir.Invoices { fmt.Println() @@ -47,32 +48,32 @@ func (cmd *ReviewInvoicesCmd) Execute(args []string) error { totalRate := float64(invoice.TotalCostUSD) / float64(invoice.TotalHours) - fmt.Printf(" User ID: %v\n", invoice.UserID) - fmt.Printf(" Username: %v\n", invoice.Username) - fmt.Printf(" Token: %v\n", invoice.Token) - fmt.Printf(" ------------------------------------------\n") + log.Printf(" User ID: %v\n", invoice.UserID) + log.Printf(" Username: %v\n", invoice.Username) + log.Printf(" Token: %v\n", invoice.Token) + log.Printf(" ------------------------------------------\n") for lineItemIdx, lineItem := range invoice.LineItems { if lineItemIdx > 0 { - fmt.Printf(" --------------------------------\n") + log.Printf(" --------------------------------\n") } rate := float64(lineItem.TotalCost) / float64(lineItem.Hours) - fmt.Printf(" Type: %v\n", lineItem.Type) + log.Printf(" Type: %v\n", lineItem.Type) if lineItem.Subtype != "" { - fmt.Printf(" Subtype: %v\n", lineItem.Subtype) + log.Printf(" Subtype: %v\n", lineItem.Subtype) } - fmt.Printf(" Description: %v\n", lineItem.Description) + log.Printf(" Description: %v\n", lineItem.Description) if lineItem.Proposal != "" { - fmt.Printf(" Politeia proposal: %v\n", lineItem.Proposal) + log.Printf(" Politeia proposal: %v\n", lineItem.Proposal) } - fmt.Printf(" Hours: %v\n", lineItem.Hours) - fmt.Printf(" Total cost: $%v\n", lineItem.TotalCost) - fmt.Printf(" Rate: $%.2f / hr\n", rate) + log.Printf(" Hours: %v\n", lineItem.Hours) + log.Printf(" Total cost: $%v\n", lineItem.TotalCost) + log.Printf(" Rate: $%.2f / hr\n", rate) } - fmt.Printf(" ------------------------------------------\n") - fmt.Printf(" Hours: %v\n", invoice.TotalHours) - fmt.Printf(" Total cost: $%v\n", invoice.TotalCostUSD) - fmt.Printf(" Average Rate: $%.2f / hr\n", totalRate) + log.Printf(" ------------------------------------------\n") + log.Printf(" Hours: %v\n", invoice.TotalHours) + log.Printf(" Total cost: $%v\n", invoice.TotalCostUSD) + log.Printf(" Average Rate: $%.2f / hr\n", totalRate) } } } diff --git a/cmswww/cmd/cmswwwcli/commands/setinvoicestatus.go b/cmswww/cmd/cmswwwcli/commands/setinvoicestatus.go index 39eab0a..98acd88 100644 --- a/cmswww/cmd/cmswwwcli/commands/setinvoicestatus.go +++ b/cmswww/cmd/cmswwwcli/commands/setinvoicestatus.go @@ -3,6 +3,7 @@ package commands import ( "encoding/hex" "fmt" + "log" "strconv" "strings" @@ -52,7 +53,7 @@ func (cmd *SetInvoiceStatusCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Status changed to %v", v1.InvoiceStatus[sisr.Invoice.Status]) + log.Printf("Status changed to %v", v1.InvoiceStatus[sisr.Invoice.Status]) } return nil diff --git a/cmswww/cmd/cmswwwcli/commands/submitinvoice.go b/cmswww/cmd/cmswwwcli/commands/submitinvoice.go index c0a86b5..0b4ee4c 100644 --- a/cmswww/cmd/cmswwwcli/commands/submitinvoice.go +++ b/cmswww/cmd/cmswwwcli/commands/submitinvoice.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "log" "os" "strings" "time" @@ -177,7 +178,7 @@ func (cmd *SubmitInvoiceCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Invoice submitted successfully! The censorship record has"+ + log.Printf("Invoice submitted successfully! The censorship record has"+ " been stored in %v for your future reference.", filename) } diff --git a/cmswww/cmd/cmswwwcli/commands/updateinvoicepayment.go b/cmswww/cmd/cmswwwcli/commands/updateinvoicepayment.go index f6e48aa..627ad71 100644 --- a/cmswww/cmd/cmswwwcli/commands/updateinvoicepayment.go +++ b/cmswww/cmd/cmswwwcli/commands/updateinvoicepayment.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -36,7 +36,7 @@ func (cmd *UpdateInvoicePaymentCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Invoice payment has been updated\n") + log.Printf("Invoice payment has been updated\n") } return nil diff --git a/cmswww/cmd/cmswwwcli/commands/userdetails.go b/cmswww/cmd/cmswwwcli/commands/userdetails.go index 24abb83..800e170 100644 --- a/cmswww/cmd/cmswwwcli/commands/userdetails.go +++ b/cmswww/cmd/cmswwwcli/commands/userdetails.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -34,14 +34,14 @@ func (cmd *UserDetailsCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf(" ID: %v\n", udr.User.ID) - fmt.Printf(" Email: %v\n", udr.User.Email) - fmt.Printf(" Username: %v\n", udr.User.Username) - fmt.Printf(" Admin: %v\n", udr.User.Admin) - fmt.Printf(" Extended public key: %v\n", udr.User.ExtendedPublicKey) - fmt.Printf(" Last login: %v\n", udr.User.LastLogin) - fmt.Printf(" Failed login attempts: %v\n", udr.User.FailedLoginAttempts) - fmt.Printf(" Locked: %v\n", + log.Printf(" ID: %v\n", udr.User.ID) + log.Printf(" Email: %v\n", udr.User.Email) + log.Printf(" Username: %v\n", udr.User.Username) + log.Printf(" Admin: %v\n", udr.User.Admin) + log.Printf(" Extended public key: %v\n", udr.User.ExtendedPublicKey) + log.Printf(" Last login: %v\n", udr.User.LastLogin) + log.Printf(" Failed login attempts: %v\n", udr.User.FailedLoginAttempts) + log.Printf(" Locked: %v\n", udr.User.FailedLoginAttempts >= v1.LoginAttemptsToLockUser) } diff --git a/cmswww/cmd/cmswwwcli/commands/users.go b/cmswww/cmd/cmswwwcli/commands/users.go index bfb5ca6..86b3849 100644 --- a/cmswww/cmd/cmswwwcli/commands/users.go +++ b/cmswww/cmd/cmswwwcli/commands/users.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -28,14 +28,14 @@ func (cmd *UsersCmd) Execute(args []string) error { } if !config.JSONOutput { - fmt.Printf("Displaying %v / %v matches\n", len(ur.Users), ur.TotalMatches) - fmt.Printf("---------------------------\n") + log.Printf("Displaying %v / %v matches\n", len(ur.Users), ur.TotalMatches) + log.Printf("---------------------------\n") for _, user := range ur.Users { - fmt.Printf(" ID: %v\n", user.ID) - fmt.Printf(" Email: %v\n", user.Email) - fmt.Printf(" Username: %v\n", user.Username) - fmt.Printf(" Admin: %v\n", user.Admin) - fmt.Printf("---------------------------\n") + log.Printf(" ID: %v\n", user.ID) + log.Printf(" Email: %v\n", user.Email) + log.Printf(" Username: %v\n", user.Username) + log.Printf(" Admin: %v\n", user.Admin) + log.Printf("---------------------------\n") } } diff --git a/cmswww/cmd/cmswwwcli/commands/verifyidentity.go b/cmswww/cmd/cmswwwcli/commands/verifyidentity.go index 8e82705..a425934 100644 --- a/cmswww/cmd/cmswwwcli/commands/verifyidentity.go +++ b/cmswww/cmd/cmswwwcli/commands/verifyidentity.go @@ -2,7 +2,7 @@ package commands import ( "encoding/hex" - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -21,7 +21,7 @@ func (cmd *VerifyIdentityCmd) Execute(args []string) error { } if config.LoggedInUser == nil { - fmt.Printf("You must be logged in to perform this action.\n") + log.Printf("You must be logged in to perform this action.\n") return nil } diff --git a/cmswww/cmd/cmswwwcli/commands/version.go b/cmswww/cmd/cmswwwcli/commands/version.go index afef22d..52d7d07 100644 --- a/cmswww/cmd/cmswwwcli/commands/version.go +++ b/cmswww/cmd/cmswwwcli/commands/version.go @@ -1,7 +1,7 @@ package commands import ( - "fmt" + "log" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/cmd/cmswwwcli/config" @@ -21,17 +21,17 @@ func (cmd *VersionCmd) Execute(args []string) error { if vr.User != nil { config.LoggedInUser = vr.User if !config.JSONOutput && !config.SuppressOutput { - fmt.Printf("You are currently logged in as %v\n", vr.User.Username) + log.Printf("You are currently logged in as %v\n", vr.User.Username) } // Load identity, if available. _, err = config.LoadUserIdentity(vr.User.Email) if err != nil && !config.JSONOutput { - fmt.Printf("WARNING: Your identity could not be loaded, please generate" + + log.Printf("WARNING: Your identity could not be loaded, please generate" + " a new one using the newidentity command\n") } } else if !config.JSONOutput && !config.SuppressOutput { - fmt.Printf("You are not currently logged in\n") + log.Printf("You are not currently logged in\n") } // CSRF protection works via double-submit method. One token is stored in the diff --git a/cmswww/cmd/cmswwwcli/main.go b/cmswww/cmd/cmswwwcli/main.go index b4ce689..3b9bddb 100644 --- a/cmswww/cmd/cmswwwcli/main.go +++ b/cmswww/cmd/cmswwwcli/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "os" flags "github.com/jessevdk/go-flags" @@ -45,6 +46,8 @@ func _main() error { } func main() { + log.SetFlags(log.LstdFlags) + log.SetOutput(os.Stdout) err := _main() if err != nil { //fmt.Fprintf(os.Stderr, "%v\n", err) diff --git a/cmswww/cmd/cmswwwdataload/client/client.go b/cmswww/cmd/cmswwwdataload/client/client.go index 1380577..2a150ca 100644 --- a/cmswww/cmd/cmswwwdataload/client/client.go +++ b/cmswww/cmd/cmswwwdataload/client/client.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/ioutil" + "log" "os/exec" "path/filepath" "strconv" @@ -34,7 +35,7 @@ func NewClient(cfg *config.Config) *Client { func (c *Client) ExecuteCommand(args ...string) *exec.Cmd { if c.cfg.Verbose { - fmt.Printf(" $ %v\n", strings.Join(args, " ")) + log.Printf(" $ %v\n", strings.Join(args, " ")) } return exec.Command(args[0], args[1:]...) } @@ -119,24 +120,19 @@ func (c *Client) ExecuteCliCommand(reply interface{}, verify verifyReply, args . return err } - var stderrBytes, stdoutBytes []byte - var errStderr, errStdout error - go func() { - stderrBytes, errStderr = ioutil.ReadAll(stderr) - if errStderr == nil && len(stderrBytes) > 0 { - errStderr = fmt.Errorf("unexpected error output from %v: %v", cli, - string(stderrBytes)) - } - }() + stderrBytes, errStderr := ioutil.ReadAll(stderr) + stdoutBytes, errStdout := ioutil.ReadAll(stdout) - go func() { - stdoutBytes, errStdout = ioutil.ReadAll(stdout) - }() + if errStderr == nil && len(stderrBytes) > 0 { + errStderr = fmt.Errorf("unexpected error output from %v: %v", cli, + string(stderrBytes)) + } err := cmd.Wait() if err != nil { return err } + if errStderr != nil { return errStderr } @@ -160,7 +156,7 @@ func (c *Client) ExecuteCliCommand(reply interface{}, verify verifyReply, args . } if c.cfg.Verbose { - fmt.Printf(" %v\n", line) + log.Printf(" %v\n", line) } var er v1.ErrorReply @@ -182,7 +178,7 @@ func (c *Client) ExecuteCliCommand(reply interface{}, verify verifyReply, args . } func (c *Client) Version() error { - fmt.Printf("Getting version\n") + log.Printf("Getting version\n") var vr v1.VersionReply return c.ExecuteCliCommand( @@ -195,7 +191,7 @@ func (c *Client) Version() error { } func (c *Client) Login(email, password string) (*v1.LoginReply, error) { - fmt.Printf("Logging in as: %v\n", email) + log.Printf("Logging in as: %v\n", email) var lr v1.LoginReply err := c.ExecuteCliCommand( @@ -211,12 +207,12 @@ func (c *Client) Login(email, password string) (*v1.LoginReply, error) { } func (c *Client) Logout() error { - fmt.Printf("Logging out\n") + log.Printf("Logging out\n") return c.ExecuteCommandAndWait(cli, "logout") } func (c *Client) NewIdentity() (string, error) { - fmt.Printf("Generating new identity\n") + log.Printf("Generating new identity\n") var nir v1.NewIdentityReply err := c.ExecuteCliCommand( &nir, @@ -230,12 +226,12 @@ func (c *Client) NewIdentity() (string, error) { } func (c *Client) VerifyIdentity(token string) error { - fmt.Printf("Verifying new identity\n") + log.Printf("Verifying new identity\n") return c.ExecuteCommandAndWait(cli, "verifyidentity", token) } func (c *Client) InviteUser(email string) (string, error) { - fmt.Printf("Inviting user: %v\n", email) + log.Printf("Inviting user: %v\n", email) var inur v1.InviteNewUserReply err := c.ExecuteCliCommand( @@ -251,7 +247,7 @@ func (c *Client) InviteUser(email string) (string, error) { } func (c *Client) ResendInvite(email string) (string, error) { - fmt.Printf("Re-inviting user: %v\n", email) + log.Printf("Re-inviting user: %v\n", email) var mur v1.ManageUserReply err := c.ExecuteCliCommand( @@ -272,7 +268,7 @@ func (c *Client) ResendInvite(email string) (string, error) { } func (c *Client) UserDetails(userID string) (*v1.UserDetailsReply, error) { - fmt.Printf("Fetching user details\n") + log.Printf("Fetching user details\n") var udr v1.UserDetailsReply err := c.ExecuteCliCommand( @@ -288,7 +284,7 @@ func (c *Client) UserDetails(userID string) (*v1.UserDetailsReply, error) { } func (c *Client) EditUser(name, location, extendedPublicKey string) error { - fmt.Printf("Editing user\n") + log.Printf("Editing user\n") var eur v1.EditUserReply err := c.ExecuteCliCommand( @@ -319,7 +315,7 @@ func (c *Client) RegisterUser( email, username, password, name, location, extendedPublicKey, token string, ) error { - fmt.Printf("Registering user: %v\n", email) + log.Printf("Registering user: %v\n", email) var rr v1.RegisterReply return c.ExecuteCliCommand( @@ -339,7 +335,7 @@ func (c *Client) RegisterUser( } func (c *Client) SubmitInvoice(file string) (string, error) { - fmt.Printf("Submitting invoice\n") + log.Printf("Submitting invoice\n") file = filepath.ToSlash(file) @@ -357,7 +353,7 @@ func (c *Client) SubmitInvoice(file string) (string, error) { } func (c *Client) EditInvoice(token, file string) error { - fmt.Printf("Editing invoice\n") + log.Printf("Editing invoice\n") file = filepath.ToSlash(file) @@ -376,7 +372,7 @@ func (c *Client) EditInvoice(token, file string) error { } func (c *Client) ApproveInvoice(token string) error { - fmt.Printf("Approving invoice: %v\n", token) + log.Printf("Approving invoice: %v\n", token) var sisr v1.SetInvoiceStatusReply return c.ExecuteCliCommand( @@ -391,7 +387,7 @@ func (c *Client) ApproveInvoice(token string) error { } func (c *Client) RejectInvoice(token, reason string) error { - fmt.Printf("Rejecting invoice: %v\n", token) + log.Printf("Rejecting invoice: %v\n", token) var sisr v1.SetInvoiceStatusReply return c.ExecuteCliCommand( @@ -475,7 +471,7 @@ func (c *Client) UpdateInvoicePayment( } func (c *Client) GetAllInvoices() ([]v1.InvoiceRecord, error) { - fmt.Printf("Fetching all invoices\n") + log.Printf("Fetching all invoices\n") var ir v1.InvoicesReply err := c.ExecuteCliCommand( @@ -490,7 +486,7 @@ func (c *Client) GetAllInvoices() ([]v1.InvoiceRecord, error) { } func (c *Client) ChangePassword(currentPassword, newPassword string) error { - fmt.Printf("Changing password to: %v\n", newPassword) + log.Printf("Changing password to: %v\n", newPassword) var cpr v1.ChangePasswordReply return c.ExecuteCliCommand( @@ -505,7 +501,7 @@ func (c *Client) ChangePassword(currentPassword, newPassword string) error { } func (c *Client) ResetPassword(email, newPassword string) error { - fmt.Printf("Resetting password: %v\n", email) + log.Printf("Resetting password: %v\n", email) var rpr v1.ResetPasswordReply return c.ExecuteCliCommand( @@ -520,7 +516,7 @@ func (c *Client) ResetPassword(email, newPassword string) error { } func (c *Client) CreateAdminUser(email, username, password string) error { - fmt.Printf("Creating admin user: %v\n", c.cfg.AdminEmail) + log.Printf("Creating admin user: %v\n", c.cfg.AdminEmail) _, err := c.ExecuteCommandWithErrorHandling( dbutil, "-testnet", diff --git a/cmswww/cmd/cmswwwdataload/config/config.go b/cmswww/cmd/cmswwwdataload/config/config.go index 0483a7f..67b626f 100644 --- a/cmswww/cmd/cmswwwdataload/config/config.go +++ b/cmswww/cmd/cmswwwdataload/config/config.go @@ -7,6 +7,7 @@ package config import ( "fmt" + "log" "os" "path/filepath" "strings" @@ -186,7 +187,7 @@ func Load() (*Config, error) { } if configFileError != nil { - fmt.Printf("WARNING: %v\n", configFileError) + log.Printf("WARNING: %v\n", configFileError) } cfg.PoliteiadLogFile = filepath.Join(cfg.DataDir, diff --git a/cmswww/cmd/cmswwwdataload/main.go b/cmswww/cmd/cmswwwdataload/main.go index 40a6a37..9a95ca3 100644 --- a/cmswww/cmd/cmswwwdataload/main.go +++ b/cmswww/cmd/cmswwwdataload/main.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "io" + "log" "os" "os/exec" "path" @@ -38,7 +39,7 @@ func createLogFile(path string) (*os.File, error) { } func createInvoiceFile(month, year uint16, numRecords int) (string, error) { - fmt.Printf("Creating invoice file\n") + log.Printf("Creating invoice file\n") date := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, time.UTC) filepath := path.Join(cfg.DataDir, date.Format("2006-01.csv")) file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644) @@ -65,7 +66,7 @@ func waitForStartOfDay(out io.Reader) { } func startCmswww() error { - fmt.Printf("Starting cmswww\n") + log.Printf("Starting cmswww\n") cmswwwCmd = c.CreateCmswwwCmd() stdout, _ := cmswwwCmd.StdoutPipe() @@ -91,7 +92,7 @@ func startCmswww() error { } func startPoliteiad() error { - fmt.Printf("Starting politeiad\n") + log.Printf("Starting politeiad\n") politeiadCmd = c.CreatePoliteiadCmd() stdout, _ := politeiadCmd.StdoutPipe() @@ -417,7 +418,7 @@ func createContractorUser( } func deleteExistingData() error { - fmt.Printf("Deleting existing data\n") + log.Printf("Deleting existing data\n") // politeiad data dir politeiadDataDir := filepath.Join(dcrutil.AppDataDir("politeiad", false), "data") @@ -443,7 +444,7 @@ func deleteExistingData() error { func stopPoliteiad() { if politeiadCmd != nil { - fmt.Printf("Stopping politeiad\n") + log.Printf("Stopping politeiad\n") politeiadCmd.Process.Kill() politeiadCmd = nil } @@ -451,7 +452,7 @@ func stopPoliteiad() { func stopCmswww() { if cmswwwCmd != nil { - fmt.Printf("Stopping cmswww\n") + log.Printf("Stopping cmswww\n") cmswwwCmd.Process.Kill() cmswwwCmd = nil } @@ -617,14 +618,16 @@ func _main() error { invoiceToPayToken, 12, 2018) } - fmt.Printf("Load data complete\n") + log.Printf("Load data complete\n") return nil } func main() { + log.SetFlags(log.LstdFlags) + log.SetOutput(os.Stdout) err := _main() if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + fmt.Fprintf(os.Stderr, "main error: %v\n", err) } stopServers() if err != nil { diff --git a/cmswww/cmd/cmswwwdbutil/cmswwwdbutil.go b/cmswww/cmd/cmswwwdbutil/cmswwwdbutil.go index 0b8c23a..f034643 100644 --- a/cmswww/cmd/cmswwwdbutil/cmswwwdbutil.go +++ b/cmswww/cmd/cmswwwdbutil/cmswwwdbutil.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "flag" "fmt" + "log" "os" "path/filepath" @@ -32,9 +33,9 @@ var ( ) func dumpUser(user *database.User) { - fmt.Printf("Key : %v\n", hex.EncodeToString([]byte(user.Email))) - fmt.Printf("Record : %v", spew.Sdump(*user)) - fmt.Printf("---------------------------------------\n") + log.Printf("Key : %v\n", hex.EncodeToString([]byte(user.Email))) + log.Printf("Record : %v", spew.Sdump(*user)) + log.Printf("---------------------------------------\n") } func dumpUserAction() error { @@ -50,12 +51,12 @@ func dumpUserAction() error { return err } - fmt.Printf("---------------------------------------\n") + log.Printf("---------------------------------------\n") dumpUser(user) return nil } - fmt.Printf("---------------------------------------\n") + log.Printf("---------------------------------------\n") return db.GetAllUsers(func(user *database.User) { dumpUser(user) }) @@ -109,7 +110,7 @@ func createAdminUserAction() error { } } - fmt.Printf("Admin user with email %v created\n", user.Email) + log.Printf("Admin user with email %v created\n", user.Email) return nil } @@ -151,6 +152,8 @@ func _main() error { } func main() { + log.SetFlags(log.LstdFlags) + log.SetOutput(os.Stdout) err := _main() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) diff --git a/cmswww/convert.go b/cmswww/convert.go index 8fa401a..9013877 100644 --- a/cmswww/convert.go +++ b/cmswww/convert.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "strconv" "strings" v1 "github.com/decred/contractor-mgmt/cmswww/api/v1" @@ -41,7 +40,7 @@ type BackendInvoiceMDPayment struct { func convertDatabaseUserToUser(user *database.User) v1.User { return v1.User{ - ID: strconv.FormatUint(user.ID, 10), + ID: user.ID.String(), Email: user.Email, Username: user.Username, Name: user.Name, @@ -249,7 +248,7 @@ func convertDatabaseInvoiceToInvoice(dbInvoice *database.Invoice) *v1.InvoiceRec invoice.Timestamp = dbInvoice.Timestamp invoice.Month = dbInvoice.Month invoice.Year = dbInvoice.Year - invoice.UserID = strconv.FormatUint(dbInvoice.UserID, 10) + invoice.UserID = dbInvoice.UserID.String() invoice.Username = dbInvoice.Username invoice.PublicKey = dbInvoice.PublicKey invoice.Signature = dbInvoice.UserSignature diff --git a/cmswww/database/cockroachdb/cockroachdb.go b/cmswww/database/cockroachdb/cockroachdb.go index a34a341..ef9e6c0 100644 --- a/cmswww/database/cockroachdb/cockroachdb.go +++ b/cmswww/database/cockroachdb/cockroachdb.go @@ -5,10 +5,10 @@ import ( "math/rand" "net/url" "path/filepath" - "strconv" "strings" "github.com/badoux/checkmail" + "github.com/gofrs/uuid" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/postgres" @@ -17,7 +17,10 @@ import ( ) var ( - _ database.Database = (*cockroachdb)(nil) + _ database.Database = (*cockroachdb)(nil) + identitySort = func(db *gorm.DB) *gorm.DB { + return db.Order("identities.created_at ASC") + } ) // cockroachdb implements the database interface. @@ -91,7 +94,7 @@ func (c *cockroachdb) UpdateUser(dbUser *database.User) error { // GetUser satisfies the backend interface. func (c *cockroachdb) GetUserByEmail(email string) (*database.User, error) { var user User - result := c.db.Where("email = ?", email).Preload("Identities").First(&user) + result := c.db.Where("email = ?", email).Preload("Identities", identitySort).First(&user) if result.Error != nil { if gorm.IsRecordNotFoundError(result.Error) { return nil, database.ErrUserNotFound @@ -107,7 +110,7 @@ func (c *cockroachdb) GetUserByEmail(email string) (*database.User, error) { // GetUserByUsername satisfies the backend interface. func (c *cockroachdb) GetUserByUsername(username string) (*database.User, error) { var user User - result := c.db.Where("username = ?", username).Preload("Identities").First(&user) + result := c.db.Where("username = ?", username).Preload("Identities", identitySort).First(&user) if result.Error != nil { if gorm.IsRecordNotFoundError(result.Error) { return nil, database.ErrUserNotFound @@ -121,9 +124,10 @@ func (c *cockroachdb) GetUserByUsername(username string) (*database.User, error) // GetUserById returns a user record given its id, if found in the database. // // GetUserById satisfies the backend interface. -func (c *cockroachdb) GetUserById(id uint64) (*database.User, error) { +func (c *cockroachdb) GetUserById(id uuid.UUID) (*database.User, error) { var user User - result := c.db.Preload("Identities").First(&user, id) + log.Debugf("GetUserById ID: %v", id) + result := c.db.Preload("Identities", identitySort).Where("id = ?", id).First(&user) if result.Error != nil { if gorm.IsRecordNotFoundError(result.Error) { return nil, database.ErrUserNotFound @@ -137,17 +141,17 @@ func (c *cockroachdb) GetUserById(id uint64) (*database.User, error) { // GetUserIdByPublicKey returns a user record given its id, if found in the database. // // GetUserIdByPublicKey satisfies the backend interface. -func (c *cockroachdb) GetUserIdByPublicKey(publicKey string) (uint64, error) { +func (c *cockroachdb) GetUserIdByPublicKey(publicKey string) (uuid.UUID, error) { var id Identity result := c.db.Where("key = ?", publicKey).First(&id) if result.Error != nil { if gorm.IsRecordNotFoundError(result.Error) { - return 0, database.ErrUserNotFound + return uuid.UUID{}, database.ErrUserNotFound } - return 0, result.Error + return uuid.UUID{}, result.Error } - return uint64(id.UserID), nil + return id.UserID, nil } // Executes a callback on every user in the database. @@ -271,10 +275,7 @@ func (c *cockroachdb) GetInvoices(invoicesRequest database.InvoicesRequest) ([]d paramsMap := make(map[string]interface{}) var err error if invoicesRequest.UserID != "" { - paramsMap["i.user_id"], err = strconv.ParseUint(invoicesRequest.UserID, 10, 64) - if err != nil { - return nil, 0, err - } + paramsMap["i.user_id"] = invoicesRequest.UserID } if invoicesRequest.StatusMap != nil && len(invoicesRequest.StatusMap) > 0 { @@ -415,5 +416,9 @@ func New(dataDir, dbName, username, host string) (*cockroachdb, error) { &InvoicePayment{}, ) + // Add foreign key constraints + c.db.Model(&Invoice{}).AddIndex("idx_invoice_user_id", "user_id") + c.db.Model(&Identity{}).AddIndex("idx_identities_user_id", "user_id") + return &c, nil } diff --git a/cmswww/database/cockroachdb/encoding.go b/cmswww/database/cockroachdb/encoding.go index 3bad722..9ec7ccc 100644 --- a/cmswww/database/cockroachdb/encoding.go +++ b/cmswww/database/cockroachdb/encoding.go @@ -12,7 +12,7 @@ import ( func EncodeUser(dbUser *database.User) *User { user := User{} - user.ID = uint(dbUser.ID) + user.ID = dbUser.ID user.Email = dbUser.Email user.Name = dbUser.Name user.Location = dbUser.Location @@ -79,7 +79,7 @@ func EncodeUser(dbUser *database.User) *User { // DecodeUser decodes a cockroachdb User instance into a generic database.User. func DecodeUser(user *User) (*database.User, error) { dbUser := database.User{ - ID: uint64(user.ID), + ID: user.ID, Email: user.Email, Username: user.Username.String, Name: user.Name, @@ -151,8 +151,8 @@ func DecodeUser(user *User) (*database.User, error) { func EncodeIdentity(dbID *database.Identity) *Identity { id := Identity{} - id.ID = uint(dbID.ID) - id.UserID = uint(dbID.UserID) + id.ID = dbID.ID + id.UserID = dbID.UserID if len(dbID.Key) != 0 { id.Key.Valid = true @@ -176,8 +176,8 @@ func EncodeIdentity(dbID *database.Identity) *Identity { func DecodeIdentity(id *Identity) (*database.Identity, error) { dbID := database.Identity{} - dbID.ID = uint64(id.ID) - dbID.UserID = uint64(id.UserID) + dbID.ID = id.ID + dbID.UserID = id.UserID if id.Key.Valid { pk, err := hex.DecodeString(id.Key.String) @@ -205,7 +205,7 @@ func EncodeInvoice(dbInvoice *database.Invoice) *Invoice { invoice := Invoice{} invoice.Token = dbInvoice.Token - invoice.UserID = uint(dbInvoice.UserID) + invoice.UserID = dbInvoice.UserID invoice.Month = uint(dbInvoice.Month) invoice.Year = uint(dbInvoice.Year) invoice.Status = uint(dbInvoice.Status) @@ -255,7 +255,7 @@ func EncodeInvoiceChange(dbInvoiceChange *database.InvoiceChange) *InvoiceChange func EncodeInvoicePayment(dbInvoicePayment *database.InvoicePayment) *InvoicePayment { invoicePayment := InvoicePayment{} - invoicePayment.ID = uint(dbInvoicePayment.ID) + invoicePayment.ID = dbInvoicePayment.ID invoicePayment.InvoiceToken = dbInvoicePayment.InvoiceToken invoicePayment.IsTotalCost = dbInvoicePayment.IsTotalCost invoicePayment.Address = dbInvoicePayment.Address @@ -272,7 +272,7 @@ func DecodeInvoice(invoice *Invoice) (*database.Invoice, error) { dbInvoice := database.Invoice{} dbInvoice.Token = invoice.Token - dbInvoice.UserID = uint64(invoice.UserID) + dbInvoice.UserID = invoice.UserID dbInvoice.Username = invoice.Username dbInvoice.Month = uint16(invoice.Month) dbInvoice.Year = uint16(invoice.Year) @@ -322,7 +322,7 @@ func DecodeInvoiceChange(invoiceChange *InvoiceChange) *database.InvoiceChange { func DecodeInvoicePayment(invoicePayment *InvoicePayment) *database.InvoicePayment { dbInvoicePayment := database.InvoicePayment{} - dbInvoicePayment.ID = uint64(invoicePayment.ID) + dbInvoicePayment.ID = invoicePayment.ID dbInvoicePayment.InvoiceToken = invoicePayment.InvoiceToken dbInvoicePayment.IsTotalCost = invoicePayment.IsTotalCost dbInvoicePayment.Address = invoicePayment.Address diff --git a/cmswww/database/cockroachdb/models.go b/cmswww/database/cockroachdb/models.go index 8af20d5..4207da8 100644 --- a/cmswww/database/cockroachdb/models.go +++ b/cmswww/database/cockroachdb/models.go @@ -4,7 +4,7 @@ import ( "database/sql" "time" - "github.com/jinzhu/gorm" + "github.com/gofrs/uuid" _ "github.com/jinzhu/gorm/dialects/postgres" "github.com/lib/pq" ) @@ -17,8 +17,15 @@ const ( tableNameInvoicePayment = "invoice_payments" ) +type Model struct { + ID uuid.UUID `sql:"type:uuid;primary_key;default:gen_random_uuid()"` + CreatedAt *time.Time `sql:"not null;DEFAULT:current_timestamp"` + UpdatedAt *time.Time `sql:"not null;DEFAULT:current_timestamp"` + DeletedAt *time.Time `sql:"index"` +} + type User struct { - gorm.Model + Model Email string `gorm:"type:varchar(100);unique_index"` Username sql.NullString `gorm:"unique"` HashedPassword sql.NullString @@ -48,8 +55,9 @@ func (u User) TableName() string { } type Identity struct { - gorm.Model - UserID uint `gorm:"not_null"` + Model + User User `gorm:"foreignkey:UserID"` + UserID uuid.UUID `gorm:"not_null"` Key sql.NullString `gorm:"unique"` Activated pq.NullTime Deactivated pq.NullTime @@ -61,7 +69,8 @@ func (i Identity) TableName() string { type Invoice struct { Token string `gorm:"primary_key"` - UserID uint `gorm:"not_null"` + User User `gorm:"foreignkey:UserID;association_foreignkey:ID"` + UserID uuid.UUID `gorm:"not_null"` Username string `gorm:"-"` // Only populated when reading from the database Month uint `gorm:"not_null"` Year uint `gorm:"not_null"` @@ -91,7 +100,7 @@ func (i Invoice) TableName() string { } type InvoiceChange struct { - gorm.Model + Model InvoiceToken string AdminPublicKey string NewStatus uint @@ -103,7 +112,7 @@ func (i InvoiceChange) TableName() string { } type InvoicePayment struct { - gorm.Model + Model InvoiceToken string IsTotalCost bool `gorm:"not_null"` Address string `gorm:"not_null"` diff --git a/cmswww/database/database.go b/cmswww/database/database.go index ec2503e..7e82c41 100644 --- a/cmswww/database/database.go +++ b/cmswww/database/database.go @@ -10,6 +10,7 @@ import ( "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/politeia/politeiad/api/v1/identity" + "github.com/gofrs/uuid" ) var ( @@ -45,8 +46,8 @@ type Database interface { UpdateUser(*User) error // Update existing user GetUserByEmail(string) (*User, error) // Return user record given the email address GetUserByUsername(string) (*User, error) // Return user record given the username - GetUserById(uint64) (*User, error) // Return user record given its id - GetUserIdByPublicKey(string) (uint64, error) // Return user id by public key + GetUserById(uuid.UUID) (*User, error) // Return user record given its id + GetUserIdByPublicKey(string) (uuid.UUID, error) // Return user id by public key GetAllUsers(callbackFn func(u *User)) error // Iterate all users GetUsers(username string, page int) ([]User, int, error) // Returns a list of users and total count that match the provided username. @@ -65,7 +66,7 @@ type Database interface { // User record. type User struct { - ID uint64 + ID uuid.UUID Email string Username string Name string @@ -92,8 +93,8 @@ type User struct { // Identity wraps an ed25519 public key and timestamps to indicate if it is // active. If deactivated != 0 then the key is no longer valid. type Identity struct { - ID uint64 - UserID uint64 + ID uuid.UUID + UserID uuid.UUID Key [identity.PublicKeySize]byte Activated int64 Deactivated int64 @@ -101,7 +102,7 @@ type Identity struct { type Invoice struct { Token string - UserID uint64 + UserID uuid.UUID Username string // Only populated when reading from the database Month uint16 Year uint16 @@ -133,7 +134,7 @@ type InvoiceChange struct { } type InvoicePayment struct { - ID uint64 + ID uuid.UUID InvoiceToken string IsTotalCost bool // Whether this payment represents the total cost of the invoice Address string diff --git a/cmswww/invoice.go b/cmswww/invoice.go index 42b908b..692bb61 100644 --- a/cmswww/invoice.go +++ b/cmswww/invoice.go @@ -142,7 +142,7 @@ func (c *cmswww) deriveTotalCostFromInvoice( func (c *cmswww) createInvoiceReview(invoice *database.Invoice) (*v1.InvoiceReview, error) { invoiceReview := v1.InvoiceReview{ - UserID: strconv.FormatUint(invoice.UserID, 10), + UserID: invoice.UserID.String(), Username: invoice.Username, Token: invoice.Token, LineItems: make([]v1.InvoiceReviewLineItem, 0), @@ -269,7 +269,7 @@ func (c *cmswww) createInvoicePayment( costUSD uint64, ) (*v1.InvoicePayment, error) { invoicePayment := v1.InvoicePayment{ - UserID: strconv.FormatUint(dbInvoice.UserID, 10), + UserID: dbInvoice.UserID.String(), Username: dbInvoice.Username, Token: dbInvoice.Token, } @@ -634,7 +634,7 @@ func (c *cmswww) HandleUserInvoices( } invoices, numMatches, err := c.getInvoices(database.InvoicesRequest{ - UserID: strconv.FormatUint(user.ID, 10), + UserID: user.ID.String(), StatusMap: statusMap, Page: int(ui.Page), }) @@ -845,7 +845,7 @@ func (c *cmswww) HandleSubmitInvoice( } invoices, _, err := c.db.GetInvoices(database.InvoicesRequest{ - UserID: strconv.FormatUint(user.ID, 10), + UserID: user.ID.String(), Month: ni.Month, Year: ni.Year, }) diff --git a/cmswww/session.go b/cmswww/session.go index e3a6f2b..60d9d01 100644 --- a/cmswww/session.go +++ b/cmswww/session.go @@ -5,7 +5,6 @@ import ( "net/http" "os" "path/filepath" - "strconv" "time" "github.com/decred/politeia/util" @@ -306,7 +305,7 @@ func (c *cmswww) CreateLoginReply(user *database.User, lastLogin int64) (*v1.Log reply := v1.LoginReply{ IsAdmin: user.Admin, - UserID: strconv.FormatUint(user.ID, 10), + UserID: user.ID.String(), Email: user.Email, Username: user.Username, PublicKey: activeIdentity, diff --git a/cmswww/user.go b/cmswww/user.go index ad43f7e..9ee7e72 100644 --- a/cmswww/user.go +++ b/cmswww/user.go @@ -9,6 +9,7 @@ import ( "github.com/decred/politeia/politeiad/api/v1/identity" "github.com/decred/politeia/util" + "github.com/gofrs/uuid" "golang.org/x/crypto/bcrypt" "github.com/decred/contractor-mgmt/cmswww/api/v1" @@ -42,7 +43,7 @@ func (c *cmswww) generateVerificationTokenAndExpiry() ([]byte, int64, error) { // getUsernameByID returns the username given its id. If the id is invalid, // it returns an empty string. func (c *cmswww) getUsernameByID(idStr string) string { - id, err := strconv.ParseUint(idStr, 10, 64) + id, err := uuid.FromString(idStr) if err != nil { return "" } @@ -64,7 +65,7 @@ func (c *cmswww) findUser(idStr, email, username string, isAdmin bool) (*databas ) if idStr != "" { - id, err := strconv.ParseUint(idStr, 10, 64) + id, err := uuid.FromString(idStr) if err == nil { user, err = c.db.GetUserById(id) if err != nil && err != database.ErrUserNotFound { diff --git a/cmswww/validate.go b/cmswww/validate.go index a587bae..a5d649b 100644 --- a/cmswww/validate.go +++ b/cmswww/validate.go @@ -13,6 +13,7 @@ import ( "github.com/decred/politeia/politeiad/api/v1/identity" "github.com/decred/politeia/util" + "github.com/gofrs/uuid" "github.com/decred/contractor-mgmt/cmswww/api/v1" "github.com/decred/contractor-mgmt/cmswww/database" @@ -168,11 +169,8 @@ func validateInvoice( // Invoices should only be viewable by admins and the users who submit them. func validateUserCanSeeInvoice(invoice *v1.InvoiceRecord, user *database.User) error { - authorID, err := strconv.ParseUint(invoice.UserID, 10, 64) - if err != nil { - return err - } - if user == nil || (!user.Admin && user.ID != authorID) { + authorID := invoice.UserID + if user == nil || (!user.Admin && user.ID.String() != authorID) { return v1.UserError{ ErrorCode: v1.ErrorStatusInvoiceNotFound, } @@ -247,7 +245,7 @@ func (c *cmswww) validatePubkeyIsUnique(publicKey string, user *database.User) e return err } - if userID == 0 { + if userID == uuid.Nil { return nil } diff --git a/go.mod b/go.mod index fc11de4..f145dad 100644 --- a/go.mod +++ b/go.mod @@ -14,14 +14,14 @@ require ( github.com/denisenkom/go-mssqldb v0.0.0-20190111225525-2fea367d496d // indirect github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect github.com/go-sql-driver/mysql v1.4.1 // indirect - github.com/gofrs/uuid v3.2.0+incompatible // indirect + github.com/gofrs/uuid v3.2.0+incompatible github.com/google/go-cmp v0.2.0 // indirect github.com/gorilla/csrf v1.5.1 github.com/gorilla/mux v1.6.2 github.com/gorilla/schema v1.0.2 github.com/gorilla/sessions v1.1.3 github.com/jessevdk/go-flags v1.4.0 - github.com/jinzhu/gorm v1.9.2 + github.com/jinzhu/gorm v1.9.3-0.20190102133208-9f1a7f535111 github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect github.com/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3 // indirect github.com/jrick/logrotate v1.0.0 diff --git a/go.sum b/go.sum index ee856ae..f13d764 100644 --- a/go.sum +++ b/go.sum @@ -161,6 +161,8 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw= github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/gorm v1.9.3-0.20190102133208-9f1a7f535111 h1:CnMZ6YNYy5LK78EwSKih/mYWJp9hHBtEDbARu7xWhxg= +github.com/jinzhu/gorm v1.9.3-0.20190102133208-9f1a7f535111/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3 h1:xvj06l8iSwiWpYgm8MbPp+naBg+pwfqmdXabzqPCn/8=