mirror of
https://github.com/songquanpeng/message-pusher.git
synced 2025-10-06 00:26:51 +08:00
feat: channel email is done
This commit is contained in:
1
channel/ding-talk.go
Normal file
1
channel/ding-talk.go
Normal file
@@ -0,0 +1 @@
|
||||
package channel
|
18
channel/email.go
Normal file
18
channel/email.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"message-pusher/common"
|
||||
"message-pusher/model"
|
||||
)
|
||||
|
||||
func SendEmailMessage(message *Message, user *model.User) error {
|
||||
if user.Email == "" {
|
||||
return errors.New("未配置邮箱地址")
|
||||
}
|
||||
subject := message.Description
|
||||
if subject == "" {
|
||||
subject = message.Title
|
||||
}
|
||||
return common.SendEmail(subject, user.Email, message.Content)
|
||||
}
|
1
channel/lark.go
Normal file
1
channel/lark.go
Normal file
@@ -0,0 +1 @@
|
||||
package channel
|
39
channel/main.go
Normal file
39
channel/main.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"message-pusher/model"
|
||||
)
|
||||
|
||||
const (
|
||||
TypeEmail = "email"
|
||||
TypeWeChatTestAccount = "test"
|
||||
TypeWeChatCorpAccount = "corp"
|
||||
TypeLark = "lark"
|
||||
TypeDingTalk = "ding"
|
||||
TypeTelegram = "telegram"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Content string `json:"content"`
|
||||
URL string `json:"url"`
|
||||
Channel string `json:"channel"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func (message *Message) Send(user *model.User) error {
|
||||
switch message.Channel {
|
||||
case TypeEmail:
|
||||
return SendEmailMessage(message, user)
|
||||
case TypeWeChatTestAccount:
|
||||
case TypeWeChatCorpAccount:
|
||||
case TypeLark:
|
||||
case TypeDingTalk:
|
||||
case TypeTelegram:
|
||||
default:
|
||||
return errors.New("不支持的消息通道:" + message.Channel)
|
||||
}
|
||||
return nil
|
||||
}
|
1
channel/telegram.go
Normal file
1
channel/telegram.go
Normal file
@@ -0,0 +1 @@
|
||||
package channel
|
1
channel/wechat-corp-account.go
Normal file
1
channel/wechat-corp-account.go
Normal file
@@ -0,0 +1 @@
|
||||
package channel
|
1
channel/wechat-test-account.go
Normal file
1
channel/wechat-test-account.go
Normal file
@@ -0,0 +1 @@
|
||||
package channel
|
@@ -75,6 +75,7 @@ var (
|
||||
var RateLimitKeyExpirationDuration = 20 * time.Minute
|
||||
|
||||
const (
|
||||
UserStatusNonExisted = 0
|
||||
UserStatusEnabled = 1 // don't use 0, 0 is the default value!
|
||||
UserStatusDisabled = 2 // also don't use 0
|
||||
)
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package common
|
||||
|
||||
import "gopkg.in/gomail.v2"
|
||||
import (
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
func SendEmail(subject string, receiver string, content string) error {
|
||||
m := gomail.NewMessage()
|
||||
|
109
controller/message.go
Normal file
109
controller/message.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yuin/goldmark"
|
||||
"message-pusher/channel"
|
||||
"message-pusher/common"
|
||||
"message-pusher/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetPushMessage(c *gin.Context) {
|
||||
message := channel.Message{
|
||||
Title: c.Query("title"),
|
||||
Description: c.Query("description"),
|
||||
Content: c.Query("content"),
|
||||
URL: c.Query("url"),
|
||||
Channel: c.Query("channel"),
|
||||
Token: c.Query("token"),
|
||||
}
|
||||
if message.Description == "" {
|
||||
// Keep compatible with ServerChan
|
||||
message.Description = c.Query("desp")
|
||||
}
|
||||
pushMessageHelper(c, &message)
|
||||
}
|
||||
|
||||
func PostPushMessage(c *gin.Context) {
|
||||
message := channel.Message{}
|
||||
err := json.NewDecoder(c.Request.Body).Decode(&message)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "无法解析请求体,请检查其是否为合法 JSON",
|
||||
})
|
||||
return
|
||||
}
|
||||
pushMessageHelper(c, &message)
|
||||
}
|
||||
|
||||
func pushMessageHelper(c *gin.Context, message *channel.Message) {
|
||||
user := model.User{Username: c.Param("username")}
|
||||
user.FillUserByUsername()
|
||||
if user.Status == common.UserStatusNonExisted {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"success": false,
|
||||
"message": "用户不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
if user.Status == common.UserStatusDisabled {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"success": false,
|
||||
"message": "用户已被封禁",
|
||||
})
|
||||
return
|
||||
}
|
||||
if user.Token != "" {
|
||||
if message.Token == "" {
|
||||
message.Token = c.Request.Header.Get("Authorization")
|
||||
}
|
||||
if user.Token != message.Token {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"success": false,
|
||||
"message": "无效的 token",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
if message.Title == "" {
|
||||
message.Title = common.SystemName
|
||||
}
|
||||
if message.Content != "" {
|
||||
var buf bytes.Buffer
|
||||
err := goldmark.Convert([]byte(message.Content), &buf)
|
||||
if err != nil {
|
||||
common.SysLog(err.Error())
|
||||
} else {
|
||||
message.Content = buf.String()
|
||||
}
|
||||
} else {
|
||||
if message.Description != "" {
|
||||
message.Content = message.Description
|
||||
} else {
|
||||
message.Content = "无内容"
|
||||
}
|
||||
}
|
||||
if message.Channel == "" {
|
||||
message.Channel = user.Channel
|
||||
if message.Channel == "" {
|
||||
message.Channel = channel.TypeEmail
|
||||
}
|
||||
}
|
||||
err := message.Send(&user)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "ok",
|
||||
})
|
||||
return
|
||||
}
|
1
go.mod
1
go.mod
@@ -41,6 +41,7 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/yuin/goldmark v1.5.2 // indirect
|
||||
golang.org/x/net v0.1.0 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
|
2
go.sum
2
go.sum
@@ -94,6 +94,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
|
||||
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"message-pusher/common"
|
||||
"message-pusher/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -14,11 +13,7 @@ func authHelper(c *gin.Context, minRole int) {
|
||||
role := session.Get("role")
|
||||
id := session.Get("id")
|
||||
status := session.Get("status")
|
||||
authByToken := false
|
||||
if username == nil {
|
||||
// Check token
|
||||
token := c.Request.Header.Get("Authorization")
|
||||
if token == "" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "无权进行此操作,未登录或 token 无效",
|
||||
@@ -26,23 +21,6 @@ func authHelper(c *gin.Context, minRole int) {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
user := model.ValidateUserToken(token)
|
||||
if user != nil && user.Username != "" {
|
||||
// Token is valid
|
||||
username = user.Username
|
||||
role = user.Role
|
||||
id = user.Id
|
||||
status = user.Status
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "无权进行此操作,未登录或 token 无效",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
authByToken = true
|
||||
}
|
||||
if status.(int) == common.UserStatusDisabled {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
@@ -62,7 +40,6 @@ func authHelper(c *gin.Context, minRole int) {
|
||||
c.Set("username", username)
|
||||
c.Set("role", role)
|
||||
c.Set("id", id)
|
||||
c.Set("authByToken", authByToken)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
@@ -83,35 +60,3 @@ func RootAuth() func(c *gin.Context) {
|
||||
authHelper(c, common.RoleRootUser)
|
||||
}
|
||||
}
|
||||
|
||||
// NoTokenAuth You should always use this after normal auth middlewares.
|
||||
func NoTokenAuth() func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
authByToken := c.GetBool("authByToken")
|
||||
if authByToken {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "本接口不支持使用 token 进行验证",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// TokenOnlyAuth You should always use this after normal auth middlewares.
|
||||
func TokenOnlyAuth() func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
authByToken := c.GetBool("authByToken")
|
||||
if !authByToken {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "本接口仅支持使用 token 进行验证",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ type User struct {
|
||||
Email string `json:"email" gorm:"index" validate:"max=50"`
|
||||
GitHubId string `json:"github_id" gorm:"column:github_id;index"`
|
||||
WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"`
|
||||
Channel string `json:"channel"`
|
||||
VerificationCode string `json:"verification_code" gorm:"-:all"`
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,7 @@ func SetApiRouter(router *gin.Engine) {
|
||||
userRoute.GET("/logout", controller.Logout)
|
||||
|
||||
selfRoute := userRoute.Group("/")
|
||||
selfRoute.Use(middleware.UserAuth(), middleware.NoTokenAuth())
|
||||
selfRoute.Use(middleware.UserAuth())
|
||||
{
|
||||
selfRoute.GET("/self", controller.GetSelf)
|
||||
selfRoute.PUT("/self", controller.UpdateSelf)
|
||||
@@ -37,7 +37,7 @@ func SetApiRouter(router *gin.Engine) {
|
||||
}
|
||||
|
||||
adminRoute := userRoute.Group("/")
|
||||
adminRoute.Use(middleware.AdminAuth(), middleware.NoTokenAuth())
|
||||
adminRoute.Use(middleware.AdminAuth())
|
||||
{
|
||||
adminRoute.GET("/", controller.GetAllUsers)
|
||||
adminRoute.GET("/search", controller.SearchUsers)
|
||||
@@ -49,7 +49,7 @@ func SetApiRouter(router *gin.Engine) {
|
||||
}
|
||||
}
|
||||
optionRoute := apiRouter.Group("/option")
|
||||
optionRoute.Use(middleware.RootAuth(), middleware.NoTokenAuth())
|
||||
optionRoute.Use(middleware.RootAuth())
|
||||
{
|
||||
optionRoute.GET("/", controller.GetOptions)
|
||||
optionRoute.PUT("/", controller.UpdateOption)
|
||||
@@ -61,4 +61,10 @@ func SetApiRouter(router *gin.Engine) {
|
||||
fileRoute.DELETE("/:id", middleware.UserAuth(), controller.DeleteFile)
|
||||
}
|
||||
}
|
||||
pushRouter := router.Group("/push")
|
||||
pushRouter.Use(middleware.GlobalAPIRateLimit())
|
||||
{
|
||||
pushRouter.GET("/:username", controller.GetPushMessage)
|
||||
pushRouter.POST("/:username", controller.PostPushMessage)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user