// httpList provides users with an endpoint to update their subscriptions. // Depending on the HTTP method passed, different functionality will occur. func httpList(e *env, header http.Header) interface{} { c := e.Context now := time.Now() id := e.Req.FormValue("id") noneMatch := e.Req.Header.Get("If-None-Match") var reqAtHead bool // Validate lkey; on PUT or DELETE we're modifying our subscription so we // must have a complete key. GET or POST may have either. if e.Req.Method == "PUT" || e.Req.Method == "DELETE" { if id == "" { return http.StatusBadRequest } } else if e.Req.Method != "POST" && e.Req.Method != "GET" { panic("Unknown/unsupported HTTP method: " + e.Req.Method) } var list *model.List var user *model.User // Perform a XG-transaction on our User and List, to keep them in sync. err := datastore.RunInTransaction(c, func(c appengine.Context) (err error) { var ukey *datastore.Key ukey, user, err = loadOrCreateUser(c, e.User) if err != nil { return err } if id != "" { data := (e.Req.Method == "GET" || e.Req.Method == "POST") list, err = app_model.Get(c, id, data) if err != nil { return err } if noneMatch != "" { state := list.State() reqAtHead = (state != "" && noneMatch == state) } } switch e.Req.Method { default: // If we're a GET, don't do anything. return nil case "POST": if list == nil { list, err = app_model.Create(c) user.Accept(list) c.Infof("create list=%s", list.Id) } else { c.Infof("update list=%s", list.Id) } var update model.EntryMap err = json.Unmarshal(e.Body, &update) if err != nil { return err } list.Data.UpdateWith(update) c.Infof("updating with EntryMap=%s", update) case "PUT": user.Accept(list) c.Infof("join list=%s", list.Id) case "DELETE": user.Refuse(list) c.Infof("part list=%s", list.Id) } list.Version += 1 list.Updated = now // TODO: update Nonce. Possibly only on content changes. _, err = datastore.Put(c, ukey, user) if err == nil { err = app_model.Put(c, list) } return err }, &datastore.TransactionOptions{XG: true}) // We're a GET without an ID; return the state of all subscribed lists. if err == nil && list == nil { if e.Req.Method != "GET" { panic("Must have List key unless calling with GET") } out, err := listState(c, user) if err == nil { return out } } if err != nil { if err == datastore.ErrNoSuchEntity { return http.StatusNotFound } return err } // Render the complete state of the list. if state := list.State(); state != "" { header.Set("ETag", state) } header.Set("Last-Modified", list.Updated.Format(time.RFC1123Z)) header.Set("X-List-Id", list.Id) for _, v := range list.Data { // Note that these are case-insensitive: this is fine, since all generated IDs are case-insensitive. if v.Hint != "" { // TODO: is \u0000 always going to be 1: ? header.Set("X-Entry-"+v.Id, v.Hint[1:]) } } if reqAtHead { // TODO: If we're a POST, this status code doesn't make a whole lot of sense. return http.StatusNotModified } return list.Data }
// 生成 退订 邮件的 token func GenUnsubscribeToken(user *model.User) string { return util.Md5(user.String() + Config["unsubscribe_token_key"]) }
// 生成 退订 邮件的 token func (EmailLogic) GenUnsubscribeToken(user *model.User) string { return goutils.Md5(user.String() + config.ConfigFile.MustValue("security", "unsubscribe_token_key")) }