func (a *RethinkDbAdapter) UserUpdateStatus(uid t.Uid, status interface{}) error { update := map[string]interface{}{"Status": status} _, err := rdb.DB(a.dbName).Table("users").Get(uid.String()). Update(update, rdb.UpdateOpts{Durability: "soft"}).RunWrite(a.conn) return err }
// Get a subscription of a user to a topic func (a *RethinkDbAdapter) SubscriptionGet(topic string, user t.Uid) (*t.Subscription, error) { rows, err := rdb.DB(a.dbName).Table("subscriptions").Get(topic + ":" + user.String()).Run(a.conn) if err != nil { return nil, err } var sub t.Subscription err = rows.One(&sub) return &sub, rows.Err() }
func (a *RethinkDbAdapter) UserUpdateLastSeen(uid t.Uid, userAgent string, when time.Time) error { update := struct { LastSeen time.Time UserAgent string }{when, userAgent} _, err := rdb.DB(a.dbName).Table("users").Get(uid.String()). Update(update, rdb.UpdateOpts{Durability: "soft"}).RunWrite(a.conn) return err }
// UserGet fetches a single user by user id. If user is not found it returns (nil, nil) func (a *RethinkDbAdapter) UserGet(uid t.Uid) (*t.User, error) { if row, err := rdb.DB(a.dbName).Table("users").Get(uid.String()).Run(a.conn); err == nil && !row.IsNil() { var user t.User if err = row.One(&user); err == nil { return &user, nil } return nil, err } else { // If user does not exist, it returns nil, nil return nil, err } }
// Add user's authentication record func (a *RethinkDbAdapter) AddAuthRecord(uid t.Uid, unique string, secret []byte, expires time.Time) (error, bool) { _, err := rdb.DB(a.dbName).Table("auth").Insert( map[string]interface{}{ "unique": unique, "userid": uid.String(), "secret": secret, "expires": expires}).RunWrite(a.conn) if err != nil { if rdb.IsConflictErr(err) { return errors.New("duplicate credential"), true } return err, false } return nil, false }
// SubsForUser loads a list of user's subscriptions to topics func (a *RethinkDbAdapter) SubsForUser(forUser t.Uid) ([]t.Subscription, error) { if forUser.IsZero() { return nil, errors.New("RethinkDb adapter: invalid user ID in TopicGetAll") } q := rdb.DB(a.dbName).Table("subscriptions").GetAllByIndex("User", forUser.String()).Limit(MAX_RESULTS) rows, err := q.Run(a.conn) if err != nil { return nil, err } var subs []t.Subscription var ss t.Subscription for rows.Next(&ss) { subs = append(subs, ss) } return subs, rows.Err() }
// Create creates a topic and owner's subscription to topic func (TopicsObjMapper) Create(topic *types.Topic, owner types.Uid, private interface{}) error { topic.InitTimes() err := adaptr.TopicCreate(topic) if err != nil { return err } if !owner.IsZero() { err = Subs.Create(&types.Subscription{ ObjHeader: types.ObjHeader{CreatedAt: topic.CreatedAt}, User: owner.String(), Topic: topic.Name, ModeGiven: types.ModeFull, ModeWant: topic.GetAccess(owner), Private: private}) } return err }
// approveSub processes a request to initiate an invite or approve a subscription request from another user: // Handle these cases: // A. Manager is inviting another user for the first time (no prior subscription) // B. Manager is re-inviting another user (adjusting modeGiven, modeWant is still "N") // C. Manager is changing modeGiven for another user, modeWant != "N" func (t *Topic) approveSub(h *Hub, sess *Session, target types.Uid, set *MsgClientSet) error { now := time.Now().UTC().Round(time.Millisecond) // Check if requester actually has permission to manage sharing if userData, ok := t.perUser[sess.uid]; !ok || !userData.modeGiven.IsManager() || !userData.modeWant.IsManager() { sess.queueOut(ErrPermissionDenied(set.Id, t.original, now)) return errors.New("topic access denied") } // Parse the access mode granted var modeGiven types.AccessMode if set.Sub.Mode != "" { modeGiven.UnmarshalText([]byte(set.Sub.Mode)) } // If the user is banned from topic, make sute it's the only change if modeGiven.IsBanned() { modeGiven = types.ModeBanned } // Make sure no one but the owner can do an ownership transfer if modeGiven.IsOwner() && t.owner != sess.uid { sess.queueOut(ErrPermissionDenied(set.Id, t.original, now)) return errors.New("attempt to transfer ownership by non-owner") } var givenBefore types.AccessMode // Check if it's a new invite. If so, save it to database as a subscription. // Saved subscription does not mean the user is allowed to post/read userData, ok := t.perUser[target] if !ok { if modeGiven == types.ModeNone { if t.accessAuth != types.ModeNone { // Request to use default access mode for the new subscriptions. modeGiven = t.accessAuth } else { sess.queueOut(ErrMalformed(set.Id, t.original, now)) return errors.New("cannot invite without giving any access rights") } } // Add subscription to database sub := &types.Subscription{ User: target.String(), Topic: t.name, ModeWant: types.ModeNone, ModeGiven: modeGiven, } if err := store.Subs.Create(sub); err != nil { sess.queueOut(ErrUnknown(set.Id, t.original, now)) return err } userData = perUserData{ modeGiven: sub.ModeGiven, modeWant: sub.ModeWant, private: nil, } t.perUser[target] = userData } else { // Action on an existing subscription (re-invite or confirm/decline) givenBefore = userData.modeGiven // Request to re-send invite without changing the access mode if modeGiven == types.ModeNone { modeGiven = userData.modeGiven } else if modeGiven != userData.modeGiven { userData.modeGiven = modeGiven // Save changed value to database if err := store.Subs.Update(t.name, sess.uid, map[string]interface{}{"ModeGiven": modeGiven}); err != nil { return err } t.perUser[target] = userData } } // The user does not want to be bothered, no further action is needed if userData.modeWant.IsBanned() { sess.queueOut(ErrPermissionDenied(set.Id, t.original, now)) return errors.New("topic access denied") } // Handle the following cases: // * a ban of the user, modeGive.IsBanned = true (if user is banned no need to invite anyone) // * regular invite: modeWant = "N", modeGiven > 0 // * access rights update: old modeGiven != new modeGiven if !modeGiven.IsBanned() { if userData.modeWant == types.ModeNone { // (re-)Send the invite to target h.route <- t.makeInvite(target, target, sess.uid, types.InvJoin, userData.modeWant, modeGiven, set.Sub.Info) } else if givenBefore != modeGiven { // Inform target that the access has changed h.route <- t.makeInvite(target, target, sess.uid, types.InvInfo, userData.modeWant, modeGiven, set.Sub.Info) } } // Has anything actually changed? if givenBefore != modeGiven { // inform requester of the change made h.route <- t.makeInvite(sess.uid, target, sess.uid, types.InvInfo, userData.modeWant, modeGiven, map[string]string{"before": givenBefore.String()}) } return nil }
// SubsDelete deletes a subscription. func (a *RethinkDbAdapter) SubsDelete(topic string, user t.Uid) error { _, err := rdb.DB(a.dbName).Table("subscriptions").Get(topic + ":" + user.String()).Delete().RunWrite(a.conn) return err }
// SubsUpdate updates a single subscription. func (a *RethinkDbAdapter) SubsUpdate(topic string, user t.Uid, update map[string]interface{}) error { _, err := rdb.DB(a.dbName).Table("subscriptions").Get(topic + ":" + user.String()).Update(update).RunWrite(a.conn) return err }
// Update time when the user was last attached to the topic func (a *RethinkDbAdapter) SubsLastSeen(topic string, user t.Uid, lastSeen map[string]time.Time) error { _, err := rdb.DB(a.dbName).Table("subscriptions").Get(topic+":"+user.String()). Update(map[string]interface{}{"LastSeen": lastSeen}, rdb.UpdateOpts{Durability: "soft"}).RunWrite(a.conn) return err }
// TopicsForUser loads user's contact list: p2p and grp topics, except for 'me' subscription. func (a *RethinkDbAdapter) TopicsForUser(uid t.Uid) ([]t.Subscription, error) { // Fetch user's subscriptions // Subscription have Topic.UpdatedAt denormalized into Subscription.UpdatedAt q := rdb.DB(a.dbName).Table("subscriptions").GetAllByIndex("User", uid.String()).Limit(MAX_RESULTS) //log.Printf("RethinkDbAdapter.TopicsForUser q: %+v", q) rows, err := q.Run(a.conn) if err != nil { return nil, err } // Fetch subscriptions. Two queries are needed: users table (me & p2p) and topics table (p2p and grp). // Prepare a list of Separate subscriptions to users vs topics var sub t.Subscription join := make(map[string]t.Subscription) // Keeping these to make a join with table for .private and .access topq := make([]interface{}, 0, 16) usrq := make([]interface{}, 0, 16) for rows.Next(&sub) { tcat := t.GetTopicCat(sub.Topic) // 'me' subscription, skip if tcat == t.TopicCat_Me || tcat == t.TopicCat_Fnd { continue // p2p subscription, find the other user to get user.Public } else if tcat == t.TopicCat_P2P { uid1, uid2, _ := t.ParseP2P(sub.Topic) if uid1 == uid { usrq = append(usrq, uid2.String()) } else { usrq = append(usrq, uid1.String()) } topq = append(topq, sub.Topic) // grp subscription } else { topq = append(topq, sub.Topic) } join[sub.Topic] = sub } //log.Printf("RethinkDbAdapter.TopicsForUser topq, usrq: %+v, %+v", topq, usrq) var subs []t.Subscription if len(topq) > 0 || len(usrq) > 0 { subs = make([]t.Subscription, 0, len(join)) } if len(topq) > 0 { // Fetch grp & p2p topics rows, err = rdb.DB(a.dbName).Table("topics").GetAll(topq...).Run(a.conn) if err != nil { return nil, err } var top t.Topic for rows.Next(&top) { sub = join[top.Id] sub.ObjHeader.MergeTimes(&top.ObjHeader) sub.SetSeqId(top.SeqId) sub.SetHardClearId(top.ClearId) if t.GetTopicCat(sub.Topic) == t.TopicCat_Grp { // all done with a grp topic sub.SetPublic(top.Public) subs = append(subs, sub) } else { // put back the updated value of a p2p subsription, will process further below join[top.Id] = sub } } //log.Printf("RethinkDbAdapter.TopicsForUser 1: %#+v", subs) } // Fetch p2p users and join to p2p tables if len(usrq) > 0 { rows, err = rdb.DB(a.dbName).Table("users").GetAll(usrq...).Run(a.conn) if err != nil { return nil, err } var usr t.User for rows.Next(&usr) { uid2 := t.ParseUid(usr.Id) topic := uid.P2PName(uid2) if sub, ok := join[topic]; ok { sub.ObjHeader.MergeTimes(&usr.ObjHeader) sub.SetWith(uid2.UserId()) sub.SetPublic(usr.Public) sub.SetLastSeenAndUA(usr.LastSeen, usr.UserAgent) subs = append(subs, sub) } } //log.Printf("RethinkDbAdapter.TopicsForUser 2: %#+v", subs) } return subs, nil }
func (a *RethinkDbAdapter) UserUpdate(uid t.Uid, update map[string]interface{}) error { _, err := rdb.DB(a.dbName).Table("users").Get(uid.String()).Update(update).RunWrite(a.conn) return err }
// Delete user's all authentication records func (a *RethinkDbAdapter) DelAllAuthRecords(uid t.Uid) (int, error) { res, err := rdb.DB(a.dbName).Table("auth").GetAllByIndex("userid", uid.String()).Delete().RunWrite(a.conn) return res.Deleted, err }