message.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. package controller
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/gin-gonic/gin"
  7. "message-pusher/channel"
  8. "message-pusher/common"
  9. "message-pusher/model"
  10. "net/http"
  11. "strconv"
  12. "strings"
  13. "time"
  14. )
  15. func keepCompatible(message *model.Message) {
  16. // Keep compatible with ServerChan: https://sct.ftqq.com/sendkey
  17. if message.Description == "" {
  18. message.Description = message.Short
  19. }
  20. if message.Content == "" {
  21. message.Content = message.Desp
  22. }
  23. if message.To == "" {
  24. message.To = message.OpenId
  25. }
  26. }
  27. func GetPushMessage(c *gin.Context) {
  28. message := model.Message{
  29. Title: c.Query("title"),
  30. Description: c.Query("description"),
  31. Content: c.Query("content"),
  32. URL: c.Query("url"),
  33. Channel: c.Query("channel"),
  34. Token: c.Query("token"),
  35. To: c.Query("to"),
  36. Desp: c.Query("desp"),
  37. Short: c.Query("short"),
  38. OpenId: c.Query("openid"),
  39. Async: c.Query("async") == "true",
  40. RenderMode: c.Query("render_mode"),
  41. }
  42. keepCompatible(&message)
  43. pushMessageHelper(c, &message)
  44. }
  45. func PostPushMessage(c *gin.Context) {
  46. var message model.Message
  47. if strings.Contains(strings.ToLower(c.Request.Header.Get("Content-Type")), "application/json") {
  48. // Looks like the user is using JSON
  49. message = model.Message{}
  50. err := json.NewDecoder(c.Request.Body).Decode(&message)
  51. if err != nil {
  52. c.JSON(http.StatusOK, gin.H{
  53. "success": false,
  54. "message": "无法解析请求体,请检查其是否为合法 JSON",
  55. })
  56. return
  57. }
  58. } else {
  59. message = model.Message{
  60. Title: c.PostForm("title"),
  61. Description: c.PostForm("description"),
  62. Content: c.PostForm("content"),
  63. URL: c.PostForm("url"),
  64. Channel: c.PostForm("channel"),
  65. Token: c.PostForm("token"),
  66. To: c.PostForm("to"),
  67. Desp: c.PostForm("desp"),
  68. Short: c.PostForm("short"),
  69. OpenId: c.PostForm("openid"),
  70. Async: c.PostForm("async") == "true",
  71. RenderMode: c.PostForm("render_mode"),
  72. }
  73. }
  74. if message == (model.Message{}) {
  75. c.JSON(http.StatusOK, gin.H{
  76. "success": false,
  77. "message": "请求体为空,如果使用 JSON 请设置 Content-Type 为 application/json,否则请使用表单提交",
  78. })
  79. return
  80. }
  81. if message.Token == "" {
  82. message.Token = c.Query("token")
  83. }
  84. keepCompatible(&message)
  85. pushMessageHelper(c, &message)
  86. }
  87. func pushMessageHelper(c *gin.Context, message *model.Message) {
  88. user := model.User{Username: c.Param("username")}
  89. err := user.FillUserByUsername()
  90. if err != nil {
  91. c.JSON(http.StatusOK, gin.H{
  92. "success": false,
  93. "message": err.Error(),
  94. })
  95. return
  96. }
  97. if user.Status == common.UserStatusNonExisted {
  98. c.JSON(http.StatusOK, gin.H{
  99. "success": false,
  100. "message": "用户不存在",
  101. })
  102. return
  103. }
  104. if user.Status == common.UserStatusDisabled {
  105. c.JSON(http.StatusOK, gin.H{
  106. "success": false,
  107. "message": "用户已被封禁",
  108. })
  109. return
  110. }
  111. if message.Token == "" {
  112. message.Token = strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer ")
  113. }
  114. processMessage(c, message, &user, true)
  115. }
  116. func authMessage(messageToken string, userToken string, channelToken *string) bool {
  117. if userToken != "" {
  118. if messageToken == userToken {
  119. return true
  120. }
  121. }
  122. if channelToken != nil && *channelToken != "" {
  123. if messageToken != *channelToken {
  124. return false
  125. }
  126. }
  127. return true
  128. }
  129. func processMessage(c *gin.Context, message *model.Message, user *model.User, needAuth bool) {
  130. if message.Title == "" {
  131. message.Title = common.SystemName
  132. }
  133. if message.Channel == "" {
  134. message.Channel = user.Channel
  135. if message.Channel == "" {
  136. message.Channel = model.TypeEmail
  137. }
  138. }
  139. channel_, err := model.GetChannelByName(message.Channel, user.Id)
  140. if err != nil {
  141. c.JSON(http.StatusOK, gin.H{
  142. "success": false,
  143. "message": "无效的渠道名称:" + message.Channel,
  144. })
  145. return
  146. }
  147. if needAuth && !authMessage(message.Token, user.Token, channel_.Token) {
  148. if message.Token == "" {
  149. c.JSON(http.StatusUnauthorized, gin.H{
  150. "success": false,
  151. "message": "通道维度或用户维度设置了鉴权令牌,需要提供鉴权令牌",
  152. })
  153. return
  154. }
  155. c.JSON(http.StatusUnauthorized, gin.H{
  156. "success": false,
  157. "message": "无效的 token",
  158. })
  159. return
  160. }
  161. if message.RenderMode == "code" {
  162. if message.Content != "" {
  163. message.Content = fmt.Sprintf("```\n%s\n```", message.Content)
  164. }
  165. }
  166. err = saveAndSendMessage(user, message, channel_)
  167. if err != nil {
  168. c.JSON(http.StatusOK, gin.H{
  169. "success": false,
  170. "message": err.Error(),
  171. })
  172. return
  173. }
  174. c.JSON(http.StatusOK, gin.H{
  175. "success": true,
  176. "message": "",
  177. "uuid": message.Link,
  178. })
  179. }
  180. func saveAndSendMessage(user *model.User, message *model.Message, channel_ *model.Channel) error {
  181. if channel_.Status != common.ChannelStatusEnabled {
  182. return errors.New("该渠道已被禁用")
  183. }
  184. common.MessageCount += 1 // We don't need to use atomic here because it's not a critical value
  185. message.Link = common.GetUUID()
  186. if message.URL == "" {
  187. message.URL = fmt.Sprintf("%s/message/%s", common.ServerAddress, message.Link)
  188. }
  189. success := false
  190. if common.MessagePersistenceEnabled || user.SaveMessageToDatabase == common.SaveMessageToDatabaseAllowed {
  191. defer func() {
  192. // Update the status of the message
  193. status := common.MessageSendStatusFailed
  194. if message.Async {
  195. status = common.MessageSendStatusAsyncPending
  196. } else {
  197. if success {
  198. status = common.MessageSendStatusSent
  199. }
  200. }
  201. err := message.UpdateStatus(status)
  202. if err != nil {
  203. common.SysError("failed to update the status of the message: " + err.Error())
  204. }
  205. if message.Async {
  206. channel.AsyncMessageQueue <- message.Id
  207. }
  208. }()
  209. err := message.UpdateAndInsert(user.Id)
  210. if err != nil {
  211. return err
  212. }
  213. go syncMessageToUser(message, user.Id)
  214. } else {
  215. if message.Async {
  216. return errors.New("异步发送消息需要用户具备消息持久化的权限")
  217. }
  218. message.Link = "unsaved" // This is for user to identify whether the message is saved
  219. go syncMessageToUser(message, user.Id)
  220. }
  221. if !message.Async {
  222. err := channel.SendMessage(message, user, channel_)
  223. if err != nil {
  224. return err
  225. }
  226. }
  227. success = true
  228. return nil // After this line, the message status will be updated
  229. }
  230. func GetStaticFile(c *gin.Context) {
  231. path := c.Param("file")
  232. c.FileFromFS("public/static/"+path, http.FS(common.FS))
  233. }
  234. func RenderMessage(c *gin.Context) {
  235. if !common.MessageRenderEnabled {
  236. c.HTML(http.StatusOK, "message.html", gin.H{
  237. "title": "无法渲染",
  238. "time": time.Now().Format("2006-01-02 15:04:05"),
  239. "description": "超级管理员禁用了消息渲染",
  240. "content": "很抱歉,您所使用的消息推送服务的管理员禁用了消息渲染功能,因此您的消息无法渲染。",
  241. })
  242. return
  243. }
  244. link := c.Param("link")
  245. if link == "unsaved" {
  246. c.HTML(http.StatusOK, "message.html", gin.H{
  247. "title": "无法渲染",
  248. "time": time.Now().Format("2006-01-02 15:04:05"),
  249. "description": "超级管理员禁用了消息持久化",
  250. "content": "很抱歉,您所使用的消息推送服务的管理员禁用了消息持久化功能,您的消息并没有存储到数据库中,因此无法渲染。",
  251. })
  252. return
  253. }
  254. message, err := model.GetMessageByLink(link)
  255. if err != nil {
  256. c.Status(http.StatusNotFound)
  257. return
  258. }
  259. if message.RenderMode != "raw" {
  260. if message.Description != "" {
  261. message.Description, err = common.Markdown2HTML(message.Description)
  262. if err != nil {
  263. common.SysLog(err.Error())
  264. }
  265. }
  266. if message.Content != "" {
  267. message.HTMLContent, err = common.Markdown2HTML(message.Content)
  268. if err != nil {
  269. common.SysLog(err.Error())
  270. }
  271. }
  272. }
  273. c.HTML(http.StatusOK, "message.html", gin.H{
  274. "title": message.Title,
  275. "time": time.Unix(message.Timestamp, 0).Format("2006-01-02 15:04:05"),
  276. "description": message.Description,
  277. "content": message.HTMLContent,
  278. })
  279. return
  280. }
  281. func GetUserMessages(c *gin.Context) {
  282. userId := c.GetInt("id")
  283. p, _ := strconv.Atoi(c.Query("p"))
  284. if p < 0 {
  285. p = 0
  286. }
  287. messages, err := model.GetMessagesByUserId(userId, p*common.ItemsPerPage, common.ItemsPerPage)
  288. if err != nil {
  289. c.JSON(http.StatusOK, gin.H{
  290. "success": false,
  291. "message": err.Error(),
  292. })
  293. return
  294. }
  295. c.JSON(http.StatusOK, gin.H{
  296. "success": true,
  297. "message": "",
  298. "data": messages,
  299. })
  300. return
  301. }
  302. func GetMessage(c *gin.Context) {
  303. messageId, _ := strconv.Atoi(c.Param("id"))
  304. userId := c.GetInt("id")
  305. message, err := model.GetMessageByIds(messageId, userId)
  306. if err != nil {
  307. c.JSON(http.StatusOK, gin.H{
  308. "success": false,
  309. "message": err.Error(),
  310. })
  311. return
  312. }
  313. c.JSON(http.StatusOK, gin.H{
  314. "success": true,
  315. "message": "",
  316. "data": message,
  317. })
  318. return
  319. }
  320. func GetMessageStatus(c *gin.Context) {
  321. link := c.Param("link")
  322. status, err := model.GetMessageStatusByLink(link)
  323. if err != nil {
  324. c.JSON(http.StatusOK, gin.H{
  325. "success": false,
  326. "message": err.Error(),
  327. })
  328. return
  329. }
  330. c.JSON(http.StatusOK, gin.H{
  331. "success": true,
  332. "message": "",
  333. "status": status,
  334. })
  335. return
  336. }
  337. func SearchMessages(c *gin.Context) {
  338. keyword := c.Query("keyword")
  339. messages, err := model.SearchMessages(keyword)
  340. if err != nil {
  341. c.JSON(http.StatusOK, gin.H{
  342. "success": false,
  343. "message": err.Error(),
  344. })
  345. return
  346. }
  347. c.JSON(http.StatusOK, gin.H{
  348. "success": true,
  349. "message": "",
  350. "data": messages,
  351. })
  352. return
  353. }
  354. func ResendMessage(c *gin.Context) {
  355. messageId, _ := strconv.Atoi(c.Param("id"))
  356. userId := c.GetInt("id")
  357. helper := func() error {
  358. message, err := model.GetMessageByIds(messageId, userId)
  359. message.Id = 0
  360. if err != nil {
  361. return err
  362. }
  363. user, err := model.GetUserById(userId, true)
  364. if err != nil {
  365. return err
  366. }
  367. channel_, err := model.GetChannelByName(message.Channel, user.Id)
  368. if err != nil {
  369. return err
  370. }
  371. err = saveAndSendMessage(user, message, channel_)
  372. if err != nil {
  373. return err
  374. }
  375. return nil
  376. }
  377. err := helper()
  378. if err != nil {
  379. c.JSON(http.StatusOK, gin.H{
  380. "success": false,
  381. "message": err.Error(),
  382. })
  383. return
  384. }
  385. c.JSON(http.StatusOK, gin.H{
  386. "success": true,
  387. "message": "",
  388. })
  389. return
  390. }
  391. func DeleteMessage(c *gin.Context) {
  392. messageId, _ := strconv.Atoi(c.Param("id"))
  393. userId := c.GetInt("id")
  394. err := model.DeleteMessageById(messageId, userId)
  395. if err != nil {
  396. c.JSON(http.StatusOK, gin.H{
  397. "success": false,
  398. "message": err.Error(),
  399. })
  400. return
  401. }
  402. c.JSON(http.StatusOK, gin.H{
  403. "success": true,
  404. "message": "",
  405. })
  406. return
  407. }
  408. func DeleteAllMessages(c *gin.Context) {
  409. err := model.DeleteAllMessages()
  410. if err != nil {
  411. c.JSON(http.StatusOK, gin.H{
  412. "success": false,
  413. "message": err.Error(),
  414. })
  415. return
  416. }
  417. c.JSON(http.StatusOK, gin.H{
  418. "success": true,
  419. "message": "",
  420. })
  421. return
  422. }