func tokensList(c *cli.Context) int { // Connect to RethinkDB _, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Get tokens from database cursor, err := r.Table("tokens").Map(func(row r.Term) r.Term { return r.Branch( row.HasFields("client_id"), row.Merge(map[string]interface{}{ "owners_address": r.Table("accounts").Get(row.Field("owner")).Field("main_address"), "client_name": r.Table("applications").Get(row.Field("client_id")).Field("name"), }), row.Merge(map[string]interface{}{ "owners_address": r.Table("accounts").Get(row.Field("owner")).Field("main_address"), }), ) }).Run(session) if err != nil { writeError(c, err) return 1 } var tokens []struct { models.Token OwnersAddress string `gorethink:"owners_address" json:"owners_address"` ClientName string `gorethink:"client_name" json:"client_name,omitempty"` } if err := cursor.All(&tokens); err != nil { writeError(c, err) return 1 } // Write the output if c.Bool("json") { if err := json.NewEncoder(c.App.Writer).Encode(tokens); err != nil { writeError(c, err) return 1 } fmt.Fprint(c.App.Writer, "\n") } else { table := termtables.CreateTable() table.AddHeaders("id", "type", "owner", "client_name", "expired", "date_created") for _, token := range tokens { table.AddRow( token.ID, token.Type, token.OwnersAddress, token.ClientName, !token.ExpiryDate.IsZero() && token.ExpiryDate.Before(time.Now()), token.DateCreated.Format(time.RubyDate), ) } fmt.Fprintln(c.App.Writer, table.Render()) } return 0 }
func applicationsList(c *cli.Context) int { // Connect to RethinkDB _, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Get applications from database cursor, err := r.Table("applications").Map(func(row r.Term) r.Term { return row.Merge(map[string]interface{}{ "owners_address": r.Table("accounts").Get(row.Field("owner")).Field("main_address"), }) }).Run(session) if err != nil { writeError(c, err) return 1 } var applications []struct { models.Application OwnersAddress string `gorethink:"owners_address" json:"owners_address"` } if err := cursor.All(&applications); err != nil { writeError(c, err) return 1 } // Write the output if c.Bool("json") { if err := json.NewEncoder(c.App.Writer).Encode(applications); err != nil { writeError(c, err) return 1 } fmt.Fprint(c.App.Writer, "\n") } else { table := termtables.CreateTable() table.AddHeaders("id", "name", "owner", "homepage", "date_created") for _, application := range applications { table.AddRow( application.ID, application.Name, application.OwnersAddress, application.Homepage, application.DateCreated.Format(time.RubyDate), ) } fmt.Fprintln(c.App.Writer, table.Render()) } return 0 }
func connectToRethinkDB(c *cli.Context) (*r.ConnectOpts, *r.Session, bool) { opts, err := utils.ParseRethinkDBString(c.GlobalString("rethinkdb")) if err != nil { writeError(c, err) return nil, nil, false } session, err := r.Connect(opts) if err != nil { writeError(c, err) return nil, nil, false } return &opts, session, true }
func applicationsAdd(c *cli.Context) int { // Connect to RethinkDB _, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Input struct var input struct { Owner string `json:"owner"` Callback string `json:"callback"` Homepage string `json:"homepage"` Name string `json:"name"` Description string `json:"description"` } // Read JSON from stdin if c.Bool("json") { if err := json.NewDecoder(c.App.Env["reader"].(io.Reader)).Decode(&input); err != nil { writeError(c, err) return 1 } } else { // Buffer stdin rd := bufio.NewReader(c.App.Env["reader"].(io.Reader)) var err error // Acquire from interactive input fmt.Fprint(c.App.Writer, "Owner's ID: ") input.Owner, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Owner = strings.TrimSpace(input.Owner) fmt.Fprint(c.App.Writer, "Application's name: ") input.Name, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Name = strings.TrimSpace(input.Name) fmt.Fprint(c.App.Writer, "Homepage URL: ") input.Homepage, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Homepage = strings.TrimSpace(input.Homepage) fmt.Fprint(c.App.Writer, "Description: ") input.Description, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Description = strings.TrimSpace(input.Description) fmt.Fprint(c.App.Writer, "Callback URL: ") input.Callback, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Callback = strings.TrimSpace(input.Callback) } // Validate the input // Check if account ID exists cursor, err := r.Table("accounts").Get(input.Owner).Ne(nil).Run(session) if err != nil { writeError(c, err) return 1 } defer cursor.Close() var exists bool if err := cursor.One(&exists); err != nil { writeError(c, err) return 1 } if !exists { writeError(c, fmt.Errorf("Account %s doesn't exist", input.Owner)) return 1 } // Homepage URL should be a URL if !govalidator.IsURL(input.Homepage) { writeError(c, fmt.Errorf("%s is not a URL", input.Homepage)) return 1 } // Callback URL should be a URL if !govalidator.IsURL(input.Callback) { writeError(c, fmt.Errorf("%s is not a URL", input.Callback)) return 1 } // Insert into database application := &models.Application{ ID: uniuri.NewLen(uniuri.UUIDLen), DateCreated: time.Now(), DateModified: time.Now(), Owner: input.Owner, Secret: uniuri.NewLen(32), Callback: input.Callback, Homepage: input.Homepage, Name: input.Name, Description: input.Description, } if !c.GlobalBool("dry") { if err := r.Table("applications").Insert(application).Exec(session); err != nil { writeError(c, err) return 1 } } // Write a success message fmt.Fprintf(c.App.Writer, "Created a new application with ID %s\n", application.ID) return 0 }
func tokensAdd(c *cli.Context) int { // Connect to RethinkDB _, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Input struct var input struct { Owner string `json:"owner"` ExpiryDate time.Time `json:"expiry_date"` Type string `json:"type"` Scope []string `json:"scope"` ClientID string `json:"client_id"` } // Read JSON from stdin if c.Bool("json") { if err := json.NewDecoder(c.App.Env["reader"].(io.Reader)).Decode(&input); err != nil { writeError(c, err) return 1 } } else { // Buffer stdin rd := bufio.NewReader(c.App.Env["reader"].(io.Reader)) var err error // Acquire from interactive input fmt.Fprint(c.App.Writer, "Owner's ID: ") input.Owner, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Owner = strings.TrimSpace(input.Owner) fmt.Fprint(c.App.Writer, "Type [auth/activate/code]: ") input.Type, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Type = strings.TrimSpace(input.Type) fmt.Fprint(c.App.Writer, "Expiry date [2006-01-02T15:04:05Z07:00/empty]: ") expiryDate, err := rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } expiryDate = strings.TrimSpace(expiryDate) if expiryDate != "" { input.ExpiryDate, err = time.Parse(time.RFC3339, expiryDate) if err != nil { writeError(c, err) return 1 } } if input.Type == "auth" || input.Type == "code" { fmt.Fprint(c.App.Writer, "Client ID: ") input.ClientID, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.ClientID = strings.TrimSpace(input.ClientID) fmt.Fprint(c.App.Writer, "Scope (seperated by commas): ") scope, err := rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } scope = strings.TrimSpace(scope) input.Scope = strings.Split(scope, ",") } } // Validate the input // Type has to be either auth or activate if input.Type != "auth" && input.Type != "activate" && input.Type != "code" { writeError(c, fmt.Errorf("Token type must be either auth or activate. Got %s.", input.Type)) return 1 } // Scopes must exist if input.Scope != nil && len(input.Scope) > 0 { for _, scope := range input.Scope { if _, ok := models.Scopes[scope]; !ok { writeError(c, fmt.Errorf("Scope %s doesn't exist", scope)) return 1 } } } // Owner must exist cursor, err := r.Table("accounts").Get(input.Owner).Ne(nil).Run(session) if err != nil { writeError(c, err) } defer cursor.Close() var exists bool if err := cursor.One(&exists); err != nil { writeError(c, err) return 1 } if !exists { writeError(c, fmt.Errorf("Account %s doesn't exist", input.Owner)) return 1 } // Application must exist if input.ClientID != "" { cursor, err = r.Table("applications").Get(input.ClientID).Ne(nil).Run(session) if err != nil { writeError(c, err) } defer cursor.Close() var exists bool if err := cursor.One(&exists); err != nil { writeError(c, err) return 1 } if !exists { writeError(c, fmt.Errorf("Application %s doesn't exist", input.ClientID)) return 1 } } // Insert into database token := &models.Token{ ID: uniuri.NewLen(uniuri.UUIDLen), DateCreated: time.Now(), DateModified: time.Now(), Owner: input.Owner, ExpiryDate: input.ExpiryDate, Type: input.Type, Scope: input.Scope, ClientID: input.ClientID, } if !c.GlobalBool("dry") { if err := r.Table("tokens").Insert(token).Exec(session); err != nil { writeError(c, err) return 1 } } // Write a success message fmt.Fprintf(c.App.Writer, "Created a new %s token with ID %s\n", token.Type, token.ID) return 0 }
func addressesAdd(c *cli.Context) int { // Connect to RethinkDB _, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Input struct var input struct { ID string `json:"id"` Owner string `json:"owner"` } // Read JSON from stdin if c.Bool("json") { if err := json.NewDecoder(c.App.Env["reader"].(io.Reader)).Decode(&input); err != nil { writeError(c, err) return 1 } } else { // Buffer stdin rd := bufio.NewReader(c.App.Env["reader"].(io.Reader)) var err error // Acquire from interactive input fmt.Fprintf(c.App.Writer, "Address: ") input.ID, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.ID = strings.TrimSpace(input.ID) fmt.Fprintf(c.App.Writer, "Owner ID: ") input.Owner, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Owner = strings.TrimSpace(input.Owner) } // First of all, the address. Append domain if it has no such suffix. if strings.Index(input.ID, "@") == -1 { input.ID += "@" + c.GlobalString("default_domain") } // And format it styledID := utils.NormalizeAddress(input.ID) input.ID = utils.RemoveDots(styledID) // Then check if it's taken. cursor, err := r.Table("addresses").Get(input.ID).Ne(nil).Run(session) if err != nil { writeError(c, err) return 1 } defer cursor.Close() var taken bool if err := cursor.One(&taken); err != nil { writeError(c, err) return 1 } if taken { writeError(c, fmt.Errorf("Address %s is already taken", input.ID)) return 1 } // Check if account ID exists cursor, err = r.Table("accounts").Get(input.Owner).Ne(nil).Run(session) if err != nil { writeError(c, err) } defer cursor.Close() var exists bool if err := cursor.One(&exists); err != nil { writeError(c, err) return 1 } if !exists { writeError(c, fmt.Errorf("Account %s doesn't exist", input.ID)) return 1 } // Insert the address into the database address := &models.Address{ ID: input.ID, StyledID: styledID, DateCreated: time.Now(), DateModified: time.Now(), Owner: input.Owner, } if !c.GlobalBool("dry") { if err := r.Table("addresses").Insert(address).Exec(session); err != nil { writeError(c, err) return 1 } } // Write a success message fmt.Fprintf(c.App.Writer, "Created a new address - %s\n", address.StyledID) return 0 }
func accountsList(c *cli.Context) int { // Connect to RethinkDB _, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Get accounts without passwords from database cursor, err := r.Table("accounts").Map(func(row r.Term) r.Term { return row.Without("password").Merge(map[string]interface{}{ "addresses": r.Table("addresses").GetAllByIndex("owner", row.Field("id")).CoerceTo("array"), }) }).Run(session) if err != nil { writeError(c, err) return 1 } var accounts []struct { models.Account Addresses []*models.Address `gorethink:"addresses" json:"addresses` } if err := cursor.All(&accounts); err != nil { writeError(c, err) return 1 } // Write the output if c.Bool("json") { if err := json.NewEncoder(c.App.Writer).Encode(accounts); err != nil { writeError(c, err) return 1 } fmt.Fprint(c.App.Writer, "\n") } else { table := termtables.CreateTable() table.AddHeaders("id", "addresses", "subscription", "status", "date_created") for _, account := range accounts { emails := []string{} for _, address := range account.Addresses { if address.ID == account.MainAddress { address.StyledID = "*" + address.StyledID emails = append([]string{address.StyledID}, emails...) } else { emails = append(emails, address.StyledID) } } table.AddRow( account.ID, strings.Join(emails, ", "), account.Subscription, account.Status, account.DateCreated.Format(time.RubyDate), ) } fmt.Fprintln(c.App.Writer, table.Render()) } return 0 }
func accountsAdd(c *cli.Context) int { // Connect to RethinkDB _, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Input struct var input struct { MainAddress string `json:"main_address"` Password string `json:"password"` Subscription string `json:"subscription"` AltEmail string `json:"alt_email"` Status string `json:"status"` } // Read JSON from stdin if c.Bool("json") { if err := json.NewDecoder(c.App.Env["reader"].(io.Reader)).Decode(&input); err != nil { writeError(c, err) return 1 } } else { // Buffer stdin rd := bufio.NewReader(c.App.Env["reader"].(io.Reader)) var err error // Acquire from interactive input fmt.Fprint(c.App.Writer, "Main address: ") input.MainAddress, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.MainAddress = strings.TrimSpace(input.MainAddress) fmt.Fprint(c.App.Writer, "Password: "******"Password: "******"Subscription [beta/admin]: ") input.Subscription, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Subscription = strings.TrimSpace(input.Subscription) fmt.Fprint(c.App.Writer, "Alternative address: ") input.AltEmail, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.AltEmail = strings.TrimSpace(input.AltEmail) fmt.Fprint(c.App.Writer, "Status [inactive/active/suspended]: ") input.Status, err = rd.ReadString('\n') if err != nil { writeError(c, err) return 1 } input.Status = strings.TrimSpace(input.Status) } // Analyze the input // First of all, the address. Append domain if it has no such suffix. if strings.Index(input.MainAddress, "@") == -1 { input.MainAddress += "@" + c.GlobalString("default_domain") } // And format it styledID := utils.NormalizeAddress(input.MainAddress) input.MainAddress = utils.RemoveDots(styledID) // Then check if it's taken. cursor, err := r.Table("addresses").Get(input.MainAddress).Ne(nil).Run(session) if err != nil { writeError(c, err) return 1 } defer cursor.Close() var taken bool if err := cursor.One(&taken); err != nil { writeError(c, err) return 1 } if taken { writeError(c, fmt.Errorf("Address %s is already taken", input.MainAddress)) return 1 } // If the password isn't 64 characters long, then hash it. var password []byte if len(input.Password) != 64 { hash := sha256.Sum256([]byte(input.Password)) password = hash[:] } else { password, err = hex.DecodeString(input.Password) if err != nil { writeError(c, err) return 1 } } // Subscription has to be beta or admin if input.Subscription != "beta" && input.Subscription != "admin" { writeError(c, fmt.Errorf("Subscription has to be either beta or admin. Got %s.", input.Subscription)) return 1 } // AltEmail must be an email if !govalidator.IsEmail(input.AltEmail) { writeError(c, fmt.Errorf("Email %s has an incorrect format", input.AltEmail)) return 1 } // Status has to be inactive/active/suspended if input.Status != "inactive" && input.Status != "active" && input.Status != "suspended" { writeError(c, fmt.Errorf("Status has to be either inactive, active or suspended. Got %s.", input.Status)) return 1 } // Prepare structs to insert account := &models.Account{ ID: uniuri.NewLen(uniuri.UUIDLen), DateCreated: time.Now(), DateModified: time.Now(), MainAddress: input.MainAddress, Subscription: input.Subscription, AltEmail: input.AltEmail, Status: input.Status, } if err := account.SetPassword([]byte(password)); err != nil { writeError(c, err) return 1 } address := &models.Address{ ID: input.MainAddress, StyledID: styledID, DateCreated: time.Now(), DateModified: time.Now(), Owner: account.ID, } var labels []*models.Label if account.Status != "inactive" { labels = []*models.Label{ &models.Label{ ID: uniuri.NewLen(uniuri.UUIDLen), DateCreated: time.Now(), DateModified: time.Now(), Owner: account.ID, Name: "Inbox", System: true, }, &models.Label{ ID: uniuri.NewLen(uniuri.UUIDLen), DateCreated: time.Now(), DateModified: time.Now(), Owner: account.ID, Name: "Spam", System: true, }, &models.Label{ ID: uniuri.NewLen(uniuri.UUIDLen), DateCreated: time.Now(), DateModified: time.Now(), Owner: account.ID, Name: "Sent", System: true, }, &models.Label{ ID: uniuri.NewLen(uniuri.UUIDLen), DateCreated: time.Now(), DateModified: time.Now(), Owner: account.ID, Name: "Starred", System: true, }, } } // Insert them into database if !c.Bool("dry") { if err := r.Table("addresses").Insert(address).Exec(session); err != nil { writeError(c, err) return 1 } if err := r.Table("accounts").Insert(account).Exec(session); err != nil { writeError(c, err) return 1 } if labels != nil { if err := r.Table("labels").Insert(labels).Exec(session); err != nil { writeError(c, err) return 1 } } } // Write a success message fmt.Fprintf(c.App.Writer, "Created a new account with ID %s\n", account.ID) return 0 }
func databaseMigrate(c *cli.Context) int { // Connect to RethinkDB opts, session, connected := connectToRethinkDB(c) if !connected { return 1 } // Get the migration status from the database version, err := getDatabaseVersion(opts, session) if err != nil { writeError(c, err) return 1 } // Show the current migration's status fmt.Fprintf(c.App.Writer, "Current database schema's version is %d.\n", version) fmt.Fprintf(c.App.Writer, "Latest migration's version is %d.\n", len(migrations)-1) // Only proceed if the schema is outdated if version >= len(migrations)-1 { fmt.Fprintln(c.App.Writer, "Your schema is up to date.") return 0 } // I don't know why would anyone use it, but it's here if c.Bool("no") { fmt.Fprintln(c.App.Writer, "Aborting the command because of the --no option.") return 1 } // Ask for confirmations if !c.Bool("yes") { want, err := utils.AskForConfirmation( c.App.Writer, c.App.Env["reader"].(io.Reader), "Would you like to run "+strconv.Itoa(len(migrations)-1-version)+" migrations? [y/n]: ", ) if err != nil { writeError(c, err) return 1 } if !want { fmt.Fprintln(c.App.Writer, "Aborting the command.") return 1 } } // Collect all queries queries := []r.Term{} for _, migration := range migrations[version+1:] { queries = append(queries, migration.Migrate(opts)...) queries = append(queries, r.Table("migration_status").Get("revision").Update(map[string]interface{}{ "value": migration.Revision, })) } // Create a new progress bar bar := pb.StartNew(len(queries)) for i, query := range queries { if c.Bool("dry") { fmt.Fprintf(c.App.Writer, "Executing %s\n", query.String()) } else { if err := query.Exec(session); err != nil { bar.FinishPrint("Failed to execute migration #" + strconv.Itoa(i) + ":") fmt.Fprintf(c.App.Writer, "\tQuery: %s\n", query.String()) fmt.Fprintf(c.App.Writer, "\tError: %v\n", err) return 1 } } bar.Increment() } // Show a "finished" message bar.FinishPrint("Migration completed. " + strconv.Itoa(len(queries)) + " queries executed.") return 0 }