func render(w http.ResponseWriter, r *http.Request, status int, file string, data interface{}) { fmap := template.FuncMap{ "getUser": func(id int) *User { return getUser(id) }, "getCurrentUser": func() *User { return getCurrentUser(w, r) }, "isFriend": func(id int) bool { return isFriend(w, r, id) }, "prefectures": func() []string { return prefs }, "substring": func(s string, l int) string { if len(s) > l { return s[:l] } return s }, "split": strings.Split, "getEntry": func(id int) Entry { row := db.QueryRow(`SELECT entries.id, entries.user_id, private, entries.title, created_at FROM entries WHERE id=?`, id) var entryID, userID, private int var subject string var createdAt time.Time checkErr(row.Scan(&entryID, &userID, &private, &subject, &createdAt)) return Entry{id, userID, private == 1, subject, "", createdAt} }, "numComments": func(id int) int { return numComments(id) }, "watch": func(name string) string { stopwatch.Watch(name) return "" }, } tpl := template.Must(template.New(file).Funcs(fmap).ParseFiles(getTemplatePath(file), getTemplatePath("header.html"))) w.WriteHeader(status) checkErr(tpl.Execute(w, data)) }
func ListEntries(w http.ResponseWriter, r *http.Request) { stopwatch.Reset("GET /diary/entries/{account_name}") defer stopwatch.Watch("finish GET /diary/entries/{account_name}") if !authenticated(w, r) { return } account := mux.Vars(r)["account_name"] owner := getUserFromAccount(w, account) var query string if permitted(w, r, owner.ID) { query = `SELECT id, user_id, private, body, created_at FROM entries WHERE user_id = ? ORDER BY created_at DESC LIMIT 20` } else { query = `SELECT id, user_id, private, body, created_at FROM entries WHERE user_id = ? AND private=0 ORDER BY created_at DESC LIMIT 20` } rows, err := db.Query(query, owner.ID) if err != sql.ErrNoRows { checkErr(err) } entries := make([]Entry, 0, 20) for rows.Next() { var id, userID, private int var body string var createdAt time.Time checkErr(rows.Scan(&id, &userID, &private, &body, &createdAt)) entry := Entry{id, userID, private == 1, strings.SplitN(body, "\n", 2)[0], strings.SplitN(body, "\n", 2)[1], createdAt} entries = append(entries, entry) } rows.Close() markFootprint(w, r, owner.ID) w.WriteHeader(http.StatusOK) checkErr(EntriesMyTmpl(w, struct { Owner *User Entries []Entry Myself bool }{owner, entries, getCurrentUser(w, r).ID == owner.ID})) }
func GetIndex(w http.ResponseWriter, r *http.Request) { stopwatch.Reset("GET /") defer stopwatch.Watch("finish GET /") if !authenticated(w, r) { return } stopwatch.Watch("After authorize") user := getCurrentUser(w, r) stopwatch.Watch("After getCurrentUser") prof := Profile{} row := db.QueryRow(`SELECT * FROM profiles WHERE user_id = ?`, user.ID) err := row.Scan(&prof.UserID, &prof.FirstName, &prof.LastName, &prof.Sex, &prof.Birthday, &prof.Pref, &prof.UpdatedAt) if err != sql.ErrNoRows { checkErr(err) } stopwatch.Watch("After Profile") rows, err := db.Query(`SELECT id, user_id, private, body, created_at FROM entries WHERE user_id = ? ORDER BY created_at LIMIT 5`, user.ID) if err != sql.ErrNoRows { checkErr(err) } entries := make([]Entry, 0, 5) for rows.Next() { var id, userID, private int var body string var createdAt time.Time checkErr(rows.Scan(&id, &userID, &private, &body, &createdAt)) entries = append(entries, Entry{id, userID, private == 1, strings.SplitN(body, "\n", 2)[0], strings.SplitN(body, "\n", 2)[1], createdAt}) } rows.Close() stopwatch.Watch("After Entry") rows, err = db.Query(`SELECT c.id AS id, c.entry_id AS entry_id, c.user_id AS user_id, c.summary, c.created_at AS created_at FROM comments c JOIN entries e ON c.entry_id = e.id WHERE e.user_id = ? ORDER BY c.created_at DESC LIMIT 10`, user.ID) if err != sql.ErrNoRows { checkErr(err) } commentsForMe := make([]Comment, 0, 10) for rows.Next() { c := Comment{} checkErr(rows.Scan(&c.ID, &c.EntryID, &c.UserID, &c.Comment, &c.CreatedAt)) commentsForMe = append(commentsForMe, c) } rows.Close() stopwatch.Watch("After Comment") relations := friendsForMe[user.ID] friendsMap := make(map[int]time.Time) friendIds := make([]string, 0) for _, r := range relations { friendID := r.anotherID if _, ok := friendsMap[friendID]; !ok { friendsMap[friendID] = r.createdAt friendIds = append(friendIds, strconv.Itoa(friendID)) } } friends := make([]Friend, 0, len(friendsMap)) for key, val := range friendsMap { friends = append(friends, Friend{key, val}) } rows.Close() stopwatch.Watch("After Relation") entryIndex := "" if len(friendIds) > 20 { entryIndex = "created_at" } else { entryIndex = "user_id" } // fmt.Println(entryIndex) rows, err = db.Query(fmt.Sprintf(`SELECT id, user_id, private, title, created_at FROM entries FORCE INDEX(%s) WHERE user_id IN (%s) ORDER BY created_at DESC LIMIT 10`, entryIndex, strings.Join(friendIds, ","))) if err != sql.ErrNoRows { checkErr(err) } entriesOfFriends := make([]Entry, 0, 10) for rows.Next() { var id, userID, private int var subject string var createdAt time.Time checkErr(rows.Scan(&id, &userID, &private, &subject, &createdAt)) if !isFriend2(friendsMap, userID) { continue } entriesOfFriends = append(entriesOfFriends, Entry{id, userID, private == 1, subject, "", createdAt}) if len(entriesOfFriends) >= 10 { break } } rows.Close() stopwatch.Watch("After entries") // コメントを1000件取得し、privateであれば自分と相手がフレンドかどうかをチェックする commentIndex := "" if len(friendIds) > 20 { commentIndex = "created_at" } else { commentIndex = "user_id" } query := fmt.Sprintf(`SELECT c.id, c.entry_id, c.user_id, c.summary, c.created_at, e.id, e.user_id, e.private, e.title, e.created_at FROM comments c FORCE INDEX (%s) JOIN entries e ON c.entry_id = e.id WHERE c.user_id IN (%s) ORDER BY c.created_at DESC LIMIT 10`, commentIndex, strings.Join(friendIds, ",")) // fmt.Println(query) rows, err = db.Query(query) if err != sql.ErrNoRows { checkErr(err) } commentsOfFriends := make([]CommentWithEntry, 0, 10) for rows.Next() { c := CommentWithEntry{} var id, userID, private int var subject string var createdAt time.Time checkErr(rows.Scan(&c.ID, &c.EntryID, &c.UserID, &c.Comment, &c.CreatedAt, &id, &userID, &private, &subject, &createdAt)) if !isFriend2(friendsMap, c.UserID) { continue } entry := Entry{id, userID, private == 1, subject, "", createdAt} c.Entry = entry if entry.Private { if !permitted(w, r, entry.UserID) { continue } } commentsOfFriends = append(commentsOfFriends, c) if len(commentsOfFriends) >= 10 { break } } rows.Close() stopwatch.Watch("After Friend Comments") footprints, err := getFootprintsFromRedis(user.ID, 10) if err != nil { checkErr(err) } stopwatch.Watch("After Get Friend Comments") w.WriteHeader(http.StatusOK) checkErr(MyTmpl(w, struct { User User Profile Profile Entries []Entry CommentsForMe []Comment EntriesOfFriends []Entry CommentsOfFriends []CommentWithEntry Friends []Friend Footprints []Footprint }{ *user, prof, entries, commentsForMe, entriesOfFriends, commentsOfFriends, friends, footprints, })) stopwatch.Watch("After after render") }
func GetData(w http.ResponseWriter, r *http.Request) { stopwatch.Watch("GetData") user := getCurrentUser(w, r) if user == nil { w.WriteHeader(http.StatusForbidden) return } row := db.QueryRow(`SELECT arg FROM subscriptions WHERE user_id=$1`, user.ID) var argJson string checkErr(row.Scan(&argJson)) var arg Arg checkErr(json.Unmarshal([]byte(argJson), &arg)) data := make([]Data, 0, len(arg)) var services []string for service, _ := range arg { services = append(services, `'`+service+`'`) } if len(services) == 0 { w.Header().Set("Content-Type", "application/json") body, err := json.Marshal(data) checkErr(err) w.Write(body) return } query := fmt.Sprintf(`SELECT meth, token_type, token_key, uri, service FROM endpoints WHERE service IN (%s)`, strings.Join(services, ",")) rows, err := db.Query(query) defer rows.Close() checkErr(err) for rows.Next() { var method string var tokenType *string var tokenKey *string var uriTemplate *string var serv *string checkErr(rows.Scan(&method, &tokenType, &tokenKey, &uriTemplate, &serv)) service := *serv conf, _ := arg[service] headers := make(map[string]string) params := conf.Params if params == nil { params = make(map[string]string) } if tokenType != nil && tokenKey != nil { switch *tokenType { case "header": headers[*tokenKey] = conf.Token break case "param": params[*tokenKey] = conf.Token break } } ks := make([]interface{}, len(conf.Keys)) ks2 := make([]string, len(conf.Keys)) for i, s := range conf.Keys { ks[i] = s ks2[i] = s } uri := fmt.Sprintf(*uriTemplate, ks...) stopwatch.Watch(service + " start") lc := logcache(time.Now()) if service == "ken" { key := ks2[0] cache, ok := kenCache[key] if ok { data = append(data, cache) lc("hit", user.ID, service, key) } else { d := Data{service, fetchApi(method, uri, headers, params)} kenCache[key] = d data = append(data, d) lc("miss", user.ID, service, key) } } else if service == "ken2" { q, _ := params["zipcode"] cache, ok := ken2Cache[q] if ok { data = append(data, cache) lc("hit", user.ID, service, q) } else { d := Data{service, fetchApi(method, uri, headers, params)} ken2Cache[q] = d data = append(data, d) lc("miss", user.ID, service, q) } } else if service == "surname" { q, _ := params["q"] cache, ok := surnameCache[q] if ok { data = append(data, cache) lc("hit", user.ID, service, q) } else { d := Data{service, fetchApi(method, uri, headers, params)} surnameCache[q] = d data = append(data, d) lc("miss", user.ID, service, q) } } else if service == "givenname" { q, _ := params["q"] cache, ok := givennameCache[q] if ok { data = append(data, cache) lc("hit", user.ID, service, q) } else { d := Data{service, fetchApi(method, uri, headers, params)} givennameCache[q] = d data = append(data, d) lc("miss", user.ID, service, q) } } else if service == "tenki" { q, _ := params["zipcode"] cache, found := tenkiCache.Get(q) if found { data = append(data, cache.(Data)) lc("hit", user.ID, service, q) } else { d := Data{service, fetchApi(method, uri, headers, params)} tenkiCache.Set(q, d, 2*time.Second) data = append(data, d) lc("miss", user.ID, service, q) } } else if service == "perfectsec_attacked" { key := fmt.Sprintf("pa_%s", headers["X-PERFECT-SECURITY-TOKEN"]) cache, found := exCache.Get(key) if found { data = append(data, cache.(Data)) lc("hit", user.ID, service, key) } else { d := Data{service, fetchApi(method, uri, headers, params)} exCache.Set(key, d, 10*time.Second) data = append(data, d) lc("miss", user.ID, service, key) } } else { data = append(data, Data{service, fetchApi(method, uri, headers, params)}) lc("uncached", user.ID, service, "-") } stopwatch.Watch(service + " finish") } w.Header().Set("Content-Type", "application/json") body, err := json.Marshal(data) checkErr(err) w.Write(body) }