mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-07-01 02:24:47 +08:00
1. Config Verify 和 Contact 的 InitalizeData 写得过于直接了。 会导致正常的数据被覆盖。实际上,数据的Init只需要更换数据 库的时候更新,如何判断数据库要被更新了,就是判断新出现的 column 有没有非零值的,如果有,就说明数据库不是第一次更新。 2. 前端的请求经过yunionapi,最终会调用climc里面注册的ResourceManager, 所以这个地方一定要和后端的参数处理统一。之前因为添加 uname 的支持破坏了这个地方的统一,虽然climc可以调用成功但是前端接口调用会失败。 3. Resend 的bug,会导致任务的阻塞。 4. 为了兼容kapacitor对climc notify的调用,分成了notify和notify-batch两个命令。
428 lines
15 KiB
Go
428 lines
15 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package notify
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
|
|
"yunion.io/x/onecloud/pkg/appctx"
|
|
"yunion.io/x/onecloud/pkg/appsrv"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
|
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
|
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
|
"yunion.io/x/onecloud/pkg/notify/cache"
|
|
"yunion.io/x/onecloud/pkg/notify/models"
|
|
"yunion.io/x/onecloud/pkg/notify/options"
|
|
"yunion.io/x/onecloud/pkg/notify/utils"
|
|
)
|
|
|
|
func InitHandlers(app *appsrv.Application) {
|
|
db.AddProjectResourceCountHandler("api/v1", app)
|
|
db.RegisterModelManager(models.ContactManager)
|
|
db.RegisterModelManager(models.VerifyManager)
|
|
db.RegisterModelManager(models.NotificationManager)
|
|
db.RegisterModelManager(models.ConfigManager)
|
|
db.RegisterModelManager(cache.UserCacheManager)
|
|
db.RegisterModelManager(cache.UserGroupCacheManager)
|
|
db.RegisterModelManager(models.TemplateManager)
|
|
AddNotifyDispatcher("/api/v1/", app)
|
|
}
|
|
|
|
// Middleware
|
|
func middleware(f appsrv.FilterHandler) appsrv.FilterHandler {
|
|
hander := func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := r.URL.Query()["uname"]; ok {
|
|
// Uname
|
|
params := appctx.AppContextParams(ctx)
|
|
if uid, ok := params["<uid>"]; ok {
|
|
userDetail, err := utils.GetUserByIDOrName(ctx, uid)
|
|
if err != nil {
|
|
httperrors.NotFoundError(w, "Uid or Uname Not Found")
|
|
return
|
|
}
|
|
params["<uid>"] = userDetail.Id
|
|
}
|
|
ctx = context.WithValue(ctx, "uname", true)
|
|
}
|
|
f(ctx, w, r)
|
|
}
|
|
if consts.IsRbacEnabled() {
|
|
return auth.AuthenticateWithDelayDecision(hander, true)
|
|
} else {
|
|
return auth.Authenticate(hander)
|
|
}
|
|
}
|
|
|
|
func AddNotifyDispatcher(prefix string, app *appsrv.Application) {
|
|
var metadata map[string]interface{}
|
|
var tags map[string]string
|
|
|
|
// Contact Handler
|
|
modelDispatcher := NewNotifyModelDispatcher(models.ContactManager)
|
|
metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/<uid>/update-contact", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(contactUpdateHandler), metadata, "contact_update", tags)
|
|
// List
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(listHandler), metadata, "list_contacts", tags)
|
|
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/users", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(keyStoneUserListHandler), metadata, "list_users", tags)
|
|
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/<uid>", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(getHandler), metadata, "list_by_uid", tags)
|
|
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/delete-contact", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(deleteContactHandler), metadata, "delete", tags)
|
|
|
|
// verify-trigger
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/<uid>/verify", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(verifyTriggerHandler), metadata, "verify_trigger", tags)
|
|
|
|
// Verify Handler, this modelDispatcher need db.DBModelDispatcher'Create function to create Contact so this modelDispatcher is
|
|
// NotifyModelDispatcher whose DBModelDispatcher has modelManager models.ContactManager
|
|
metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": models.VerifyManager.KeywordPlural()}
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/<id>", prefix, models.VerifyManager.KeywordPlural()),
|
|
middleware(verifyHandler), metadata, "verify", tags)
|
|
|
|
// notification Handler
|
|
modelDispatcher = NewNotifyModelDispatcher(models.NotificationManager)
|
|
metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(notificationHandler), metadata, "send_notifications", tags)
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(listHandler), metadata, "send_notifications", tags)
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/<id>", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(listHandler), metadata, "list_notification_by_id", tags)
|
|
|
|
// config Handler
|
|
modelDispatcher = NewNotifyModelDispatcher(models.ConfigManager)
|
|
metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(configUpdateHandler), metadata, "update_configs", tags)
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(configGetHandler), metadata, "get_configs", tags)
|
|
app.AddHandler2("DELETE",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(configDeleteHandler), metadata, "delete_configs", tags)
|
|
|
|
// email handler for being compatible
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/", prefix, EMAIL_KEYWORDPLURAL),
|
|
middleware(emailConfigUpdateHandler), metadata, "", tags)
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, EMAIL_KEYWORDPLURAL),
|
|
middleware(emailConfigGetHandler), metadata, "", tags)
|
|
app.AddHandler2("DELETE",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, EMAIL_KEYWORDPLURAL),
|
|
middleware(emailConfigDeleteHandler), metadata, "", tags)
|
|
app.AddHandler2("PUT",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, EMAIL_KEYWORDPLURAL),
|
|
middleware(emailConfigUpdateHandler), metadata, "", tags)
|
|
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/", prefix, SMS_KEYWORDPLURAL),
|
|
middleware(smsConfigUpdateHandler), metadata, "", tags)
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, SMS_KEYWORDPLURAL),
|
|
middleware(smsConfigGetHandler), metadata, "", tags)
|
|
app.AddHandler2("DELETE",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, SMS_KEYWORDPLURAL),
|
|
middleware(smsConfigDeleteHandler), metadata, "", tags)
|
|
app.AddHandler2("PUT",
|
|
fmt.Sprintf("%s/%s/<type>", prefix, SMS_KEYWORDPLURAL),
|
|
middleware(smsConfigUpdateHandler), metadata, "", tags)
|
|
|
|
// Template Handler
|
|
modelDispatcher = NewNotifyModelDispatcher(models.TemplateManager)
|
|
metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/<ctype>/update-template", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(templateUpdateHandler), metadata, "update_template", tags)
|
|
// List
|
|
app.AddHandler2("GET",
|
|
fmt.Sprintf("%s/%s", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(listHandler), metadata, "list_template", tags)
|
|
|
|
app.AddHandler2("POST",
|
|
fmt.Sprintf("%s/%s/delete-template", prefix, modelDispatcher.KeywordPlural()),
|
|
middleware(deleteTemplateHandler), metadata, "delete", tags)
|
|
}
|
|
|
|
func templateUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, query, body := fetchEnv(ctx, w, r)
|
|
data, err := body.GetArray(manager.Keyword(), manager.KeywordPlural())
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, httperrors.NewInputParameterError("need %s and %s",
|
|
manager.Keyword(), manager.KeywordPlural()))
|
|
return
|
|
}
|
|
ctype := params["<ctype>"]
|
|
if len(ctype) == 0 {
|
|
httperrors.InputParameterError(w, "ctype of template should not be empty")
|
|
}
|
|
err = manager.UpdateTemplate(ctx, ctype, mergeQueryParams(params, query), data)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
}
|
|
}
|
|
|
|
func deleteTemplateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, _, query, body := fetchEnv(ctx, w, r)
|
|
if !body.Contains("contact_type") {
|
|
httperrors.InputParameterError(w, "miss contact_type")
|
|
return
|
|
}
|
|
ctype, _ := body.GetString("contact_type")
|
|
topic, _ := body.GetString("topic")
|
|
err := manager.DeleteTemplate(ctx, query, ctype, topic)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
}
|
|
}
|
|
|
|
func configDeleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, _, _ := fetchEnv(ctx, w, r)
|
|
err := manager.DeleteConfig(ctx, params)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
}
|
|
}
|
|
|
|
func configGetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, query, _ := fetchEnv(ctx, w, r)
|
|
ret, err := manager.GetConfig(ctx, params, query)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
return
|
|
}
|
|
appsrv.SendJSON(w, ret)
|
|
}
|
|
|
|
func configUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, _, _, body := fetchEnv(ctx, w, r)
|
|
body, err := body.Get(models.ConfigManager.Keyword())
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, httperrors.NewInputParameterError("need config or configs"))
|
|
return
|
|
}
|
|
err = manager.UpdateConfig(ctx, body)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
}
|
|
}
|
|
|
|
func notificationHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, _, _, body := fetchEnv(ctx, w, r)
|
|
data, err := body.Get(manager.Keyword())
|
|
if err != nil {
|
|
httperrors.BadRequestError(w, "request body should contain %s", manager.Keyword())
|
|
return
|
|
}
|
|
if !data.Contains("gid") && !data.Contains("uid") {
|
|
httperrors.MissingParameterError(w, "gid | uid")
|
|
return
|
|
}
|
|
ret, err := manager.CreateNotification(ctx, data)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
return
|
|
}
|
|
appsrv.SendJSON(w, ret)
|
|
}
|
|
|
|
// verify handler
|
|
func verifyHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, query, _ := fetchEnv(ctx, w, r)
|
|
err := manager.Verify(ctx, params, query)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
}
|
|
}
|
|
|
|
// contact update handler
|
|
func contactUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, query, body := fetchEnv(ctx, w, r)
|
|
|
|
var data []jsonutils.JSONObject
|
|
data, err := body.GetArray(manager.Keyword(), manager.KeywordPlural())
|
|
if err != nil {
|
|
log.Errorf("body: %s, err: %s\n", body.String(), err)
|
|
httperrors.GeneralServerError(w, httperrors.NewInputParameterError("need %s and %s",
|
|
manager.Keyword(), manager.KeywordPlural()))
|
|
return
|
|
}
|
|
|
|
uid := params["<uid>"]
|
|
queryDict := mergeQueryParams(params, query)
|
|
update, _ := body.Bool(manager.Keyword(), "update_dingtalk")
|
|
if update {
|
|
dict := queryDict.(*jsonutils.JSONDict)
|
|
dict.Add(jsonutils.JSONTrue, "update_dingtalk")
|
|
}
|
|
err = manager.UpdateContacts(ctx, uid, queryDict, data, nil)
|
|
if err != nil {
|
|
log.Errorf(err.Error())
|
|
httperrors.BadRequestError(w, "")
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// delete contact handler
|
|
func deleteContactHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, _, _, body := fetchEnv(ctx, w, r)
|
|
|
|
var data []jsonutils.JSONObject
|
|
var err error
|
|
if body != nil {
|
|
data, err = body.GetArray(manager.KeywordPlural())
|
|
if err != nil {
|
|
httperrors.BadRequestError(w, "request body should have %s", manager.KeywordPlural())
|
|
return
|
|
}
|
|
}
|
|
err = manager.DeleteContacts(ctx, data)
|
|
|
|
if err != nil {
|
|
log.Errorf("delete contact of %s failed, error: %s", data, err)
|
|
httperrors.GeneralServerError(w, errors.Error("delete failed"))
|
|
}
|
|
}
|
|
|
|
// verify trigger handler
|
|
func verifyTriggerHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, _, body := fetchEnv(ctx, w, r)
|
|
data, err := body.Get(models.ContactManager.Keyword())
|
|
if err != nil {
|
|
httperrors.BadRequestError(w, "request body should have %s", manager.Keyword())
|
|
return
|
|
}
|
|
ret, err := manager.VerifyTrigger(ctx, params, data)
|
|
if err != nil {
|
|
log.Errorf("verifyTrigger failed beacause %s", err)
|
|
httperrors.GeneralServerError(w, err)
|
|
return
|
|
}
|
|
appsrv.SendJSON(w, ret)
|
|
}
|
|
|
|
// list handler for all resource in notify module
|
|
func listHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, query, _ := fetchEnv(ctx, w, r)
|
|
listResult, err := manager.List(ctx, mergeQueryParams(params, query), nil)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
return
|
|
}
|
|
appsrv.SendJSON(w, modulebase.ListResult2JSONWithKey(listResult, manager.KeywordPlural()))
|
|
}
|
|
|
|
func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, params, query, _ := fetchEnv(ctx, w, r)
|
|
listResult, err := manager.List(ctx, mergeQueryParams(params, query), nil)
|
|
if err != nil {
|
|
httperrors.GeneralServerError(w, err)
|
|
return
|
|
}
|
|
var data jsonutils.JSONObject
|
|
if len(listResult.Data) == 0 {
|
|
data = jsonutils.NewDict()
|
|
} else {
|
|
data = listResult.Data[0]
|
|
}
|
|
appsrv.SendJSON(w, wrap(data, manager.Keyword()))
|
|
}
|
|
|
|
func wrap(data jsonutils.JSONObject, key string) jsonutils.JSONObject {
|
|
ret := jsonutils.NewDict()
|
|
ret.Add(data, key)
|
|
return ret
|
|
}
|
|
|
|
// offset ang limit is not useable for here
|
|
func keyStoneUserListHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
manager, _, query, _ := fetchEnv(ctx, w, r)
|
|
haveContacts := jsonutils.QueryBoolean(query, "have_contacts", false)
|
|
|
|
userCred := policy.FetchUserCredential(ctx)
|
|
s := auth.GetSession(ctx, userCred, options.Options.Region, "")
|
|
users, err := modules.UsersV3.List(s, query)
|
|
if err != nil {
|
|
log.Errorf("keystone list error: %s", err)
|
|
httperrors.InternalServerError(w, err.Error())
|
|
return
|
|
}
|
|
q := models.ContactManager.Query("uid").GroupBy("uid")
|
|
row, err := q.Rows()
|
|
if err != nil {
|
|
log.Errorf("get contact's uid error: %s", err)
|
|
httperrors.InternalServerError(w, err.Error())
|
|
return
|
|
}
|
|
defer row.Close()
|
|
uidSet, uid := make(map[string]struct{}), ""
|
|
for row.Next() {
|
|
row.Scan(&uid)
|
|
uidSet[uid] = struct{}{}
|
|
}
|
|
|
|
type sPair struct {
|
|
ID string
|
|
Name string
|
|
}
|
|
newDatas := make([]sPair, 0, len(users.Data))
|
|
|
|
for _, data := range users.Data {
|
|
id, _ := data.GetString("id")
|
|
name, _ := data.GetString("name")
|
|
if _, ok := uidSet[id]; ok {
|
|
if haveContacts {
|
|
newDatas = append(newDatas, sPair{id, name})
|
|
}
|
|
continue
|
|
}
|
|
if haveContacts {
|
|
continue
|
|
}
|
|
newDatas = append(newDatas, sPair{id, name})
|
|
}
|
|
ret := jsonutils.NewDict()
|
|
ret.Add(jsonutils.Marshal(newDatas), manager.Keyword())
|
|
appsrv.SendJSON(w, ret)
|
|
}
|