message.go 9.5 KB

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