欢迎订阅下面的Feed,您可以及时跟踪我的更新:
+diff --git a/config/db.sql b/config/db.sql index 24a85197..7c8bb3ef 100644 --- a/config/db.sql +++ b/config/db.sql @@ -321,7 +321,7 @@ CREATE TABLE IF NOT EXISTS `articles` ( KEY (`top`), KEY (`author_txt`), KEY (`domain`), - KEY (`ctime`) + KEY (`mtime`) )ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '网络文章聚合表'; CREATE TABLE IF NOT EXISTS `crawl_rule` ( diff --git a/install.sh b/install.sh index 3a9f6262..b4774733 100755 --- a/install.sh +++ b/install.sh @@ -24,7 +24,6 @@ go install server/indexer go install server/crawler export GOPATH="$OLDGOPATH" -export PATH="$OLDPATH" echo 'finished' diff --git a/src/http/controller/feed.go b/src/http/controller/feed.go new file mode 100644 index 00000000..0c5413c3 --- /dev/null +++ b/src/http/controller/feed.go @@ -0,0 +1,101 @@ +// Copyright 2016 The StudyGolang Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// http://studygolang.com +// Author: polaris polaris@studygolang.com + +package controller + +import ( + "fmt" + "logic" + "model" + "net/http" + "time" + + . "http" + + "github.com/gorilla/feeds" + "github.com/labstack/echo" +) + +type FeedController struct{} + +// 注册路由 +func (self FeedController) RegisterRoute(g *echo.Group) { + g.Get("/feed.html", self.Atom) + g.Get("/feed.xml", self.List) +} + +func (self FeedController) Atom(ctx echo.Context) error { + return Render(ctx, "atom.html", map[string]interface{}{}) +} + +func (self FeedController) List(ctx echo.Context) error { + link := logic.WebsiteSetting.Domain + if logic.WebsiteSetting.OnlyHttps { + link = "https://" + link + "/" + } else { + link = "http://" + link + "/" + } + + now := time.Now() + + feed := &feeds.Feed{ + Title: logic.WebsiteSetting.Name, + Link: &feeds.Link{Href: link}, + Description: logic.WebsiteSetting.Slogan, + Author: &feeds.Author{Name: "polaris", Email: "polaris@studygolang.com"}, + Created: now, + Updated: now, + } + + respBody, err := logic.DefaultSearcher.FindAtomFeeds(50) + if err != nil { + return err + } + + feed.Items = make([]*feeds.Item, len(respBody.Docs)) + + for i, doc := range respBody.Docs { + url := "" + + switch doc.Objtype { + case model.TypeTopic: + url = fmt.Sprintf("%s/topics/%d", link, doc.Objid) + case model.TypeArticle: + url = fmt.Sprintf("%s/articles/%d", link, doc.Objid) + case model.TypeResource: + url = fmt.Sprintf("%s/resources/%d", link, doc.Objid) + case model.TypeProject: + url = fmt.Sprintf("%s/p/%d", link, doc.Objid) + case model.TypeWiki: + url = fmt.Sprintf("%s/wiki/%d", link, doc.Objid) + case model.TypeBook: + url = fmt.Sprintf("%s/book/%d", link, doc.Objid) + } + feed.Items[i] = &feeds.Item{ + Title: doc.Title, + Link: &feeds.Link{Href: url}, + Author: &feeds.Author{Name: doc.Author}, + Description: doc.Content, + Created: time.Time(doc.SortTime), + Updated: time.Time(doc.UpdatedAt), + } + } + + atom, err := feed.ToAtom() + if err != nil { + return err + } + + return self.responseXML(ctx, atom) +} + +func (FeedController) responseXML(ctx echo.Context, data string) (err error) { + response := ctx.Response() + response.Header().Set(echo.HeaderContentType, echo.MIMEApplicationXMLCharsetUTF8) + response.WriteHeader(http.StatusOK) + _, err = response.Write([]byte(data)) + return +} diff --git a/src/http/controller/routes.go b/src/http/controller/routes.go index f7fa2825..5bb4a234 100644 --- a/src/http/controller/routes.go +++ b/src/http/controller/routes.go @@ -37,6 +37,8 @@ func RegisterRoutes(g *echo.Group) { new(DownloadController).RegisterRoute(g) new(LinkController).RegisterRoute(g) + new(FeedController).RegisterRoute(g) + new(WechatController).RegisterRoute(g) new(InstallController).RegisterRoute(g) diff --git a/src/logic/common.go b/src/logic/common.go index b2eafd5a..910d9e0b 100644 --- a/src/logic/common.go +++ b/src/logic/common.go @@ -62,14 +62,14 @@ func CanEdit(me *model.Me, curModel interface{}) bool { return false } - if me.IsAdmin { - return true - } - canEditTime := time.Duration(UserSetting["can_edit_time"]) * time.Second switch entity := curModel.(type) { case *model.Topic: + if me.Uid != entity.Uid && me.IsAdmin { + return true + } + if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime { return false } @@ -78,6 +78,10 @@ func CanEdit(me *model.Me, curModel interface{}) bool { return true } case *model.Article: + if me.IsAdmin { + return true + } + // 文章的能编辑时间是15天 if time.Now().Sub(time.Time(entity.Ctime)) > 15*86400*time.Second { return false @@ -95,6 +99,10 @@ func CanEdit(me *model.Me, curModel interface{}) bool { return true } case *model.OpenProject: + if me.IsAdmin { + return true + } + // 开源项目的能编辑时间是30天 if time.Now().Sub(time.Time(entity.Ctime)) > 30*86400*time.Second { return false @@ -104,6 +112,9 @@ func CanEdit(me *model.Me, curModel interface{}) bool { return true } case *model.Wiki: + if me.IsAdmin { + return true + } if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime { return false } @@ -112,6 +123,9 @@ func CanEdit(me *model.Me, curModel interface{}) bool { return true } case *model.Book: + if me.IsAdmin { + return true + } if time.Now().Sub(time.Time(entity.CreatedAt)) > canEditTime { return false } @@ -120,6 +134,10 @@ func CanEdit(me *model.Me, curModel interface{}) bool { return true } case map[string]interface{}: + if adminCanEdit(entity, me) { + return true + } + if ctime, ok := entity["ctime"]; ok { if time.Now().Sub(time.Time(ctime.(model.OftenTime))) > canEditTime { return false @@ -182,3 +200,21 @@ func website() string { } return host + WebsiteSetting.Domain } + +func adminCanEdit(entity map[string]interface{}, me *model.Me) bool { + if uid, ok := entity["uid"]; ok { + if me.Uid != uid.(int) && me.IsAdmin { + return true + } + return false + } + + if username, ok := entity["username"]; ok { + if me.Username != username.(string) && me.IsAdmin { + return true + } + return false + } + + return false +} diff --git a/src/logic/searcher.go b/src/logic/searcher.go index d1c3096f..a7ca788c 100644 --- a/src/logic/searcher.go +++ b/src/logic/searcher.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "strconv" + "time" "util" . "db" @@ -51,44 +52,51 @@ func (self SearcherLogic) IndexingArticle(isAll bool) { err error ) - if isAll { - id := 0 - for { - articleList = make([]*model.Article, 0) + id := 0 + for { + articleList = make([]*model.Article, 0) + if isAll { err = MasterDB.Where("id>?", id).Limit(self.maxRows).OrderBy("id ASC").Find(&articleList) - if err != nil { - logger.Errorln("IndexingArticle error:", err) - break - } + } else { + timeAgo := time.Now().Add(-5 * time.Minute).Format("2006-01-02 15:04:05") + err = MasterDB.Where("mtime>?", timeAgo).Find(&articleList) + } + if err != nil { + logger.Errorln("IndexingArticle error:", err) + break + } - if len(articleList) == 0 { - break - } + if len(articleList) == 0 { + break + } - for _, article := range articleList { - logger.Infoln("deal article_id:", article.Id) + for _, article := range articleList { + logger.Infoln("deal article_id:", article.Id) - if id < article.Id { - id = article.Id - } + if id < article.Id { + id = article.Id + } - if article.Tags == "" { - // 自动生成 - article.Tags = model.AutoTag(article.Title, article.Txt, 4) - if article.Tags != "" { - MasterDB.Id(article.Id).Cols("tags").Update(article) - } + if article.Tags == "" { + // 自动生成 + article.Tags = model.AutoTag(article.Title, article.Txt, 4) + if article.Tags != "" { + MasterDB.Id(article.Id).Cols("tags").Update(article) } + } - document := model.NewDocument(article, nil) - if article.Status != model.ArticleStatusOffline { - solrClient.PushAdd(model.NewDefaultArgsAddCommand(document)) - } else { - solrClient.PushDel(model.NewDelCommand(document)) - } + document := model.NewDocument(article, nil) + if article.Status != model.ArticleStatusOffline { + solrClient.PushAdd(model.NewDefaultArgsAddCommand(document)) + } else { + solrClient.PushDel(model.NewDelCommand(document)) } + } + + solrClient.Post() - solrClient.Post() + if !isAll { + break } } } @@ -104,54 +112,61 @@ func (self SearcherLogic) IndexingTopic(isAll bool) { err error ) - if isAll { - id := 0 - for { - topicList = make([]*model.Topic, 0) - topicExList = make(map[int]*model.TopicUpEx) + id := 0 + for { + topicList = make([]*model.Topic, 0) + topicExList = make(map[int]*model.TopicUpEx) + if isAll { err = MasterDB.Where("tid>?", id).OrderBy("tid ASC").Limit(self.maxRows).Find(&topicList) - if err != nil { - logger.Errorln("IndexingTopic error:", err) - break - } + } else { + timeAgo := time.Now().Add(-5 * time.Minute).Format("2006-01-02 15:04:05") + err = MasterDB.Where("mtime>?", timeAgo).Find(&topicList) + } + if err != nil { + logger.Errorln("IndexingTopic error:", err) + break + } - if len(topicList) == 0 { - break - } + if len(topicList) == 0 { + break + } - tids := util.Models2Intslice(topicList, "Tid") + tids := util.Models2Intslice(topicList, "Tid") - err = MasterDB.In("tid", tids).Find(&topicExList) - if err != nil { - logger.Errorln("IndexingTopic error:", err) - break - } + err = MasterDB.In("tid", tids).Find(&topicExList) + if err != nil { + logger.Errorln("IndexingTopic error:", err) + break + } - for _, topic := range topicList { - logger.Infoln("deal topic_id:", topic.Tid) + for _, topic := range topicList { + logger.Infoln("deal topic_id:", topic.Tid) - if id < topic.Tid { - id = topic.Tid - } + if id < topic.Tid { + id = topic.Tid + } - if topic.Tags == "" { - // 自动生成 - topic.Tags = model.AutoTag(topic.Title, topic.Content, 4) - if topic.Tags != "" { - MasterDB.Id(topic.Tid).Cols("tags").Update(topic) - } + if topic.Tags == "" { + // 自动生成 + topic.Tags = model.AutoTag(topic.Title, topic.Content, 4) + if topic.Tags != "" { + MasterDB.Id(topic.Tid).Cols("tags").Update(topic) } + } - topicEx := topicExList[topic.Tid] + topicEx := topicExList[topic.Tid] - document := model.NewDocument(topic, topicEx) - addCommand := model.NewDefaultArgsAddCommand(document) + document := model.NewDocument(topic, topicEx) + addCommand := model.NewDefaultArgsAddCommand(document) - solrClient.PushAdd(addCommand) - } + solrClient.PushAdd(addCommand) + } + + solrClient.Post() - solrClient.Post() + if !isAll { + break } } } @@ -166,54 +181,61 @@ func (self SearcherLogic) IndexingResource(isAll bool) { err error ) - if isAll { - id := 0 - for { - resourceList = make([]*model.Resource, 0) - resourceExList = make(map[int]*model.ResourceEx) + id := 0 + for { + resourceList = make([]*model.Resource, 0) + resourceExList = make(map[int]*model.ResourceEx) + if isAll { err = MasterDB.Where("id>?", id).OrderBy("id ASC").Limit(self.maxRows).Find(&resourceList) - if err != nil { - logger.Errorln("IndexingResource error:", err) - break - } + } else { + timeAgo := time.Now().Add(-5 * time.Minute).Format("2006-01-02 15:04:05") + err = MasterDB.Where("mtime>?", timeAgo).Find(&resourceList) + } + if err != nil { + logger.Errorln("IndexingResource error:", err) + break + } - if len(resourceList) == 0 { - break - } + if len(resourceList) == 0 { + break + } - ids := util.Models2Intslice(resourceList, "Id") + ids := util.Models2Intslice(resourceList, "Id") - err = MasterDB.In("id", ids).Find(&resourceExList) - if err != nil { - logger.Errorln("IndexingResource error:", err) - break - } + err = MasterDB.In("id", ids).Find(&resourceExList) + if err != nil { + logger.Errorln("IndexingResource error:", err) + break + } - for _, resource := range resourceList { - logger.Infoln("deal resource_id:", resource.Id) + for _, resource := range resourceList { + logger.Infoln("deal resource_id:", resource.Id) - if id < resource.Id { - id = resource.Id - } + if id < resource.Id { + id = resource.Id + } - if resource.Tags == "" { - // 自动生成 - resource.Tags = model.AutoTag(resource.Title+resource.CatName, resource.Content, 4) - if resource.Tags != "" { - MasterDB.Id(resource.Id).Cols("tags").Update(resource) - } + if resource.Tags == "" { + // 自动生成 + resource.Tags = model.AutoTag(resource.Title+resource.CatName, resource.Content, 4) + if resource.Tags != "" { + MasterDB.Id(resource.Id).Cols("tags").Update(resource) } + } - resourceEx := resourceExList[resource.Id] + resourceEx := resourceExList[resource.Id] - document := model.NewDocument(resource, resourceEx) - addCommand := model.NewDefaultArgsAddCommand(document) + document := model.NewDocument(resource, resourceEx) + addCommand := model.NewDefaultArgsAddCommand(document) - solrClient.PushAdd(addCommand) - } + solrClient.PushAdd(addCommand) + } + + solrClient.Post() - solrClient.Post() + if !isAll { + break } } } @@ -227,46 +249,55 @@ func (self SearcherLogic) IndexingOpenProject(isAll bool) { err error ) - if isAll { - id := 0 - for { - projectList = make([]*model.OpenProject, 0) + id := 0 + for { + projectList = make([]*model.OpenProject, 0) + + if isAll { err = MasterDB.Where("id>?", id).OrderBy("id ASC").Limit(self.maxRows).Find(&projectList) - if err != nil { - logger.Errorln("IndexingArticle error:", err) - break - } + } else { + timeAgo := time.Now().Add(-5 * time.Minute).Format("2006-01-02 15:04:05") + err = MasterDB.Where("mtime>?", timeAgo).Find(&projectList) + } + if err != nil { + logger.Errorln("IndexingArticle error:", err) + break + } - if len(projectList) == 0 { - break - } + if len(projectList) == 0 { + break + } - for _, project := range projectList { - logger.Infoln("deal project_id:", project.Id) + for _, project := range projectList { + logger.Infoln("deal project_id:", project.Id) - if id < project.Id { - id = project.Id - } + if id < project.Id { + id = project.Id + } - if project.Tags == "" { - // 自动生成 - project.Tags = model.AutoTag(project.Name+project.Category, project.Desc, 4) - if project.Tags != "" { - MasterDB.Id(project.Id).Cols("tags").Update(project) - } + if project.Tags == "" { + // 自动生成 + project.Tags = model.AutoTag(project.Name+project.Category, project.Desc, 4) + if project.Tags != "" { + MasterDB.Id(project.Id).Cols("tags").Update(project) } + } - document := model.NewDocument(project, nil) - if project.Status != model.ProjectStatusOffline { - solrClient.PushAdd(model.NewDefaultArgsAddCommand(document)) - } else { - solrClient.PushDel(model.NewDelCommand(document)) - } + document := model.NewDocument(project, nil) + if project.Status != model.ProjectStatusOffline { + solrClient.PushAdd(model.NewDefaultArgsAddCommand(document)) + } else { + solrClient.PushDel(model.NewDelCommand(document)) } + } + + solrClient.Post() - solrClient.Post() + if !isAll { + break } } + } const searchContentLen = 350 @@ -417,6 +448,39 @@ func (this *SearcherLogic) SearchByField(field, value string, start, rows int, s return searchResponse.RespBody, nil } +func (this *SearcherLogic) FindAtomFeeds(rows int) (*model.ResponseBody, error) { + selectUrl := this.engineUrl + "/select?" + + var values = url.Values{ + "q": []string{"*:*"}, + "sort": []string{"sort_time desc"}, + "wt": []string{"json"}, + "start": []string{"0"}, + "rows": []string{strconv.Itoa(rows)}, + } + + resp, err := http.Get(selectUrl + values.Encode()) + if err != nil { + logger.Errorln("search error:", err) + return &model.ResponseBody{}, err + } + + defer resp.Body.Close() + + var searchResponse model.SearchResponse + err = json.NewDecoder(resp.Body).Decode(&searchResponse) + if err != nil { + logger.Errorln("parse response error:", err) + return &model.ResponseBody{}, err + } + + if searchResponse.RespBody == nil { + searchResponse.RespBody = &model.ResponseBody{} + } + + return searchResponse.RespBody, nil +} + func (this *SearcherLogic) FillNodeAndUser(ctx context.Context, respBody *model.ResponseBody) (map[int]*model.User, map[int]*model.TopicNode) { if respBody.NumFound == 0 { return nil, nil diff --git a/src/server/server.go b/src/server/server.go index 8582912d..ab1832d5 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -36,6 +36,10 @@ func IndexingServer() { c := cron.New() // 构建 solr 需要的索引数据 + // 1 分钟一次增量 + c.AddFunc("@every 1m", func() { + indexing(false) + }) // 一天一次全量 c.AddFunc("@daily", func() { indexing(true) diff --git a/src/vendor/manifest b/src/vendor/manifest index 70813443..e1b35a11 100644 --- a/src/vendor/manifest +++ b/src/vendor/manifest @@ -145,6 +145,12 @@ "revision": "1ea25387ff6f684839d82767c1733ff4d4d15d0a", "branch": "master" }, + { + "importpath": "github.com/gorilla/feeds", + "repository": "https://github.com/gorilla/feeds", + "revision": "b78e02c3f88b84269be5c20c2b98127bbea1fcba", + "branch": "master" + }, { "importpath": "github.com/gorilla/schema", "repository": "https://github.com/gorilla/schema", diff --git a/template/atom.html b/template/atom.html new file mode 100644 index 00000000..c2348831 --- /dev/null +++ b/template/atom.html @@ -0,0 +1,30 @@ +{{define "title"}}Feed订阅 {{end}} +{{define "content"}} +
欢迎订阅下面的Feed,您可以及时跟踪我的更新:
+