func (c *Customer) validateEmail() error { if c.Email == "" { return fmt.Errorf("error: %s", "invalid email address") } sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() col := sess.DB("CurtCart").C("customer") var tmp Customer err = col.Find(bson.M{"email": c.Email}).One(&tmp) if err != nil && err != mgo.ErrNotFound { return err } if tmp.Id.Hex() != "" { return fmt.Errorf("error: %s", "email already exists") } return nil }
func (o *Order) Update() error { if o.Id.Hex() == "" { return fmt.Errorf("error: %s", "invalid order identifier") } o.UpdatedAt = time.Now() if err := o.validate(); err != nil { return err } o.bindCustomer() sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() updateDoc, err := o.mapUpdate() if err != nil { return err } var change = mgo.Change{ ReturnNew: true, Update: bson.M{ "$set": updateDoc, }, } _, err = sess.DB("CurtCart").C("order").Find(bson.M{"_id": o.Id, "shop_id": o.ShopId}).Apply(change, o) return err }
func (o *Order) Create() error { o.Token = bson.NewObjectId() o.CreatedAt = time.Now() o.ProcessedAt = time.Now() o.UpdatedAt = time.Now() if err := o.validate(); err != nil { return err } count, err := getOrderCount(o.ShopId) if err != nil { return err } o.OrderNumber = count + 1 o.bindCustomer() if o.Id.Hex() == "" { o.Id = bson.NewObjectId() } sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() col := sess.DB("CurtCart").C("order") _, err = col.UpsertId(o.Id, o) return err }
// Add new customer. func (c *Customer) Insert(ref string) error { if err := c.validateEmail(); err != nil { c.Password = "" return err } if c.Password == "" { return fmt.Errorf("error: %s", "invalid password") } if c.FirstName == "" { c.Password = "" return fmt.Errorf("error: %s", "invalid first name") } if c.LastName == "" { c.Password = "" return fmt.Errorf("error: %s", "invalid last name") } if c.Id.Hex() == "" { c.Id = bson.NewObjectId() } if c.CreatedAt.IsZero() { c.CreatedAt = time.Now() } c.UpdatedAt = time.Now() cryptic, err := bcrypt.GenerateFromPassword([]byte(c.Password), bcrypt.DefaultCost) if err != nil { c.Password = "" return fmt.Errorf("error: %s", err.Error()) } pass := string(cryptic) c.generateToken(ref) c.Password = pass sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { c.Password = "" return err } defer sess.Close() col := sess.DB("CurtCart").C("customer") _, err = col.UpsertId(c.Id, c) if err != nil { c.Password = "" return err } // index the document idx := mgo.Index{ Key: []string{"email", "first_name", "last_name", "meta_fields", "note", "state"}, Background: true, Sparse: false, DropDups: true, } col.EnsureIndex(idx) c.Password = "" return nil }
func AuthenticateAccount(token string) (Customer, error) { sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return Customer{}, err } defer sess.Close() var cust Customer qs := bson.M{ "token": token, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } err = sess.DB("CurtCart").C("customer").Find(qs).One(&cust) if err != nil || !cust.Id.Valid() { return Customer{}, fmt.Errorf("error: %s", "failed to authenticate using JWT") } return cust, nil }
func (c *Customer) AddAddress(addr CustomerAddress) error { if c.Id.Hex() == "" { return fmt.Errorf("error: %s", "cannot update a customer that doesn't exist") } if addr.Id == nil || !addr.Id.Valid() { addrId := bson.NewObjectId() addr.Id = &addrId } if err := addr.Validate(); err != nil { return err } addr.UpdatedAt = time.Now() addr.CreatedAt = time.Now() sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() var change = mgo.Change{ ReturnNew: true, Update: bson.M{ "$addToSet": bson.M{ "addresses": addr, }, }, } _, err = sess.DB("CurtCart").C("customer").Find(bson.M{"_id": c.Id, "shop_id": c.ShopId}).Apply(change, c) return err }
// Get a customer by email. func (c *Customer) GetByEmail() error { sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() col := sess.DB("CurtCart").C("customer") qs := bson.M{ "email": c.Email, "shop_id": c.ShopId, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } err = col.Find(qs).One(&c) if err != nil { return err } c.Password = "" return nil }
func (c *Customer) SaveAddress(addr CustomerAddress) error { if c.Id.Hex() == "" { return fmt.Errorf("error: %s", "cannot update a customer that doesn't exist") } if err := addr.Validate(); err != nil { return err } addr.UpdatedAt = time.Now() sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() qry := bson.M{ "addresses._id": addr.Id, } change := bson.M{ "$set": bson.M{ "customer.$.address": addr, }, } return sess.DB("CurtCart").C("customer").Update(qry, change) }
func getOrderCount(shopId bson.ObjectId) (int, error) { sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return 0, err } defer sess.Close() return sess.DB("CurtCart").C("order").Find(bson.M{"shop_id": shopId}).Count() }
// Get all customers since a defined Id. func CustomersSinceId(shopId bson.ObjectId, since_id bson.ObjectId, page, limit int, created_at_min, created_at_max, updated_at_min, updated_at_max *time.Time) ([]Customer, error) { custs := []Customer{} sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return custs, err } defer sess.Close() c := sess.DB("CurtCart").C("customer") qs := bson.M{ "shop_id": shopId.String(), "_id": bson.M{ "$gt": since_id.String(), }, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } if created_at_min != nil || created_at_max != nil { createdQs := bson.M{} if created_at_min != nil { createdQs["&qt"] = created_at_min.String() } if created_at_max != nil { createdQs["<"] = created_at_max.String() } qs["created_at"] = createdQs } if updated_at_min != nil || updated_at_max != nil { updatedQs := bson.M{} if updated_at_min != nil { updatedQs["&qt"] = updated_at_min.String() } if updated_at_max != nil { updatedQs["<"] = updated_at_max.String() } qs["updated_at"] = updatedQs } if page == 1 { page = 0 } err = c.Find(qs).Skip(page * limit).Limit(limit).All(&custs) if err != nil { return []Customer{}, err } for i, _ := range custs { custs[i].Password = "" } return custs, err }
func clearMongo() { sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return } defer sess.Close() sess.DB("CurtCart").C("customer").RemoveAll(bson.M{}) sess.DB("CurtCart").C("order").RemoveAll(bson.M{}) sess.DB("CurtCart").C("shop").RemoveAll(bson.M{}) }
// Update a customer. // Updates updated_at, accepts_marketing, addresses, default_address, // email, first_name, last_name, meta_fields, note, state, tags. func (c *Customer) Update() error { if c.Id.Hex() == "" { c.Password = "" return fmt.Errorf("error: %s", "cannot update a customer that doesn't exist") } if c.FirstName == "" { c.Password = "" return fmt.Errorf("error: %s", "invalid first anem") } if c.LastName == "" { c.Password = "" return fmt.Errorf("error: %s", "invalid last name") } c.UpdatedAt = time.Now() sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() var change = mgo.Change{ ReturnNew: true, Update: bson.M{ "$set": bson.M{ "accepts_marketing": c.AcceptsMarketing, "addresses": c.Addresses, "default_address": c.DefaultAddress, "first_name": c.FirstName, "last_name": c.LastName, "meta_fields": c.MetaFields, "note": c.Note, "state": c.State, "tags": c.Tags, }, }, } qs := bson.M{ "_id": c.Id, "shop_id": c.ShopId, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } _, err = sess.DB("CurtCart").C("customer").Find(qs).Apply(change, c) c.Password = "" return err }
// This method is used explicitly for generating test data // DO NOT EXPOSE func InsertTestData() *bson.ObjectId { sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return nil } sh := Shop{} sh.Id = bson.NewObjectId() sh.Name = "Test Shop" sh.Address1 = "1119 Sunset Lane" sh.City = "Altoona" sh.Province = "Wisconsin" sh.ProvinceCode = "WI" sh.Country = "US" sh.CountryCode = "US" sh.CountryName = "United States" sh.Zip = "54720" sh.CreatedAt = time.Now() sh.Currency = "USD" sh.Domain, _ = url.Parse("http://store.ninneman.org") sh.Email = "*****@*****.**" sh.MoneyFormat = "$" sh.MoneyWithCurrencyFormat = "$ USD" sh.PasswordEnabled = true sh.Phone = "7153082604" sh.Public = false sh.ShopOwner = "Alex Ninneman" sh.TaxShipping = true sh.TaxesIncluded = false sh.CountyTaxes = false sh.Timezone = "US/Central" sh.HasStorefront = false l := geocoding.Lookup{ Address: fmt.Sprintf("%s, %s, %s %s", sh.Address1, sh.City, sh.ProvinceCode, sh.Zip), } resp, err := l.Search() if err == nil && len(resp.Results) > 0 { sh.Longitude = resp.Results[0].Geometry.Location.Longitude sh.Latitude = resp.Results[0].Geometry.Location.Latitude } if err := sess.DB("CurtCart").C("shop").Insert(sh); err != nil { return nil } return &sh.Id }
func (sh *Shop) Get() error { sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() c := sess.DB("CurtCart").C("shop") err = c.Find(bson.M{"_id": sh.Id}).One(&sh) if err != nil { return err } return nil }
// Delete a customer. // A customer can't be deleted if they have existing orders func (c *Customer) Delete() error { if c.Id.Hex() == "" { return fmt.Errorf("error: %s", "invalid customer reference") } sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() if err := c.Get(); err != nil { return err } if c.Orders != nil && len(c.Orders) > 0 { return fmt.Errorf("error: %s", "can't remove a customer that has order information") } return sess.DB("CurtCart").C("customer").RemoveId(c.Id) }
func CustomerCount(shopId bson.ObjectId) (int, error) { if shopId.Hex() == "" { return 0, fmt.Errorf("error: %s", "invalid shop reference") } sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return 0, err } defer sess.Close() qs := bson.M{ "shop_id": shopId, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } return sess.DB("CurtCart").C("customer").Find(qs).Count() }
func (c *Customer) DeleteAddress(addr CustomerAddress) error { if c.Id.Hex() == "" { return fmt.Errorf("error: %s", "cannot update a customer that doesn't exist") } if err := addr.Validate(); err != nil { return err } addr.UpdatedAt = time.Now() sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() qry := bson.M{ "addresses._id": addr.Id, } change := bson.M{ "$set": bson.M{ "customer.$.address.deleted": true, }, } err = sess.DB("CurtCart").C("customer").Update(qry, change) if err != nil { return err } for k, a := range c.Addresses { if a.Id == addr.Id { c.Addresses = append(c.Addresses[:k], c.Addresses[k+1:]...) } } return nil }
func (c *Customer) generateToken(referer string) error { c.Password = "" var err error // create token token := jwt.New(jwt.SigningMethodHS256) // assign claims token.Claims["iss"] = "carter.curtmfg.com" token.Claims["sub"] = referer token.Claims["exp"] = time.Now().Add(time.Hour * 1).Unix() token.Claims["iat"] = time.Now().Unix() c.Token, err = token.SignedString([]byte(jwtSigningKey)) if err != nil { return err } sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() var change = mgo.Change{ ReturnNew: true, Update: bson.M{ "$set": bson.M{ "token": c.Token, }, }, } _, err = sess.DB("CurtCart").C("customer").Find(bson.M{"_id": c.Id, "shop_id": c.ShopId}).Apply(change, c) c.Password = "" return err }
func SearchCustomers(query string, shopId bson.ObjectId) ([]Customer, error) { var custs []Customer if query == "" { return custs, fmt.Errorf("error: %s", "invalid query") } sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return custs, err } defer sess.Close() qs := bson.M{ "$text": bson.M{ "$search": query, }, "shop_id": shopId, "password": 0, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } err = sess.DB("CurtCart").C("customer").Find(qs).All(&custs) if err != nil { return []Customer{}, err } for i, _ := range custs { custs[i].Password = "" } return custs, err }
func IdentifierFromToken(t string) (bson.ObjectId, error) { sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return "", err } defer sess.Close() var cust Customer qs := bson.M{ "token": t, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } err = sess.DB("CurtCart").C("customer").Find(qs).One(&cust) if err != nil || !cust.Id.Valid() { return "", fmt.Errorf("error: %s", "failed to identify using JWT") } return cust.Id, nil }
// Login a customer. func (c *Customer) Login(ref string) error { pass := c.Password c.Password = "" sess, err := mgo.DialWithInfo(database.MongoConnectionString()) if err != nil { return err } defer sess.Close() col := sess.DB("CurtCart").C("customer") var custs []Customer qs := bson.M{ "email": c.Email, "shop_id": c.ShopId, "$or": []bson.M{ bson.M{"customer.addresses.deleted": false}, bson.M{"customer.addresses.deleted": bson.M{ "$exists": false, }}, }, } err = col.Find(qs).All(&custs) if err != nil { return err } if custs == nil || len(custs) == 0 { return fmt.Errorf("error: %s", "no account for this email address") } for _, cust := range custs { if err := bcrypt.CompareHashAndPassword([]byte(cust.Password), []byte(pass)); err != nil { continue } c.Id = cust.Id c.ShopId = cust.ShopId c.AcceptsMarketing = cust.AcceptsMarketing c.Addresses = cust.Addresses c.DefaultAddress = cust.DefaultAddress c.CreatedAt = cust.CreatedAt c.Email = cust.Email c.FirstName = cust.FirstName c.LastName = cust.LastName c.MetaFields = cust.MetaFields c.LastOrderId = cust.LastOrderId c.LastOrderName = cust.LastOrderName c.OrdersCount = cust.OrdersCount c.Note = cust.Note c.State = cust.State c.Tags = cust.Tags c.UpdatedAt = cust.UpdatedAt c.VerifiedEmail = cust.VerifiedEmail c.Orders = cust.Orders c.generateToken(ref) return nil } return fmt.Errorf("error: %s", "credentials do not match") }