├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── build.bat ├── conf.json ├── controllers ├── reply │ └── reply.go ├── sign │ └── sign.go ├── site │ └── site.go ├── topic │ └── topic.go └── user │ └── user.go ├── database └── mysql.go ├── go.mod ├── go.png ├── go.sum ├── main.go ├── mgoModels ├── message.go ├── reply.go ├── topic.go └── user.go ├── public ├── 19ITP3EmsT.txt ├── github-card.html ├── images │ ├── cngo.svg │ ├── cngo2.svg │ ├── cngo3.svg │ ├── cngo_icon_32.png │ ├── cnode_icon_32.png │ ├── cnode_icon_32.svg │ ├── cnode_icon_64.png │ ├── cnode_logo_128.png │ ├── cnode_logo_32.png │ ├── cnodejs.svg │ ├── cnodejs_light.svg │ ├── digitalocean.png │ ├── egg-logo.png │ ├── golangtc-logo.png │ ├── iojs-logo-w150h50.png │ ├── iojs-logo.png │ ├── logo.png │ ├── logo_bak.png │ ├── nodejs_black.png │ ├── phphub-logo.png │ ├── qiniu.png │ ├── ruby-china-20150529.png │ ├── ruby-china-logo2.png │ ├── search.png │ ├── ucloud.png │ └── upyun_logo.png ├── img │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png ├── javascripts │ ├── main.js │ └── responsive.js ├── libs │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-responsive.css │ │ │ ├── bootstrap-responsive.min.css │ │ │ ├── bootstrap.css │ │ │ └── bootstrap.min.css │ │ ├── img │ │ │ ├── glyphicons-halflings-white.png │ │ │ └── glyphicons-halflings.png │ │ └── js │ │ │ ├── bootstrap.js │ │ │ └── bootstrap.min.js │ ├── code-prettify │ │ ├── lang-apollo.js │ │ ├── lang-clj.js │ │ ├── lang-css.js │ │ ├── lang-go.js │ │ ├── lang-hs.js │ │ ├── lang-lisp.js │ │ ├── lang-lua.js │ │ ├── lang-ml.js │ │ ├── lang-n.js │ │ ├── lang-proto.js │ │ ├── lang-scala.js │ │ ├── lang-sql.js │ │ ├── lang-tex.js │ │ ├── lang-vb.js │ │ ├── lang-vhdl.js │ │ ├── lang-wiki.js │ │ ├── lang-xq.js │ │ ├── lang-yaml.js │ │ ├── prettify.css │ │ └── prettify.js │ ├── editor │ │ ├── editor.css │ │ ├── editor.js │ │ ├── ext.js │ │ └── fonts │ │ │ ├── icomoon.dev.svg │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.svg │ │ │ ├── icomoon.ttf │ │ │ └── icomoon.woff │ ├── font-awesome │ │ ├── css │ │ │ └── font-awesome.css │ │ └── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ ├── jquery-2.1.0.js │ ├── jquery-ujs.js │ ├── jquery.atwho.js │ ├── jquery.caret.js │ ├── less.min.js │ ├── lodash.compat.js │ ├── markdownit.js │ ├── qrcode.js │ └── webuploader │ │ ├── webuploader.css │ │ └── webuploader.withoutimage.js ├── stylesheets │ ├── common.css │ ├── jquery.atwho.css │ ├── responsive.css │ └── style.less └── upload │ └── .gitkeep ├── router └── router.go ├── service ├── cache │ ├── .DS_Store │ └── cache.go └── mail │ ├── .DS_Store │ └── mail.go ├── utils ├── dysms.go └── loadConf.go └── views ├── about └── about.html ├── api └── api.html ├── common ├── _sponsors.html ├── card.html ├── footer.html ├── header.html ├── sidebar.html └── sidebar2.html ├── edit └── edit.html ├── getStart └── getstart.html ├── index ├── abstract.html ├── index.html ├── main.html └── topic_list.html ├── message ├── message.html └── message_index.html ├── notify └── notify.html ├── searchPass └── search_pass.html ├── setting └── setting.html ├── showSignUp └── showsignup.html ├── signIn └── signin.html ├── signUp └── signup.html ├── topic ├── edit.html ├── reply.html └── topicIndex.html └── user ├── index.html ├── replies.html ├── top100.html └── topics.html /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | .DS_Store -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${fileDirname}", 13 | "env": {}, 14 | "args": [] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.formatTool": "goimports" 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go_cnode 2 | 3 | #### 项目介绍 4 | http://cnodejs.org/ 是一个用nodejs语言编写的开源论坛 5 | 打算是用golang语言仿写 6 | 项目部署在 http://52.77.218.37:9035/ 账号密码admin 7 | #### 安装 8 | go version 1.12.4 9 | git version 2.19 10 | go版本>1.11 11 | git版本>2.17 12 | 用了go mod 管理依赖 13 | 安装mongodb 14 | 安装redis 15 | 16 | ## 使用 17 | 18 | ### 使用命令行 19 | 20 | ```bash 21 | $ git clone https://github.com/dangyanglim/go_cnode.git 22 | $ cd go_cnode 23 | $ redis-server # 要安装redis 24 | $ mongod --fork --logpath=/data/logs.log # 要安装mongodb 25 | $ go run main.go # 访问 http://localhost:9035 26 | ``` 27 | #### 功能介绍 28 | - 邮箱注册/Github第三方注册 29 | - Go 模块管理 30 | - 后台 Gin+mongodb+redis 31 | - 前台 bootstrap+jquery+渲染模板 32 | 33 | ![go.png](go.png) 34 | 35 | ## 目录结构 36 | ``` 37 | ├─.vscode 38 | ├─controllers 39 | │ ├─reply 40 | │ ├─sign 41 | │ ├─site 42 | │ └─topic 43 | ├─database 44 | ├─mgoModels 45 | ├─public 46 | │ ├─images 47 | │ ├─img 48 | │ ├─javascripts 49 | │ ├─libs 50 | │ │ ├─bootstrap 51 | │ │ │ ├─css 52 | │ │ │ ├─img 53 | │ │ │ └─js 54 | │ │ ├─code-prettify 55 | │ │ ├─editor 56 | │ │ │ └─fonts 57 | │ │ ├─font-awesome 58 | │ │ │ ├─css 59 | │ │ │ └─fonts 60 | │ │ └─webuploader 61 | │ ├─stylesheets 62 | │ └─upload 63 | ├─router 64 | ├─service 65 | │ ├─cache 66 | │ └─mail 67 | ├─utils 68 | └─views 69 | ├─about 70 | ├─api 71 | ├─common 72 | ├─edit 73 | ├─getStart 74 | ├─index 75 | ├─message 76 | ├─notify 77 | ├─searchPass 78 | ├─setting 79 | ├─showSignUp 80 | ├─signIn 81 | ├─signUp 82 | └─topic 83 | 84 | ``` 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | SET CGO_ENABLED=0 2 | SET GOOS=linux 3 | SET GOARCH=amd64 4 | go build -o go_cnode main.go -------------------------------------------------------------------------------- /conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "mongo_url": "fenghuangyu.cn:27017", 3 | "redis_url": "127.0.0.1:6379", 4 | "port": ":9035", 5 | "smtp_username":"dangyanglim@qq.com", 6 | "smtp_password":"uicmeimalcnybgdj", 7 | "smtp_hostname":"smtp.qq.com", 8 | "smtp_active_Url":"http://fenghuangyu.cn:9035/active_account", 9 | "github_client_id":"bafc506847f325223094", 10 | "github_client_secret":"172d5424cc25be8c8ac8095b40d79fea859588ea", 11 | "github_AuthURL":"https://github.com/login/oauth/authorize?", 12 | "github_UserURL":"https://api.github.com/user?", 13 | "github_TokenURL":"https://github.com/login/oauth/access_token?" 14 | } -------------------------------------------------------------------------------- /controllers/reply/reply.go: -------------------------------------------------------------------------------- 1 | package reply 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | //"regexp" 7 | 8 | "go_cnode/mgoModels" 9 | //"github.com/dangyanglim/go_cnode/service/mail" 10 | "encoding/json" 11 | "github.com/gin-gonic/gin" 12 | "github.com/tommy351/gin-sessions" 13 | "go_cnode/service/cache" 14 | "regexp" 15 | ) 16 | 17 | var userModel = new(models.UserModel) 18 | var topicModel = new(models.TopicModel) 19 | var replyModel = new(models.ReplyModel) 20 | var messageModel = new(models.MessageModel) 21 | 22 | func ShowCreate(c *gin.Context) { 23 | session := sessions.Get(c) 24 | var name string 25 | user := models.User{} 26 | //var err error 27 | 28 | if nil != session.Get("loginname") { 29 | name = session.Get("loginname").(string) 30 | user, _ = userModel.GetUserByName(name) 31 | } 32 | tabs := [3]map[string]string{{"value": "share", "text": "分享"}, {"value": "ask", "text": "问答"}, {"value": "job", "text": "招聘"}} 33 | c.HTML(http.StatusOK, "edit", gin.H{ 34 | "user": user, 35 | "tabs": tabs, 36 | "config": gin.H{ 37 | "description": "CNode:Node.js专业中文社区", 38 | }, 39 | }) 40 | } 41 | 42 | func Index(c *gin.Context) { 43 | session := sessions.Get(c) 44 | var name string 45 | var no_reply_topics []models.Topic 46 | type Temp struct { 47 | Topic models.Topic 48 | Author models.User 49 | Replies []models.Reply 50 | RepliyWithAuthors []models.ReplyAndAuthor 51 | } 52 | var temp Temp 53 | user := models.User{} 54 | if nil != session.Get("loginname") { 55 | name = session.Get("loginname").(string) 56 | user, _ = userModel.GetUserByName(name) 57 | } 58 | id := c.Param("id") 59 | topic, author, replies, repliyWithAuthors, _ := topicModel.GetTopicByIdWithReply(id) 60 | temp.Author = author 61 | temp.Topic = topic 62 | temp.Replies = replies 63 | NoOfRepliy := len(replies) 64 | temp.RepliyWithAuthors = repliyWithAuthors 65 | no_reply_topics2, err2 := cache.Get("no_reply_topics") 66 | json.Unmarshal(no_reply_topics2.([]byte), &no_reply_topics) 67 | log.Println("temp") 68 | log.Println(err2) 69 | //log.Println(temp) 70 | if err2 != nil { 71 | no_reply_topics, _ = topicModel.GetTopicNoReply() 72 | no_reply_topics_json, _ := json.Marshal(no_reply_topics) 73 | cache.SetEx("no_reply_topics", no_reply_topics_json) 74 | } 75 | other_topics, _ := topicModel.GetAuthorOtherTopics(author.Id.Hex(), id) 76 | c.HTML(http.StatusOK, "topicIndex", gin.H{ 77 | "title": "布局页面", 78 | "user": user, 79 | "topic": temp, 80 | "NoOfRepliy": NoOfRepliy, 81 | "no_reply_topics": no_reply_topics, 82 | "author_other_topics": other_topics, 83 | "config": gin.H{ 84 | "description": "CNode:Node.js专业中文社区", 85 | }, 86 | }) 87 | } 88 | func Create(c *gin.Context) { 89 | session := sessions.Get(c) 90 | var name string 91 | user := models.User{} 92 | if nil != session.Get("loginname") { 93 | name = session.Get("loginname").(string) 94 | user, _ = userModel.GetUserByName(name) 95 | } 96 | id := user.Id.Hex() 97 | log.Println(id) 98 | tab := c.Request.FormValue("tab") 99 | title := c.Request.FormValue("title") 100 | content := c.Request.FormValue("content") 101 | topic, _ := topicModel.NewAndSave(title, tab, id, content) 102 | url := "/topic/" + topic.Id.Hex() 103 | c.Redirect(301, url) 104 | } 105 | func Add(c *gin.Context) { 106 | topic_id := c.Param("topic_id") 107 | r_content := c.Request.FormValue("r_content") 108 | user_id := c.Request.FormValue("user_id") 109 | if user_id==""{ 110 | c.HTML(http.StatusOK, "notify", gin.H{ 111 | "error": "您还没登录呢", 112 | }) 113 | return 114 | } 115 | 116 | topic,_:=topicModel.GetTopicById(topic_id) 117 | reply, _ := replyModel.NewAndSave(r_content, topic_id, user_id, "") 118 | topicModel.UpdateReplyCount(topic_id, reply.Id) 119 | log.Println(r_content) 120 | r, _ := regexp.Compile("@([a-z0-9]+)") 121 | usersArray:=r.FindAllString(r_content, -1) 122 | log.Println(len(usersArray)) 123 | var users map[string]int 124 | users=make(map[string]int) 125 | if len(usersArray)>0 { 126 | for _,user:=range usersArray{ 127 | user=user[1 : len(user)] 128 | users[user]=1 129 | } 130 | for userName:=range users{ 131 | log.Println(userName) 132 | user,_:=userModel.GetUserByName(userName) 133 | log.Println(user) 134 | if user_id!=user.Id.Hex() { 135 | messageModel.SendAtMessage(user.Id.Hex(),user_id,topic_id,reply.Id) 136 | } 137 | } 138 | } 139 | log.Println(users) 140 | if topic.Author_id.Hex()!=user_id { 141 | messageModel.SendReplyMessage(topic.Author_id.Hex(),user_id,topic_id,reply.Id) 142 | } 143 | url := "/topic/" + topic_id 144 | c.Redirect(301, url) 145 | } 146 | func Edit(c *gin.Context) { 147 | 148 | reply_id := c.Param("reply_id") 149 | t_content := c.Request.FormValue("t_content") 150 | user_id := c.Request.FormValue("user_id") 151 | reply, _ := replyModel.GetReplyById(reply_id) 152 | 153 | if reply.Author_id.Hex() != user_id { 154 | c.HTML(http.StatusOK, "notify", gin.H{ 155 | "error": "你不能编辑此回复", 156 | }) 157 | return 158 | } 159 | 160 | replyModel.Update(t_content, reply_id) 161 | 162 | url := "/topic/" + reply.Topic_id.Hex() + "#" + reply.Id.Hex() 163 | c.Redirect(301, url) 164 | } 165 | func Delete(c *gin.Context) { 166 | log.Print("delete3") 167 | reply_id := c.Param("reply_id") 168 | session := sessions.Get(c) 169 | var name string 170 | user := models.User{} 171 | if nil != session.Get("loginname") { 172 | name = session.Get("loginname").(string) 173 | user, _ = userModel.GetUserByName(name) 174 | } 175 | reply, _ := replyModel.GetReplyById(reply_id) 176 | 177 | var msg struct { 178 | Status string `json:"status"` 179 | } 180 | if reply.Author_id.Hex() != user.Id.Hex() { 181 | 182 | msg.Status = "failed" 183 | c.JSON(http.StatusOK, msg) 184 | return 185 | } 186 | 187 | replyModel.Delete(reply_id) 188 | 189 | msg.Status = "success" 190 | c.JSON(http.StatusOK, msg) 191 | } 192 | func ShowEdit(c *gin.Context) { 193 | reply_id := c.Param("reply_id") 194 | user_id := c.Request.FormValue("user_id") 195 | reply, err := replyModel.GetReplyById(reply_id) 196 | 197 | if err != nil { 198 | c.HTML(http.StatusOK, "notify", gin.H{ 199 | "error": "评论不存在", 200 | }) 201 | return 202 | } 203 | c.HTML(http.StatusOK, "reply/edit", gin.H{ 204 | "reply_id": reply.Id.Hex(), 205 | "content": reply.Content, 206 | "user_id": user_id, 207 | }) 208 | } 209 | -------------------------------------------------------------------------------- /controllers/topic/topic.go: -------------------------------------------------------------------------------- 1 | package topic 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | //"regexp" 7 | //"fmt" 8 | //"os" 9 | //"io" 10 | "go_cnode/mgoModels" 11 | //"github.com/dangyanglim/go_cnode/service/mail" 12 | "encoding/json" 13 | "github.com/gin-gonic/gin" 14 | "github.com/russross/blackfriday" 15 | "github.com/tommy351/gin-sessions" 16 | "go_cnode/service/cache" 17 | "html/template" /* */ 18 | "strings" 19 | ) 20 | 21 | var userModel = new(models.UserModel) 22 | var topicModel = new(models.TopicModel) 23 | 24 | func ShowCreate(c *gin.Context) { 25 | session := sessions.Get(c) 26 | var name string 27 | user := models.User{} 28 | //var err error 29 | 30 | if nil != session.Get("loginname") { 31 | name = session.Get("loginname").(string) 32 | user, _ = userModel.GetUserByName(name) 33 | } 34 | tabs := [3]map[string]string{{"value": "share", "text": "分享"}, {"value": "ask", "text": "问答"}, {"value": "job", "text": "招聘"}} 35 | c.HTML(http.StatusOK, "edit", gin.H{ 36 | "user": user, 37 | "tabs": tabs, 38 | "tab": "share", 39 | "config": gin.H{ 40 | "description": "CNode:Node.js专业中文社区", 41 | }, 42 | }) 43 | } 44 | 45 | func Index(c *gin.Context) { 46 | session := sessions.Get(c) 47 | var name string 48 | var no_reply_topics []models.Topic 49 | type Temp struct { 50 | Topic models.Topic 51 | LinkContent template.HTML 52 | Author models.User 53 | Replies []models.Reply 54 | RepliyWithAuthors []models.ReplyAndAuthor 55 | } 56 | var temp Temp 57 | user := models.User{} 58 | if nil != session.Get("loginname") { 59 | name = session.Get("loginname").(string) 60 | user, _ = userModel.GetUserByName(name) 61 | } 62 | id := c.Param("id") 63 | topic, author, replies, repliyWithAuthors, _ := topicModel.GetTopicByIdWithReply(id) 64 | temp.Author = author 65 | topic.Content = strings.Replace(topic.Content, "\r\n", "
", -1) 66 | //temp.LinkContent=topic.Content 67 | temp.LinkContent = template.HTML(blackfriday.Run([]byte(topic.Content))) 68 | temp.Topic = topic 69 | temp.Replies = replies 70 | NoOfRepliy := len(replies) 71 | temp.RepliyWithAuthors = repliyWithAuthors 72 | no_reply_topics2, err2 := cache.Get("no_reply_topics") 73 | json.Unmarshal(no_reply_topics2.([]byte), &no_reply_topics) 74 | 75 | if err2 != nil { 76 | no_reply_topics, _ = topicModel.GetTopicNoReply() 77 | no_reply_topics_json, _ := json.Marshal(no_reply_topics) 78 | cache.SetEx("no_reply_topics", no_reply_topics_json) 79 | } 80 | topicModel.UpdateVisitCount(id) 81 | other_topics, _ := topicModel.GetAuthorOtherTopics(author.Id.Hex(), id) 82 | c.HTML(http.StatusOK, "topicIndex", gin.H{ 83 | "title": "布局页面", 84 | "user": user, 85 | "topic": temp, 86 | "NoOfRepliy": NoOfRepliy, 87 | "no_reply_topics": no_reply_topics, 88 | "author_other_topics": other_topics, 89 | "config": gin.H{ 90 | "description": "CNode:Node.js专业中文社区", 91 | }, 92 | }) 93 | } 94 | func Top(c *gin.Context) { 95 | session := sessions.Get(c) 96 | var name string 97 | user := models.User{} 98 | if nil != session.Get("loginname") { 99 | name = session.Get("loginname").(string) 100 | user, _ = userModel.GetUserByName(name) 101 | } 102 | if user.Name != "admin" { 103 | c.HTML(http.StatusOK, "notify", gin.H{ 104 | "error": "没权限", 105 | }) 106 | return 107 | } 108 | id := c.Param("id") 109 | topic, err := topicModel.GetTopicById(id) 110 | if err != nil { 111 | c.HTML(http.StatusOK, "notify", gin.H{ 112 | "error": "话题不存在", 113 | }) 114 | return 115 | } 116 | topicModel.SetTop(id, !topic.Top) 117 | msg := "" 118 | if topic.Top { 119 | msg = "此话题取消置顶成功。" 120 | } else { 121 | msg = "此话题置顶成功。" 122 | } 123 | 124 | c.HTML(http.StatusOK, "notify", gin.H{ 125 | "success": msg, 126 | }) 127 | } 128 | func Detele(c *gin.Context) { 129 | session := sessions.Get(c) 130 | var name string 131 | user := models.User{} 132 | if nil != session.Get("loginname") { 133 | name = session.Get("loginname").(string) 134 | user, _ = userModel.GetUserByName(name) 135 | } 136 | var msg struct { 137 | Message string `json:"message"` 138 | Success bool `json:"success"` 139 | } 140 | id := c.Param("id") 141 | topic, err := topicModel.GetTopicById(id) 142 | 143 | if err != nil { 144 | msg.Message = "此话题不存在" 145 | msg.Success = false 146 | c.JSON(http.StatusOK, msg) 147 | return 148 | } 149 | if topic.Author_id.Hex() != user.Id.Hex() { 150 | msg.Message = "无权限" 151 | msg.Success = false 152 | c.JSON(http.StatusForbidden, msg) 153 | return 154 | } 155 | 156 | topicModel.Delete(id) 157 | msg.Message = "话题删除成功" 158 | msg.Success = true 159 | c.JSON(http.StatusOK, msg) 160 | } 161 | func ShowEdit(c *gin.Context) { 162 | session := sessions.Get(c) 163 | var name string 164 | user := models.User{} 165 | if nil != session.Get("loginname") { 166 | name = session.Get("loginname").(string) 167 | user, _ = userModel.GetUserByName(name) 168 | } 169 | var msg struct { 170 | Message string `json:"message"` 171 | Success bool `json:"success"` 172 | } 173 | id := c.Param("id") 174 | topic, err := topicModel.GetTopicById(id) 175 | 176 | if err != nil { 177 | msg.Message = "此话题不存在" 178 | msg.Success = false 179 | c.JSON(http.StatusNotFound, msg) 180 | return 181 | } 182 | if topic.Author_id.Hex() != user.Id.Hex() { 183 | msg.Message = "对不起,你不能编辑此话题" 184 | msg.Success = false 185 | c.JSON(http.StatusForbidden, msg) 186 | return 187 | } 188 | tabs := [3]map[string]string{{"value": "share", "text": "分享"}, {"value": "ask", "text": "问答"}, {"value": "job", "text": "招聘"}} 189 | c.HTML(http.StatusOK, "edit", gin.H{ 190 | "user": user, 191 | "action": "edit", 192 | "topic_id": topic.Id.Hex(), 193 | "title": topic.Title, 194 | "content": topic.Content, 195 | "tab": topic.Tab, 196 | "tabs": tabs, 197 | "config": gin.H{ 198 | "description": "CNode:Node.js专业中文社区", 199 | }, 200 | }) 201 | 202 | } 203 | func Update(c *gin.Context) { 204 | session := sessions.Get(c) 205 | var name string 206 | user := models.User{} 207 | if nil != session.Get("loginname") { 208 | name = session.Get("loginname").(string) 209 | user, _ = userModel.GetUserByName(name) 210 | } 211 | var msg struct { 212 | Message string `json:"message"` 213 | Success bool `json:"success"` 214 | } 215 | id := c.Param("id") 216 | topic, err := topicModel.GetTopicById(id) 217 | 218 | if err != nil { 219 | msg.Message = "此话题不存在" 220 | msg.Success = false 221 | c.JSON(http.StatusNotFound, msg) 222 | return 223 | } 224 | if topic.Author_id.Hex() != user.Id.Hex() { 225 | msg.Message = "对不起,你不能编辑此话题" 226 | msg.Success = false 227 | c.JSON(http.StatusForbidden, msg) 228 | return 229 | } 230 | tabs := [3]map[string]string{{"value": "share", "text": "分享"}, {"value": "ask", "text": "问答"}, {"value": "job", "text": "招聘"}} 231 | tab := c.Request.FormValue("tab") 232 | title := c.Request.FormValue("title") 233 | content := c.Request.FormValue("content") 234 | // 验证 235 | editError := "" 236 | if title == "" { 237 | editError = "标题不能是空的。" 238 | } 239 | if len(title) < 5 || len(title) > 100 { 240 | editError = "标题字数太多或太少。" 241 | } 242 | if tab == "" { 243 | editError = "必须选择一个版块" 244 | } 245 | if content == "" { 246 | editError = "内容不可为空。" 247 | } 248 | if editError != "" { 249 | c.HTML(http.StatusOK, "edit", gin.H{ 250 | "user": user, 251 | "editError": editError, 252 | "action": "edit", 253 | "topic_id": topic.Id.Hex(), 254 | "title": topic.Title, 255 | "content": topic.Content, 256 | "tab": topic.Tab, 257 | "tabs": tabs, 258 | "config": gin.H{ 259 | "description": "CNode:Node.js专业中文社区", 260 | }, 261 | }) 262 | return 263 | } 264 | topic.Title=title 265 | topic.Tab=tab 266 | topic.Content=content 267 | topicModel.Update(topic) 268 | url := "/topic/" + topic.Id.Hex() 269 | c.Redirect(301, url) 270 | 271 | } 272 | func Create(c *gin.Context) { 273 | session := sessions.Get(c) 274 | var name string 275 | user := models.User{} 276 | if nil != session.Get("loginname") { 277 | name = session.Get("loginname").(string) 278 | user, _ = userModel.GetUserByName(name) 279 | } 280 | id := user.Id.Hex() 281 | 282 | tab := c.Request.FormValue("tab") 283 | title := c.Request.FormValue("title") 284 | content := c.Request.FormValue("content") 285 | topic, _ := topicModel.NewAndSave(title, tab, id, content) 286 | url := "/topic/" + topic.Id.Hex() 287 | c.Redirect(301, url) 288 | } 289 | 290 | const ( 291 | upload_path string = "./public/upload/" 292 | upload_path2 string = "/public/upload/" 293 | ) 294 | 295 | func Upload(c *gin.Context) { 296 | session := sessions.Get(c) 297 | var name string 298 | user := models.User{} 299 | if nil != session.Get("loginname") { 300 | name = session.Get("loginname").(string) 301 | user, _ = userModel.GetUserByName(name) 302 | } 303 | 304 | id := user.Id.Hex() 305 | log.Println(id) 306 | //picName := c.Request.FormValue("name") 307 | file, _ := c.FormFile("file") 308 | 309 | //创建文件 310 | // fW, err := os.Create(upload_path + file.Filename) 311 | // if err != nil { 312 | // fmt.Println("文件创建失败") 313 | // return 314 | // } 315 | // defer fW.Close() 316 | // _, err = io.Copy(fW, file) 317 | // if err != nil { 318 | // fmt.Println("文件保存失败") 319 | // return 320 | // } 321 | c.SaveUploadedFile(file, upload_path+file.Filename) 322 | var msg struct { 323 | Success bool `json:"success"` 324 | Url string `json:"url"` 325 | } 326 | msg.Success = true 327 | msg.Url = upload_path2 + file.Filename 328 | c.JSON(http.StatusOK, msg) 329 | 330 | } 331 | -------------------------------------------------------------------------------- /controllers/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | 5 | "net/http" 6 | //"regexp" 7 | "log" 8 | "go_cnode/mgoModels" 9 | //"github.com/dangyanglim/go_cnode/service/mail" 10 | "encoding/json" 11 | "github.com/gin-gonic/gin" 12 | "github.com/tommy351/gin-sessions" 13 | "time" 14 | "strconv" 15 | "math" 16 | 17 | ) 18 | 19 | var userModel = new(models.UserModel) 20 | var topicModel = new(models.TopicModel) 21 | var replyModel = new(models.ReplyModel) 22 | 23 | 24 | func Index(c *gin.Context) { 25 | session := sessions.Get(c) 26 | var loginname string 27 | 28 | user := models.User{} 29 | if nil != session.Get("loginname") { 30 | loginname = session.Get("loginname").(string) 31 | user, _ = userModel.GetUserByName(loginname) 32 | } 33 | name := c.Param("name") 34 | user, _ = userModel.GetUserByName(name) 35 | _,topics, _ := topicModel.GetAuthorTopics(user.Id.Hex(),5,0) 36 | var recent_topics []map[string]interface{} 37 | json.Unmarshal([]byte(topics), &recent_topics) 38 | 39 | for _, v := range recent_topics { 40 | timeString := v["topic"].(map[string]interface{})["Create_at"].(string) 41 | t, _ := time.Parse("2006-01-02T15:04:05-07:00", timeString) 42 | v["topic"].(map[string]interface{})["Create_at"] = t.Format("2006-01-02 15:04:05") 43 | timeString = v["topic"].(map[string]interface{})["last_reply_at"].(string) 44 | t, _ = time.Parse("2006-01-02T15:04:05-07:00", timeString) 45 | v["topic"].(map[string]interface{})["last_reply_at"] = t.Format("2006-01-02 15:04:05") 46 | if v["reply"].(map[string]interface{})["Author_id"].(string) != "" { 47 | author, _ := userModel.GetUserById(v["reply"].(map[string]interface{})["Author_id"].(string)) 48 | j, _ := json.Marshal(author) 49 | m := make(map[string]interface{}) 50 | json.Unmarshal(j, &m) 51 | v["reply"].(map[string]interface{})["author"] = m 52 | } 53 | } 54 | topics2, _ := topicModel.GetReplyTopics(user.Id.Hex(),20,0,5) 55 | var recent_replies []map[string]interface{} 56 | json.Unmarshal([]byte(topics2), &recent_replies) 57 | for _, v := range recent_replies { 58 | timeString := v["topic"].(map[string]interface{})["Create_at"].(string) 59 | t, _ := time.Parse("2006-01-02T15:04:05-07:00", timeString) 60 | v["topic"].(map[string]interface{})["Create_at"] = t.Format("2006-01-02 15:04:05") 61 | timeString = v["topic"].(map[string]interface{})["last_reply_at"].(string) 62 | t, _ = time.Parse("2006-01-02T15:04:05-07:00", timeString) 63 | v["topic"].(map[string]interface{})["last_reply_at"] = t.Format("2006-01-02 15:04:05") 64 | if v["reply"].(map[string]interface{})["Author_id"].(string) != "" { 65 | author, _ := userModel.GetUserById(v["reply"].(map[string]interface{})["Author_id"].(string)) 66 | j, _ := json.Marshal(author) 67 | m := make(map[string]interface{}) 68 | json.Unmarshal(j, &m) 69 | v["reply"].(map[string]interface{})["author"] = m 70 | } 71 | } 72 | c.HTML(http.StatusOK, "userIndex", gin.H{ 73 | "recent_topics":recent_topics, 74 | "recent_replies":recent_replies, 75 | "user": user, 76 | }) 77 | } 78 | func Topics(c *gin.Context) { 79 | session := sessions.Get(c) 80 | var loginname string 81 | var pageSize = 20 82 | 83 | page := c.Request.FormValue("page") 84 | if page == "" { 85 | page = "1" 86 | } 87 | current_page, _ := strconv.Atoi(page) 88 | user := models.User{} 89 | if nil != session.Get("loginname") { 90 | loginname = session.Get("loginname").(string) 91 | user, _ = userModel.GetUserByName(loginname) 92 | } 93 | name := c.Param("name") 94 | user, _ = userModel.GetUserByName(name) 95 | _,topics, _ := topicModel.GetAuthorTopics(user.Id.Hex(),pageSize,(current_page-1)*pageSize) 96 | var recent_topics []map[string]interface{} 97 | json.Unmarshal([]byte(topics), &recent_topics) 98 | 99 | for _, v := range recent_topics { 100 | timeString := v["topic"].(map[string]interface{})["Create_at"].(string) 101 | t, _ := time.Parse("2006-01-02T15:04:05-07:00", timeString) 102 | v["topic"].(map[string]interface{})["Create_at"] = t.Format("2006-01-02 15:04:05") 103 | timeString = v["topic"].(map[string]interface{})["last_reply_at"].(string) 104 | t, _ = time.Parse("2006-01-02T15:04:05-07:00", timeString) 105 | v["topic"].(map[string]interface{})["last_reply_at"] = t.Format("2006-01-02 15:04:05") 106 | if v["reply"].(map[string]interface{})["Author_id"].(string) != "" { 107 | author, _ := userModel.GetUserById(v["reply"].(map[string]interface{})["Author_id"].(string)) 108 | j, _ := json.Marshal(author) 109 | m := make(map[string]interface{}) 110 | json.Unmarshal(j, &m) 111 | v["reply"].(map[string]interface{})["author"] = m 112 | } 113 | } 114 | var page_start int 115 | pages, _:= topicModel.GetTopicByAuthorQueryCount(user.Id,"all", false) 116 | pages = int(math.Floor(float64(pages)/float64(pageSize))) + 1 117 | base_url := "?page=" 118 | var page_end int 119 | 120 | log.Println(current_page) 121 | if (current_page - 2) > 0 { 122 | page_start = current_page - 2 123 | } else { 124 | page_start = 1 125 | } 126 | if (page_start + 4) > pages { 127 | page_end = pages 128 | } else { 129 | page_end = page_start + 4 130 | } 131 | pagesArray := []int{} 132 | var i int 133 | for i = 1; i < pages+1; i++ { 134 | pagesArray = append(pagesArray, i) 135 | } 136 | //log.Println(recent_topics) 137 | c.HTML(http.StatusOK, "userTopics", gin.H{ 138 | "user": user, 139 | "topicss":recent_topics, 140 | "pages": pages, 141 | "page_start": page_start, 142 | "page_end": page_end, 143 | "pagesArray": pagesArray, 144 | "base_url": base_url, 145 | "current_page": current_page, 146 | }) 147 | 148 | } 149 | func Replies(c *gin.Context) { 150 | session := sessions.Get(c) 151 | var loginname string 152 | var pageSize = 20 153 | 154 | page := c.Request.FormValue("page") 155 | if page == "" { 156 | page = "1" 157 | } 158 | current_page, _ := strconv.Atoi(page) 159 | user := models.User{} 160 | if nil != session.Get("loginname") { 161 | loginname = session.Get("loginname").(string) 162 | user, _ = userModel.GetUserByName(loginname) 163 | } 164 | name := c.Param("name") 165 | user, _ = userModel.GetUserByName(name) 166 | topics2, _ := topicModel.GetReplyTopics(user.Id.Hex(),pageSize,(current_page-1)*pageSize,pageSize) 167 | var recent_replies []map[string]interface{} 168 | json.Unmarshal([]byte(topics2), &recent_replies) 169 | for _, v := range recent_replies { 170 | timeString := v["topic"].(map[string]interface{})["Create_at"].(string) 171 | t, _ := time.Parse("2006-01-02T15:04:05-07:00", timeString) 172 | v["topic"].(map[string]interface{})["Create_at"] = t.Format("2006-01-02 15:04:05") 173 | timeString = v["topic"].(map[string]interface{})["last_reply_at"].(string) 174 | t, _ = time.Parse("2006-01-02T15:04:05-07:00", timeString) 175 | v["topic"].(map[string]interface{})["last_reply_at"] = t.Format("2006-01-02 15:04:05") 176 | if v["reply"].(map[string]interface{})["Author_id"].(string) != "" { 177 | author, _ := userModel.GetUserById(v["reply"].(map[string]interface{})["Author_id"].(string)) 178 | j, _ := json.Marshal(author) 179 | m := make(map[string]interface{}) 180 | json.Unmarshal(j, &m) 181 | v["reply"].(map[string]interface{})["author"] = m 182 | } 183 | } 184 | 185 | var page_start int 186 | pages, _:= replyModel.GetReplyByAuthorQueryCount(user.Id) 187 | pages = int(math.Floor(float64(pages)/float64(pageSize))) + 1 188 | base_url := "?page=" 189 | var page_end int 190 | 191 | log.Println(current_page) 192 | if (current_page - 2) > 0 { 193 | page_start = current_page - 2 194 | } else { 195 | page_start = 1 196 | } 197 | if (page_start + 4) > pages { 198 | page_end = pages 199 | } else { 200 | page_end = page_start + 4 201 | } 202 | pagesArray := []int{} 203 | var i int 204 | for i = 1; i < pages+1; i++ { 205 | pagesArray = append(pagesArray, i) 206 | } 207 | //log.Println(recent_topics) 208 | c.HTML(http.StatusOK, "userReplies", gin.H{ 209 | "user": user, 210 | "topicss":recent_replies, 211 | "pages": pages, 212 | "page_start": page_start, 213 | "page_end": page_end, 214 | "pagesArray": pagesArray, 215 | "base_url": base_url, 216 | "current_page": current_page, 217 | }) 218 | 219 | } 220 | func Top100(c *gin.Context) { 221 | session := sessions.Get(c) 222 | var loginname string 223 | 224 | user := models.User{} 225 | if nil != session.Get("loginname") { 226 | loginname = session.Get("loginname").(string) 227 | user, _ = userModel.GetUserByName(loginname) 228 | } 229 | tops, _ := userModel.GetUserTops(100) 230 | c.HTML(http.StatusOK, "userTop100", gin.H{ 231 | "user": user, 232 | "users":tops, 233 | }) 234 | } 235 | -------------------------------------------------------------------------------- /database/mysql.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | //"database/sql" 5 | "fmt" 6 | "log" 7 | "github.com/garyburd/redigo/redis" 8 | //_ "github.com/go-sql-driver/mysql" 9 | "gopkg.in/mgo.v2" 10 | //"log" 11 | ) 12 | 13 | type MongoLog struct { 14 | } 15 | 16 | func (MongoLog)Output(calldepth int, s string) error { 17 | log.SetFlags(log.Lshortfile) 18 | return log.Output(calldepth,s) 19 | } 20 | 21 | //var SqlDB *sql.DB 22 | var MogSession *mgo.Session 23 | var Redis redis.Conn 24 | var Mgodb *mgo.Database 25 | func init() { 26 | } 27 | //var mgodb *mgo.Database 28 | func Config(mogo_url string,redis_url string) { 29 | var err error 30 | var mgoerr error 31 | // SqlDB, err = sql.Open("mysql", "root@tcp(127.0.0.1:3306)/test?parseTime=true") 32 | // if err != nil { 33 | // log.Fatal(err.Error()) 34 | // } 35 | // err = SqlDB.Ping() 36 | // if err != nil { 37 | // log.Fatal(err.Error()) 38 | // } 39 | MogSession, mgoerr = mgo.Dial(mogo_url) 40 | if mgoerr != nil { 41 | panic(mgoerr) 42 | } 43 | MogSession.SetMode(mgo.Monotonic, true) 44 | Mgodb=MogSession.DB("egg_cnode") 45 | Redis, err = redis.Dial("tcp", redis_url) 46 | if err != nil { 47 | fmt.Println("Connect to redis error", err) 48 | return 49 | } 50 | 51 | 52 | // defer mogSession.Close() 53 | // session.SetMode(mgo.Monotonic, true) 54 | // mgodb = session.DB("egg_cnode") 55 | // countNum, _ :=mgodb.C("users").Count() 56 | // log.Println(countNum) 57 | mgo.SetDebug(false) // 设置DEBUG模式 58 | //mgo.SetLogger(new(MongoLog)) // 设置日志. 59 | } 60 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go_cnode 2 | 3 | go 1.12 4 | 5 | replace golang.org/x/net => github.com/golang/net v0.0.0-20190424024845-afe8014c977f 6 | 7 | replace golang.org/x/sync => github.com/golang/sync v0.0.0-20190423024810-112230192c58 8 | 9 | replace golang.org/x/sys => github.com/golang/sys v0.0.0-20190422165155-953cdadca894 10 | 11 | replace golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190422183909-d864b10871cd 12 | 13 | replace golang.org/x/text => github.com/golang/text v0.3.0 14 | 15 | replace google.golang.org/appengine => github.com/golang/appengine v1.5.0 16 | 17 | require ( 18 | github.com/garyburd/redigo v1.6.0 19 | github.com/gin-contrib/cors v0.0.0-20190424000812-bd1331c62cae 20 | github.com/gin-gonic/gin v1.3.0 21 | github.com/go-sql-driver/mysql v1.4.1 22 | github.com/gorilla/sessions v1.1.3 // indirect 23 | github.com/russross/blackfriday v2.0.0+incompatible 24 | github.com/satori/go.uuid v1.2.0 25 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 26 | github.com/tommy351/gin-sessions v0.0.0-20150617141853-353060947eb6 27 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 28 | google.golang.org/appengine v1.5.0 // indirect 29 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce 30 | ) 31 | -------------------------------------------------------------------------------- /go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/go.png -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= 4 | github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= 5 | github.com/gin-contrib/cors v0.0.0-20190424000812-bd1331c62cae h1:sGWKl+Nw64hNtVFaYWM4Gk8LMpK3OQScMlyzFFfbHBk= 6 | github.com/gin-contrib/cors v0.0.0-20190424000812-bd1331c62cae/go.mod h1:pL2kNE+DgDU+eQ+dary5bX0Z6LPP8nR6Mqs1iejILw4= 7 | github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY= 8 | github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 9 | github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs= 10 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= 11 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 12 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 13 | github.com/golang/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 14 | github.com/golang/crypto v0.0.0-20190422183909-d864b10871cd h1:mhLrErKLIynY9Fs2Z/kFJwUzpEZFHltw72hQN1Em5+E= 15 | github.com/golang/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 16 | github.com/golang/net v0.0.0-20190424024845-afe8014c977f h1:8zPcFd05aawb15tchyXnHb0DFfCSMDpq8DPzfBOYt1g= 17 | github.com/golang/net v0.0.0-20190424024845-afe8014c977f/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 18 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 19 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 20 | github.com/golang/sync v0.0.0-20190423024810-112230192c58 h1:FVziwFqxtCKvWuys1e98ng/JsDBTeuqRNI/P2yxNfzI= 21 | github.com/golang/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 22 | github.com/golang/sys v0.0.0-20190422165155-953cdadca894 h1:C9ZDFLCRWOewEpp/wj0dsonEXGfatyyu3Z6rhdD3W5A= 23 | github.com/golang/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 24 | github.com/golang/text v0.3.0/go.mod h1:GUiq9pdJKRKKAZXiVgWFEvocYuREvC14NhI4OPgEjeE= 25 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= 26 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 27 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 28 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 29 | github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU= 30 | github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= 31 | github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE= 32 | github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 33 | github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= 34 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 35 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 36 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 37 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 38 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 39 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 40 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 41 | github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk= 42 | github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 43 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 44 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 45 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 46 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 47 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 48 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 49 | github.com/tommy351/gin-sessions v0.0.0-20150617141853-353060947eb6 h1:AlxjM5zhlVJhzzCSTrdgHOLagMl8eo2VhvJJKBXihAM= 50 | github.com/tommy351/gin-sessions v0.0.0-20150617141853-353060947eb6/go.mod h1:Z322RVSctbqRYhFCVnLI+hNZUBoAarOPebn/B5XXSJY= 51 | github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 h1:EICbibRW4JNKMcY+LsWmuwob+CRS1BmdRdjphAm9mH4= 52 | github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 53 | google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= 54 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 55 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 56 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 57 | gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= 58 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 59 | gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= 60 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 61 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= 62 | gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 63 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 64 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 65 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | db "go_cnode/database" 5 | "go_cnode/router" 6 | "go_cnode/utils" 7 | //"log" 8 | ) 9 | 10 | func main() { 11 | conf := utils.LoadConf() 12 | db.Config(conf.Mongo_url, conf.Redis_url) 13 | //defer db.SqlDB.Close() 14 | defer db.MogSession.Close() 15 | defer db.Redis.Close() 16 | 17 | router := router.InitRouter() 18 | 19 | // config := cors.DefaultConfig() 20 | // config.AllowAllOrigins = true 21 | // config.AllowCredentials = true 22 | // config.AllowMethods = []string{"*"} 23 | // config.AllowHeaders = []string{"*"} 24 | //router.Use(cors.Default()) 25 | router.Run(conf.Port) 26 | } 27 | -------------------------------------------------------------------------------- /mgoModels/message.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | db "go_cnode/database" 5 | "gopkg.in/mgo.v2/bson" 6 | "time" 7 | "log" 8 | ) 9 | 10 | //"log" 11 | 12 | type Message struct { 13 | Id bson.ObjectId `bson:"_id"` 14 | Type string `json:"type"` 15 | Master_id bson.ObjectId `json:"master_id" ` 16 | Author_id bson.ObjectId `bson:"author_id" ` 17 | Topic_id bson.ObjectId `json:"topic_id" ` 18 | Reply_id bson.ObjectId `json:"reply_id" ` 19 | Has_read bool `json:"has_read"` 20 | Create_at time.Time `bson:"create_at"` 21 | } 22 | type MessageModel struct{} 23 | 24 | func (p *MessageModel) GetMessagesCount(id string) (count int, err error) { 25 | mgodb:=db.Mgodb 26 | objectId := bson.ObjectIdHex(id) 27 | count, err = mgodb.C("messages").Find(bson.M{"master_id": objectId,"has_read": false}).Count() 28 | return count, err 29 | } 30 | func (p *MessageModel) GetMessageById(id string) (message Message, err error) { 31 | mgodb:=db.Mgodb 32 | objectId := bson.ObjectIdHex(id) 33 | err = mgodb.C("messages").Find(bson.M{"_id": objectId}).One(&message) 34 | return message, err 35 | } 36 | func (p *MessageModel) GetMessagesByUserId(id bson.ObjectId) (messages []Message, err error) { 37 | mgodb:=db.Mgodb 38 | err = mgodb.C("messages").Find(bson.M{"master_id": id, "has_read": true}).Sort("-create_at").All(&messages) 39 | return messages, err 40 | } 41 | func (p *MessageModel) GetUnreadMessagesByUserId(id bson.ObjectId) (messages []Message, err error) { 42 | mgodb:=db.Mgodb 43 | err = mgodb.C("messages").Find(bson.M{"master_id": id, "has_read": false}).Sort("-create_at").All(&messages) 44 | return messages, err 45 | } 46 | func (p *MessageModel) UpdateOneMessageToRead(msgId string) (err error) { 47 | mgodb:=db.Mgodb 48 | objectId := bson.ObjectIdHex(msgId) 49 | err = mgodb.C("messages").Update(bson.M{"_id": objectId}, 50 | bson.M{ 51 | "$set": bson.M{"has_read": true}, 52 | }) 53 | return err 54 | } 55 | func (p *MessageModel) UpdateMessagesToRead(userId string, messages []Message) (err error) { 56 | mgodb:=db.Mgodb 57 | var ids []bson.ObjectId 58 | for _, message := range messages { 59 | ids = append(ids, message.Id) 60 | } 61 | log.Println(ids) 62 | _,err = mgodb.C("messages").UpdateAll( 63 | bson.M{"master_id": bson.ObjectIdHex(userId), 64 | "_id":bson.M{"$in": ids}}, 65 | bson.M{ 66 | "$set": bson.M{"has_read": true}, 67 | }) 68 | return err 69 | } 70 | func (p *MessageModel) SendAtMessage(userId string, authorId string, topicId string, replyId bson.ObjectId) (err error) { 71 | mgodb:=db.Mgodb 72 | message := Message{ 73 | Id: bson.NewObjectId(), 74 | Type: "at", 75 | Master_id: bson.ObjectIdHex(userId), 76 | Topic_id: bson.ObjectIdHex(topicId), 77 | Author_id: bson.ObjectIdHex(authorId), 78 | Reply_id: replyId, 79 | Create_at: time.Now(), 80 | } 81 | err = mgodb.C("messages").Insert(&message) 82 | return err 83 | } 84 | func (p *MessageModel) SendReplyMessage(userId string, authorId string, topicId string, replyId bson.ObjectId) (err error) { 85 | //mgodb := db.MogSession.DB("egg_cnode") 86 | mgodb:=db.Mgodb 87 | message := Message{ 88 | Id: bson.NewObjectId(), 89 | Type: "reply", 90 | Master_id: bson.ObjectIdHex(userId), 91 | Topic_id: bson.ObjectIdHex(topicId), 92 | Author_id: bson.ObjectIdHex(authorId), 93 | Reply_id: replyId, 94 | Create_at: time.Now(), 95 | } 96 | err = mgodb.C("messages").Insert(&message) 97 | return err 98 | } 99 | -------------------------------------------------------------------------------- /mgoModels/reply.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | //"log" 5 | 6 | db "go_cnode/database" 7 | "gopkg.in/mgo.v2/bson" 8 | //"encoding/json" 9 | "github.com/russross/blackfriday" 10 | "html/template" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | //"log" 16 | 17 | type Reply struct { 18 | Id bson.ObjectId `bson:"_id"` 19 | Topic_id bson.ObjectId `bson:"topic_id"` 20 | Reply_id bson.ObjectId `bson:"reply_id,omitempty"` 21 | Author_id bson.ObjectId `bson:"author_id" ` 22 | Create_at time.Time `bson:"create_at"` 23 | Create_at_string string `json:"create_at_string,omitempty"` 24 | Update_at time.Time `bson:"update_at"` 25 | Update_at_string string `json:"update_at_string,omitempty"` 26 | Content string `json:"content"` 27 | Content_is_html bool `json:"content_is_html"` 28 | 29 | Deleted bool `json:"deleted"` 30 | } 31 | type ReplyAndAuthor struct { 32 | Reply Reply 33 | Author User 34 | LinkContent template.HTML 35 | } 36 | type ReplyModel struct{} 37 | 38 | func (p *ReplyModel) GetReplyById(id string) (reply Reply, err error) { 39 | mgodb:=db.Mgodb 40 | objectId := bson.ObjectIdHex(id) 41 | err = mgodb.C("replies").Find(bson.M{"_id": objectId,"deleted":false}).One(&reply) 42 | return reply, err 43 | } 44 | func (p *ReplyModel) GetRepliesByTopicId(id string) (replies []Reply, replyAndAuthor []ReplyAndAuthor, err error) { 45 | mgodb:=db.Mgodb 46 | objectId := bson.ObjectIdHex(id) 47 | err = mgodb.C("replies").Find(bson.M{"topic_id": objectId,"deleted":false}).Sort("_id").All(&replies) 48 | for _, v := range replies { 49 | var temp ReplyAndAuthor 50 | author, _ := userModel.GetUserById(v.Author_id.Hex()) 51 | temp.Reply = v 52 | temp.Reply.Create_at_string = v.Create_at.Format("2006-01-02 15:04:05") 53 | temp.Reply.Update_at_string = v.Update_at.Format("2006-01-02 15:04:05") 54 | temp.Reply.Content = strings.Replace(temp.Reply.Content, "\r\n", "
", -1) 55 | temp.LinkContent = template.HTML(blackfriday.Run([]byte(temp.Reply.Content))) 56 | temp.Author = author 57 | replyAndAuthor = append(replyAndAuthor, temp) 58 | } 59 | return replies, replyAndAuthor, err 60 | } 61 | func (p *ReplyModel) NewAndSave(content string, topic_id string, user_id string, reply_id string) (reply Reply, err error) { 62 | 63 | objectId := bson.ObjectIdHex(user_id) 64 | object_topic_id := bson.ObjectIdHex(topic_id) 65 | 66 | reply = Reply{ 67 | Id: bson.NewObjectId(), 68 | Topic_id: object_topic_id, 69 | Content: content, 70 | Author_id: objectId, 71 | Create_at: time.Now(), 72 | } 73 | if reply_id != "" { 74 | object_reply_id := bson.ObjectIdHex(reply_id) 75 | reply.Reply_id = object_reply_id 76 | } 77 | mgodb:=db.Mgodb 78 | err = mgodb.C("replies").Insert(&reply) 79 | 80 | return reply, err 81 | } 82 | func (p *ReplyModel) Update(content string, reply_id string) (err error) { 83 | 84 | objectId := bson.ObjectIdHex(reply_id) 85 | 86 | mgodb:=db.Mgodb 87 | err = mgodb.C("replies").Update(bson.M{"_id": objectId}, 88 | bson.M{ 89 | "$set": bson.M{"update_at": time.Now(), "content": content}, 90 | }) 91 | 92 | return err 93 | } 94 | func (p *ReplyModel) Delete( reply_id string) (err error) { 95 | 96 | objectId := bson.ObjectIdHex(reply_id) 97 | mgodb:=db.Mgodb 98 | err = mgodb.C("replies").Update(bson.M{"_id": objectId}, 99 | bson.M{ 100 | "$set": bson.M{"update_at": time.Now(), "deleted": true}, 101 | }) 102 | return err 103 | } 104 | func (p *ReplyModel) GetReplyByAuthorQueryCount(objectId bson.ObjectId) (count int, err error) { 105 | mgodb:=db.Mgodb 106 | 107 | count, err = mgodb.C("replies").Find(bson.M{"author_id": objectId}).Count() 108 | 109 | return count, err 110 | } 111 | -------------------------------------------------------------------------------- /mgoModels/topic.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | //"log" 5 | "encoding/json" 6 | db "go_cnode/database" 7 | "gopkg.in/mgo.v2/bson" 8 | "log" 9 | "time" 10 | ) 11 | 12 | //"log" 13 | 14 | type Topic struct { 15 | Id bson.ObjectId `bson:"_id"` 16 | Title string `json:"title"` 17 | Content string `json:"content" ` 18 | Author_id bson.ObjectId `bson:"author_id" ` 19 | Top bool `json:"top" ` 20 | Good bool `json:"good" ` 21 | Lock bool `json:"lock"` 22 | Reply_count uint `json:"reply_count"` 23 | Visit_count uint `json:"visit_count"` 24 | Collect_count uint `json:"collect_count"` 25 | Create_at time.Time `bson:"create_at"` 26 | Create_at_string string `json:"create_at_string,omitempty"` 27 | Update_at string `json:"update_at"` 28 | Last_reply bson.ObjectId `bson:"last_reply,omitempty"` 29 | Last_reply_at time.Time `json:"last_reply_at,omitempty"` 30 | Content_is_html bool `json:"content_is_html"` 31 | Tab string `json:"tab"` 32 | Deleted bool `json:"deleted"` 33 | } 34 | type TopicModel struct{} 35 | 36 | var userModel = new(UserModel) 37 | var replyModel = new(ReplyModel) 38 | var topicModel = new(TopicModel) 39 | 40 | type TopciAndAuthor struct { 41 | Author User `json:"author"` 42 | Topic Topic `json:"topic"` 43 | Reply Reply `json:"reply"` 44 | } 45 | 46 | func (p *TopicModel) GetTopicByQuery(tab string, good bool, limit int, skip int) (topics []Topic, err error) { 47 | mgodb := db.MogSession.DB("egg_cnode") 48 | if tab == "" || tab == "all" { 49 | err = mgodb.C("topics").Find(bson.M{"good": good,"deleted":false}).Sort("-top", "-create_at").Limit(limit).Skip(skip).All(&topics) 50 | } else { 51 | err = mgodb.C("topics").Find(bson.M{"tab": tab, "good": good,"deleted":false}).Sort("-top", "-create_at").Limit(limit).Skip(skip).All(&topics) 52 | } 53 | //log.Println(topics) 54 | return topics, err 55 | } 56 | func (p *TopicModel) GetTopicBy(tab string, good bool, limit int, skip int) (topics []Topic, topicss []byte, err error) { 57 | 58 | var temps []TopciAndAuthor 59 | mgodb := db.MogSession.DB("egg_cnode") 60 | if tab == "" || tab == "all" { 61 | err = mgodb.C("topics").Find(bson.M{"good": good,"deleted":false}).Sort("-top", "-create_at").Limit(limit).Skip(skip).All(&topics) 62 | } else { 63 | err = mgodb.C("topics").Find(bson.M{"tab": tab, "good": good,"deleted":false}).Sort("-top", "-create_at").Limit(limit).Skip(skip).All(&topics) 64 | } 65 | //log.Println(topics) 66 | for _, v := range topics { 67 | var temp TopciAndAuthor 68 | temp.Topic = v 69 | author, _ := userModel.GetUserById(v.Author_id.Hex()) 70 | temp.Author = author 71 | if v.Last_reply.Hex() != "" { 72 | reply, _ := replyModel.GetReplyById(v.Last_reply.Hex()) 73 | temp.Reply = reply 74 | } 75 | 76 | temps = append(temps, temp) 77 | } 78 | topicss, _ = json.Marshal(temps) 79 | 80 | return topics, topicss, err 81 | } 82 | func (p *TopicModel) GetTopicByQueryCount(tab string, good bool) (count int, err error) { 83 | mgodb := db.MogSession.DB("egg_cnode") 84 | if tab == "" || tab == "all" { 85 | count, err = mgodb.C("topics").Find(bson.M{"deleted":false}).Count() 86 | } else { 87 | if good == true { 88 | count, err = mgodb.C("topics").Find(bson.M{"good": good,"deleted":false}).Count() 89 | } else { 90 | count, err = mgodb.C("topics").Find(bson.M{"tab": tab,"deleted":false}).Count() 91 | } 92 | 93 | } 94 | 95 | return count, err 96 | } 97 | func (p *TopicModel) GetTopicByAuthorQueryCount(objectId bson.ObjectId, tab string, good bool) (count int, err error) { 98 | mgodb := db.MogSession.DB("egg_cnode") 99 | if tab == "" || tab == "all" { 100 | count, err = mgodb.C("topics").Find(bson.M{"author_id": objectId,"deleted":false}).Count() 101 | } else { 102 | if good == true { 103 | count, err = mgodb.C("topics").Find(bson.M{"author_id": objectId, "good": good,"deleted":false}).Count() 104 | } else { 105 | count, err = mgodb.C("topics").Find(bson.M{"author_id": objectId, "tab": tab,"deleted":false}).Count() 106 | } 107 | 108 | } 109 | 110 | return count, err 111 | } 112 | 113 | // type ReplyAndAuthor struct { 114 | // Author models.User 115 | // Reply models.Reply 116 | // } 117 | func (p *TopicModel) GetTopicById(id string) (topic Topic, err error) { 118 | //mgodb := db.MogSession.DB("egg_cnode") 119 | mgodb:=db.Mgodb 120 | objectId := bson.ObjectIdHex(id) 121 | err = mgodb.C("topics").Find(bson.M{"_id": objectId}).One(&topic) 122 | return topic, err 123 | } 124 | func (p *TopicModel) GetTopicByIdWithReply(id string) (topic Topic, author User, replies []Reply, repliyWithAuthors []ReplyAndAuthor, err error) { 125 | mgodb:=db.Mgodb 126 | objectId := bson.ObjectIdHex(id) 127 | 128 | err = mgodb.C("topics").Find(bson.M{"_id": objectId}).One(&topic) 129 | 130 | author, _ = userModel.GetUserById(topic.Author_id.Hex()) 131 | topic.Create_at_string = topic.Create_at.Format("2006-01-02 15:04:05") 132 | replies, repliyWithAuthors, _ = replyModel.GetRepliesByTopicId(topic.Id.Hex()) 133 | return topic, author, replies, repliyWithAuthors, err 134 | } 135 | func (p *TopicModel) NewAndSave(title string, tab string, id string, content string) (topic Topic, err error) { 136 | 137 | objectId := bson.ObjectIdHex(id) 138 | topic = Topic{ 139 | Id: bson.NewObjectId(), 140 | Title: title, 141 | Content: content, 142 | Tab: tab, 143 | Author_id: objectId, 144 | Create_at: time.Now(), 145 | } 146 | mgodb:=db.Mgodb 147 | err = mgodb.C("topics").Insert(&topic) 148 | log.Println(err) 149 | return topic, err 150 | } 151 | func (p *TopicModel) GetTopicNoReply() (topics []Topic, err error) { 152 | mgodb:=db.Mgodb 153 | 154 | err = mgodb.C("topics").Find(bson.M{"reply_count": 0,"deleted":false}).Sort("-create_at").Limit(5).All(&topics) 155 | 156 | return topics, err 157 | } 158 | func (p *TopicModel) GetAuthorOtherTopics(author_id string, topic_id string) (topics []Topic, err error) { 159 | mgodb:=db.Mgodb 160 | objectId := bson.ObjectIdHex(author_id) 161 | topic_objectId := bson.ObjectIdHex(topic_id) 162 | err = mgodb.C("topics").Find(bson.M{"author_id": objectId,"deleted":false, "_id": bson.M{"$nin": []bson.ObjectId{topic_objectId}}}).Limit(5).Sort("-last_reply_at").All(&topics) 163 | return topics, err 164 | } 165 | func (p *TopicModel) GetAuthorTopics(author_id string, limit int, skip int) (topics []Topic, topicss []byte, err error) { 166 | var temps []TopciAndAuthor 167 | mgodb:=db.Mgodb 168 | objectId := bson.ObjectIdHex(author_id) 169 | err = mgodb.C("topics").Find(bson.M{"author_id": objectId,"deleted":false}).Skip(skip).Limit(limit).Sort("-create_at").All(&topics) 170 | for _, v := range topics { 171 | var temp TopciAndAuthor 172 | temp.Topic = v 173 | author, _ := userModel.GetUserById(v.Author_id.Hex()) 174 | temp.Author = author 175 | if v.Last_reply.Hex() != "" { 176 | reply, _ := replyModel.GetReplyById(v.Last_reply.Hex()) 177 | temp.Reply = reply 178 | } 179 | 180 | temps = append(temps, temp) 181 | } 182 | topicss, _ = json.Marshal(temps) 183 | return topics, topicss, err 184 | } 185 | func (p *TopicModel) GetReplyTopics(author_id string, limit int, skip int, most int) (topicss []byte, err error) { 186 | var temps []TopciAndAuthor 187 | var replies []Reply 188 | var topic_ids map[bson.ObjectId]int 189 | var topic_id_ints []bson.ObjectId 190 | topic_ids = make(map[bson.ObjectId]int) 191 | mgodb:=db.Mgodb 192 | objectId := bson.ObjectIdHex(author_id) 193 | err = mgodb.C("replies").Find(bson.M{"author_id": objectId,"deleted":false}).Sort("-create_at").Skip(skip).Limit(limit).All(&replies) 194 | for i := 0; i < len(replies); i++ { 195 | if _, ok := topic_ids[replies[i].Topic_id]; ok { 196 | } else { 197 | topic_ids[replies[i].Topic_id] = len(topic_ids) + 1 198 | topic_id_ints = append(topic_id_ints, replies[i].Topic_id) 199 | } 200 | if len(topic_ids) == most { 201 | break 202 | } 203 | } 204 | for j := 0; j < len(topic_id_ints); j++ { 205 | topic, err2 := topicModel.GetTopicById(topic_id_ints[j].Hex()) 206 | if err2 == nil && topic.Author_id.Hex() != "" { 207 | var temp TopciAndAuthor 208 | temp.Topic = topic 209 | author, _ := userModel.GetUserById(topic.Author_id.Hex()) 210 | temp.Author = author 211 | if topic.Last_reply.Hex() != "" { 212 | reply, _ := replyModel.GetReplyById(topic.Last_reply.Hex()) 213 | temp.Reply = reply 214 | } 215 | temps = append(temps, temp) 216 | } 217 | } 218 | topicss, _ = json.Marshal(temps) 219 | return topicss, err 220 | } 221 | func (p *TopicModel) UpdateReplyCount(id string, replyId bson.ObjectId) (err error) { 222 | mgodb:=db.Mgodb 223 | objectId := bson.ObjectIdHex(id) 224 | //objectReplyId := bson.ObjectIdHex(replyId) 225 | err = mgodb.C("topics").Update(bson.M{"_id": objectId}, 226 | bson.M{ 227 | "$inc": bson.M{"reply_count": 1}, 228 | "$set": bson.M{"last_reply_at": time.Now(), "last_reply": replyId}, 229 | }) 230 | log.Println(err) 231 | return err 232 | } 233 | func (p *TopicModel) UpdateVisitCount(id string) (err error) { 234 | mgodb:=db.Mgodb 235 | objectId := bson.ObjectIdHex(id) 236 | 237 | err = mgodb.C("topics").Update(bson.M{"_id": objectId}, 238 | bson.M{ 239 | "$inc": bson.M{"visit_count": 1}, 240 | }) 241 | log.Println(err) 242 | return err 243 | } 244 | func (p *TopicModel) SetTop(id string, value bool) (err error) { 245 | mgodb:=db.Mgodb 246 | objectId := bson.ObjectIdHex(id) 247 | 248 | err = mgodb.C("topics").Update(bson.M{"_id": objectId}, 249 | bson.M{ 250 | "$set": bson.M{"top": value}, 251 | }) 252 | log.Println(err) 253 | return err 254 | } 255 | func (p *TopicModel) Delete( id string) (err error) { 256 | 257 | objectId := bson.ObjectIdHex(id) 258 | mgodb:=db.Mgodb 259 | err = mgodb.C("topics").Update(bson.M{"_id": objectId}, 260 | bson.M{ 261 | "$set": bson.M{"update_at": time.Now(), "deleted": true}, 262 | }) 263 | return err 264 | } 265 | func (p *TopicModel) Update(topic Topic) (err error) { 266 | mgodb:=db.Mgodb 267 | err = mgodb.C("topics").Update(bson.M{"_id": topic.Id}, 268 | bson.M{ 269 | "$set": bson.M{"update_at": time.Now(), "title": topic.Title, 270 | 271 | "tab":topic.Tab,"content":topic.Content}, 272 | }) 273 | log.Println(err) 274 | return err 275 | } 276 | -------------------------------------------------------------------------------- /mgoModels/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/satori/go.uuid" 5 | db "go_cnode/database" 6 | "golang.org/x/crypto/bcrypt" 7 | "gopkg.in/mgo.v2/bson" 8 | "log" 9 | "time" 10 | ) 11 | 12 | //"log" 13 | 14 | type User struct { 15 | Id bson.ObjectId `bson:"_id"` 16 | Name string `json:"name"` 17 | Loginname string `json:"loginname"` 18 | Pass string `json:"pass,omitempty"` 19 | Email string `json:"email"` 20 | Avatar string `json:"avatar" ` 21 | AccessToken string `json:"accessToken"` 22 | Score uint `json:"score"` 23 | Collect_topic_count uint `json:"collect_topic_count"` 24 | Topic_count uint `json:"topic_count"` 25 | Reply_count uint `json:"reply_count"` 26 | Active bool `json:"active"` 27 | Is_block bool `json:"is_block"` 28 | GithubUsername string `json:"githubUsername,omitempty"` 29 | GithubAccessToken string `json:"githubAccessToken,omitempty"` 30 | Messages_count int `json:"messages_count,omitempty"` 31 | GithubId int `json:"githubId,omitempty"` 32 | Url string `json:"url"` 33 | Location string `json:"location"` 34 | Weibo string `json:"weibo"` 35 | Create_at time.Time `bson:"create_at"` 36 | Is_star string `json:"is_star"` 37 | } 38 | type UserModel struct{} 39 | var messageModel = new(MessageModel) 40 | func (p *UserModel) GetUserByGithubId(id int) (user User, err error) { 41 | mgodb := db.MogSession.DB("egg_cnode") 42 | log.Println(id) 43 | err = mgodb.C("users").Find(bson.M{"GithubId": id}).One(&user) 44 | if err==nil{ 45 | user.Messages_count,_=messageModel.GetMessagesCount(user.Id.Hex()) 46 | } 47 | return user, err 48 | } 49 | func (p *UserModel) GetUserById(id string) (user User, err error) { 50 | mgodb := db.MogSession.DB("egg_cnode") 51 | objectId := bson.ObjectIdHex(id) 52 | err = mgodb.C("users").Find(bson.M{"_id": objectId}).One(&user) 53 | if err==nil{ 54 | user.Messages_count,_=messageModel.GetMessagesCount(id) 55 | } 56 | return user, err 57 | } 58 | func (p *UserModel) GetUserTops(limit int) (users []User, err error) { 59 | mgodb := db.MogSession.DB("egg_cnode") 60 | err = mgodb.C("users").Find(bson.M{}).Sort("-score").Limit(limit).All(&users) 61 | return users, err 62 | } 63 | func (p *UserModel) GetUserByName(name string) (user User, err error) { 64 | mgodb := db.MogSession.DB("egg_cnode") 65 | err = mgodb.C("users").Find(bson.M{"name": name}).One(&user) 66 | if err==nil{ 67 | user.Messages_count,_=messageModel.GetMessagesCount(user.Id.Hex()) 68 | } 69 | return user, err 70 | } 71 | func (p *UserModel) ActiveUserByName(name string) (err error) { 72 | mgodb := db.MogSession.DB("egg_cnode") 73 | err = mgodb.C("users").Update(bson.M{"name": name}, bson.M{"$set": bson.M{"active": true}}) 74 | return err 75 | } 76 | func (p *UserModel) GetUserByNameOrEmail(name string, email string) (user User, err error) { 77 | mgodb := db.MogSession.DB("egg_cnode") 78 | err = mgodb.C("users").Find(bson.M{"$or": []bson.M{bson.M{"name": name}, bson.M{"email": email}}}).One(&user) 79 | return user, err 80 | } 81 | func (p *UserModel) NewAndSave(name string, loginname string, email string, pass string, avatar_url string, active bool) (err error) { 82 | hashPass, _ := bcrypt.GenerateFromPassword([]byte(pass), 10) 83 | mgodb := db.MogSession.DB("egg_cnode") 84 | u2 := uuid.NewV4() 85 | user := User{ 86 | Id: bson.NewObjectId(), 87 | Name: name, 88 | Loginname: loginname, 89 | Pass: string(hashPass), 90 | Avatar: "http://www.gravatar.com/avatar/81f36fbf3b658c6a2330ca6840f7cb12?size=48", 91 | Email: email, 92 | Active: active, 93 | AccessToken: u2.String(), 94 | Create_at: time.Now(), 95 | } 96 | err = mgodb.C("users").Insert(&user) 97 | log.Println(err) 98 | return err 99 | } 100 | func (p *UserModel) GithubNewAndSave(name string, loginname string, email string, avatar_url string, active bool, githubId int) (temp User, err error) { 101 | 102 | mgodb := db.MogSession.DB("egg_cnode") 103 | u2 := uuid.NewV4() 104 | user := User{ 105 | Id: bson.NewObjectId(), 106 | Name: name, 107 | Loginname: loginname, 108 | Avatar: avatar_url, 109 | Email: email, 110 | Active: active, 111 | AccessToken: u2.String(), 112 | GithubId: githubId, 113 | Create_at: time.Now(), 114 | } 115 | err = mgodb.C("users").Insert(&user) 116 | log.Println(err) 117 | return user, err 118 | } 119 | -------------------------------------------------------------------------------- /public/19ITP3EmsT.txt: -------------------------------------------------------------------------------- 1 | 7a35f0ff6fc19ea2756c83f235c7b2d7 -------------------------------------------------------------------------------- /public/github-card.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | -------------------------------------------------------------------------------- /public/images/cngo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | background 6 | 7 | 8 | 9 | 10 | 11 | 12 | Layer 1 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/images/cngo2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | background 6 | 7 | 8 | 9 | 10 | 11 | 12 | Layer 1 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/images/cngo3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | background 6 | 7 | 8 | 9 | 10 | 11 | 12 | Layer 1 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/images/cngo_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/cngo_icon_32.png -------------------------------------------------------------------------------- /public/images/cnode_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/cnode_icon_32.png -------------------------------------------------------------------------------- /public/images/cnode_icon_32.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | background 6 | 7 | 8 | 9 | 10 | 11 | 12 | Layer 1 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/images/cnode_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/cnode_icon_64.png -------------------------------------------------------------------------------- /public/images/cnode_logo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/cnode_logo_128.png -------------------------------------------------------------------------------- /public/images/cnode_logo_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/cnode_logo_32.png -------------------------------------------------------------------------------- /public/images/cnodejs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 18 | 25 | 28 | 36 | 42 | 45 | 54 | 55 | -------------------------------------------------------------------------------- /public/images/cnodejs_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 18 | 25 | 28 | 36 | 42 | 45 | 54 | 55 | -------------------------------------------------------------------------------- /public/images/digitalocean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/digitalocean.png -------------------------------------------------------------------------------- /public/images/egg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/egg-logo.png -------------------------------------------------------------------------------- /public/images/golangtc-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/golangtc-logo.png -------------------------------------------------------------------------------- /public/images/iojs-logo-w150h50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/iojs-logo-w150h50.png -------------------------------------------------------------------------------- /public/images/iojs-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/iojs-logo.png -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/logo.png -------------------------------------------------------------------------------- /public/images/logo_bak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/logo_bak.png -------------------------------------------------------------------------------- /public/images/nodejs_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/nodejs_black.png -------------------------------------------------------------------------------- /public/images/phphub-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/phphub-logo.png -------------------------------------------------------------------------------- /public/images/qiniu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/qiniu.png -------------------------------------------------------------------------------- /public/images/ruby-china-20150529.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/ruby-china-20150529.png -------------------------------------------------------------------------------- /public/images/ruby-china-logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/ruby-china-logo2.png -------------------------------------------------------------------------------- /public/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/search.png -------------------------------------------------------------------------------- /public/images/ucloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/ucloud.png -------------------------------------------------------------------------------- /public/images/upyun_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/images/upyun_logo.png -------------------------------------------------------------------------------- /public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/javascripts/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | const windowHeight = $(window).height(); 3 | const $backtotop = $('#backtotop'); 4 | const top = windowHeight - $backtotop.height() - 200; 5 | 6 | 7 | function moveBacktotop() { 8 | $backtotop.css({ top, right: 0 }); 9 | } 10 | 11 | function footerFixBottom() { 12 | if ($(document.body).height() < windowHeight) { 13 | $('#footer').addClass('fix-bottom'); 14 | } else { 15 | $('#footer').removeClass('fix-bottom'); 16 | } 17 | } 18 | 19 | $backtotop.click(function() { 20 | $('html,body').animate({ scrollTop: 0 }); 21 | return false; 22 | }); 23 | $(window).scroll(function() { 24 | const windowHeight = $(window).scrollTop(); 25 | if (windowHeight > 200) { 26 | $backtotop.fadeIn(); 27 | } else { 28 | $backtotop.fadeOut(); 29 | } 30 | }); 31 | 32 | moveBacktotop(); 33 | footerFixBottom(); 34 | $(window).resize(moveBacktotop); 35 | $(window).resize(footerFixBottom); 36 | 37 | $('.topic_content a,.reply_content a').attr('target', '_blank'); 38 | 39 | // pretty code 40 | prettyPrint(); 41 | 42 | // data-loading-text="提交中" 43 | $('.submit_btn').click(function() { 44 | $(this).button('loading'); 45 | }); 46 | 47 | // 广告的统计信息 48 | $('.sponsor_outlink').click(function() { 49 | const $this = $(this); 50 | const label = $this.data('label'); 51 | ga('send', 'event', 'banner', 'click', label, 1.00, { nonInteraction: 1 }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /public/javascripts/responsive.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | var $responsiveBtn = $('#responsive-sidebar-trigger'), 3 | $sidebarMask = $('#sidebar-mask'), 4 | $sidebar = $('#sidebar'), 5 | $main = $('#main'), 6 | winWidth = $(window).width(), 7 | startX = 0, 8 | startY = 0, 9 | delta = { 10 | x: 0, 11 | y: 0, 12 | }, 13 | swipeThreshold = winWidth / 3, 14 | toggleSideBar = function() { 15 | let isShow = $responsiveBtn.data('is-show'), 16 | mainHeight = $main.height(), 17 | sidebarHeight = $sidebar.outerHeight(); 18 | $sidebar.css({ right: isShow ? -300 : 0 }); 19 | $responsiveBtn.data('is-show', !isShow); 20 | if (!isShow && mainHeight < sidebarHeight) { 21 | $main.height(sidebarHeight); 22 | } 23 | $sidebarMask[isShow ? 'fadeOut' : 'fadeIn']().height($('body').height()); 24 | $sidebar[isShow ? 'hide' : 'show'](); 25 | }, 26 | touchstart = function(e) { 27 | const touchs = e.targetTouches; 28 | startX = +touchs[0].pageX; 29 | startY = +touchs[0].pageY; 30 | delta.x = delta.y = 0; 31 | document.body.addEventListener('touchmove', touchmove, false); 32 | document.body.addEventListener('touchend', touchend, false); 33 | }, 34 | touchmove = function(e) { 35 | const touchs = e.changedTouches; 36 | delta.x = +touchs[0].pageX - startX; 37 | delta.y = +touchs[0].pageY - startY; 38 | // 当水平距离大于垂直距离时,才认为是用户想滑动打开右侧栏 39 | if (Math.abs(delta.x) > Math.abs(delta.y)) { 40 | e.preventDefault(); 41 | } 42 | }, 43 | touchend = function(e) { 44 | let touchs = e.changedTouches, 45 | isShow = $responsiveBtn.data('is-show'); 46 | delta.x = +touchs[0].pageX - startX; 47 | // 右侧栏未显示&&用户touch点在屏幕右侧1/4区域内&&move距离大于阀值时,打开右侧栏 48 | if (!isShow && (startX > winWidth * 3 / 4) && Math.abs(delta.x) > swipeThreshold) { 49 | $responsiveBtn.trigger('click'); 50 | } 51 | // 右侧栏显示中&&用户touch点在屏幕左侧侧1/4区域内&&move距离大于阀值时,关闭右侧栏 52 | if (isShow && (startX < winWidth * 1 / 4) && Math.abs(delta.x) > swipeThreshold) { 53 | $responsiveBtn.trigger('click'); 54 | } 55 | startX = startY = 0; 56 | delta.x = delta.y = 0; 57 | document.body.removeEventListener('touchmove', touchmove, false); 58 | document.body.removeEventListener('touchend', touchend, false); 59 | }; 60 | 61 | if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { 62 | document.body.addEventListener('touchstart', touchstart); 63 | } 64 | 65 | $responsiveBtn.on('click', toggleSideBar); 66 | 67 | $sidebarMask.on('click', function() { 68 | $responsiveBtn.trigger('click'); 69 | }); 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /public/libs/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/libs/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-apollo.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["com", /^#[^\n\r]*/, null, "#"], 3 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"], 4 | ["str", /^"(?:[^"\\]|\\[\S\s])*(?:"|$)/, null, '"'] 5 | ], [ 6 | ["kwd", /^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/, 7 | null], 8 | ["typ", /^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[ES]?BANK=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/, null], 9 | ["lit", /^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/], 10 | ["pln", /^-*(?:[!-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/], 11 | ["pun", /^[^\w\t\n\r "'-);\\\xa0]+/] 12 | ]), ["apollo", "agc", "aea"]); 13 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-clj.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | var a = null; 17 | PR.registerLangHandler(PR.createSimpleLexer([ 18 | ["opn", /^[([{]+/, a, "([{"], 19 | ["clo", /^[)\]}]+/, a, ")]}"], 20 | ["com", /^;[^\n\r]*/, a, ";"], 21 | ["pln", /^[\t\n\r \xa0]+/, a, "\t\n\r \xa0"], 22 | ["str", /^"(?:[^"\\]|\\[\S\s])*(?:"|$)/, a, '"'] 23 | ], [ 24 | ["kwd", /^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/, a], 25 | ["typ", /^:[\dA-Za-z-]+/] 26 | ]), ["clj"]); 27 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\f\r ]+/, null, " \t\r\n "] 3 | ], [ 4 | ["str", /^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/, null], 5 | ["str", /^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/, null], 6 | ["lang-css-str", /^url\(([^"')]*)\)/i], 7 | ["kwd", /^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i, null], 8 | ["lang-css-kw", /^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i], 9 | ["com", /^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//], 10 | ["com", 11 | /^(?:<\!--|--\>)/], 12 | ["lit", /^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i], 13 | ["lit", /^#[\da-f]{3,6}/i], 14 | ["pln", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i], 15 | ["pun", /^[^\s\w"']+/] 16 | ]), ["css"]); 17 | PR.registerLangHandler(PR.createSimpleLexer([], [ 18 | ["kwd", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i] 19 | ]), ["css-kw"]); 20 | PR.registerLangHandler(PR.createSimpleLexer([], [ 21 | ["str", /^[^"')]+/] 22 | ]), ["css-str"]); 23 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-go.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"], 3 | ["pln", /^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/, null, "\"'"] 4 | ], [ 5 | ["com", /^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/], 6 | ["pln", /^(?:[^"'/`]|\/(?![*/]))+/] 7 | ]), ["go"]); 8 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-hs.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t-\r ]+/, null, "\t\n \r "], 3 | ["str", /^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/, null, '"'], 4 | ["str", /^'(?:[^\n\f\r'\\]|\\[^&])'?/, null, "'"], 5 | ["lit", /^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i, null, "0123456789"] 6 | ], [ 7 | ["com", /^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/], 8 | ["kwd", /^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/, 9 | null], 10 | ["pln", /^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/], 11 | ["pun", /^[^\d\t-\r "'A-Za-z]+/] 12 | ]), ["hs"]); 13 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-lisp.js: -------------------------------------------------------------------------------- 1 | var a = null; 2 | PR.registerLangHandler(PR.createSimpleLexer([ 3 | ["opn", /^\(+/, a, "("], 4 | ["clo", /^\)+/, a, ")"], 5 | ["com", /^;[^\n\r]*/, a, ";"], 6 | ["pln", /^[\t\n\r \xa0]+/, a, "\t\n\r \xa0"], 7 | ["str", /^"(?:[^"\\]|\\[\S\s])*(?:"|$)/, a, '"'] 8 | ], [ 9 | ["kwd", /^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/, a], 10 | ["lit", /^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i], 11 | ["lit", /^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/], 12 | ["pln", /^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i], 13 | ["pun", /^[^\w\t\n\r "'-);\\\xa0]+/] 14 | ]), ["cl", "el", "lisp", "scm"]); 15 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-lua.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"], 3 | ["str", /^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/, null, "\"'"] 4 | ], [ 5 | ["com", /^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/], 6 | ["str", /^\[(=*)\[[\S\s]*?(?:]\1]|$)/], 7 | ["kwd", /^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/, null], 8 | ["lit", /^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i], 9 | ["pln", /^[_a-z]\w*/i], 10 | ["pun", /^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/] 11 | ]), ["lua"]); 12 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-ml.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"], 3 | ["com", /^#(?:if[\t\n\r \xa0]+(?:[$_a-z][\w']*|``[^\t\n\r`]*(?:``|$))|else|endif|light)/i, null, "#"], 4 | ["str", /^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])(?:'|$))/, null, "\"'"] 5 | ], [ 6 | ["com", /^(?:\/\/[^\n\r]*|\(\*[\S\s]*?\*\))/], 7 | ["kwd", /^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/], 8 | ["lit", /^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i], 9 | ["pln", /^(?:[_a-z][\w']*[!#?]?|``[^\t\n\r`]*(?:``|$))/i], 10 | ["pun", /^[^\w\t\n\r "'\xa0]+/] 11 | ]), ["fs", "ml"]); 12 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-n.js: -------------------------------------------------------------------------------- 1 | var a = null; 2 | PR.registerLangHandler(PR.createSimpleLexer([ 3 | ["str", /^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/, a, '"'], 4 | ["com", /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/, a, "#"], 5 | ["pln", /^\s+/, a, " \r\n\t\xa0"] 6 | ], [ 7 | ["str", /^@"(?:[^"]|"")*(?:"|$)/, a], 8 | ["str", /^<#[^#>]*(?:#>|$)/, a], 9 | ["str", /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/, a], 10 | ["com", /^\/\/[^\n\r]*/, a], 11 | ["com", /^\/\*[\S\s]*?(?:\*\/|$)/, 12 | a], 13 | ["kwd", /^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/, 14 | a], 15 | ["typ", /^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/, a], 16 | ["lit", /^@[$_a-z][\w$@]*/i, a], 17 | ["typ", /^@[A-Z]+[a-z][\w$@]*/, a], 18 | ["pln", /^'?[$_a-z][\w$@]*/i, a], 19 | ["lit", /^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i, a, "0123456789"], 20 | ["pun", /^.[^\s\w"-$'./@`]*/, a] 21 | ]), ["n", "nemerle"]); 22 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-proto.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.sourceDecorator({keywords: "bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true", types: /^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/, cStyleComments: !0}), ["proto"]); 2 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-scala.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"], 3 | ["str", /^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/, null, '"'], 4 | ["lit", /^`(?:[^\n\r\\`]|\\.)*`?/, null, "`"], 5 | ["pun", /^[!#%&(--:-@[-^{-~]+/, null, "!#%&()*+,-:;<=>?@[\\]^{|}~"] 6 | ], [ 7 | ["str", /^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/], 8 | ["lit", /^'[$A-Z_a-z][\w$]*(?![\w$'])/], 9 | ["kwd", /^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/], 10 | ["lit", /^(?:true|false|null|this)\b/], 11 | ["lit", /^(?:0(?:[0-7]+|x[\da-f]+)l?|(?:0|[1-9]\d*)(?:(?:\.\d+)?(?:e[+-]?\d+)?f?|l?)|\\.\d+(?:e[+-]?\d+)?f?)/i], 12 | ["typ", /^[$_]*[A-Z][\d$A-Z_]*[a-z][\w$]*/], 13 | ["pln", /^[$A-Z_a-z][\w$]*/], 14 | ["com", /^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/], 15 | ["pun", /^(?:\.+|\/)/] 16 | ]), ["scala"]); 17 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-sql.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"], 3 | ["str", /^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, null, "\"'"] 4 | ], [ 5 | ["com", /^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/], 6 | ["kwd", /^(?:add|all|alter|and|any|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|merge|national|nocheck|nonclustered|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|percent|plan|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rule|save|schema|select|session_user|set|setuser|shutdown|some|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|union|unique|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|writetext)(?=[^\w-]|$)/i, 7 | null], 8 | ["lit", /^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i], 9 | ["pln", /^[_a-z][\w-]*/i], 10 | ["pun", /^[^\w\t\n\r "'\xa0][^\w\t\n\r "'+\xa0-]*/] 11 | ]), ["sql"]); 12 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-tex.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"], 3 | ["com", /^%[^\n\r]*/, null, "%"] 4 | ], [ 5 | ["kwd", /^\\[@-Za-z]+/], 6 | ["kwd", /^\\./], 7 | ["typ", /^[$&]/], 8 | ["lit", /[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i], 9 | ["pun", /^[()=[\]{}]+/] 10 | ]), ["latex", "tex"]); 11 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-vb.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0\u2028\u2029]+/, null, "\t\n\r �\xa0

"], 3 | ["str", /^(?:["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})(?:["\u201c\u201d]c|$)|["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})*(?:["\u201c\u201d]|$))/i, null, '"“”'], 4 | ["com", /^['\u2018\u2019].*/, null, "'‘’"] 5 | ], [ 6 | ["kwd", /^(?:addhandler|addressof|alias|and|andalso|ansi|as|assembly|auto|boolean|byref|byte|byval|call|case|catch|cbool|cbyte|cchar|cdate|cdbl|cdec|char|cint|class|clng|cobj|const|cshort|csng|cstr|ctype|date|decimal|declare|default|delegate|dim|directcast|do|double|each|else|elseif|end|endif|enum|erase|error|event|exit|finally|for|friend|function|get|gettype|gosub|goto|handles|if|implements|imports|in|inherits|integer|interface|is|let|lib|like|long|loop|me|mod|module|mustinherit|mustoverride|mybase|myclass|namespace|new|next|not|notinheritable|notoverridable|object|on|option|optional|or|orelse|overloads|overridable|overrides|paramarray|preserve|private|property|protected|public|raiseevent|readonly|redim|removehandler|resume|return|select|set|shadows|shared|short|single|static|step|stop|string|structure|sub|synclock|then|throw|to|try|typeof|unicode|until|variant|wend|when|while|with|withevents|writeonly|xor|endif|gosub|let|variant|wend)\b/i, 7 | null], 8 | ["com", /^rem.*/i], 9 | ["lit", /^(?:true\b|false\b|nothing\b|\d+(?:e[+-]?\d+[dfr]?|[dfilrs])?|(?:&h[\da-f]+|&o[0-7]+)[ils]?|\d*\.\d+(?:e[+-]?\d+)?[dfr]?|#\s+(?:\d+[/-]\d+[/-]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:am|pm))?)?|\d+:\d+(?::\d+)?(\s*(?:am|pm))?)\s+#)/i], 10 | ["pln", /^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*])/i], 11 | ["pun", /^[^\w\t\n\r "'[\]\xa0\u2018\u2019\u201c\u201d\u2028\u2029]+/], 12 | ["pun", /^(?:\[|])/] 13 | ]), ["vb", "vbs"]); 14 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-vhdl.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\r \xa0]+/, null, "\t\n\r �\xa0"] 3 | ], [ 4 | ["str", /^(?:[box]?"(?:[^"]|"")*"|'.')/i], 5 | ["com", /^--[^\n\r]*/], 6 | ["kwd", /^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i, 7 | null], 8 | ["typ", /^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i, null], 9 | ["typ", /^'(?:active|ascending|base|delayed|driving|driving_value|event|high|image|instance_name|last_active|last_event|last_value|left|leftof|length|low|path_name|pos|pred|quiet|range|reverse_range|right|rightof|simple_name|stable|succ|transaction|val|value)(?=[^\w-]|$)/i, null], 10 | ["lit", /^\d+(?:_\d+)*(?:#[\w.\\]+#(?:[+-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:e[+-]?\d+(?:_\d+)*)?)/i], 11 | ["pln", /^(?:[a-z]\w*|\\[^\\]*\\)/i], 12 | ["pun", /^[^\w\t\n\r "'\xa0][^\w\t\n\r "'\xa0-]*/] 13 | ]), ["vhdl", "vhd"]); 14 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-wiki.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\d\t a-gi-z\xa0]+/, null, "\t �\xa0abcdefgijklmnopqrstuvwxyz0123456789"], 3 | ["pun", /^[*=[\]^~]+/, null, "=*~^[]"] 4 | ], [ 5 | ["lang-wiki.meta", /(?:^^|\r\n?|\n)(#[a-z]+)\b/], 6 | ["lit", /^[A-Z][a-z][\da-z]+[A-Z][a-z][^\W_]+\b/], 7 | ["lang-", /^{{{([\S\s]+?)}}}/], 8 | ["lang-", /^`([^\n\r`]+)`/], 9 | ["str", /^https?:\/\/[^\s#/?]*(?:\/[^\s#?]*)?(?:\?[^\s#]*)?(?:#\S*)?/i], 10 | ["pln", /^(?:\r\n|[\S\s])[^\n\r#*=A-[^`h{~]*/] 11 | ]), ["wiki"]); 12 | PR.registerLangHandler(PR.createSimpleLexer([ 13 | ["kwd", /^#[a-z]+/i, null, "#"] 14 | ], []), ["wiki.meta"]); 15 | -------------------------------------------------------------------------------- /public/libs/code-prettify/lang-yaml.js: -------------------------------------------------------------------------------- 1 | var a = null; 2 | PR.registerLangHandler(PR.createSimpleLexer([ 3 | ["pun", /^[:>?|]+/, a, ":|>?"], 4 | ["dec", /^%(?:YAML|TAG)[^\n\r#]+/, a, "%"], 5 | ["typ", /^&\S+/, a, "&"], 6 | ["typ", /^!\S*/, a, "!"], 7 | ["str", /^"(?:[^"\\]|\\.)*(?:"|$)/, a, '"'], 8 | ["str", /^'(?:[^']|'')*(?:'|$)/, a, "'"], 9 | ["com", /^#[^\n\r]*/, a, "#"], 10 | ["pln", /^\s+/, a, " \t\r\n"] 11 | ], [ 12 | ["dec", /^(?:---|\.\.\.)(?:[\n\r]|$)/], 13 | ["pun", /^-/], 14 | ["kwd", /^\w+:[\n\r ]/], 15 | ["pln", /^\w+/] 16 | ]), ["yaml", "yml"]); 17 | -------------------------------------------------------------------------------- /public/libs/code-prettify/prettify.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | 3 | /* SPAN elements with the classes below are added by prettyprint. */ 4 | .pln { 5 | color: #000 6 | } 7 | 8 | /* plain text */ 9 | 10 | @media screen { 11 | .str { 12 | color: #080 13 | } 14 | 15 | /* string content */ 16 | .kwd { 17 | color: #008 18 | } 19 | 20 | /* a keyword */ 21 | .com { 22 | color: #800 23 | } 24 | 25 | /* a comment */ 26 | .typ { 27 | color: #606 28 | } 29 | 30 | /* a type name */ 31 | .lit { 32 | color: #066 33 | } 34 | 35 | /* a literal value */ 36 | /* punctuation, lisp open bracket, lisp close bracket */ 37 | .pun, .opn, .clo { 38 | color: #660 39 | } 40 | 41 | .tag { 42 | color: #008 43 | } 44 | 45 | /* a markup tag name */ 46 | .atn { 47 | color: #606 48 | } 49 | 50 | /* a markup attribute name */ 51 | .atv { 52 | color: #080 53 | } 54 | 55 | /* a markup attribute value */ 56 | .dec, .var { 57 | color: #606 58 | } 59 | 60 | /* a declaration; a variable name */ 61 | .fun { 62 | color: red 63 | } 64 | 65 | /* a function name */ 66 | } 67 | 68 | /* Use higher contrast and text-weight for printable form. */ 69 | @media print, projection { 70 | .str { 71 | color: #060 72 | } 73 | 74 | .kwd { 75 | color: #006; 76 | font-weight: bold 77 | } 78 | 79 | .com { 80 | color: #600; 81 | font-style: italic 82 | } 83 | 84 | .typ { 85 | color: #404; 86 | font-weight: bold 87 | } 88 | 89 | .lit { 90 | color: #044 91 | } 92 | 93 | .pun, .opn, .clo { 94 | color: #440 95 | } 96 | 97 | .tag { 98 | color: #006; 99 | font-weight: bold 100 | } 101 | 102 | .atn { 103 | color: #404 104 | } 105 | 106 | .atv { 107 | color: #060 108 | } 109 | } 110 | 111 | /* Put a border around prettyprinted code snippets. */ 112 | pre.prettyprint { 113 | padding: 2px; 114 | border: 1px solid #888 115 | } 116 | 117 | /* Specify class=linenums on a pre to get line numbering */ 118 | ol.linenums { 119 | margin-top: 0; 120 | margin-bottom: 0 121 | } 122 | 123 | /* IE indents via margin-left */ 124 | li.L0, 125 | li.L1, 126 | li.L2, 127 | li.L3, 128 | li.L5, 129 | li.L6, 130 | li.L7, 131 | li.L8 { 132 | list-style-type: none 133 | } 134 | 135 | /* Alternate shading for lines */ 136 | li.L1, 137 | li.L3, 138 | li.L5, 139 | li.L7, 140 | li.L9 { 141 | background: #eee 142 | } -------------------------------------------------------------------------------- /public/libs/editor/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/editor/fonts/icomoon.eot -------------------------------------------------------------------------------- /public/libs/editor/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | This is a custom SVG font generated by IcoMoon. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 39 | 41 | 43 | 44 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /public/libs/editor/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/editor/fonts/icomoon.ttf -------------------------------------------------------------------------------- /public/libs/editor/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/editor/fonts/icomoon.woff -------------------------------------------------------------------------------- /public/libs/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /public/libs/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/libs/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/libs/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/libs/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/libs/webuploader/webuploader.css: -------------------------------------------------------------------------------- 1 | .webuploader-container { 2 | position: relative; 3 | } 4 | .webuploader-element-invisible { 5 | position: absolute !important; 6 | clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ 7 | clip: rect(1px,1px,1px,1px); 8 | } 9 | .webuploader-pick { 10 | position: relative; 11 | display: inline-block; 12 | cursor: pointer; 13 | background: #00b7ee; 14 | padding: 10px 15px; 15 | color: #fff; 16 | text-align: center; 17 | border-radius: 3px; 18 | overflow: hidden; 19 | } 20 | .webuploader-pick-hover { 21 | background: #00a2d4; 22 | } 23 | 24 | .webuploader-pick-disable { 25 | opacity: 0.6; 26 | pointer-events:none; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /public/stylesheets/common.css: -------------------------------------------------------------------------------- 1 | body, p, input, textarea { 2 | font-size: 14px; 3 | word-break: break-word; 4 | } 5 | 6 | textarea, input[type="text"], 7 | input[type="password"], 8 | input[type="datetime"], 9 | input[type="datetime-local"], 10 | input[type="date"], 11 | input[type="month"], 12 | input[type="time"], 13 | input[type="week"], 14 | input[type="number"], 15 | input[type="email"], 16 | input[type="url"], 17 | input[type="search"], 18 | input[type="tel"], 19 | input[type="color"], 20 | .uneditable-input { 21 | background: hsla(0, 0%, 0%, 0) 22 | } 23 | 24 | pre { 25 | background: #fee9cc; 26 | border: 1px dashed #ccc; 27 | line-height: 22px; 28 | } 29 | 30 | code { 31 | padding: 0; 32 | border: none; 33 | } 34 | 35 | p code { 36 | background: none; 37 | color: hsl(0, 0%, 50%); 38 | margin: 0 1px; 39 | padding: 1px 4px; 40 | border-radius: 1px; 41 | } 42 | 43 | div pre.prettyprint { 44 | font-size: 14px; 45 | border-radius: 0px; 46 | padding: 0 15px; 47 | border: none; 48 | margin: 20px -10px; 49 | border-width: 1px 0px; 50 | background: #f7f7f7; 51 | -o-tab-size: 4; 52 | -moz-tab-size: 4; 53 | tab-size: 4; 54 | } 55 | 56 | form { 57 | margin-bottom: 0; 58 | } 59 | 60 | textarea { 61 | margin-bottom: 0; 62 | } 63 | 64 | input, textarea { 65 | background: hsla(0, 0%, 0%, 0); 66 | } 67 | -------------------------------------------------------------------------------- /public/stylesheets/jquery.atwho.css: -------------------------------------------------------------------------------- 1 | .atwho-view { 2 | position:absolute; 3 | top: 0; 4 | left: 0; 5 | display: none; 6 | margin-top: 18px; 7 | background: white; 8 | color: black; 9 | border: 1px solid #DDD; 10 | border-radius: 3px; 11 | box-shadow: 0 0 5px rgba(0,0,0,0.1); 12 | min-width: 120px; 13 | z-index: 11110 !important; 14 | } 15 | 16 | .atwho-view .cur { 17 | background: #3366FF; 18 | color: white; 19 | } 20 | .atwho-view .cur small { 21 | color: white; 22 | } 23 | .atwho-view strong { 24 | color: #3366FF; 25 | } 26 | .atwho-view .cur strong { 27 | color: white; 28 | font:bold; 29 | } 30 | .atwho-view ul { 31 | /* width: 100px; */ 32 | list-style:none; 33 | padding:0; 34 | margin:auto; 35 | } 36 | .atwho-view ul li { 37 | display: block; 38 | padding: 5px 10px; 39 | border-bottom: 1px solid #DDD; 40 | cursor: pointer; 41 | /* border-top: 1px solid #C8C8C8; */ 42 | } 43 | .atwho-view small { 44 | font-size: smaller; 45 | color: #777; 46 | font-weight: normal; 47 | } 48 | -------------------------------------------------------------------------------- /public/stylesheets/responsive.css: -------------------------------------------------------------------------------- 1 | @-ms-viewport { 2 | width: device-width; 3 | } 4 | 5 | #sidebar-mask { 6 | background-color: #333; 7 | width: 100%; 8 | height: 100%; 9 | filter: alpha(opacity=60); 10 | opacity: .6; 11 | z-index: 99; 12 | position: absolute; 13 | top: 0; 14 | left: 0; 15 | display: none; 16 | } 17 | 18 | @media (max-width: 400px) { 19 | .navbar .brand { 20 | float: none; 21 | margin: 0 auto; 22 | } 23 | 24 | .navbar .navbar-search { 25 | clear: both; 26 | margin: 0 auto; 27 | float: none; 28 | } 29 | 30 | .navbar .search-query { 31 | display: block; 32 | margin: 0 auto; 33 | } 34 | } 35 | 36 | @media (max-width: 979px) { 37 | 38 | .navbar { 39 | margin: 0 5px; 40 | z-index: 999; 41 | width: auto !important; 42 | } 43 | 44 | .navbar .container, #main, 45 | #content, #footer_main { 46 | width: 100%; 47 | min-width: 0; 48 | } 49 | 50 | .navbar .nav.pull-right { 51 | float: none; 52 | clear: both; 53 | } 54 | 55 | #responsive-sidebar-trigger { 56 | display: none; 57 | } 58 | 59 | #main { 60 | /*overflow: hidden;*/ 61 | margin: 20px auto; 62 | min-height: 0; 63 | } 64 | 65 | #content .panel { 66 | margin: 0 5px; 67 | } 68 | 69 | #sidebar { 70 | float: none; 71 | position: absolute; 72 | right: -100%; 73 | top: 0; 74 | background-color: #fff; 75 | z-index: 999; 76 | border: 5px solid #ccc; 77 | border-right: 0; 78 | -webkit-transition: .3s right; 79 | -moz-transition: .3s right; 80 | -ms-transition: .3s right; 81 | -o-transition: .3s right; 82 | transition: .3s right; 83 | display: none; 84 | } 85 | 86 | #content .topic_title { 87 | font-size: 1em; 88 | width: 100%; 89 | } 90 | 91 | #content .last_time { 92 | position: absolute; 93 | bottom: 0; 94 | right: 10px; 95 | font-size: .8em; 96 | } 97 | 98 | #content .last_time img { 99 | display: none; 100 | } 101 | 102 | #content .reply_count { 103 | position: absolute; 104 | bottom: 0; 105 | left: 85px; 106 | text-align: left; 107 | line-height: 2em; 108 | font-size: 10px; 109 | } 110 | 111 | .topic_title_wrapper { 112 | padding-left: 40px; 113 | } 114 | 115 | #main .topic_content p a.content_img, 116 | #main .reply_content p a.content_img { 117 | width: 100%; 118 | } 119 | 120 | #footer { 121 | margin: 0 5px 5px; 122 | } 123 | 124 | #footer_main { 125 | display: none; 126 | } 127 | 128 | #backtotop { 129 | background-color: #f5f5f5; 130 | border: 1px solid #ccc; 131 | border-right: 0; 132 | } 133 | 134 | .form-horizontal .control-label { 135 | float: none; 136 | width: auto; 137 | padding-top: 0; 138 | text-align: left; 139 | } 140 | 141 | .form-horizontal .controls { 142 | margin-left: 0; 143 | } 144 | 145 | .form-horizontal .control-list { 146 | padding-top: 0; 147 | } 148 | 149 | .form-horizontal .form-actions { 150 | padding-right: 10px; 151 | padding-left: 10px; 152 | } 153 | 154 | #content .reply_content { 155 | clear: both; 156 | padding-left: 0; 157 | padding-top: 5px; 158 | } 159 | 160 | #content .action { 161 | display: none; 162 | } 163 | 164 | .user_profile { 165 | margin-top: 0; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /public/upload/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/public/upload/.gitkeep -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-contrib/cors" 5 | "github.com/gin-gonic/gin" 6 | "github.com/tommy351/gin-sessions" 7 | "go_cnode/controllers/reply" 8 | "go_cnode/controllers/sign" 9 | "go_cnode/controllers/site" 10 | "go_cnode/controllers/topic" 11 | "go_cnode/controllers/user" 12 | "html/template" 13 | "net/http" 14 | // _ "net/http/pprof" 15 | // "log" 16 | ) 17 | 18 | func add(left int, right int) int { 19 | return left + right 20 | } 21 | func InitRouter() *gin.Engine { 22 | router := gin.Default() 23 | router.Use(cors.Default()) 24 | store := sessions.NewCookieStore([]byte("secret123")) 25 | router.Use(sessions.Middleware("my_session", store)) 26 | //router.Delims("([{", "}])")//模板函数隔离符 27 | router.SetFuncMap(template.FuncMap{"add": add}) 28 | router.LoadHTMLGlob("views/**/*") 29 | router.StaticFS("/public", http.Dir("./public")) 30 | router.StaticFile("/favicon.ico", "./public/images/cnode_icon_32.png") 31 | router.StaticFile("/19ITP3EmsT.txt", "./public/19ITP3EmsT.txt")//小程序 32 | router.GET("/", site.Index) 33 | router.GET("/about", site.About) 34 | router.GET("/signup", sign.ShowSignup) 35 | router.POST("/signup", sign.Signup) 36 | router.POST("/signout", sign.Signout) 37 | router.GET("/signin", sign.Signin) 38 | router.GET("/passport/github", sign.GithubSignup) 39 | router.GET("/github/callback", sign.GithubCallBack) 40 | router.GET("/setting", sign.Setting) 41 | router.GET("/my/messages", sign.Message) 42 | 43 | router.POST("/passport/local", sign.Login) 44 | router.GET("/search_pass", sign.SearchPass) 45 | router.GET("/api", site.Api) 46 | router.GET("/getstart", site.Getstart) 47 | router.GET("/topic/:id", topic.Index) 48 | router.GET("/topic/:id/top", topic.Top) 49 | router.GET("/topic/:id/edit", topic.ShowEdit) 50 | router.POST("/edit/topic/:id", topic.Update) 51 | router.POST("/delete/topic/:id", topic.Detele) 52 | router.GET("/topics/create", topic.ShowCreate) 53 | router.POST("/topic/create", topic.Create) 54 | router.GET("/active_account", sign.ActiveAccount) // 帐号激活 55 | router.POST("/reply/:topic_id", reply.Add) 56 | router.POST("/edit/reply/:reply_id", reply.Edit) 57 | router.POST("/delete/reply/:reply_id", reply.Delete) 58 | router.GET("/edit/reply/:reply_id", reply.ShowEdit) 59 | router.POST("/upload", topic.Upload) // 上传图片 60 | router.GET("/user/:name",user.Index)//个人主页 61 | router.GET("/user/:name/topics",user.Topics)// 62 | router.GET("/user/:name/replies",user.Replies) 63 | router.GET("/users/top100",user.Top100) 64 | // go func() { 65 | // log.Println(http.ListenAndServe("localhost:10000", nil)) 66 | // }() 67 | 68 | return router 69 | } 70 | -------------------------------------------------------------------------------- /service/cache/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/service/cache/.DS_Store -------------------------------------------------------------------------------- /service/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | //"log" 5 | db "go_cnode/database" 6 | "github.com/garyburd/redigo/redis" 7 | ) 8 | 9 | func Get(key string) (interface{}, error) { 10 | temp, err := redis.Bytes(db.Redis.Do("GET", key)) 11 | return temp, err 12 | } 13 | func Set(key string, data interface{}) error { 14 | _, err := redis.String(db.Redis.Do("SET", key, data)) 15 | 16 | return err 17 | } 18 | func SetEx(key string, data interface{}) error { 19 | _, err := redis.String(db.Redis.Do("SET", key, data, "EX", 60)) 20 | 21 | return err 22 | } 23 | -------------------------------------------------------------------------------- /service/mail/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangyanglim/go_cnode/9e4d845b743654c54e7abe7bdfaf37b97ed8381f/service/mail/.DS_Store -------------------------------------------------------------------------------- /service/mail/mail.go: -------------------------------------------------------------------------------- 1 | package mail 2 | 3 | import ( 4 | "fmt" 5 | //"log" 6 | //"net/http" 7 | "net/smtp" 8 | //"regexp" 9 | "strings" 10 | 11 | "go_cnode/mgoModels" 12 | //"github.com/gin-gonic/gin" 13 | //"github.com/tommy351/gin-sessions" 14 | "crypto/tls" 15 | 16 | "log" 17 | "net" 18 | "go_cnode/utils" 19 | ) 20 | 21 | var userModel = new(models.UserModel) 22 | 23 | func Dial(addr string) (*smtp.Client, error) { 24 | conn, err := tls.Dial("tcp", addr, nil) 25 | if err != nil { 26 | log.Println("Dialing Error:", err) 27 | return nil, err 28 | } 29 | 30 | host, _, _ := net.SplitHostPort(addr) 31 | return smtp.NewClient(conn, host) 32 | } 33 | 34 | func SendMailViaTLS(addr string, auth smtp.Auth, from string, 35 | to []string, msg []byte) (err error) { 36 | 37 | c, err := Dial(addr) 38 | if err != nil { 39 | log.Println("Create SMTP Client fail:", err) 40 | return err 41 | } 42 | defer c.Close() 43 | 44 | if auth != nil { 45 | if ok, _ := c.Extension("AUTH"); ok { 46 | if err = c.Auth(auth); err != nil { 47 | log.Println("AUTH ERROR ", err) 48 | return err 49 | } 50 | } 51 | } 52 | 53 | if err = c.Mail(from); err != nil { 54 | return err 55 | } 56 | 57 | for _, addr := range to { 58 | if err = c.Rcpt(addr); err != nil { 59 | return err 60 | } 61 | } 62 | 63 | w, err := c.Data() 64 | if err != nil { 65 | return err 66 | } 67 | 68 | _, err = w.Write(msg) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | err = w.Close() 74 | if err != nil { 75 | return err 76 | } 77 | 78 | return c.Quit() 79 | } 80 | 81 | func SendActiveMail(who string, token string, name string) { 82 | conf := utils.LoadConf() 83 | auth := smtp.PlainAuth("", conf.Smtp_username, conf.Smtp_password, conf.Smtp_hostname) 84 | to := []string{who} 85 | nickname := name 86 | user := conf.Smtp_username 87 | subject := "Go_Cnode社区账号激活" 88 | content_type := "Content-Type: text/html; charset=UTF-8" 89 | body := "

您好:" + name + "

" + 90 | "

我们收到您在Go_Cnode社区的注册信息,请点击下面的链接来激活帐户:

" + 91 | "激活链接" + 92 | "

若您没有在Go_Cnode社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。

" + 93 | "

Go_Cnode社区 谨上。

" 94 | msg := []byte("To: " + strings.Join(to, ",") + "\r\nFrom: " + nickname + 95 | "<" + user + ">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body) 96 | //err := smtp.SendMail("smtp.qq.com:465", auth, user, to, msg) 97 | err := SendMailViaTLS( 98 | conf.Smtp_hostname+":465", 99 | auth, 100 | user, 101 | to, 102 | msg, 103 | ) 104 | if err != nil { 105 | fmt.Printf("send mail error: %v", err) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /utils/dysms.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha1" 6 | "encoding/base64" 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "io/ioutil" 11 | "math/rand" 12 | "net/http" 13 | "net/url" 14 | "sort" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | // SendSmsReply 发送短信返回 20 | type SendSmsReply struct { 21 | Code string `json:"Code,omitempty"` 22 | Message string `json:"Message,omitempty"` 23 | } 24 | 25 | func replace(in string) string { 26 | rep := strings.NewReplacer("+", "%20", "*", "%2A", "%7E", "~") 27 | return rep.Replace(url.QueryEscape(in)) 28 | } 29 | 30 | // SendSms 发送短信 31 | func SendSms(accessKeyID, accessSecret, phoneNumbers, signName, templateParam, templateCode string) error { 32 | paras := map[string]string{ 33 | "SignatureMethod": "HMAC-SHA1", 34 | "SignatureNonce": fmt.Sprintf("%d", rand.Int63()), 35 | "AccessKeyId": accessKeyID, 36 | "SignatureVersion": "1.0", 37 | "Timestamp": time.Now().UTC().Format("2006-01-02T15:04:05Z"), 38 | "Format": "JSON", 39 | 40 | "Action": "SendSms", 41 | "Version": "2017-05-25", 42 | "RegionId": "cn-hangzhou", 43 | "PhoneNumbers": phoneNumbers, 44 | "SignName": signName, 45 | "TemplateParam": templateParam, 46 | "TemplateCode": templateCode, 47 | } 48 | 49 | var keys []string 50 | 51 | for k := range paras { 52 | keys = append(keys, k) 53 | } 54 | 55 | sort.Strings(keys) 56 | 57 | var sortQueryString string 58 | 59 | for _, v := range keys { 60 | sortQueryString = fmt.Sprintf("%s&%s=%s", sortQueryString, replace(v), replace(paras[v])) 61 | } 62 | 63 | stringToSign := fmt.Sprintf("GET&%s&%s", replace("/"), replace(sortQueryString[1:])) 64 | 65 | mac := hmac.New(sha1.New, []byte(fmt.Sprintf("%s&", accessSecret))) 66 | mac.Write([]byte(stringToSign)) 67 | sign := replace(base64.StdEncoding.EncodeToString(mac.Sum(nil))) 68 | 69 | str := fmt.Sprintf("http://dysmsapi.aliyuncs.com/?Signature=%s%s", sign, sortQueryString) 70 | 71 | resp, err := http.Get(str) 72 | if err != nil { 73 | return err 74 | } 75 | defer resp.Body.Close() 76 | body, err := ioutil.ReadAll(resp.Body) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | ssr := &SendSmsReply{} 82 | 83 | if err := json.Unmarshal(body, ssr); err != nil { 84 | return err 85 | } 86 | 87 | if ssr.Code == "SignatureNonceUsed" { 88 | return SendSms(accessKeyID, accessSecret, phoneNumbers, signName, templateParam, templateCode) 89 | } else if ssr.Code != "OK" { 90 | return errors.New(ssr.Code) 91 | } 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /utils/loadConf.go: -------------------------------------------------------------------------------- 1 | package utils 2 | import( 3 | "os" 4 | "encoding/json" 5 | ) 6 | 7 | type configuration struct { 8 | Port string 9 | Mongo_url string 10 | Redis_url string 11 | Smtp_username string 12 | Smtp_password string 13 | Smtp_hostname string 14 | Smtp_active_Url string 15 | Github_client_id string 16 | Github_client_secret string 17 | Github_AuthURL string 18 | Github_UserURL string 19 | Github_TokenURL string 20 | } 21 | func LoadConf() (conf configuration) { 22 | // 打开文件 23 | file, _ := os.Open("conf.json") 24 | 25 | // 关闭文件 26 | defer file.Close() 27 | 28 | //NewDecoder创建一个从file读取并解码json对象的*Decoder,解码器有自己的缓冲,并可能超前读取部分json数据。 29 | decoder := json.NewDecoder(file) 30 | 31 | //Decode从输入流读取下一个json编码值并保存在v指向的值里 32 | decoder.Decode(&conf) 33 | return conf 34 | } -------------------------------------------------------------------------------- /views/about/about.html: -------------------------------------------------------------------------------- 1 | {{ define "about" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 |
15 |
16 | {{.about}} 17 |
18 |
19 |
20 |
21 |
22 |
23 | {{ template "common/footer" .}} 24 | {{ end }} 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /views/api/api.html: -------------------------------------------------------------------------------- 1 | {{ define "api" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 |
15 |
16 | {{.api}} 17 |
18 |
19 |
20 |
21 |
22 |
23 | {{ template "common/footer" .}} 24 | {{ end }} 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /views/common/_sponsors.html: -------------------------------------------------------------------------------- 1 | {{ define "common/_sponsors" }} 2 |
3 |

CNode 社区为国内最专业的 Node.js 开源技术社区,致力于 Node.js 的技术研究。

4 |

服务器搭建在 5 | 10 | ,存储赞助商为 11 | 16 |

17 |

新手搭建 Node.js 服务器,推荐使用无需备案的 DigitalOcean(https://www.digitalocean.com/)

18 |
19 | {{end}} -------------------------------------------------------------------------------- /views/common/card.html: -------------------------------------------------------------------------------- 1 | {{define "common/card"}} 2 | 3 | 4 |
5 |
6 | 7 | 8 | 9 | {{.user.Loginname}} 10 |
11 |
12 | 积分: {{.user.Score }} 13 |
14 |
15 |
16 | 17 | 18 | 19 | “ 这家伙很懒,什么个性签名都没有留下。” 20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 | {{end}} -------------------------------------------------------------------------------- /views/common/footer.html: -------------------------------------------------------------------------------- 1 | {{ define "common/footer" }} 2 | 12 | 13 | 14 | 15 | {{end}} -------------------------------------------------------------------------------- /views/common/header.html: -------------------------------------------------------------------------------- 1 | {{ define "common/header" }} 2 | 3 | 4 | 5 | CNode:Node.js专业中文社区 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 46 | 47 | 48 | 92 | {{end}} 93 | -------------------------------------------------------------------------------- /views/common/sidebar.html: -------------------------------------------------------------------------------- 1 | {{ define "common/sidebar" }} 2 | 139 | {{end}} 140 | -------------------------------------------------------------------------------- /views/common/sidebar2.html: -------------------------------------------------------------------------------- 1 | {{ define "common/sidebar2" }} 2 | 20 | {{end}} -------------------------------------------------------------------------------- /views/edit/edit.html: -------------------------------------------------------------------------------- 1 | {{ define "edit" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 | {{$edit:="edit"}} 6 |
7 |
8 |
9 | 18 |
19 |
20 | {{if .edit_error }} 21 |
22 | × 23 | {{ .edit_error }} 24 |
25 | {{end}} 26 | {{ if .error}} 27 |
28 | {{.error }} 29 |
30 | {{else}} 31 | {{if .action }} 32 |
33 | {{else}} 34 | 35 | {{end}} 36 |
37 | 选择版块: 38 | 62 | 63 | 66 | 67 |
68 |
69 | 72 | 73 |
74 | 76 |
77 |
78 | 79 |
80 | 81 | 82 | 83 |
84 |
85 |
86 | {{end}} 87 |
88 |
89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 132 | {{ template "common/footer" .}} 133 | {{ end }} 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /views/getStart/getstart.html: -------------------------------------------------------------------------------- 1 | {{ define "getstart" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 |
15 |
16 | {{.getstart}} 17 |
18 |
19 |
20 |
21 |
22 |
23 | {{ template "common/footer" .}} 24 | {{ end }} 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /views/index/abstract.html: -------------------------------------------------------------------------------- 1 | {{define "abstract"}} 2 | {{range $i,$v:=.}} 3 |
4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | {{$v.topic.reply_count}} 14 | 15 | / 16 | 17 | {{$v.topic.visit_count}} 18 | 19 | 20 | {{if not $v.topic.reply_count }} 21 | 22 | {{$v.topic.Create_at}} 23 | 24 | {{else}} 25 | 26 | 27 | {{$v.topic.last_reply_at}} 28 | 29 | {{end}} 30 |
31 | 32 | {{if or $v.topic.top $v.topic.good}} 33 | {{if $v.topic.top}} 34 | 置顶 35 | {{end}} 36 | {{ if $v.topic.tood}} 37 | 精华 38 | {{end}} 39 | {{else}} 40 | {{ if $v.topic.tab}} 41 | {{if eq $v.topic.tab "share"}} 42 | 分享 43 | {{end}} 44 | {{if eq $v.topic.tab "ask"}} 45 | 问答 46 | {{end}} 47 | {{if eq $v.topic.tab "job"}} 48 | 招聘 49 | {{end}} 50 | {{end}} 51 | {{end}} 52 | 53 | 54 | {{$v.topic.title}} 55 | 56 |
57 |
58 | {{end}} 59 | {{end}} -------------------------------------------------------------------------------- /views/index/index.html: -------------------------------------------------------------------------------- 1 | {{ define "index" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "main" .}} 5 |
6 | {{ template "common/footer" .}} 7 | {{ end }} -------------------------------------------------------------------------------- /views/index/main.html: -------------------------------------------------------------------------------- 1 | 2 | {{ define "main" }} 3 | 4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 9 | {{ if eq .tab "all"}} 10 | 全部 11 | {{else}} 12 | 全部 13 | {{end}} 14 | {{ if eq .tab "good"}} 15 | 精华 16 | {{else}} 17 | 精华 18 | {{end}} 19 | {{ if eq .tab "share"}} 20 | 分享 21 | {{else}} 22 | 分享 23 | {{end}} 24 | {{ if eq .tab "ask"}} 25 | 问答 26 | {{else}} 27 | 问答 28 | {{end}} 29 | {{ if eq .tab "job"}} 30 | 招聘 31 | {{else}} 32 | 招聘 33 | {{end}} 34 |
35 | {{ if .topics}} 36 |
37 | 43 | {{ template "topic_list" .}} 44 |
45 | {{else}} 46 |
47 |

无话题

48 |
49 | {{end}} 50 |
51 |
52 | {{end}} 53 | -------------------------------------------------------------------------------- /views/index/topic_list.html: -------------------------------------------------------------------------------- 1 | {{ define "topic_list" }} 2 |
3 | 6 | 7 | {{ template "abstract" .topicss}} 8 | 9 |
10 | 45 | 62 | {{end}} 63 | -------------------------------------------------------------------------------- /views/message/message.html: -------------------------------------------------------------------------------- 1 | {{ define "message" }} 2 | {{if .Has_read}} 3 |
4 | {{else}} 5 |
6 | {{end}} 7 | {{ if eq .Type "reply"}} 8 | 9 | {{.Author.Loginname}} 10 | 回复了你的话题 11 | 12 | {{.Topic.Title}} 13 | 14 | {{end}} 15 | {{ if eq .Type "reply2"}} 16 | 17 | {{.Author.Loginname}} 18 | 在话题 19 | {{.Topic.Title}} 20 | 中回复了你的回复 21 | 22 | {{end}} 23 | {{ if eq .Type "follow"}} 24 | 25 | {{.Author.Loginname}} 26 | 关注了你 27 | 28 | {{end}} 29 | {{if eq .Type "at"}} 30 | 31 | {{.Author.Loginname}} 32 | 在话题 33 | {{.Topic.Title}} 34 | 中@了你 35 | 36 | {{end}} 37 |
38 | {{ end }} -------------------------------------------------------------------------------- /views/message/message_index.html: -------------------------------------------------------------------------------- 1 | {{ define "message_index" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 | {{if .hasnot_read_messages}} 14 | {{range $i,$message:=.hasnot_read_messages}} 15 | {{ template "message" $message}} 16 | {{end}} 17 | 20 | {{else}} 21 |
22 |

无消息

23 |
24 | {{end}} 25 |
26 |
27 |
28 | 过往信息 29 |
30 | {{if .has_read_messages}} 31 | {{range $i,$message:=.has_read_messages}} 32 | {{ template "message" $message}} 33 | {{end}} 34 | {{else}} 35 |
36 |

无消息

37 |
38 | {{end}} 39 |
40 |
41 |
42 | {{ template "common/footer" .}} 43 | {{ end }} 44 | -------------------------------------------------------------------------------- /views/notify/notify.html: -------------------------------------------------------------------------------- 1 | {{ define "notify" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{if .error}} 15 |
16 | {{.error}} 17 |
18 | {{end}} 19 | {{if .success}} 20 |
21 | {{.success}} 22 |
23 | {{end}} 24 | 返回 25 |
26 |
27 |
28 | 29 |
30 | {{ template "common/footer" .}} 31 | {{ end }} 32 | 33 | 34 | -------------------------------------------------------------------------------- /views/searchPass/search_pass.html: -------------------------------------------------------------------------------- 1 | {{ define "search_pass" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{if .error}} 15 |
16 | × 17 | {{.error}} 18 |
19 | {{end}} 20 |
21 |
22 | 23 | 24 |
25 | {{if .email }} 26 | 27 | {{else}} 28 | 29 | {{end}} 30 |

请输入您注册帐户时使用的电子邮箱

31 |
32 | 33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 | {{ template "common/footer" .}} 43 | {{ end }} 44 | 45 | 46 | -------------------------------------------------------------------------------- /views/setting/setting.html: -------------------------------------------------------------------------------- 1 | {{ define "setting" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{if .us3er.error}} 15 |
16 | × 17 | {{.u3ser.error }} 18 |
19 | {{end}} 20 | {{ if .use4r.success}} 21 |
22 | {{ .us3er.success }} 23 |
24 | {{end}} 25 |
26 |
27 | 28 | 29 |
30 | 32 |
33 |
34 |
35 | 36 | 37 |
38 | 40 | 41 |

同时决定了 Gravatar 头像

42 |
43 |
44 |
45 | 46 | 47 |
48 | 50 |
51 |
52 |
53 | 54 | 55 |
56 | 58 |
59 |
60 | 61 |
62 | 63 | 64 |
65 | 68 |
69 |
70 |
71 | 72 | 73 |
74 | 77 |

请通过 GitHub 登陆 CNode 来修改此处

78 |
79 |
80 |
81 | 82 | 83 |
84 | 85 |
86 |
87 | 88 | 89 | 90 |
91 | 92 |
93 |
94 |
95 |
96 | 97 |
98 |
99 | 更改密码 100 |
101 |
102 |
103 |
104 | 105 | 106 |
107 | 108 |
109 |
110 |
111 | 112 | 113 |
114 | 115 |
116 |
117 | 118 | 119 | 120 |
121 | 122 |
123 |
124 |
125 |
126 | 127 |
128 |
129 | Access Token 130 |
131 |
132 |
133 | 字符串: 134 | {{.us3er.accessToken}} 135 |
136 |
137 | 二维码: 138 | 139 |
140 |
141 |
142 |
143 | 144 | 154 |
155 | {{ template "common/footer" .}} 156 | {{ end }} 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /views/showSignUp/showsignup.html: -------------------------------------------------------------------------------- 1 | {{ define "showsignup" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar2" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{ if .error }} 15 |
16 | × 17 | {{ .error }} 18 |
19 | {{else}} 20 | {{ if .success }} 21 |
22 | {{.success}} 23 |
24 | {{end}} 25 |
26 |
27 | 28 | 29 |
30 | {{ if .loginname }} 31 | 32 | {{else}} 33 | 34 | {{end}} 35 |
36 |
37 |
38 | 39 | 40 |
41 | 42 |
43 |
44 |
45 | 46 | 47 |
48 | 49 |
50 |
51 |
52 | 53 | 54 |
55 | {{if .email }} 56 | 57 | {{else}} 58 | 59 | {{end}} 60 |
61 |
62 | 63 | 64 | 72 |
73 | {{end}} 74 |
75 |
76 |
77 |
78 | {{ template "common/footer" .}} 79 | {{ end }} 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /views/signIn/signin.html: -------------------------------------------------------------------------------- 1 | {{ define "signin" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar2" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{if .error}} 15 |
16 | × 17 | {{.error }} 18 |
19 | {{end}} 20 |
21 |
22 | 23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 | 31 |
32 | 33 |
34 |
35 | 36 | 37 | 46 |
47 |
48 |
49 |
50 |
51 | {{ template "common/footer" .}} 52 | {{ end }} 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /views/signUp/signup.html: -------------------------------------------------------------------------------- 1 | {{ define "signup" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar2" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{ if .error}} 15 |
16 | × 17 | {{.error}} 18 |
19 | {{end}} 20 | {{if .success}} 21 |
22 | {{.success}} 23 |
24 | {{else}} 25 |
26 |
27 | 28 | 29 |
30 | {{ if .loginname}} 31 | 32 | {{else}} 33 | 34 | {{end}} 35 |
36 |
37 |
38 | 39 | 40 |
41 | 42 |
43 |
44 |
45 | 46 | 47 |
48 | 49 |
50 |
51 |
52 | 53 | 54 |
55 | {{if .email}} 56 | 57 | {{else}} 58 | 59 | {{end}} 60 |
61 |
62 | 63 | 64 | 72 |
73 | {{end}} 74 |
75 |
76 |
77 |
78 | {{ template "common/footer" .}} 79 | {{ end }} 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /views/topic/edit.html: -------------------------------------------------------------------------------- 1 | {{define "reply/edit"}} 2 | 3 | {{ template "common/header" .}} 4 | 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{if .edit_error}} 15 |
16 | × 17 | {{.edit_error }} 18 |
19 | {{else}} 20 |
21 |
22 |
23 |
24 | 28 | 29 |
30 | 32 |
33 |
34 | 35 |
36 | 37 | 38 |
39 |
40 | 41 | {{end}} 42 |
43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 56 | {{ template "common/footer" .}} 57 | {{ end }} -------------------------------------------------------------------------------- /views/topic/reply.html: -------------------------------------------------------------------------------- 1 | {{define "reply"}} 2 | {{range $i,$v:=.topic.RepliyWithAuthors}} 3 |
4 |
5 | 6 | 7 | 8 | 9 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | {{if $.user}} 24 | {{if eq $.user.Id.Hex $v.Author.Id.Hex}} 25 | 26 | 27 | 28 | 29 | 30 | 31 | {{end}} 32 | {{end}} 33 | 34 | {{if $.user}} 35 | 36 | {{end}} 37 | 38 |
39 |
40 |
41 | {{$v.LinkContent}} 42 |
43 |
44 |
45 | {{if $.user}} 46 |
47 | 48 | 49 | 50 |
51 |
52 | 54 | 55 |
56 | 58 |
59 |
60 | 61 |
62 | 63 |
64 | {{end}} 65 |
66 |
67 |
68 | {{end}} 69 | {{end}} 70 | -------------------------------------------------------------------------------- /views/user/index.html: -------------------------------------------------------------------------------- 1 | {{ define "userIndex" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 15 |
16 | 17 | {{if .user}} 18 |
19 |
20 | 21 |
22 | {{.user.Loginname}} 23 | 61 |

注册时间 {{.user.Create_at}}

62 | {{if eq .user.Name "admin"}} 63 | {{if not .user.Is_star}} 64 | 设为达人 65 | {{else}} 66 | 取消达人 67 | {{end}} 68 | 69 | {{if not .user.Is_block}} 70 | 屏蔽用户 71 | {{else}} 72 | 取消屏蔽用户 73 | {{end}} 74 | 75 | 删除所有发言

76 | 77 | Email (Seen by Administrator): {{.user.Email}} 78 | 79 | {{if not .user.Active}} 80 | 82 | 83 | 激活账号 84 | 85 | 86 | {{end}} 87 | {{end}} 88 |
89 | {{end}} 90 |
91 | {{if .user}} 92 |
93 |
94 | 最近创建的话题 95 |
96 | {{if .recent_topics}} 97 | {{ template "abstract" .recent_topics}} 98 |
99 | 查看更多» 100 |
101 | {{else}} 102 |
103 |

无话题

104 |
105 | {{end}} 106 |
107 |
108 |
109 | 最近参与的话题 110 |
111 | {{if .recent_replies}} 112 | {{ template "abstract" .recent_replies}} 113 |
114 | 查看更多» 115 |
116 | {{else}} 117 |
118 |

无话题

119 |
120 | {{end}} 121 |
122 | {{end}} 123 |
124 |
125 | 184 | {{ template "common/footer" .}} 185 | {{ end }} -------------------------------------------------------------------------------- /views/user/replies.html: -------------------------------------------------------------------------------- 1 | {{ define "userReplies" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 |
15 |
{{.user.Loginname}} 参与的话题
16 |
17 | {{if .topicss}} 18 | {{ template "topic_list" .}} 19 | {{else}} 20 |
21 |

无话题

22 |
23 | {{end}} 24 |
25 |
26 |
27 | {{ template "common/footer" .}} 28 | {{ end }} -------------------------------------------------------------------------------- /views/user/top100.html: -------------------------------------------------------------------------------- 1 | {{ define "userTop100" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 | {{if .users}} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {{range $i,$v:=.users}} 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | {{end}} 38 | 39 |
#用户名积分主题数评论数
{{$i}} 28 | 29 | 30 | 31 | 32 | {{$v.Loginname}}{{$v.Score}}{{$v.Topic_count}}{{$v.Reply_count}}
40 | {{else}} 41 |

还没有用户

42 | {{end}} 43 |
44 |
45 |
46 |
47 | {{ template "common/footer" .}} 48 | {{ end }} -------------------------------------------------------------------------------- /views/user/topics.html: -------------------------------------------------------------------------------- 1 | {{ define "userTopics" }} 2 | {{ template "common/header" .}} 3 |
4 | {{ template "common/sidebar" .}} 5 |
6 |
7 |
8 | 12 |
13 |
14 |
15 |
{{.user.Loginname}} 创建的话题
16 |
17 | {{if .topicss}} 18 | {{ template "topic_list" .}} 19 | {{else}} 20 |
21 |

无话题

22 |
23 | {{end}} 24 |
25 |
26 |
27 | {{ template "common/footer" .}} 28 | {{ end }} --------------------------------------------------------------------------------