From d26e57876294b7a0dabcbc4112854de73f12e42a Mon Sep 17 00:00:00 2001 From: JustSong Date: Thu, 22 Dec 2022 17:59:12 +0800 Subject: [PATCH] feat: save messages to database (close #37) --- channel/bark.go | 2 +- channel/client.go | 12 +- channel/corp.go | 2 +- channel/ding.go | 2 +- channel/email.go | 2 +- channel/lark.go | 2 +- channel/main.go | 16 +- channel/telegram.go | 2 +- channel/wechat-corp-account.go | 5 +- channel/wechat-test-account.go | 4 +- common/public/message.html | 31 +++ common/public/static/app.css | 399 +++++++++++++++++++++++++++++++++ common/template.go | 17 ++ controller/message.go | 129 ++++++++++- main.go | 1 + model/main.go | 4 + model/message.go | 76 +++++++ router/api-router.go | 7 + router/web-router.go | 3 + 19 files changed, 682 insertions(+), 34 deletions(-) create mode 100644 common/public/message.html create mode 100644 common/public/static/app.css create mode 100644 common/template.go create mode 100644 model/message.go diff --git a/channel/bark.go b/channel/bark.go index 9716894..e2264a8 100644 --- a/channel/bark.go +++ b/channel/bark.go @@ -13,7 +13,7 @@ type barkMessageResponse struct { Message string `json:"message"` } -func SendBarkMessage(message *Message, user *model.User) error { +func SendBarkMessage(message *model.Message, user *model.User) error { if user.BarkServer == "" || user.BarkSecret == "" { return errors.New("未配置 Bark 消息推送方式") } diff --git a/channel/client.go b/channel/client.go index 62524eb..b59b576 100644 --- a/channel/client.go +++ b/channel/client.go @@ -19,7 +19,7 @@ const ( type webSocketClient struct { userId int conn *websocket.Conn - message chan *Message + message chan *model.Message pong chan bool stop chan bool timestamp int64 @@ -98,7 +98,7 @@ func (c *webSocketClient) handleDataWriting() { } } -func (c *webSocketClient) sendMessage(message *Message) { +func (c *webSocketClient) sendMessage(message *model.Message) { c.message <- message } @@ -122,21 +122,21 @@ func RegisterClient(userId int, conn *websocket.Conn) { oldClient, existed := clientMap[userId] clientConnMapMutex.Unlock() if existed { - byeMessage := &Message{ + byeMessage := &model.Message{ Title: common.SystemName, Description: "其他客户端已连接服务器,本客户端已被挤下线!", } oldClient.sendMessage(byeMessage) oldClient.close() } - helloMessage := &Message{ + helloMessage := &model.Message{ Title: common.SystemName, Description: "客户端连接成功!", } newClient := &webSocketClient{ userId: userId, conn: conn, - message: make(chan *Message), + message: make(chan *model.Message), pong: make(chan bool), stop: make(chan bool), timestamp: time.Now().UnixMilli(), @@ -149,7 +149,7 @@ func RegisterClient(userId int, conn *websocket.Conn) { clientConnMapMutex.Unlock() } -func SendClientMessage(message *Message, user *model.User) error { +func SendClientMessage(message *model.Message, user *model.User) error { if user.ClientSecret == "" { return errors.New("未配置 WebSocket 客户端消息推送方式") } diff --git a/channel/corp.go b/channel/corp.go index d0ba935..58f298a 100644 --- a/channel/corp.go +++ b/channel/corp.go @@ -24,7 +24,7 @@ type corpMessageResponse struct { Message string `json:"errmsg"` } -func SendCorpMessage(message *Message, user *model.User) error { +func SendCorpMessage(message *model.Message, user *model.User) error { if user.CorpWebhookURL == "" { return errors.New("未配置企业微信群机器人消息推送方式") } diff --git a/channel/ding.go b/channel/ding.go index fbea720..7436030 100644 --- a/channel/ding.go +++ b/channel/ding.go @@ -30,7 +30,7 @@ type dingMessageResponse struct { Message string `json:"errmsg"` } -func SendDingMessage(message *Message, user *model.User) error { +func SendDingMessage(message *model.Message, user *model.User) error { if user.DingWebhookURL == "" { return errors.New("未配置钉钉群机器人消息推送方式") } diff --git a/channel/email.go b/channel/email.go index 3bb95e9..a47ba16 100644 --- a/channel/email.go +++ b/channel/email.go @@ -8,7 +8,7 @@ import ( "message-pusher/model" ) -func SendEmailMessage(message *Message, user *model.User) error { +func SendEmailMessage(message *model.Message, user *model.User) error { if user.Email == "" { return errors.New("未配置邮箱地址") } diff --git a/channel/lark.go b/channel/lark.go index 7cbf720..b582463 100644 --- a/channel/lark.go +++ b/channel/lark.go @@ -45,7 +45,7 @@ type larkMessageResponse struct { Message string `json:"msg"` } -func SendLarkMessage(message *Message, user *model.User) error { +func SendLarkMessage(message *model.Message, user *model.User) error { if user.LarkWebhookURL == "" { return errors.New("未配置飞书群机器人消息推送方式") } diff --git a/channel/main.go b/channel/main.go index dcad14c..bab9673 100644 --- a/channel/main.go +++ b/channel/main.go @@ -15,20 +15,10 @@ const ( TypeTelegram = "telegram" TypeBark = "bark" TypeClient = "client" + TypeNone = "none" ) -type Message struct { - Title string `json:"title"` - Description string `json:"description"` - Desp string `json:"desp"` // alias for description - Content string `json:"content"` - URL string `json:"url"` - Channel string `json:"channel"` - Token string `json:"token"` - HTMLContent string `json:"html_content"` -} - -func (message *Message) Send(user *model.User) error { +func SendMessage(message *model.Message, user *model.User) error { switch message.Channel { case TypeEmail: return SendEmailMessage(message, user) @@ -48,6 +38,8 @@ func (message *Message) Send(user *model.User) error { return SendClientMessage(message, user) case TypeTelegram: return SendTelegramMessage(message, user) + case TypeNone: + return nil default: return errors.New("不支持的消息通道:" + message.Channel) } diff --git a/channel/telegram.go b/channel/telegram.go index 2b2d001..c1bf533 100644 --- a/channel/telegram.go +++ b/channel/telegram.go @@ -20,7 +20,7 @@ type telegramMessageResponse struct { Description string `json:"description"` } -func SendTelegramMessage(message *Message, user *model.User) error { +func SendTelegramMessage(message *model.Message, user *model.User) error { if user.TelegramBotToken == "" || user.TelegramChatId == "" { return errors.New("未配置 Telegram 机器人消息推送方式") } diff --git a/channel/wechat-corp-account.go b/channel/wechat-corp-account.go index db5f31f..56dfad0 100644 --- a/channel/wechat-corp-account.go +++ b/channel/wechat-corp-account.go @@ -95,7 +95,7 @@ type wechatCorpMessageResponse struct { ErrorMessage string `json:"errmsg"` } -func SendWeChatCorpMessage(message *Message, user *model.User) error { +func SendWeChatCorpMessage(message *model.Message, user *model.User) error { if user.WeChatCorpAccountId == "" { return errors.New("未配置微信企业号消息推送方式") } @@ -119,8 +119,7 @@ func SendWeChatCorpMessage(message *Message, user *model.User) error { messageRequest.MessageType = "textcard" messageRequest.TextCard.Title = message.Title messageRequest.TextCard.Description = message.Description - // TODO: render content and set URL - messageRequest.TextCard.URL = common.ServerAddress + messageRequest.TextCard.URL = message.URL } else { messageRequest.MessageType = "markdown" messageRequest.Markdown.Content = message.Content diff --git a/channel/wechat-test-account.go b/channel/wechat-test-account.go index f8c0747..3652aee 100644 --- a/channel/wechat-test-account.go +++ b/channel/wechat-test-account.go @@ -88,7 +88,7 @@ type wechatTestMessageResponse struct { ErrorMessage string `json:"errmsg"` } -func SendWeChatTestMessage(message *Message, user *model.User) error { +func SendWeChatTestMessage(message *model.Message, user *model.User) error { if user.WeChatTestAccountId == "" { return errors.New("未配置微信测试号消息推送方式") } @@ -97,8 +97,8 @@ func SendWeChatTestMessage(message *Message, user *model.User) error { TemplateId: user.WeChatTestAccountTemplateId, URL: "", } - // TODO: render content and set URL values.Data.Text.Value = message.Description + values.URL = message.URL jsonData, err := json.Marshal(values) if err != nil { return err diff --git a/common/public/message.html b/common/public/message.html new file mode 100644 index 0000000..738f1b6 --- /dev/null +++ b/common/public/message.html @@ -0,0 +1,31 @@ + + + + + + + {{.title}} + + + + +
+
+
+
+
+

{{.title}}

+
+ 发布于:{{.time}} +
+
+

{{.description}}

+
+ {{.content | unescape}} +
+
+
+
+
+ + \ No newline at end of file diff --git a/common/public/static/app.css b/common/public/static/app.css new file mode 100644 index 0000000..c0aa2d4 --- /dev/null +++ b/common/public/static/app.css @@ -0,0 +1,399 @@ +body { + font-family: Verdana, Candara, Arial, Helvetica, Microsoft YaHei, sans-serif; + line-height: 1.6; + margin: 0; +} + +nav { + margin-bottom: 16px; +} + +a { + text-decoration: none; + color: #007bff; +} + +a:hover { + text-decoration: none !important; + color: #007bff; +} + +.page-card-title a { + color: #368CCB; + text-decoration: none; +} + +.page-card-title a:hover { + color: #368CCB; + text-decoration: none; +} + +.wrapper { + max-width: 960px; + margin: 0 auto; +} + +#page-container { + position: relative; + min-height: 97vh; +} + +#content-wrap { + padding-bottom: 4rem; +} + +#footer { + height: 4rem; +} + +#footer a { + /*color: black;*/ +} + +code { + font-family: Consolas, 'Courier New', monospace; +} + +.page-card-list { + margin: 8px 8px; +} + +.page-card-title { + font-size: x-large; + font-weight: 500; + color: #000000; + text-decoration: none; + margin-bottom: 4px; +} + +.page-card-text { + margin-top: 16px; +} + +.pagination { + margin: 16px 4px; +} + +.pagination a { + border: none; + overflow: hidden; +} + +.shadow { + box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, .1), 0 0 0 1px rgba(10, 10, 10, .02); +} + +.nav-shadow { + box-shadow: 0 2px 3px rgba(26, 26, 26, .1); +} + +.paginator div { + border: 2px solid #000; + cursor: pointer; + display: inline-block; + min-width: 100px; + text-align: center; + font-weight: bold; + padding: 10px; +} + +.box article { + overflow-wrap: break-word; + /*font-size: larger;*/ + word-break: break-word; + line-height: 1.6; + padding: 16px; + /*margin-bottom: 16px;*/ + background-color: #ffffff; +} + +.toc-level-1 { + list-style-type: none; +} + +.toc-level-2 { + list-style-type: none; +} + +.toc-level-3 { + list-style-type: disc; +} + +.toc-level-4 { + list-style-type: circle; +} + +.toc-level-5 { + list-style-type: square; +} + +.toc-level-6 { + list-style-type: square; +} + +img { + max-width: 100%; + max-height: 100%; +} + +.article-container { + margin: auto; + max-width: 960px; + padding: 16px 16px; + overflow-wrap: break-word; + word-break: break-word; + line-height: 1.6; + /*font-size: larger;*/ +} + +article { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; + color: #24292f; +} + +article p, article blockquote, article ul, article ol, article dl, article table, article pre, article details { + margin-top: 0; + margin-bottom: 16px; +} + +article ul, article ol { + padding-left: 2em; +} + +article ul ul, article ul ol, article ol ol, article ol ul { + margin-top: 0; + margin-bottom: 0; +} + +article .tag { + font-family: Verdana, Candara, Arial, Helvetica, Microsoft YaHei, sans-serif; +} + +article a { + color: #007bff; + text-decoration: none; +} + +article a:hover { + color: #007bff; + text-decoration: none; +} + +article h2, +article h3, +article h4, +article h5, +article h6 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.5; + margin-block-start: 1em; + margin-block-end: 0.2em; +} + +article h1 { + font-size: 2em +} + +article h2 { + padding-bottom: 0.3em; + font-size: 1.5em; +} + +article h3 { + font-size: 1.25em +} + +article h4 { + font-size: 1.25em; +} + +article h5 { + font-size: 1.1em; +} + +article h6 { + font-size: 1em; + font-weight: bold +} + +@media screen and (max-width: 960px) { + article h1 { + font-size: 1.5em + } + + article h2 { + font-size: 1.35em + } + + article h3 { + font-size: 1.3em + } + + article h4 { + font-size: 1.2em; + } +} + +article p { + margin-top: 0; + margin-bottom: 16px; +} + +article table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; + vertical-align: middle; + text-align: left; + min-width: 66%; +} + +article table td, +article table th { + padding: 5px 8px; + border: 1px solid #bbb; +} + +article blockquote { + margin-left: 0; + padding: 0 1em; + border-left: 0.25em solid #ddd; +} + +article ol ul { + list-style-type: circle; +} + +article pre { + max-width: 960px; + display: block; + overflow: auto; + padding: 0; + margin-top: 12px; + margin-bottom: 12px; + border-radius: 6px; +} + +article pre code { + font-size: 14px; +} + +article ol { + text-decoration: none; + padding-inline-start: 40px; + margin-bottom: 1.25rem; + padding-left: 2em; +} + +article ul { + padding-left: 2em; +} + +article li + li { + margin-top: 0.25em; +} + +code { + font-family: "JetBrains Mono", "Cascadia Code", Consolas, Microsoft YaHei, monospace; +} + +article code { + color: #24292f; + background-color: rgb(175 184 193 / 20%); + padding: .065em .4em; + border-radius: 6px; + font-family: "JetBrains Mono", "Cascadia Code", Consolas, Microsoft YaHei, monospace; +} + +article .copyright { + display: none; +} + +.info { + font-size: 14px; + line-height: 28px; + text-align: left; + color: #738292; + margin-bottom: 24px; +} + +.info a { + text-decoration: none; + color: inherit; +} + +/* Code Page Style*/ +.code-page { + margin-top: 32px; + padding-left: 16px; + padding-right: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; +} + +.code-page code { + font-size: 16px; + width: 100%; + height: 100%; +} + +.code-page pre { + margin-top: 2px; + overflow-x: auto; + padding: 0; + font-size: 16px; + background-color: rgba(0, 0, 0, 0); +} + +.code-page .control-panel { + width: 100%; +} + +#code-display { + padding: 16px 24px; +} + +.discuss h1 { + font-size: 24px; + line-height: 36px; + text-align: left; +} + +.discuss .time { + font-size: 12px; + line-height: 18px; + text-align: left; + color: #738292; +} + +.discuss .content { + font-size: 16px; + line-height: 24px; + text-align: left; +} + +.raw { + padding: 16px; +} + +.raw .raw-content { + overflow-y: hidden; + overflow-x: scroll; +} + +.links { + margin: 16px; +} + +span.line { + display: inline-block; +} + +.toc { + position: sticky; + top: 24px; +} \ No newline at end of file diff --git a/common/template.go b/common/template.go new file mode 100644 index 0000000..df35670 --- /dev/null +++ b/common/template.go @@ -0,0 +1,17 @@ +package common + +import ( + "embed" + "html/template" +) + +//go:embed public +var FS embed.FS + +func LoadTemplate() *template.Template { + var funcMap = template.FuncMap{ + "unescape": UnescapeHTML, + } + t := template.Must(template.New("").Funcs(funcMap).ParseFS(FS, "public/*.html")) + return t +} diff --git a/controller/message.go b/controller/message.go index aba7a97..f5085e3 100644 --- a/controller/message.go +++ b/controller/message.go @@ -1,16 +1,21 @@ package controller import ( + "bytes" "encoding/json" + "fmt" "github.com/gin-gonic/gin" + "github.com/yuin/goldmark" "message-pusher/channel" "message-pusher/common" "message-pusher/model" "net/http" + "strconv" + "time" ) func GetPushMessage(c *gin.Context) { - message := channel.Message{ + message := model.Message{ Title: c.Query("title"), Description: c.Query("description"), Content: c.Query("content"), @@ -30,7 +35,7 @@ func GetPushMessage(c *gin.Context) { } func PostPushMessage(c *gin.Context) { - message := channel.Message{ + message := model.Message{ Title: c.PostForm("title"), Description: c.PostForm("description"), Content: c.PostForm("content"), @@ -39,7 +44,7 @@ func PostPushMessage(c *gin.Context) { Token: c.PostForm("token"), Desp: c.PostForm("desp"), } - if message == (channel.Message{}) { + if message == (model.Message{}) { // Looks like the user is using JSON err := json.NewDecoder(c.Request.Body).Decode(&message) if err != nil { @@ -56,7 +61,7 @@ func PostPushMessage(c *gin.Context) { pushMessageHelper(c, &message) } -func pushMessageHelper(c *gin.Context, message *channel.Message) { +func pushMessageHelper(c *gin.Context, message *model.Message) { user := model.User{Username: c.Param("username")} err := user.FillUserByUsername() if err != nil { @@ -108,7 +113,16 @@ func pushMessageHelper(c *gin.Context, message *channel.Message) { message.Channel = channel.TypeEmail } } - err = message.Send(&user) + err = message.UpdateAndInsert(user.Id) + message.URL = fmt.Sprintf("%s/message/%s", common.ServerAddress, message.Link) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + err = channel.SendMessage(message, &user) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, @@ -122,3 +136,108 @@ func pushMessageHelper(c *gin.Context, message *channel.Message) { }) return } + +func GetStaticFile(c *gin.Context) { + path := c.Param("file") + c.FileFromFS("public/static/"+path, http.FS(common.FS)) +} + +func RenderMessage(c *gin.Context) { + link := c.Param("link") + message, err := model.GetMessageByLink(link) + if err != nil { + c.Status(http.StatusNotFound) + return + } + if message.Content != "" { + var buf bytes.Buffer + err := goldmark.Convert([]byte(message.Content), &buf) + if err != nil { + common.SysLog(err.Error()) + } else { + message.HTMLContent = buf.String() + } + } + c.HTML(http.StatusOK, "message.html", gin.H{ + "title": message.Title, + "time": time.Unix(message.Timestamp, 0).Format("2006-01-02 15:04:05"), + "description": message.Description, + "content": message.HTMLContent, + }) + return +} + +func GetUserMessages(c *gin.Context) { + userId := c.GetInt("id") + p, _ := strconv.Atoi(c.Query("p")) + if p < 0 { + p = 0 + } + messages, err := model.GetMessagesByUserId(userId, p*common.ItemsPerPage, common.ItemsPerPage) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + "data": messages, + }) + return +} + +func GetMessage(c *gin.Context) { + messageId, _ := strconv.Atoi(c.Param("id")) + userId := c.GetInt("id") + message, err := model.GetMessageById(messageId, userId) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + "data": message, + }) + return +} + +func DeleteMessage(c *gin.Context) { + messageId, _ := strconv.Atoi(c.Param("id")) + userId := c.GetInt("id") + err := model.DeleteMessageById(messageId, userId) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + }) + return +} + +func DeleteAllMessages(c *gin.Context) { + err := model.DeleteAllMessages() + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + }) + return +} diff --git a/main.go b/main.go index 3ffc253..81b6172 100644 --- a/main.go +++ b/main.go @@ -54,6 +54,7 @@ func main() { // Initialize HTTP server server := gin.Default() + server.SetHTMLTemplate(common.LoadTemplate()) server.Use(gzip.Gzip(gzip.DefaultCompression)) // Initialize session store diff --git a/model/main.go b/model/main.go index 6a651f9..8c14d09 100644 --- a/model/main.go +++ b/model/main.go @@ -60,6 +60,10 @@ func InitDB() (err error) { if err != nil { return err } + err = db.AutoMigrate(&Message{}) + if err != nil { + return err + } err = createRootAccountIfNeed() return err } else { diff --git a/model/message.go b/model/message.go new file mode 100644 index 0000000..02d7292 --- /dev/null +++ b/model/message.go @@ -0,0 +1,76 @@ +package model + +import ( + "errors" + "message-pusher/common" + "time" +) + +type Message struct { + Id int `json:"id"` + UserId int `json:"user_id" gorm:"index"` + Title string `json:"title"` + Description string `json:"description"` + Desp string `json:"desp" gorm:"-:all"` // alias for description + Content string `json:"content"` + URL string `json:"url" gorm:"-:all"` + Channel string `json:"channel"` + Token string `json:"token" gorm:"-:all"` + HTMLContent string `json:"html_content" gorm:"-:all"` + Timestamp int64 `json:"timestamp" gorm:"type:int64"` + Link string `json:"link" gorm:"unique;index"` +} + +func GetMessageById(id int, userId int) (*Message, error) { + if id == 0 || userId == 0 { + return nil, errors.New("id 或 userId 为空!") + } + message := Message{Id: id, UserId: userId} + err := DB.Where(message).First(&message).Error + return &message, err +} + +func GetMessageByLink(link string) (*Message, error) { + if link == "" { + return nil, errors.New("link 为空!") + } + message := Message{Link: link} + err := DB.Where(message).First(&message).Error + return &message, err +} + +func GetMessagesByUserId(userId int, startIdx int, num int) (messages []*Message, err error) { + err = DB.Where("user_id = ?", userId).Order("id desc").Limit(num).Offset(startIdx).Find(&messages).Error + return messages, err +} + +func DeleteMessageById(id int, userId int) (err error) { + // Why we need userId here? In case user want to delete other's message. + if id == 0 || userId == 0 { + return errors.New("id 或 userId 为空!") + } + message := Message{Id: id, UserId: userId} + err = DB.Where(message).First(&message).Error + if err != nil { + return err + } + return message.Delete() +} + +func DeleteAllMessages() error { + return DB.Exec("DELETE FROM messages").Error +} + +func (message *Message) UpdateAndInsert(userId int) error { + message.Link = common.GetUUID() + message.Timestamp = time.Now().Unix() + message.UserId = userId + var err error + err = DB.Create(message).Error + return err +} + +func (message *Message) Delete() error { + err := DB.Delete(message).Error + return err +} diff --git a/router/api-router.go b/router/api-router.go index d56a298..5e3643f 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -55,6 +55,13 @@ func SetApiRouter(router *gin.Engine) { optionRoute.GET("/", controller.GetOptions) optionRoute.PUT("/", controller.UpdateOption) } + messageRoute := apiRouter.Group("/message") + { + messageRoute.GET("/all", middleware.UserAuth(), controller.GetUserMessages) + messageRoute.GET("/:id", middleware.UserAuth(), controller.GetMessage) + messageRoute.DELETE("/all", middleware.RootAuth(), controller.DeleteAllMessages) + messageRoute.DELETE("/:id", middleware.UserAuth(), controller.DeleteMessage) + } } pushRouter := router.Group("/push") pushRouter.Use(middleware.GlobalAPIRateLimit()) diff --git a/router/web-router.go b/router/web-router.go index 1db50fe..4aa6f5b 100644 --- a/router/web-router.go +++ b/router/web-router.go @@ -5,6 +5,7 @@ import ( "github.com/gin-contrib/static" "github.com/gin-gonic/gin" "message-pusher/common" + "message-pusher/controller" "message-pusher/middleware" "net/http" ) @@ -12,6 +13,8 @@ import ( func setWebRouter(router *gin.Engine, buildFS embed.FS, indexPage []byte) { router.Use(middleware.GlobalWebRateLimit()) router.Use(middleware.Cache()) + router.GET("/public/static/:file", controller.GetStaticFile) + router.GET("/message/:link", controller.RenderMessage) router.Use(static.Serve("/", common.EmbedFolder(buildFS, "web/build"))) router.NoRoute(func(c *gin.Context) { c.Data(http.StatusOK, "text/html; charset=utf-8", indexPage)