diff --git a/channel/token-store.go b/channel/token-store.go index fbcb736..01103f9 100644 --- a/channel/token-store.go +++ b/channel/token-store.go @@ -40,9 +40,9 @@ func TokenStoreInit() { } if user.WeChatCorpAccountId != "" { item := &WeChatCorpAccountTokenStoreItem{ - CorpId: user.WeChatCorpAccountId, - CorpSecret: user.WeChatCorpAccountSecret, - AgentId: user.WeChatCorpAccountAgentId, + CorpId: user.WeChatCorpAccountId, + AgentSecret: user.WeChatCorpAccountAgentSecret, + AgentId: user.WeChatCorpAccountAgentId, } items = append(items, item) } @@ -121,21 +121,21 @@ func TokenStoreUpdateUser(cleanUser *model.User, originUser *model.User) { if cleanUser.WeChatCorpAccountAgentId == originUser.WeChatCorpAccountAgentId { cleanUser.WeChatCorpAccountAgentId = "" } - if cleanUser.WeChatCorpAccountSecret == originUser.WeChatCorpAccountSecret { - cleanUser.WeChatCorpAccountSecret = "" + if cleanUser.WeChatCorpAccountAgentSecret == originUser.WeChatCorpAccountAgentSecret { + cleanUser.WeChatCorpAccountAgentSecret = "" } - if cleanUser.WeChatCorpAccountId != "" || cleanUser.WeChatCorpAccountAgentId != "" || cleanUser.WeChatCorpAccountSecret != "" { + if cleanUser.WeChatCorpAccountId != "" || cleanUser.WeChatCorpAccountAgentId != "" || cleanUser.WeChatCorpAccountAgentSecret != "" { oldWeChatCorpAccountTokenStoreItem := WeChatCorpAccountTokenStoreItem{ - CorpId: cleanUser.WeChatCorpAccountId, - CorpSecret: cleanUser.WeChatCorpAccountSecret, - AgentId: cleanUser.WeChatCorpAccountAgentId, + CorpId: cleanUser.WeChatCorpAccountId, + AgentSecret: cleanUser.WeChatCorpAccountAgentSecret, + AgentId: cleanUser.WeChatCorpAccountAgentId, } newWeChatCorpAccountTokenStoreItem := oldWeChatCorpAccountTokenStoreItem if cleanUser.WeChatCorpAccountId != "" { newWeChatCorpAccountTokenStoreItem.CorpId = cleanUser.WeChatCorpAccountId } - if cleanUser.WeChatCorpAccountSecret != "" { - newWeChatCorpAccountTokenStoreItem.CorpSecret = cleanUser.WeChatCorpAccountSecret + if cleanUser.WeChatCorpAccountAgentSecret != "" { + newWeChatCorpAccountTokenStoreItem.AgentSecret = cleanUser.WeChatCorpAccountAgentSecret } if cleanUser.WeChatCorpAccountAgentId != "" { newWeChatCorpAccountTokenStoreItem.AgentId = cleanUser.WeChatCorpAccountAgentId @@ -157,9 +157,9 @@ func TokenStoreRemoveUser(user *model.User) { TokenStoreRemoveItem(&testAccountTokenStoreItem) } corpAccountTokenStoreItem := WeChatCorpAccountTokenStoreItem{ - CorpId: user.WeChatCorpAccountId, - CorpSecret: user.WeChatCorpAccountSecret, - AgentId: user.WeChatCorpAccountAgentId, + CorpId: user.WeChatCorpAccountId, + AgentSecret: user.WeChatCorpAccountAgentSecret, + AgentId: user.WeChatCorpAccountAgentId, } if !corpAccountTokenStoreItem.IsShared() { TokenStoreRemoveItem(&corpAccountTokenStoreItem) diff --git a/channel/wechat-corp-account.go b/channel/wechat-corp-account.go index 1c0fda4..60e9c56 100644 --- a/channel/wechat-corp-account.go +++ b/channel/wechat-corp-account.go @@ -20,18 +20,18 @@ type wechatCorpAccountResponse struct { type WeChatCorpAccountTokenStoreItem struct { CorpId string - CorpSecret string + AgentSecret string AgentId string AccessToken string } func (i *WeChatCorpAccountTokenStoreItem) Key() string { - return i.CorpId + i.AgentId + i.CorpSecret + return i.CorpId + i.AgentId + i.AgentSecret } func (i *WeChatCorpAccountTokenStoreItem) IsShared() bool { - return model.DB.Where("wechat_corp_account_id = ? and wechat_corp_account_secret = ? and wechat_corp_account_agent_id = ?", - i.CorpId, i.CorpSecret, i.AgentId).Find(&model.User{}).RowsAffected != 1 + return model.DB.Where("wechat_corp_account_id = ? and wechat_corp_account_agent_secret = ? and wechat_corp_account_agent_id = ?", + i.CorpId, i.AgentSecret, i.AgentId).Find(&model.User{}).RowsAffected != 1 } func (i *WeChatCorpAccountTokenStoreItem) Token() string { @@ -44,7 +44,7 @@ func (i *WeChatCorpAccountTokenStoreItem) Refresh() { Timeout: 5 * time.Second, } req, err := http.NewRequest("GET", fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s", - i.CorpId, i.CorpSecret), nil) + i.CorpId, i.AgentSecret), nil) if err != nil { common.SysError(err.Error()) return @@ -119,7 +119,7 @@ func SendWeChatCorpMessage(message *Message, user *model.User) error { if err != nil { return err } - key := fmt.Sprintf("%s%s%s", user.WeChatCorpAccountId, user.WeChatCorpAccountAgentId, user.WeChatCorpAccountSecret) + key := fmt.Sprintf("%s%s%s", user.WeChatCorpAccountId, user.WeChatCorpAccountAgentId, user.WeChatCorpAccountAgentSecret) accessToken := TokenStoreGetToken(key) resp, err := http.Post(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s", accessToken), "application/json", bytes.NewBuffer(jsonData)) diff --git a/controller/message.go b/controller/message.go index d2254c0..ad61c44 100644 --- a/controller/message.go +++ b/controller/message.go @@ -22,6 +22,10 @@ func GetPushMessage(c *gin.Context) { // Keep compatible with ServerChan message.Description = c.Query("desp") } + if message.Channel == "" { + // Keep compatible with old version + message.Channel = c.Query("type") + } pushMessageHelper(c, &message) } diff --git a/controller/misc.go b/controller/misc.go index 6ea1634..31954f4 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -1,12 +1,16 @@ package controller import ( + "crypto/sha1" + "encoding/hex" "encoding/json" "fmt" "github.com/gin-gonic/gin" "message-pusher/common" "message-pusher/model" "net/http" + "sort" + "strings" ) func GetStatus(c *gin.Context) { @@ -167,3 +171,23 @@ func ResetPassword(c *gin.Context) { }) return } + +func WeChatTestAccountVerification(c *gin.Context) { + user := model.User{Username: c.Param("username")} + user.FillUserByUsername() + // https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html + signature := c.Query("signature") + timestamp := c.Query("timestamp") + nonce := c.Query("nonce") + echoStr := c.Query("echostr") + arr := []string{user.WeChatTestAccountVerificationToken, timestamp, nonce} + sort.Strings(arr) + str := strings.Join(arr, "") + hash := sha1.Sum([]byte(str)) + hexStr := hex.EncodeToString(hash[:]) + if signature == hexStr { + c.String(http.StatusOK, echoStr) + } else { + c.Status(http.StatusForbidden) + } +} diff --git a/controller/user.go b/controller/user.go index 69eb050..61b17fa 100644 --- a/controller/user.go +++ b/controller/user.go @@ -342,8 +342,15 @@ func UpdateUser(c *gin.Context) { if updatedUser.Password == "$I_LOVE_U" { updatedUser.Password = "" // rollback to what it should be } - updatePassword := updatedUser.Password != "" - if err := updatedUser.Update(updatePassword); err != nil { + // We only allow admin change those fields. + cleanUser := model.User{ + Id: updatedUser.Id, + Username: updatedUser.Username, + Password: updatedUser.Password, + DisplayName: updatedUser.DisplayName, + } + updatePassword := cleanUser.Password != "" + if err := cleanUser.Update(updatePassword); err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, "message": err.Error(), @@ -389,13 +396,16 @@ func UpdateSelf(c *gin.Context) { Id: c.GetInt("id"), Username: user.Username, Password: user.Password, + DisplayName: user.DisplayName, + Token: user.Token, + Channel: user.Channel, WeChatTestAccountId: user.WeChatTestAccountId, WeChatTestAccountSecret: user.WeChatTestAccountSecret, WeChatTestAccountTemplateId: user.WeChatTestAccountTemplateId, WeChatTestAccountOpenId: user.WeChatTestAccountOpenId, WeChatTestAccountVerificationToken: user.WeChatTestAccountVerificationToken, WeChatCorpAccountId: user.WeChatCorpAccountId, - WeChatCorpAccountSecret: user.WeChatCorpAccountSecret, + WeChatCorpAccountAgentSecret: user.WeChatCorpAccountAgentSecret, WeChatCorpAccountAgentId: user.WeChatCorpAccountAgentId, WeChatCorpAccountUserId: user.WeChatCorpAccountUserId, WeChatCorpAccountClientType: user.WeChatCorpAccountClientType, diff --git a/model/user.go b/model/user.go index c84136a..ac0f326 100644 --- a/model/user.go +++ b/model/user.go @@ -15,7 +15,7 @@ type User struct { DisplayName string `json:"display_name" gorm:"index" validate:"max=20"` Role int `json:"role" gorm:"type:int;default:1"` // admin, common Status int `json:"status" gorm:"type:int;default:1"` // enabled, disabled - Token string `json:"token" gorm:"index"` + Token string `json:"token"` 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"` @@ -27,10 +27,10 @@ type User struct { WeChatTestAccountOpenId string `json:"wechat_test_account_open_id" gorm:"column:wechat_test_account_open_id"` WeChatTestAccountVerificationToken string `json:"wechat_test_account_verification_token" gorm:"column:wechat_test_account_verification_token"` WeChatCorpAccountId string `json:"wechat_corp_account_id" gorm:"column:wechat_corp_account_id"` - WeChatCorpAccountSecret string `json:"wechat_corp_account_secret" gorm:"column:wechat_corp_account_secret"` + WeChatCorpAccountAgentSecret string `json:"wechat_corp_account_agent_secret" gorm:"column:wechat_corp_account_agent_secret"` WeChatCorpAccountAgentId string `json:"wechat_corp_account_agent_id" gorm:"column:wechat_corp_account_agent_id"` WeChatCorpAccountUserId string `json:"wechat_corp_account_user_id" gorm:"column:wechat_corp_account_user_id"` - WeChatCorpAccountClientType string `json:"wechat_corp_account_client_type" gorm:"wechat_corp_account_client_type;default=plugin"` + WeChatCorpAccountClientType string `json:"wechat_corp_account_client_type" gorm:"column:wechat_corp_account_client_type;default=plugin"` LarkWebhookURL string `json:"lark_webhook_url"` LarkWebhookSecret string `json:"lark_webhook_secret"` DingWebhookURL string `json:"ding_webhook_url"` @@ -64,7 +64,13 @@ func GetUserById(id int, selectAll bool) (*User, error) { if selectAll { err = DB.First(&user, "id = ?", id).Error } else { - err = DB.Select([]string{"id", "username", "display_name", "role", "status", "email", "wechat_id", "github_id"}).First(&user, "id = ?", id).Error + err = DB.Select([]string{"id", "username", "display_name", "role", "status", "email", "wechat_id", "github_id", + "channel", "token", + "wechat_test_account_id", "wechat_test_account_template_id", "wechat_test_account_open_id", + "wechat_corp_account_id", "wechat_corp_account_agent_id", "wechat_corp_account_user_id", "wechat_corp_account_client_type", + "lark_webhook_url", + "ding_webhook_url", + }).First(&user, "id = ?", id).Error } return &user, err } diff --git a/router/api-router.go b/router/api-router.go index 7706a48..2695123 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -13,6 +13,7 @@ func SetApiRouter(router *gin.Engine) { apiRouter.GET("/status", controller.GetStatus) apiRouter.GET("/notice", controller.GetNotice) apiRouter.GET("/about", controller.GetAbout) + apiRouter.GET("/wechat_test_account_verification/:username", controller.WeChatTestAccountVerification) apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification) apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail) apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword) diff --git a/web/src/components/PersonalSetting.js b/web/src/components/PersonalSetting.js index def4a1b..de2fd51 100644 --- a/web/src/components/PersonalSetting.js +++ b/web/src/components/PersonalSetting.js @@ -106,7 +106,7 @@ const PersonalSetting = () => { - + {/**/}
账号绑定
+ + +
+ 邮箱设置(email) + 通过邮件进行推送 +
+ + 邮件推送方式(email)需要设置邮箱,请前往个人设置页面绑定邮箱地址。 + + + +
+ 微信测试号设置(test) + + 通过微信测试号进行推送,点击前往配置: + + 微信公众平台接口测试帐号 + + +
+ + 接口配置信息中的 URL 填写: + {`${window.location.origin}/api/wechat_test_account_verification/${inputs.username}`} +
+ Token 填一个随机字符串,然后填入下方的「接口配置验证 Token」中。 +
+ + + + + + + + + + + + +
+ 企业微信设置(corp) + + 通过企业微信进行推送,点击前往配置: + + 企业微信应用管理 + + +
+ + + + + + + + + + + + +
+ 飞书设置(lark) + + 通过飞书群机器人进行推送,选择一个群聊 -> 设置 -> 群机器人 -> + 添加机器人 -> 自定义机器人 -> 添加( + 注意选中「签名校验」)。具体参见: + + 飞书开放文档 + + +
+ + + + + + + +
+ 钉钉设置(ding) + + 通过钉钉机器人进行推送,选择一个群聊 -> 群设置 -> 智能群助手 -> + 添加机器人(点击右侧齿轮图标) -> 自定义 -> 添加( + 注意选中「加密」)。具体参见: + + 钉钉开放文档 + + +
+ + + + + + + + + + ); +}; + +export default PushSetting; diff --git a/web/src/pages/Setting/index.js b/web/src/pages/Setting/index.js index 392e2ca..63fe939 100644 --- a/web/src/pages/Setting/index.js +++ b/web/src/pages/Setting/index.js @@ -4,6 +4,7 @@ import SystemSetting from '../../components/SystemSetting'; import { isRoot } from '../../helpers'; import OtherSetting from '../../components/OtherSetting'; import PersonalSetting from '../../components/PersonalSetting'; +import PushSetting from '../../components/PushSetting'; const Setting = () => { let panes = [ @@ -13,8 +14,16 @@ const Setting = () => { - ) - } + ), + }, + { + menuItem: '推送设置', + render: () => ( + + + + ), + }, ]; if (isRoot()) { @@ -24,7 +33,7 @@ const Setting = () => { - ) + ), }); panes.push({ menuItem: '其他设置', @@ -32,7 +41,7 @@ const Setting = () => { - ) + ), }); }