// 文章详细页 // uri: /articles/{id:[0-9]+} func ArticleDetailHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) article, prevNext, err := service.FindArticlesById(vars["id"]) if err != nil { util.Redirect(rw, req, "/articles") return } if article == nil || article.Id == 0 || article.Status == model.StatusOffline { util.Redirect(rw, req, "/articles") return } likeFlag := 0 hadCollect := 0 user, ok := filter.CurrentUser(req) if ok { uid := user["uid"].(int) likeFlag = service.HadLike(uid, article.Id, model.TYPE_ARTICLE) hadCollect = service.HadFavorite(uid, article.Id, model.TYPE_ARTICLE) } service.Views.Incr(req, model.TYPE_ARTICLE, article.Id) // 为了阅读数即时看到 article.Viewnum++ // 设置内容模板 req.Form.Set(filter.CONTENT_TPL_KEY, "/template/articles/detail.html,/template/common/comment.html") // 设置模板数据 filter.SetData(req, map[string]interface{}{"activeArticles": "active", "article": article, "prev": prevNext[0], "next": prevNext[1], "likeflag": likeFlag, "hadcollect": hadCollect}) }
// 包装链接 func WRHandler(rw http.ResponseWriter, req *http.Request) { tUrl := req.FormValue("u") if tUrl == "" { util.Redirect(rw, req, "/") return } if pUrl, err := url.Parse(tUrl); err != nil { util.Redirect(rw, req, tUrl) return } else { if !pUrl.IsAbs() { util.Redirect(rw, req, tUrl) return } // 本站 if strings.Contains(pUrl.Host, config.Config["domain"]) { util.Redirect(rw, req, tUrl) return } // 检测是否禁止了 iframe 加载 // 看是否在黑名单中 for _, denyHost := range strings.Split(config.Config["iframe_deny"], ",") { if strings.Contains(pUrl.Host, denyHost) { util.Redirect(rw, req, tUrl) return } } // 检测会比较慢,进行异步检测,记录下来,以后分析再加黑名单 go func() { resp, err := http.Head(tUrl) if err != nil { logger.Errorln("[iframe] head url:", tUrl, "error:", err) return } defer resp.Body.Close() if resp.Header.Get("X-Frame-Options") != "" { logger.Errorln("[iframe] deny:", tUrl) return } }() } // 设置内容模板 req.Form.Set(filter.CONTENT_TPL_KEY, "/template/wr.html") // 设置模板数据 filter.SetData(req, map[string]interface{}{"url": tUrl}) }
// 用户个人首页 // URI: /user/{username} func UserHomeHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) username := vars["username"] // 获取用户信息 user := service.FindUserByUsername(username) if user == nil { util.Redirect(rw, req, "/users") return } topics := service.FindRecentTopics(user.Uid, "5") resources := service.FindUserRecentResources(user.Uid) resourceCats := make(map[int]string) for _, resource := range resources { resourceCats[resource.Catid] = service.GetCategoryName(resource.Catid) } projects := service.FindUserRecentProjects(user.Username) comments := service.FindRecentComments(user.Uid, -1, "5") // 设置模板数据 filter.SetData(req, map[string]interface{}{"activeUsers": "active", "topics": topics, "resources": resources, "resource_cats": resourceCats, "projects": projects, "comments": comments, "user": user}) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/user/profile.html") }
// 点击 【我要晨读】,记录点击数,跳转 // uri: /readings/{id:[0-9]+} func IReadingHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) url := service.IReading(vars["id"]) util.Redirect(rw, req, url) return }
// 修改主题 // uri: /topics/modify{json:(|.json)} func ModifyTopicHandler(rw http.ResponseWriter, req *http.Request) { tid := req.FormValue("tid") if tid == "" { util.Redirect(rw, req, "/topics") return } nodes := service.GenNodes() vars := mux.Vars(req) // 请求编辑主题页面 if req.Method != "POST" || vars["json"] == "" { topic := service.FindTopic(tid) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/topics/new.html") filter.SetData(req, map[string]interface{}{"nodes": nodes, "topic": topic, "activeTopics": "active"}) return } user, _ := filter.CurrentUser(req) err := service.PublishTopic(user, req.PostForm) if err != nil { if err == service.NotModifyAuthorityErr { rw.WriteHeader(http.StatusForbidden) return } fmt.Fprint(rw, `{"ok": 0, "error":"内部服务错误!"}`) return } fmt.Fprint(rw, `{"ok": 1, "data":""}`) }
// 修改项目 // uri: /project/modify{json:(|.json)} func ModifyProjectHandler(rw http.ResponseWriter, req *http.Request) { id := req.FormValue("id") if id == "" { util.Redirect(rw, req, "/projects") return } vars := mux.Vars(req) // 请求编辑项目页面 if req.Method != "POST" || vars["json"] == "" { project := service.FindProject(id) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/projects/new.html") filter.SetData(req, map[string]interface{}{"project": project, "activeProjects": "active"}) return } user, _ := filter.CurrentUser(req) err := service.PublishProject(user, req.PostForm) if err != nil { if err == service.NotModifyAuthorityErr { rw.WriteHeader(http.StatusForbidden) return } fmt.Fprint(rw, `{"ok": 0, "error":"内部服务错误!"}`) return } fmt.Fprint(rw, `{"ok": 1, "data":""}`) }
// 登录 // uri : /account/login func LoginHandler(rw http.ResponseWriter, req *http.Request) { username := req.FormValue("username") if username == "" || req.Method != "POST" { req.Form.Set(filter.CONTENT_TPL_KEY, "/template/login.html") return } // 处理用户登录 passwd := req.FormValue("passwd") userLogin, err := service.Login(username, passwd) if err != nil { req.Form.Set(filter.CONTENT_TPL_KEY, "/template/login.html") filter.SetData(req, map[string]interface{}{"username": username, "error": err.Error()}) return } logger.Debugf("remember_me is %q\n", req.FormValue("remember_me")) // 登录成功,种cookie setCookie(rw, req, userLogin.Username) // 支持跳转到源页面 uri := "/" values := filter.NewFlash(rw, req).Flashes("uri") if values != nil { uri = values[0].(string) } logger.Debugln("uri===", uri) util.Redirect(rw, req, uri) }
// 社区帖子详细页 // uri: /topics/{tid:[0-9]+} func TopicDetailHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) topic, replies, err := service.FindTopicByTid(vars["tid"]) if err != nil { util.Redirect(rw, req, "/topics") return } likeFlag := 0 hadCollect := 0 user, ok := filter.CurrentUser(req) if ok { uid := user["uid"].(int) tid := topic["tid"].(int) likeFlag = service.HadLike(uid, tid, model.TYPE_TOPIC) hadCollect = service.HadFavorite(uid, tid, model.TYPE_TOPIC) } service.Views.Incr(req, model.TYPE_TOPIC, util.MustInt(vars["tid"])) // 设置内容模板 req.Form.Set(filter.CONTENT_TPL_KEY, "/template/topics/detail.html,/template/common/comment.html") // 设置模板数据 filter.SetData(req, map[string]interface{}{"activeTopics": "active", "topic": topic, "replies": replies, "likeflag": likeFlag, "hadcollect": hadCollect}) }
// 项目详情 // uri: /p/{uniq} func ProjectDetailHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) project := service.FindProject(vars["uniq"]) if project == nil { util.Redirect(rw, req, "/projects") return } likeFlag := 0 hadCollect := 0 user, ok := filter.CurrentUser(req) if ok { uid := user["uid"].(int) likeFlag = service.HadLike(uid, project.Id, model.TYPE_PROJECT) hadCollect = service.HadFavorite(uid, project.Id, model.TYPE_PROJECT) } service.Views.Incr(req, model.TYPE_PROJECT, project.Id) // 为了阅读数即时看到 project.Viewnum++ req.Form.Set(filter.CONTENT_TPL_KEY, "/template/projects/detail.html,/template/common/comment.html") filter.SetData(req, map[string]interface{}{"activeProjects": "active", "project": project, "likeflag": likeFlag, "hadcollect": hadCollect}) }
// 修改資源 // uri: /resources/modify{json:(|.json)} func ModifyResourceHandler(rw http.ResponseWriter, req *http.Request) { id := req.FormValue("id") if id == "" { util.Redirect(rw, req, "/resources") return } vars := mux.Vars(req) // 请求编辑資源页面 if req.Method != "POST" || vars["json"] == "" { resource := service.FindResourceById(id) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/resources/new.html") filter.SetData(req, map[string]interface{}{"resource": resource, "activeResources": "active", "categories": service.AllCategory}) return } user, _ := filter.CurrentUser(req) err := service.PublishResource(user, req.PostForm) if err != nil { if err == service.NotModifyAuthorityErr { rw.WriteHeader(http.StatusForbidden) return } fmt.Fprint(rw, `{"ok": 0, "error":"内部服务错误!"}`) return } fmt.Fprint(rw, `{"ok": 1, "data":""}`) }
// 重置密码 // uri: /account/resetpwd func ResetPasswdHandler(rw http.ResponseWriter, req *http.Request) { if _, ok := filter.CurrentUser(req); ok { util.Redirect(rw, req, "/") return } uuid := req.FormValue("code") if uuid == "" { util.Redirect(rw, req, "/account/login") return } req.Form.Set(filter.CONTENT_TPL_KEY, "/template/user/reset_pwd.html") data := map[string]interface{}{"activeUsers": "active"} passwd := req.FormValue("passwd") email, ok := resetPwdMap[uuid] if !ok { // 是提交重置密码 if passwd != "" && req.Method == "POST" { data["error"] = template.HTML(`非法请求!<p>将在<span id="jumpTo">3</span>秒后跳转到<a href="/" id="jump_url">首页</a></p>`) } else { data["error"] = template.HTML(`链接无效或过期,请重新操作。<a href="/account/forgetpwd">忘记密码?</a>`) } filter.SetData(req, data) return } data["valid"] = true data["code"] = uuid // 提交修改密码 if passwd != "" && req.Method == "POST" { // 简单校验 if len(passwd) < 6 || len(passwd) > 32 { data["error"] = "密码长度必须在6到32个字符之间" } else if passwd != req.FormValue("pass2") { data["error"] = "两次密码输入不一致" } else { // 更新密码 _, err := service.UpdatePasswd(email, passwd) if err != nil { data["error"] = "对不起,服务器错误,请重试!" } else { data["success"] = template.HTML(`密码重置成功,<p>将在<span id="jumpTo">3</span>秒后跳转到<a href="/account/login" id="jump_url">登录</a>页面</p>`) } } } filter.SetData(req, data) }
// 注销 // uri : /account/logout func LogoutHandler(rw http.ResponseWriter, req *http.Request) { // 删除cookie信息 session, _ := filter.Store.Get(req, "user") session.Options = &sessions.Options{Path: "/", MaxAge: -1} session.Save(req, rw) // 重定向得到登录页(TODO:重定向到什么页面比较好?) util.Redirect(rw, req, "/account/login") }
func (this *CookieFilter) PreFilter(rw http.ResponseWriter, req *http.Request) bool { user, _ := CurrentUser(req) // 已登录且请求登录页面 if user != nil && req.RequestURI == "/account/login" { util.Redirect(rw, req, "/") } return true }
func (this *LoginFilter) PreFilter(rw http.ResponseWriter, req *http.Request) bool { logger.Debugln("LoginFilter PreFilter...") if _, ok := CurrentUser(req); !ok { logger.Debugln("需要登录") // 没有登录 util.Redirect(rw, req, "/account/login") return false } return true }
func (this *LoginFilter) PreFilter(rw http.ResponseWriter, req *http.Request) bool { logger.Debugln("LoginFilter PreFilter...") if _, ok := CurrentUser(req); !ok { logger.Debugln("需要登录") // 支持跳转回原来访问的页面 NewFlash(rw, req).AddFlash(req.RequestURI, "uri") util.Redirect(rw, req, "/account/login") return false } return true }
func (this *CookieFilter) PreFilter(rw http.ResponseWriter, req *http.Request) bool { // 避免req.Form为nil req.ParseForm() logger.Debugln("CookieFilter PreFilter...") user, _ := CurrentUser(req) // 已登录且请求登录页面 if user != nil && req.RequestURI == "/account/login" { util.Redirect(rw, req, "/") } return true }
// 邮件订阅/退订页面 // URI: /user/email/unsubscribe{json:(|.json)} func EmailUnsubHandler(rw http.ResponseWriter, req *http.Request) { token := req.FormValue("u") if token == "" { util.Redirect(rw, req, "/") return } // 校验 token 的合法性 email := req.FormValue("email") user := service.FindUserByEmail(email) if user.Email == "" { util.Redirect(rw, req, "/") return } realToken := service.GenUnsubscribeToken(user) if token != realToken { util.Redirect(rw, req, "/") return } vars := mux.Vars(req) if req.Method != "POST" || vars["json"] == "" { filter.SetData(req, map[string]interface{}{ "email": email, "token": token, "unsubscribe": user.Unsubscribe, }) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/user/email_unsub.html") return } unsubscribe, _ := strconv.Atoi(req.PostFormValue("unsubscribe")) service.EmailSubscribe(user.Uid, unsubscribe) fmt.Fprint(rw, `{"ok": 1, "msg":"保存成功"}`) }
// 我的(某人的)收藏 // uri: /favorites/{username} func SomeoneFavoritesHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) username := vars["username"] user := service.FindUserByUsername(username) if user == nil { util.Redirect(rw, req, "/") return } objtype, err := strconv.Atoi(req.FormValue("objtype")) if err != nil { objtype = model.TYPE_ARTICLE } p, err := strconv.Atoi(req.FormValue("p")) if err != nil { p = 1 } data := map[string]interface{}{"objtype": objtype, "user": user} rows := 20 favorites, total := service.FindUserFavorites(user.Uid, objtype, (p-1)*rows, rows) if total > 0 { objids := util.Models2Intslice(favorites, "Objid") switch objtype { case model.TYPE_TOPIC: data["topics"] = service.FindTopicsByIds(objids) case model.TYPE_ARTICLE: data["articles"] = service.FindArticlesByIds(objids) case model.TYPE_RESOURCE: data["resources"] = service.FindResourcesByIds(objids) case model.TYPE_WIKI: // data["wikis"] = service.FindArticlesByIds(objids) case model.TYPE_PROJECT: data["projects"] = service.FindProjectsByIds(objids) } } uri := fmt.Sprintf("/favorites/%s?objtype=%d&p=%d", user.Username, objtype, p) data["pageHtml"] = service.GenPageHtml(p, rows, total, uri) // 设置模板数据 filter.SetData(req, data) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/favorite.html") }
func (this *LoginFilter) PreFilter(rw http.ResponseWriter, req *http.Request) bool { logger.Debugln("LoginFilter PreFilter...") if _, ok := CurrentUser(req); !ok { // 异步请求,却又需要登录,则返回403 if strings.HasSuffix(req.URL.Path, ".json") { rw.WriteHeader(http.StatusForbidden) return false } logger.Debugln("需要登录") // 支持跳转回原来访问的页面 NewFlash(rw, req).AddFlash(req.RequestURI, "uri") util.Redirect(rw, req, "/account/login") return false } return true }
// 登录 // uri : /account/login{json:(|.json)} func LoginHandler(rw http.ResponseWriter, req *http.Request) { username := req.PostFormValue("username") if username == "" || req.Method != "POST" { filter.SetData(req, map[string]interface{}{"error": "非法请求"}) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/login.html") return } vars := mux.Vars(req) suffix := vars["json"] // 处理用户登录 passwd := req.PostFormValue("passwd") userLogin, err := service.Login(username, passwd) if err != nil { if suffix != "" { logger.Errorln("login error:", err) fmt.Fprint(rw, `{"ok":0,"error":"`+err.Error()+`"}`) return } req.Form.Set(filter.CONTENT_TPL_KEY, "/template/login.html") filter.SetData(req, map[string]interface{}{"username": username, "error": err.Error()}) return } logger.Debugf("remember_me is %q\n", req.FormValue("remember_me")) // 登录成功,种cookie setCookie(rw, req, userLogin.Username) if suffix != "" { fmt.Fprint(rw, `{"ok":1,"msg":"success"}`) return } // 支持跳转到源页面 uri := "/" values := filter.NewFlash(rw, req).Flashes("uri") if values != nil { uri = values[0].(string) } logger.Debugln("uri===", uri) util.Redirect(rw, req, uri) }
// 用户注册 // uri: /account/register{json:(|.json)} func RegisterHandler(rw http.ResponseWriter, req *http.Request) { if _, ok := filter.CurrentUser(req); ok { util.Redirect(rw, req, "/") return } vars := mux.Vars(req) username := req.PostFormValue("username") // 请求注册页面 if username == "" || req.Method != "POST" || vars["json"] == "" { filter.SetData(req, map[string]interface{}{"captchaId": captcha.NewLen(4)}) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/register.html") return } // 校验验证码 if !captcha.VerifyString(req.PostFormValue("captchaid"), req.PostFormValue("captchaSolution")) { fmt.Fprint(rw, `{"ok": 0, "error":"验证码错误"}`) return } // 入库 errMsg, err := service.CreateUser(req.PostForm) if err != nil { // bugfix:http://studygolang.com/topics/255 if errMsg == "" { errMsg = err.Error() } fmt.Fprint(rw, `{"ok": 0, "error":"`, errMsg, `"}`) return } // 注册成功,自动为其登录 setCookie(rw, req, req.PostFormValue("username")) // 发送欢迎邮件 go sendWelcomeMail([]string{req.PostFormValue("email")}) fmt.Fprint(rw, `{"ok": 1, "msg":"注册成功"}`) }
// 忘记密码 // uri: /account/forgetpwd func ForgetPasswdHandler(rw http.ResponseWriter, req *http.Request) { if _, ok := filter.CurrentUser(req); ok { util.Redirect(rw, req, "/") return } req.Form.Set(filter.CONTENT_TPL_KEY, "/template/user/forget_pwd.html") data := map[string]interface{}{"activeUsers": "active"} email := req.FormValue("email") if email == "" || req.Method != "POST" { filter.SetData(req, data) return } // 校验email是否存在 if service.EmailExists(email) { var uuid string for { uuid = util.GenUUID() if _, ok := resetPwdMap[uuid]; !ok { resetPwdMap[uuid] = email break } logger.Infoln("GenUUID 冲突....") } var emailUrl string if strings.HasSuffix(email, "@gmail.com") { emailUrl = "http://mail.google.com" } else { pos := strings.LastIndex(email, "@") emailUrl = "http://mail." + email[pos+1:] } data["success"] = template.HTML(`一封包含了重设密码链接的邮件已经发送到您的注册邮箱,按照邮件中的提示,即可重设您的密码。<a href="` + emailUrl + `" target="_blank">立即前往邮箱</a>`) go service.SendResetpwdMail(email, uuid) } else { data["error"] = "该邮箱没有在本社区注册过!" } filter.SetData(req, data) }
// 某个资源详细页 // uri: /resources/{id:[0-9]+} func ResourceDetailHandler(rw http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) resource, comments := service.FindResource(vars["id"]) if len(resource) == 0 { util.Redirect(rw, req, "/resources") return } likeFlag := 0 hadCollect := 0 user, ok := filter.CurrentUser(req) if ok { uid := user["uid"].(int) id := resource["id"].(int) likeFlag = service.HadLike(uid, id, model.TYPE_RESOURCE) hadCollect = service.HadFavorite(uid, id, model.TYPE_RESOURCE) } service.Views.Incr(req, model.TYPE_RESOURCE, util.MustInt(vars["id"])) req.Form.Set(filter.CONTENT_TPL_KEY, "/template/resources/detail.html,/template/common/comment.html") filter.SetData(req, map[string]interface{}{"activeResources": "active", "resource": resource, "comments": comments, "likeflag": likeFlag, "hadcollect": hadCollect}) }
// 开源项目列表页 // uri: /projects func ProjectsHandler(rw http.ResponseWriter, req *http.Request) { limit := 20 lastId := req.FormValue("lastid") if lastId == "" { lastId = "0" } projects := service.FindProjects(lastId, "25") if projects == nil { // TODO:服务暂时不可用? } num := len(projects) if num == 0 { if lastId == "0" { util.Redirect(rw, req, "/") } else { util.Redirect(rw, req, "/projects") } return } var ( hasPrev, hasNext bool prevId, nextId int ) if lastId != "0" { prevId, _ = strconv.Atoi(lastId) // 避免因为项目下线,导致判断错误(所以 > 5) if prevId-projects[0].Id > 5 { hasPrev = false } else { prevId += limit hasPrev = true } } if num > limit { hasNext = true projects = projects[:limit] nextId = projects[limit-1].Id } else { nextId = projects[num-1].Id } pageInfo := map[string]interface{}{ "has_prev": hasPrev, "prev_id": prevId, "has_next": hasNext, "next_id": nextId, } // 获取当前用户喜欢对象信息 user, ok := filter.CurrentUser(req) var likeFlags map[int]int if ok { uid := user["uid"].(int) likeFlags, _ = service.FindUserLikeObjects(uid, model.TYPE_PROJECT, projects[0].Id, nextId) } req.Form.Set(filter.CONTENT_TPL_KEY, "/template/projects/list.html") // 设置模板数据 filter.SetData(req, map[string]interface{}{"projects": projects, "activeProjects": "active", "page": pageInfo, "likeflags": likeFlags}) }
// 资源索引页 // uri: /resources func ResIndexHandler(rw http.ResponseWriter, req *http.Request) { util.Redirect(rw, req, "/resources/cat/1") }
// 晨读列表页 // uri: /readings func ReadingsHandler(rw http.ResponseWriter, req *http.Request) { limit := 20 lastId := req.FormValue("lastid") if lastId == "" { lastId = "0" } rtype, err := strconv.Atoi(req.FormValue("rtype")) if err != nil { rtype = model.RtypeGo } readings := service.FindReadings(lastId, "25", rtype) if readings == nil { // TODO:服务暂时不可用? } num := len(readings) if num == 0 { if lastId == "0" { util.Redirect(rw, req, "/") } else { util.Redirect(rw, req, "/readings") } return } var ( hasPrev, hasNext bool prevId, nextId int ) if lastId != "0" { prevId, _ = strconv.Atoi(lastId) // 避免因为项目下线,导致判断错误(所以 > 5) if prevId-readings[0].Id > 5 { hasPrev = false } else { prevId += limit hasPrev = true } } if num > limit { hasNext = true readings = readings[:limit] nextId = readings[limit-1].Id } else { nextId = readings[num-1].Id } pageInfo := map[string]interface{}{ "has_prev": hasPrev, "prev_id": prevId, "has_next": hasNext, "next_id": nextId, } req.Form.Set(filter.CONTENT_TPL_KEY, "/template/readings/list.html") // 设置模板数据 filter.SetData(req, map[string]interface{}{"activeReadings": "active", "readings": readings, "page": pageInfo, "rtype": rtype}) }
// 没有权限时,跳转到没有权限提示页面 func (this *AdminFilter) PreErrorHandle(rw http.ResponseWriter, req *http.Request) { util.Redirect(rw, req, "/noauthorize") }