mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
feat(notify): Available uname in api & Unified template management
1. Uname is availbale now in api via adding "uname=true" in query. So we can use uname in climc which is much more visual and use uid in web client which avoid time-consuming remote calls that may occur. 2. Previously, the template for the message was managed by the individual sending components. Now, these templats was managed uniformly by Notify Service so that we can easily add and remove. 3. Now, failure to send a validation message will trigger a notification to the user via websocket.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -167,17 +167,10 @@ func init() {
|
||||
}
|
||||
R(&ContactsVerifyOptions{}, "contact-verify-trigger", "Trigger contact verify", func(s *mcclient.ClientSession, args *ContactsVerifyOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString(args.CONTACT_TYPE), "contact_type")
|
||||
params.Add(jsonutils.NewString(args.CONTACT), "contact")
|
||||
/*
|
||||
if len(args.Email) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Email), "email")
|
||||
}
|
||||
if len(args.Mobile) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Mobile), "mobile")
|
||||
}
|
||||
*/
|
||||
|
||||
tmpDict := jsonutils.NewDict()
|
||||
tmpDict.Add(jsonutils.NewString(args.CONTACT_TYPE), "contact_type")
|
||||
tmpDict.Add(jsonutils.NewString(args.CONTACT), "contact")
|
||||
params.Add(tmpDict, "contact")
|
||||
_, err := modules.Contacts.PerformAction(s, args.UID, "verify", params)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -37,7 +37,9 @@ func init() {
|
||||
Remark string `help:"Remark or description of the notification"`
|
||||
Group bool `help:"Send to group"`
|
||||
}
|
||||
R(&NotificationCreateOptions{}, "notify", "Send a notification to sb", func(s *mcclient.ClientSession, args *NotificationCreateOptions) error {
|
||||
R(&NotificationCreateOptions{}, "notify", "Send a notification to someones", func(s *mcclient.ClientSession,
|
||||
args *NotificationCreateOptions) error {
|
||||
|
||||
msg := notify.SNotifyMessage{}
|
||||
if args.Group {
|
||||
msg.Gid = args.Uid
|
||||
|
||||
90
cmd/climc/shell/notify_template.go
Normal file
90
cmd/climc/shell/notify_template.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type NotifyTemplateUpdateOptions struct {
|
||||
CONTACTTYPE string `help:"the contanct type, such as 'email', 'mobile'"`
|
||||
Topic string `help:"the topic of temlate, such as 'VERIFY', 'ALARM'"`
|
||||
TemplateType string `help:"the type of template" choices:"content|title|remote"`
|
||||
Content string `help:"the content of template"`
|
||||
}
|
||||
R(&NotifyTemplateUpdateOptions{}, "notify-template-update", "Create, update contact for user", func(s *mcclient.ClientSession,
|
||||
args *NotifyTemplateUpdateOptions) error {
|
||||
arr := jsonutils.NewArray()
|
||||
tmpObj := jsonutils.NewDict()
|
||||
tmpObj.Add(jsonutils.NewString(args.Topic), "topic")
|
||||
tmpObj.Add(jsonutils.NewString(args.TemplateType), "template_type")
|
||||
tmpObj.Add(jsonutils.NewString(args.Content), "content")
|
||||
arr.Add(tmpObj)
|
||||
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(arr, "notifytemplates")
|
||||
|
||||
contact, err := modules.NotifyTemplates.PerformAction(s, args.CONTACTTYPE, "update-template", params)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printObject(contact)
|
||||
return nil
|
||||
})
|
||||
|
||||
type NotifyTemplateDeleteOptions struct {
|
||||
CONTACTTYPE string `help:"the contanct type, such as 'email', 'mobile'"`
|
||||
Topic string `help:"the topic of temlate, such as 'VERIFY', 'ALARM'"`
|
||||
}
|
||||
|
||||
R(&NotifyTemplateDeleteOptions{}, "notify-template-delete", "delete notify template",
|
||||
func(s *mcclient.ClientSession, args *NotifyTemplateDeleteOptions) error {
|
||||
|
||||
tmpObj := jsonutils.NewDict()
|
||||
tmpObj.Add(jsonutils.NewString(args.CONTACTTYPE), "contact_type")
|
||||
tmpObj.Add(jsonutils.NewString(args.Topic), "topic")
|
||||
_, err := modules.NotifyTemplates.DeleteContents(s, tmpObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type NotifyTemplateListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
|
||||
R(&NotifyTemplateListOptions{}, "notify-template-list", "List all notify template",
|
||||
func(s *mcclient.ClientSession, args *NotifyTemplateListOptions) error {
|
||||
|
||||
params, err := args.BaseListOptions.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.NotifyTemplates.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.NotifyTemplates.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -103,10 +103,10 @@ func (manager *SUserCacheManager) fetchUser(
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
return manager.fetchUserFromKeystone(ctx, idStr)
|
||||
return manager.FetchUserFromKeystone(ctx, idStr)
|
||||
}
|
||||
|
||||
func (manager *SUserCacheManager) fetchUserFromKeystone(ctx context.Context, idStr string) (*SUser, error) {
|
||||
func (manager *SUserCacheManager) FetchUserFromKeystone(ctx context.Context, idStr string) (*SUser, error) {
|
||||
if len(idStr) == 0 {
|
||||
log.Debugf("fetch empty user!!!!\n%s", debug.Stack())
|
||||
return nil, fmt.Errorf("Empty idStr")
|
||||
|
||||
@@ -16,6 +16,7 @@ package modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
@@ -44,6 +45,21 @@ func (this *ContactsManager) DoBatchDeleteContacts(s *mcclient.ClientSession, pa
|
||||
return modulebase.Post(this.ResourceManager, s, path, params, this.Keyword)
|
||||
}
|
||||
|
||||
func (this *ContactsManager) PerformAction(session *mcclient.ClientSession, id string, action string,
|
||||
params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
|
||||
path := fmt.Sprintf("/%s/%s/%s?uname=true", this.ContextPath(nil), url.PathEscape(id), url.PathEscape(action))
|
||||
return modulebase.Post(this.ResourceManager, session, path, params, this.KeywordPlural)
|
||||
}
|
||||
|
||||
func (this *ContactsManager) Get(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject,
|
||||
error) {
|
||||
|
||||
q := params.(*jsonutils.JSONDict)
|
||||
q.Add(jsonutils.JSONTrue, "uname")
|
||||
return this.ResourceManager.Get(session, id, params)
|
||||
}
|
||||
|
||||
var (
|
||||
Contacts ContactsManager
|
||||
)
|
||||
|
||||
43
pkg/mcclient/modules/mod_notify_template.go
Normal file
43
pkg/mcclient/modules/mod_notify_template.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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 modules
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
type NotifyTemplateManager struct {
|
||||
modulebase.ResourceManager
|
||||
}
|
||||
|
||||
func (nm *NotifyTemplateManager) DeleteContents(s *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
path := "/templates/delete-template"
|
||||
return modulebase.Post(nm.ResourceManager, s, path, params, "")
|
||||
}
|
||||
|
||||
var (
|
||||
NotifyTemplates NotifyTemplateManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
NotifyTemplates = NotifyTemplateManager{NewNotifyManager("notifytemplate", "notifytemplates",
|
||||
[]string{"Contact_Type", "Topic", "Template_Type"},
|
||||
[]string{})}
|
||||
|
||||
register(&NotifyTemplates)
|
||||
}
|
||||
@@ -42,7 +42,8 @@ type NotificationManager struct {
|
||||
}
|
||||
|
||||
func (manager *NotificationManager) Send(s *mcclient.ClientSession, msg SNotifyMessage) error {
|
||||
_, err := manager.Create(s, jsonutils.Marshal(&msg))
|
||||
path := "/" + manager.KeywordPlural
|
||||
_, err := modulebase.Post(manager.ResourceManager, s, path, jsonutils.Marshal(&msg), manager.KeywordPlural)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
130
pkg/notify/cache/usercache.go
vendored
130
pkg/notify/cache/usercache.go
vendored
@@ -18,26 +18,21 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
type SUserCacheManager struct {
|
||||
db.SKeystoneCacheObjectManager
|
||||
db.SUserCacheManager
|
||||
}
|
||||
|
||||
type SUser struct {
|
||||
db.SKeystoneCacheObject
|
||||
db.SUser
|
||||
}
|
||||
|
||||
func (user *SUser) GetModelManager() db.IModelManager {
|
||||
@@ -46,13 +41,6 @@ func (user *SUser) GetModelManager() db.IModelManager {
|
||||
|
||||
var UserCacheManager *SUserCacheManager
|
||||
|
||||
func init() {
|
||||
UserCacheManager = &SUserCacheManager{
|
||||
db.NewKeystoneCacheObjectManager(SUser{}, "users_cache_tbl", "user", "users")}
|
||||
// log.Debugf("initialize user cache manager %s", UserCacheManager.KeywordPlural())
|
||||
UserCacheManager.SetVirtualObject(UserCacheManager)
|
||||
}
|
||||
|
||||
func RegistUserCredCacheUpdater() {
|
||||
auth.RegisterAuthHook(onAuthCompleteUpdateCache)
|
||||
}
|
||||
@@ -63,97 +51,18 @@ func onAuthCompleteUpdateCache(userCred mcclient.TokenCredential) {
|
||||
|
||||
func (ucm *SUserCacheManager) updateUserCache(userCred mcclient.TokenCredential) {
|
||||
ucm.Save(context.Background(), userCred.GetUserId(), userCred.GetUserName(),
|
||||
userCred.GetDomainId())
|
||||
userCred.GetDomainId(), userCred.GetDomainName())
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) FetchUserByIdOrName(idStr string) (*SUser, error) {
|
||||
obj, err := ucm.SKeystoneCacheObjectManager.FetchByIdOrName(nil, idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*SUser), nil
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) FetchUserById(idStr string) (*SUser, error) {
|
||||
obj, err := ucm.SKeystoneCacheObjectManager.FetchById(idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*SUser), nil
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) FetchUserByName(idStr string) (*SUser, error) {
|
||||
obj, err := ucm.SKeystoneCacheObjectManager.FetchByName(nil, idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*SUser), nil
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) Save(ctx context.Context, idStr string, name string, domainId string) (*SUser, error) {
|
||||
lockman.LockRawObject(ctx, ucm.KeywordPlural(), idStr)
|
||||
defer lockman.ReleaseRawObject(ctx, ucm.KeywordPlural(), idStr)
|
||||
|
||||
objo, err := ucm.FetchById(idStr)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
log.Errorf("FetchTenantbyId fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
if err == nil {
|
||||
obj := objo.(*SUser)
|
||||
if obj.Id == idStr && obj.Name == name && obj.DomainId == domainId {
|
||||
db.Update(obj, func() error {
|
||||
obj.LastCheck = now
|
||||
return nil
|
||||
})
|
||||
return obj, nil
|
||||
}
|
||||
_, err = db.Update(obj, func() error {
|
||||
obj.Id = idStr
|
||||
obj.Name = name
|
||||
obj.DomainId = domainId
|
||||
obj.LastCheck = now
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return obj, nil
|
||||
}
|
||||
} else {
|
||||
objm, err := db.NewModelObject(ucm)
|
||||
obj := objm.(*SUser)
|
||||
obj.Id = idStr
|
||||
obj.Name = name
|
||||
obj.DomainId = domainId
|
||||
obj.LastCheck = now
|
||||
err = ucm.TableSpec().Insert(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return obj, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) fetchUserFromKeystone(ctx context.Context, idStr string) (*SUser, error) {
|
||||
if len(idStr) == 0 {
|
||||
return nil, fmt.Errorf("Empty idStr")
|
||||
}
|
||||
s := auth.GetAdminSession(ctx, consts.GetRegion(), "v3")
|
||||
user, err := modules.UsersV3.GetById(s, idStr, nil)
|
||||
func (ucm *SUserCacheManager) dealErrFromKeystone(err error) error {
|
||||
if err != nil {
|
||||
if je, ok := err.(*httputils.JSONClientError); ok && je.Code == 404 {
|
||||
return nil, sql.ErrNoRows
|
||||
return sql.ErrNoRows
|
||||
} else {
|
||||
return errors.Wrap(err, "fetch User info from keystone")
|
||||
}
|
||||
log.Errorf("fetch project %s fail %s", idStr, err)
|
||||
return nil, errors.Wrap(err, "modules.Projects.Get")
|
||||
}
|
||||
userId, _ := user.GetString("id")
|
||||
userName, _ := user.GetString("name")
|
||||
domainId, _ := user.GetString("domain_id")
|
||||
return ucm.Save(ctx, userId, userName, domainId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) FetchUsersByIDs(ctx context.Context, ids []string) (map[string]SUser, error) {
|
||||
@@ -174,32 +83,21 @@ func (ucm *SUserCacheManager) FetchUsersByIDs(ctx context.Context, ids []string)
|
||||
if _, ok := ret[id]; ok {
|
||||
continue
|
||||
}
|
||||
user, err := ucm.fetchUserFromKeystone(ctx, id)
|
||||
user, err := ucm.FetchUserFromKeystone(ctx, id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret[id] = *user
|
||||
ret[id] = SUser{*user}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) FetchUserByID(ctx context.Context, idStr string, noExpireCheck bool) (*SUser, error) {
|
||||
|
||||
q := ucm.Query().Equals("id", idStr)
|
||||
uobj, err := db.NewModelObject(ucm)
|
||||
func (ucm *SUserCacheManager) FetchUserByIDOrName(ctx context.Context, idStr string) (*SUser, error) {
|
||||
user, err := ucm.SUserCacheManager.FetchUserByIdOrName(ctx, idStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewModelObject")
|
||||
return nil, err
|
||||
}
|
||||
err = q.First(uobj)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "query")
|
||||
} else if uobj != nil {
|
||||
user := uobj.(*SUser)
|
||||
if noExpireCheck || !user.IsExpired() {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
return ucm.fetchUserFromKeystone(ctx, idStr)
|
||||
return &SUser{*user}, nil
|
||||
}
|
||||
|
||||
func (ucm *SUserCacheManager) FetchUserLikeName(ctx context.Context, name string, noExpireCheck bool) ([]SUser,
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
@@ -228,8 +229,8 @@ func (self *NotifyModelDispatcher) VerifyTrigger(ctx context.Context, params map
|
||||
contact, _ := data.GetString("contact")
|
||||
contactType, _ := data.GetString("contact_type")
|
||||
contacts, err := models.ContactManager.FetchByMore(uid, contact, contactType)
|
||||
if err != nil {
|
||||
return nil, errors.Error(fmt.Sprintf(`uid %q don't have contact %q of contact_type %q'`, uid, contact, contactType))
|
||||
if err != nil || len(contacts) == 0 {
|
||||
return nil, errors.Error(fmt.Sprintf("uid '%s' don't have contact '%s' of contact_type '%s'", uid, contact, contactType))
|
||||
}
|
||||
userCred := policy.FetchUserCredential(ctx)
|
||||
scontact := contacts[0]
|
||||
@@ -262,12 +263,16 @@ func (self *NotifyModelDispatcher) VerifyTrigger(ctx context.Context, params map
|
||||
}
|
||||
if scontact.Status == models.CONTACT_VERIFYING {
|
||||
verifications, err := models.VerifyManager.FetchByCID(scontact.ID, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
q = q.Equals("status", models.VERIFICATION_SENT).Desc("created_at")
|
||||
q = q.In("status", []string{models.VERIFICATION_SENT, "init"}).Desc("created_at")
|
||||
return q
|
||||
})
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
if len(verifications) == 0 {
|
||||
// no verifications in status "sent"
|
||||
return makeNewVerify()
|
||||
}
|
||||
current := time.Now()
|
||||
for _, verification := range verifications {
|
||||
if current.After(verification.ExpireAt) {
|
||||
@@ -292,8 +297,11 @@ func (self *NotifyModelDispatcher) DeleteContacts(ctx context.Context, uids2 []j
|
||||
for i := range uids2 {
|
||||
uids[i] = strings.Trim(uids2[i].String(), `"`)
|
||||
}
|
||||
|
||||
contacts, err := models.ContactManager.FetchByUIDs(uids)
|
||||
uname := false
|
||||
if v := ctx.Value("uname"); v != nil {
|
||||
uname = true
|
||||
}
|
||||
contacts, err := models.ContactManager.FetchByUIDs(uids, uname)
|
||||
if err != nil {
|
||||
return httperrors.NewGeneralError(err)
|
||||
}
|
||||
@@ -425,6 +433,91 @@ func (self *NotifyModelDispatcher) UpdateContacts(ctx context.Context, idstr str
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *NotifyModelDispatcher) UpdateTemplate(ctx context.Context, ctype string, query jsonutils.JSONObject,
|
||||
datas []jsonutils.JSONObject) error {
|
||||
|
||||
type sTemplate struct {
|
||||
ContactType string
|
||||
Topic string
|
||||
TemplateType string
|
||||
Content string
|
||||
}
|
||||
templates := make([]sTemplate, 0, len(datas))
|
||||
topics := sets.NewString()
|
||||
for _, data := range datas {
|
||||
var tem sTemplate
|
||||
err := data.Unmarshal(&tem)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "data.Unmarshal")
|
||||
}
|
||||
if tem.TemplateType != models.TEMPLATE_TYPE_REMOTE && tem.TemplateType != models.
|
||||
TEMPLATE_TYPE_CONTENT && tem.TemplateType != models.TEMPLATE_TYPE_TITLE {
|
||||
|
||||
return httperrors.NewInputParameterError("no support for such template type '%s'", tem.TemplateType)
|
||||
}
|
||||
tem.ContactType = ctype
|
||||
tem.Topic = strings.ToUpper(tem.Topic)
|
||||
templates = append(templates, tem)
|
||||
topics.Insert(tem.Topic)
|
||||
}
|
||||
|
||||
q := models.TemplateManager.Query().Equals("contact_type", ctype).In("topic", topics.List())
|
||||
templateModels := make([]models.STemplate, 0, 1)
|
||||
err := db.FetchModelObjects(models.ContactManager, q, &templateModels)
|
||||
if err != nil {
|
||||
log.Errorf("db.FetchModelObjects sql: %s", q.String())
|
||||
return errors.Wrap(err, "db.FetchModelObjects")
|
||||
}
|
||||
|
||||
templateMaps := make(map[string]*models.STemplate)
|
||||
for i := range templateModels {
|
||||
k := fmt.Sprintf("%s/%s/%s", templateModels[i].ContactType, templateModels[i].Topic, templateModels[i].TemplateType)
|
||||
templateMaps[k] = &templateModels[i]
|
||||
}
|
||||
|
||||
userCred := policy.FetchUserCredential(ctx)
|
||||
for _, tem := range templates {
|
||||
k := fmt.Sprintf("%s/%s/%s", tem.ContactType, tem.Topic, tem.TemplateType)
|
||||
if tmod, ok := templateMaps[k]; ok {
|
||||
updateData := jsonutils.NewDict()
|
||||
updateData.Add(jsonutils.NewString(tem.Content), "content")
|
||||
err = UpdateItem(models.TemplateManager, tmod, ctx, userCred, query, updateData)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "fail to update template '%s'", tmod.ID)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err = self.Create(ctx, query, jsonutils.Marshal(tem), nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "fail to create template(contact_type: %s, topic: %s, template_type: %s)",
|
||||
tem.ContactType, tem.Topic, tem.TemplateType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *NotifyModelDispatcher) DeleteTemplate(ctx context.Context, query jsonutils.JSONObject, ctype, topic string) error {
|
||||
|
||||
q := models.TemplateManager.Query().Equals("contact_type", ctype)
|
||||
if len(topic) != 0 {
|
||||
q = q.Equals("topic", topic)
|
||||
}
|
||||
templates := make([]models.STemplate, 0, 1)
|
||||
err := db.FetchModelObjects(models.TemplateManager, q, &templates)
|
||||
if err != nil {
|
||||
log.Errorf("db.FetchModelObjects sql: %s", q.String())
|
||||
return errors.Wrap(err, "db.FetchModelObjects")
|
||||
}
|
||||
userCred := policy.FetchUserCredential(ctx)
|
||||
for i := range templates {
|
||||
err = DeleteItem(&templates[i], ctx, userCred, query, jsonutils.JSONNull)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "fail to delete template '%s'", templates[i].ID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetchEnv fetch handler, params, query and body from ctx(context.Context)
|
||||
func fetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (*NotifyModelDispatcher, map[string]string, jsonutils.JSONObject, jsonutils.JSONObject) {
|
||||
params, query, body := appsrv.FetchEnv(ctx, w, r)
|
||||
|
||||
@@ -18,12 +18,15 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"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"
|
||||
@@ -44,9 +47,35 @@ func InitHandlers(app *appsrv.Application) {
|
||||
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
|
||||
@@ -56,89 +85,167 @@ func AddNotifyDispatcher(prefix string, app *appsrv.Application) {
|
||||
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()),
|
||||
modelDispatcher.Filter(contactUpdateHandler), metadata, "contact_update", tags)
|
||||
middleware(contactUpdateHandler), metadata, "contact_update", tags)
|
||||
// List
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(listHandler), metadata, "list_contacts", tags)
|
||||
middleware(listHandler), metadata, "list_contacts", tags)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/users", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(keyStoneUserListHandler), metadata, "list_users", tags)
|
||||
middleware(keyStoneUserListHandler), metadata, "list_users", tags)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/<uid>", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(getHandler), metadata, "list_by_uid", tags)
|
||||
middleware(getHandler), metadata, "list_by_uid", tags)
|
||||
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/delete-contact", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(deleteContactHandler), metadata, "delete", tags)
|
||||
middleware(deleteContactHandler), metadata, "delete", tags)
|
||||
|
||||
// verify-trigger
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/<uid>/verify", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(verifyTriggerHandler), metadata, "verify_trigger", tags)
|
||||
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()),
|
||||
modelDispatcher.Filter(verifyHandler), metadata, "verify", tags)
|
||||
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()),
|
||||
modelDispatcher.Filter(notificationHandler), metadata, "send_notifications", tags)
|
||||
middleware(notificationHandler), metadata, "send_notifications", tags)
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(listHandler), metadata, "send_notifications", tags)
|
||||
middleware(listHandler), metadata, "send_notifications", tags)
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/<id>", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(listHandler), metadata, "list_notification_by_id", tags)
|
||||
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()),
|
||||
modelDispatcher.Filter(configUpdateHandler), metadata, "update_configs", tags)
|
||||
middleware(configUpdateHandler), metadata, "update_configs", tags)
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(configGetHandler), metadata, "get_configs", tags)
|
||||
middleware(configGetHandler), metadata, "get_configs", tags)
|
||||
app.AddHandler2("DELETE",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, modelDispatcher.KeywordPlural()),
|
||||
modelDispatcher.Filter(configDeleteHandler), metadata, "delete_configs", tags)
|
||||
middleware(configDeleteHandler), metadata, "delete_configs", tags)
|
||||
|
||||
// email handler for being compatible
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/", prefix, EMAIL_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(emailConfigUpdateHandler), metadata, "", tags)
|
||||
middleware(emailConfigUpdateHandler), metadata, "", tags)
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, EMAIL_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(emailConfigGetHandler), metadata, "", tags)
|
||||
middleware(emailConfigGetHandler), metadata, "", tags)
|
||||
app.AddHandler2("DELETE",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, EMAIL_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(emailConfigDeleteHandler), metadata, "", tags)
|
||||
middleware(emailConfigDeleteHandler), metadata, "", tags)
|
||||
app.AddHandler2("PUT",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, EMAIL_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(emailConfigUpdateHandler), metadata, "", tags)
|
||||
middleware(emailConfigUpdateHandler), metadata, "", tags)
|
||||
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/", prefix, SMS_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(smsConfigUpdateHandler), metadata, "", tags)
|
||||
middleware(smsConfigUpdateHandler), metadata, "", tags)
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, SMS_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(smsConfigGetHandler), metadata, "", tags)
|
||||
middleware(smsConfigGetHandler), metadata, "", tags)
|
||||
app.AddHandler2("DELETE",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, SMS_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(smsConfigDeleteHandler), metadata, "", tags)
|
||||
middleware(smsConfigDeleteHandler), metadata, "", tags)
|
||||
app.AddHandler2("PUT",
|
||||
fmt.Sprintf("%s/%s/<type>", prefix, SMS_KEYWORDPLURAL),
|
||||
modelDispatcher.Filter(smsConfigUpdateHandler), metadata, "", tags)
|
||||
middleware(smsConfigUpdateHandler), metadata, "", tags)
|
||||
|
||||
// Contact 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)
|
||||
|
||||
app.AddHandler2("POST",
|
||||
fmt.Sprintf("%s/%s/email-url", prefix, modelDispatcher.KeywordPlural()),
|
||||
middleware(updateEmailUrlHandler), metadata, "update_email_url", tags)
|
||||
|
||||
app.AddHandler2("GET",
|
||||
fmt.Sprintf("%s/%s/email-url", prefix, modelDispatcher.KeywordPlural()),
|
||||
middleware(getEmailUrlHandler), metadata, "get_email_url", tags)
|
||||
}
|
||||
|
||||
func updateEmailUrlHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
userCred := policy.FetchUserCredential(ctx)
|
||||
if !userCred.HasSystemAdminPrivilege() {
|
||||
httperrors.ForbiddenError(w, "only system admin can update email url")
|
||||
return
|
||||
}
|
||||
_, _, _, body := fetchEnv(ctx, w, r)
|
||||
if !body.Contains("email_url") {
|
||||
httperrors.InputParameterError(w, "miss email_url")
|
||||
}
|
||||
emailUrl, _ := body.GetString("email_url")
|
||||
eUrl, err := url.Parse(emailUrl)
|
||||
if err != nil {
|
||||
httperrors.InputParameterError(w, "invalid url")
|
||||
}
|
||||
models.TemplateManager.SetEmailUrl(eUrl.String())
|
||||
}
|
||||
|
||||
func getEmailUrlHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Add(jsonutils.NewString(models.TemplateManager.GetEmailUrl()), "email_url")
|
||||
appsrv.Send(w, ret.PrettyString())
|
||||
}
|
||||
|
||||
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 or %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) {
|
||||
@@ -211,14 +318,8 @@ func contactUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Re
|
||||
manager.KeywordPlural()))
|
||||
return
|
||||
}
|
||||
// check that if the uid is exist
|
||||
|
||||
uid := params["<uid>"]
|
||||
_, err = utils.GetUserByID(ctx, uid)
|
||||
if err != nil {
|
||||
log.Errorf(`uid %q not found`, uid)
|
||||
httperrors.NotFoundError(w, "Uid Not Found")
|
||||
return
|
||||
}
|
||||
queryDict := mergeQueryParams(params, query)
|
||||
update, _ := body.Bool(manager.Keyword(), "update_dingtalk")
|
||||
if update {
|
||||
@@ -258,7 +359,7 @@ func deleteContactHandler(ctx context.Context, w http.ResponseWriter, r *http.Re
|
||||
// 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())
|
||||
data, err := body.Get(models.ContactManager.Keyword(), models.ContactManager.KeywordPlural())
|
||||
if err != nil {
|
||||
httperrors.BadRequestError(w, "request body should have %s", manager.KeywordPlural())
|
||||
return
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
|
||||
)
|
||||
|
||||
type INotifyService interface {
|
||||
@@ -34,4 +35,8 @@ type IServiceConfigStore interface {
|
||||
SetConfig(serviceName string, config SConfig) error
|
||||
}
|
||||
|
||||
type ITemplateStore interface {
|
||||
NotifyFilter(contactType, topic, msg string) (params apis.SendParams, err error)
|
||||
}
|
||||
|
||||
type SConfig map[string]string
|
||||
|
||||
@@ -31,6 +31,7 @@ func InitDB() error {
|
||||
VerifyManager,
|
||||
NotificationManager,
|
||||
ConfigManager,
|
||||
TemplateManager,
|
||||
} {
|
||||
err := manager.InitializeData()
|
||||
if err != nil {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -99,20 +100,57 @@ func (self *SContactManager) InitializeData() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SContactManager) FetchByUIDs(uids []string) ([]SContact, error) {
|
||||
func (self *SContactManager) FetchByUIDs(uids []string, uname bool) ([]SContact, error) {
|
||||
var err error
|
||||
if uname {
|
||||
uids, err = self._UIDsFromUIDOrName(uids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
q := self.Query()
|
||||
q = q.Filter(sqlchemy.In(q.Field("uid"), uids))
|
||||
records := make([]SContact, 0, len(uids))
|
||||
err := db.FetchModelObjects(self, q, &records)
|
||||
err = db.FetchModelObjects(self, q, &records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (self *SContactManager) _UIDsFromUIDOrName(uidStrs []string) ([]string, error) {
|
||||
users, err := utils.GetUsersWithoutRemote(uidStrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uids := make([]string, 0, len(uidStrs))
|
||||
uidSet := sets.NewString(uidStrs...)
|
||||
var (
|
||||
uid string
|
||||
uname string
|
||||
)
|
||||
for i := range users {
|
||||
uid = users[i].Id
|
||||
uname = users[i].Name
|
||||
if uidSet.Has(uid) {
|
||||
uids = append(uids, uid)
|
||||
uidSet.Delete(uid)
|
||||
continue
|
||||
}
|
||||
if uidSet.Has(uname) {
|
||||
uids = append(uids, uid)
|
||||
uidSet.Delete(uname)
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, uid = range uidSet.UnsortedList() {
|
||||
uids = append(uids, uid)
|
||||
}
|
||||
return uids, nil
|
||||
}
|
||||
|
||||
func (self *SContactManager) FetchByUIDAndCType(uid string, contactTypes []string) ([]SContact, error) {
|
||||
q := self.Query("id", "uid", "contact_type", "contact", "enabled")
|
||||
q = q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("uid"), uid), sqlchemy.In(q.Field("contact_type"), contactTypes)))
|
||||
q := self.Query("id", "uid", "contact_type", "contact", "enabled").Equals("uid", uid).In("contact_type", contactTypes)
|
||||
records := make([]SContact, 0, len(contactTypes))
|
||||
err := db.FetchModelObjects(self, q, &records)
|
||||
if err != nil {
|
||||
@@ -122,8 +160,7 @@ func (self *SContactManager) FetchByUIDAndCType(uid string, contactTypes []strin
|
||||
}
|
||||
|
||||
func (self *SContactManager) FetchByMore(uid, contact, contactType string) ([]SContact, error) {
|
||||
q := self.Query()
|
||||
q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("uid"), uid), sqlchemy.Equals(q.Field("contact"), contact), sqlchemy.Equals(q.Field("contact_type"), contactType)))
|
||||
q := self.Query().Equals("uid", uid).Equals("contact", contact).Equals("contact_type", contactType)
|
||||
records := make([]SContact, 0, 1)
|
||||
err := db.FetchModelObjects(self, q, &records)
|
||||
if err != nil {
|
||||
@@ -215,6 +252,12 @@ func (self *SContactManager) GetAllNotify(ctx context.Context, ids []string, con
|
||||
|
||||
q := self.Query()
|
||||
if !group {
|
||||
if v := ctx.Value("uname"); v != nil {
|
||||
ids, err = self._UIDsFromUIDOrName(ids)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail to transfer array of UID or Uname to UIDs")
|
||||
}
|
||||
}
|
||||
uids = ids
|
||||
} else {
|
||||
uid := make([]string, 0)
|
||||
@@ -225,6 +268,7 @@ func (self *SContactManager) GetAllNotify(ctx context.Context, ids []string, con
|
||||
}
|
||||
uid = append(uid, tmpUids...)
|
||||
}
|
||||
uids = uid
|
||||
}
|
||||
q.Filter(sqlchemy.AND(sqlchemy.In(q.Field("uid"), uids), sqlchemy.Equals(q.Field("contact_type"),
|
||||
contactType), sqlchemy.Equals(q.Field("status"), CONTACT_VERIFIED)))
|
||||
|
||||
@@ -468,11 +468,12 @@ func sendWithoutUserCred(notifications []SNotification) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func ReSend(minutes int) {
|
||||
scope := time.Duration(minutes) * time.Minute
|
||||
func ReSend(seconds int) {
|
||||
scope := time.Duration(seconds) * time.Second
|
||||
notifications, err := NotificationManager.FetchNotOK(time.Now().Add(-scope))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debugf("Start to resend message with a total of %d", len(notifications))
|
||||
sendWithoutUserCred(notifications)
|
||||
}
|
||||
|
||||
198
pkg/notify/models/mod_template.go
Normal file
198
pkg/notify/models/mod_template.go
Normal file
@@ -0,0 +1,198 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
ptem "text/template"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
|
||||
"yunion.io/x/onecloud/pkg/notify/template"
|
||||
)
|
||||
|
||||
type STemplateManager struct {
|
||||
SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
var TemplateManager *STemplateManager
|
||||
|
||||
func init() {
|
||||
TemplateManager = &STemplateManager{
|
||||
SStandaloneResourceBaseManager: NewStandaloneResourceBaseManager(
|
||||
STemplate{},
|
||||
"notify_t_template",
|
||||
"notifytemplate",
|
||||
"notifytemplates",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
TEMPLATE_TYPE_TITLE = "title"
|
||||
TEMPLATE_TYPE_CONTENT = "content"
|
||||
TEMPLATE_TYPE_REMOTE = "remote"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultEmailUrl = ""
|
||||
EmailUrl = ""
|
||||
EmailUrlLock sync.RWMutex
|
||||
)
|
||||
|
||||
type STemplate struct {
|
||||
SStandaloneResourceBase
|
||||
|
||||
ContactType string `width:"16" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
Topic string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
|
||||
// title | content | remote
|
||||
TemplateType string `width:"10" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
Content string `length:"text" nullable:"false" create:"required" get:"user" list:"user"`
|
||||
}
|
||||
|
||||
func (tm *STemplateManager) GetEmailUrl() string {
|
||||
EmailUrlLock.RLock()
|
||||
defer EmailUrlLock.RUnlock()
|
||||
if len(EmailUrl) == 0 {
|
||||
return DefaultEmailUrl
|
||||
}
|
||||
return EmailUrl
|
||||
}
|
||||
|
||||
func (tm *STemplateManager) SetEmailUrl(url string) {
|
||||
EmailUrlLock.Lock()
|
||||
defer EmailUrlLock.Unlock()
|
||||
EmailUrl = url
|
||||
}
|
||||
|
||||
func (tm *STemplateManager) InitializeData() error {
|
||||
q := tm.Query().Equals("contact_type", "email").Equals("topic", "VERIFY").Equals("template_type", "content")
|
||||
count, _ := q.CountWithError()
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
content, err := ioutil.ReadFile(template.EMAIL_VERIFY_CONTENT_PATH)
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "os.Open for '%s'", template.EMAIL_VERIFY_CONTENT_PATH)
|
||||
}
|
||||
contentTem := STemplate{
|
||||
ContactType: "email",
|
||||
Topic: "VERIFY",
|
||||
TemplateType: "content",
|
||||
Content: string(content),
|
||||
}
|
||||
titleTem := STemplate{
|
||||
ContactType: "email",
|
||||
Topic: "VERIFY",
|
||||
TemplateType: "title",
|
||||
Content: template.EMAIL_VERIFY_TITLE,
|
||||
}
|
||||
err = tm.TableSpec().InsertOrUpdate(&contentTem)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "sqlchemy.TableSpec.InsertOrUpdate")
|
||||
}
|
||||
tm.TableSpec().InsertOrUpdate(&titleTem)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "sqlchemy.TableSpec.InsertOrUpdate")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyFilter will return the title and content generated by corresponding template.
|
||||
// Local cache about common template will be considered in case of performance issues.
|
||||
func (tm *STemplateManager) NotifyFilter(contactType, topic, msg string) (params apis.SendParams, err error) {
|
||||
params.Topic = topic
|
||||
templates := make([]STemplate, 0, 3)
|
||||
q := tm.Query().Equals("contact_type", contactType).Equals("topic", strings.ToUpper(topic))
|
||||
err = db.FetchModelObjects(tm, q, &templates)
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
// no such template, return as is
|
||||
params.Title = topic
|
||||
params.Message = msg
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "db.FetchModelObjects")
|
||||
return
|
||||
}
|
||||
for _, template := range templates {
|
||||
var title, content string
|
||||
switch template.TemplateType {
|
||||
case TEMPLATE_TYPE_TITLE:
|
||||
title, err = template.Execute(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params.Title = title
|
||||
case TEMPLATE_TYPE_CONTENT:
|
||||
content, err = template.Execute(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
params.Message = content
|
||||
case TEMPLATE_TYPE_REMOTE:
|
||||
params.RemoteTemplate = template.Content
|
||||
params.Message = msg
|
||||
default:
|
||||
err = errors.Error("no support template type")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tm *STemplate) Execute(str string) (string, error) {
|
||||
tem, err := ptem.New("tmp").Parse(tm.Content)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "Template.Parse for template %s", tm.GetId())
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
tmpMap := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(str), &tmpMap)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "json.Unmarshal")
|
||||
}
|
||||
err = tem.Execute(&buffer, tmpMap)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "template,Execute")
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
func (manager *STemplateManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
|
||||
ty, _ := data.GetString("template_type")
|
||||
if ty != TEMPLATE_TYPE_TITLE && ty != TEMPLATE_TYPE_CONTENT && ty != TEMPLATE_TYPE_REMOTE {
|
||||
return nil, httperrors.NewInputParameterError("no such support for tempalte type %s", ty)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -25,8 +25,9 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/notify/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
|
||||
"yunion.io/x/onecloud/pkg/notify/utils"
|
||||
)
|
||||
|
||||
@@ -54,7 +55,7 @@ func sendone(ctx context.Context, userCred mcclient.TokenCredential, notificatio
|
||||
err = NotifyService.Send(ctx, notification.ContactType, contact, notification.Topic, notification.Msg,
|
||||
notification.Priority)
|
||||
if err != nil {
|
||||
log.Errorf("Send notification failed because that %s.", err.Error())
|
||||
log.Errorf("Send notification failed: %s.", err.Error())
|
||||
notification.SetStatus(userCred, NOTIFY_FAIL, err.Error())
|
||||
} else {
|
||||
log.Debugf("send notification successfully")
|
||||
@@ -82,7 +83,7 @@ func sendVerifyMessage(ctx context.Context, userCred mcclient.TokenCredential, v
|
||||
)
|
||||
processId, token := verify.ID, verify.Token
|
||||
if contactType == "email" {
|
||||
emailUrl := strings.Replace(options.Options.VerifyEmailUrl, "{0}", processId, 1)
|
||||
emailUrl := strings.Replace(TemplateManager.GetEmailUrl(), "{0}", processId, 1)
|
||||
emailUrl = strings.Replace(emailUrl, "{1}", token, 1)
|
||||
|
||||
// get uName
|
||||
@@ -105,7 +106,10 @@ func sendVerifyMessage(ctx context.Context, userCred mcclient.TokenCredential, v
|
||||
err = NotifyService.Send(ctx, contactType, contact, "verify", msg, "")
|
||||
if err != nil {
|
||||
verify.SetStatus(userCred, VERIFICATION_SENT_FAIL, "")
|
||||
log.Errorf("Send verify message failed because that %s.", err.Error())
|
||||
// notify the uid through the webconsole
|
||||
notifyclient.RawNotify([]string{uid}, false, notify.NotifyByWebConsole, notify.NotifyPriorityCritical,
|
||||
"Send Verify Message Failed", jsonutils.NewString(err.Error()))
|
||||
log.Errorf("Send verify message failed: %s.", err.Error())
|
||||
return
|
||||
}
|
||||
verify.SetStatus(userCred, VERIFICATION_SENT, "")
|
||||
|
||||
@@ -22,12 +22,13 @@ type NotifyOption struct {
|
||||
options.CommonOptions
|
||||
options.DBOptions
|
||||
|
||||
DingtalkEnabled bool `help:"Enable dingtalk"`
|
||||
SocketFileDir string `help:"Socket file directory" default:"/etc/yunion/notify"`
|
||||
UpdateInterval int `help:"Update send services interval(unit:s)" default:"30"`
|
||||
VerifyEmailUrl string `help:"url of verify email"`
|
||||
ReSendScope int `help:"Resend all messages that have not been sent successfully within ReSendScope minutes"`
|
||||
InitNotificationScope int `help:"initialize data of notification with in InitNotificationScope hours" default:"100"`
|
||||
DingtalkEnabled bool `help:"Enable dingtalk"`
|
||||
SocketFileDir string `help:"Socket file directory" default:"/etc/yunion/notify"`
|
||||
UpdateInterval int `help:"Update send services interval(unit:s)" default:"30"`
|
||||
VerifyEmailUrl string `help:"url of verify email"`
|
||||
ReSendScope int `help:"Resend all messages that have not been sent successfully within ReSendScope
|
||||
seconds"`
|
||||
InitNotificationScope int `help:"initialize data of notification with in InitNotificationScope hours" default:"100"`
|
||||
}
|
||||
|
||||
var Options NotifyOption
|
||||
|
||||
@@ -40,10 +40,12 @@ var _ = math.Inf
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type SendParams struct {
|
||||
Contact string `protobuf:"bytes,1,opt,name=contact,proto3" json:"contact,omitempty"`
|
||||
Topic string `protobuf:"bytes,2,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
|
||||
Priority string `protobuf:"bytes,4,opt,name=Priority,proto3" json:"Priority,omitempty"`
|
||||
Contact string `protobuf:"bytes,1,opt,name=Contact,proto3" json:"Contact,omitempty"`
|
||||
Topic string `protobuf:"bytes,2,opt,name=Topic,proto3" json:"Topic,omitempty"`
|
||||
Title string `protobuf:"bytes,3,opt,name=Title,proto3" json:"Title,omitempty"`
|
||||
Message string `protobuf:"bytes,4,opt,name=Message,proto3" json:"Message,omitempty"`
|
||||
Priority string `protobuf:"bytes,5,opt,name=Priority,proto3" json:"Priority,omitempty"`
|
||||
RemoteTemplate string `protobuf:"bytes,6,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -88,6 +90,13 @@ func (m *SendParams) GetTopic() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SendParams) GetTitle() string {
|
||||
if m != nil {
|
||||
return m.Title
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SendParams) GetMessage() string {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
@@ -102,6 +111,13 @@ func (m *SendParams) GetPriority() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SendParams) GetRemoteTemplate() string {
|
||||
if m != nil {
|
||||
return m.RemoteTemplate
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type UpdateConfigParams struct {
|
||||
Configs map[string]string `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
@@ -262,28 +278,30 @@ func init() {
|
||||
func init() { proto.RegisterFile("send_server.proto", fileDescriptor_63fdd68f7eb311f9) }
|
||||
|
||||
var fileDescriptor_63fdd68f7eb311f9 = []byte{
|
||||
// 325 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xcd, 0x4a, 0x03, 0x31,
|
||||
0x10, 0xc7, 0xd9, 0x7e, 0xda, 0x69, 0x91, 0x1a, 0x8b, 0xc4, 0x3d, 0x95, 0x85, 0x42, 0x2f, 0xee,
|
||||
0xa1, 0x22, 0x48, 0x2f, 0xa2, 0x52, 0x3c, 0x09, 0xa5, 0xd2, 0xb3, 0xa4, 0xdd, 0xe9, 0x12, 0xec,
|
||||
0x6e, 0x96, 0x24, 0x2d, 0xec, 0x63, 0xf8, 0x2a, 0x3e, 0xa1, 0xe4, 0x63, 0xb5, 0xc5, 0x7a, 0xcb,
|
||||
0x6f, 0x66, 0xfe, 0x99, 0xf9, 0x4f, 0x02, 0x17, 0x0a, 0xf3, 0xe4, 0x5d, 0xa1, 0xdc, 0xa3, 0x8c,
|
||||
0x0b, 0x29, 0xb4, 0x20, 0x0d, 0x56, 0x70, 0x15, 0x49, 0x80, 0x37, 0xcc, 0x93, 0x39, 0x93, 0x2c,
|
||||
0x53, 0x84, 0x42, 0x7b, 0x2d, 0x72, 0xcd, 0xd6, 0x9a, 0x06, 0xc3, 0x60, 0xdc, 0x59, 0x54, 0x48,
|
||||
0x06, 0xd0, 0xd4, 0xa2, 0xe0, 0x6b, 0x5a, 0xb3, 0x71, 0x07, 0xa6, 0x3e, 0x43, 0xa5, 0x58, 0x8a,
|
||||
0xb4, 0xee, 0xea, 0x3d, 0x92, 0x10, 0xce, 0xe6, 0x92, 0x0b, 0xc9, 0x75, 0x49, 0x1b, 0x36, 0xf5,
|
||||
0xc3, 0xd1, 0x67, 0x00, 0x64, 0x59, 0x24, 0x4c, 0xe3, 0xb3, 0xc8, 0x37, 0x3c, 0xf5, 0xcd, 0x1f,
|
||||
0x6c, 0xf3, 0x0d, 0x4f, 0x15, 0x0d, 0x86, 0xf5, 0x71, 0x77, 0x32, 0x8a, 0xcd, 0x88, 0xf1, 0xdf,
|
||||
0xd2, 0xd8, 0x81, 0x9a, 0xe5, 0x5a, 0x96, 0x8b, 0x4a, 0x15, 0x4e, 0xa1, 0x77, 0x98, 0x20, 0x7d,
|
||||
0xa8, 0x7f, 0x60, 0xe9, 0x9d, 0x98, 0xa3, 0x71, 0xb1, 0x67, 0xdb, 0x1d, 0x56, 0x2e, 0x2c, 0x4c,
|
||||
0x6b, 0xf7, 0x41, 0x14, 0xc3, 0x60, 0xa9, 0x50, 0xf2, 0xe4, 0xa9, 0x7c, 0x15, 0x2b, 0xbe, 0x45,
|
||||
0x3f, 0xd4, 0x15, 0xb4, 0x32, 0xcb, 0xfe, 0x1a, 0x4f, 0x51, 0x1b, 0x9a, 0xb3, 0xac, 0xd0, 0x65,
|
||||
0x74, 0x03, 0x97, 0xc7, 0xc2, 0x05, 0x16, 0xdb, 0xd2, 0xe8, 0x76, 0x36, 0x5c, 0xe9, 0x1c, 0x4d,
|
||||
0xbe, 0x02, 0xe8, 0x98, 0x85, 0x3f, 0xa6, 0x98, 0x6b, 0x32, 0x82, 0x86, 0x01, 0xd2, 0x77, 0x4e,
|
||||
0x7f, 0x5f, 0x22, 0xec, 0xba, 0x88, 0xed, 0x41, 0xee, 0xa0, 0x77, 0xb8, 0x04, 0x42, 0xff, 0x5b,
|
||||
0xcc, 0xb1, 0xec, 0x05, 0xce, 0x8f, 0x47, 0x23, 0xa1, 0x17, 0x9e, 0x70, 0x1a, 0x5e, 0x9f, 0xca,
|
||||
0x59, 0x33, 0xab, 0x96, 0xfd, 0x31, 0xb7, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x49, 0x12, 0x30,
|
||||
0x1e, 0x46, 0x02, 0x00, 0x00,
|
||||
// 357 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xcf, 0x6a, 0xe3, 0x30,
|
||||
0x10, 0xc6, 0x71, 0xfe, 0x6e, 0x26, 0x21, 0x64, 0xb5, 0x61, 0xd1, 0xfa, 0x14, 0x0c, 0x59, 0x72,
|
||||
0x59, 0x1f, 0xb2, 0x2c, 0x2c, 0xb9, 0x94, 0x36, 0x84, 0x9e, 0x02, 0xc1, 0x4d, 0xce, 0x45, 0x89,
|
||||
0xa7, 0x41, 0xd4, 0xb6, 0x8c, 0xa4, 0x04, 0xfc, 0x18, 0x7d, 0x93, 0xd2, 0x27, 0x2c, 0x92, 0xe5,
|
||||
0x36, 0x69, 0xd3, 0x9b, 0x7f, 0xdf, 0xe8, 0x1b, 0xcf, 0x37, 0x12, 0x7c, 0x57, 0x98, 0xc5, 0xf7,
|
||||
0x0a, 0xe5, 0x11, 0x65, 0x98, 0x4b, 0xa1, 0x05, 0x69, 0xb0, 0x9c, 0xab, 0xe0, 0xd9, 0x03, 0xb8,
|
||||
0xc3, 0x2c, 0x5e, 0x31, 0xc9, 0x52, 0x45, 0x28, 0xb4, 0xe7, 0x22, 0xd3, 0x6c, 0xa7, 0xa9, 0x37,
|
||||
0xf2, 0x26, 0x9d, 0xa8, 0x42, 0x32, 0x84, 0xe6, 0x5a, 0xe4, 0x7c, 0x47, 0x6b, 0x56, 0x2f, 0xc1,
|
||||
0xaa, 0x5c, 0x27, 0x48, 0xeb, 0x4e, 0x35, 0x60, 0xba, 0x2c, 0x51, 0x29, 0xb6, 0x47, 0xda, 0x28,
|
||||
0xbb, 0x38, 0x24, 0x3e, 0x7c, 0x5b, 0x49, 0x2e, 0x24, 0xd7, 0x05, 0x6d, 0xda, 0xd2, 0x1b, 0x93,
|
||||
0xdf, 0xd0, 0x8f, 0x30, 0x15, 0x1a, 0xd7, 0x98, 0xe6, 0x09, 0xd3, 0x48, 0x5b, 0xf6, 0xc4, 0x07,
|
||||
0x35, 0x78, 0xf2, 0x80, 0x6c, 0xf2, 0x98, 0x69, 0x9c, 0x8b, 0xec, 0x81, 0xef, 0xdd, 0xe8, 0x57,
|
||||
0xd0, 0xde, 0x59, 0x56, 0xd4, 0x1b, 0xd5, 0x27, 0xdd, 0xe9, 0x38, 0x34, 0x09, 0xc3, 0xcf, 0x47,
|
||||
0xc3, 0x12, 0xd4, 0x22, 0xd3, 0xb2, 0x88, 0x2a, 0x97, 0x3f, 0x83, 0xde, 0x69, 0x81, 0x0c, 0xa0,
|
||||
0xfe, 0x88, 0x85, 0xdb, 0x83, 0xf9, 0x34, 0x69, 0x8f, 0x2c, 0x39, 0x60, 0xb5, 0x03, 0x0b, 0xb3,
|
||||
0xda, 0x7f, 0x2f, 0x08, 0x61, 0xb8, 0x51, 0x28, 0x79, 0x7c, 0x53, 0x2c, 0xc5, 0x96, 0x27, 0xe8,
|
||||
0x86, 0xfa, 0x09, 0xad, 0xd4, 0xb2, 0x6b, 0xe3, 0x28, 0x68, 0x43, 0x73, 0x91, 0xe6, 0xba, 0x08,
|
||||
0xfe, 0xc0, 0x8f, 0x73, 0x63, 0x84, 0x79, 0x52, 0x18, 0xdf, 0xc1, 0xca, 0x95, 0xaf, 0xa4, 0xe9,
|
||||
0x8b, 0x07, 0x1d, 0x73, 0x5d, 0xd7, 0x7b, 0xcc, 0x34, 0x19, 0x43, 0xc3, 0x00, 0x19, 0x94, 0x49,
|
||||
0xdf, 0xef, 0xd1, 0xef, 0x96, 0x8a, 0xfd, 0x07, 0xf9, 0x07, 0xbd, 0xd3, 0x25, 0x10, 0xfa, 0xd5,
|
||||
0x62, 0xce, 0x6d, 0xb7, 0xd0, 0x3f, 0x1f, 0x8d, 0xf8, 0xce, 0x78, 0x21, 0xa9, 0xff, 0xeb, 0x52,
|
||||
0xcd, 0x86, 0xd9, 0xb6, 0xec, 0x83, 0xfb, 0xfb, 0x1a, 0x00, 0x00, 0xff, 0xff, 0xd7, 0x6c, 0xcb,
|
||||
0x1b, 0x85, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
// 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package apis;
|
||||
|
||||
message SendParams {
|
||||
string contact = 1;
|
||||
string topic = 2;
|
||||
string message = 3;
|
||||
string Priority = 4;
|
||||
string Contact = 1;
|
||||
string Topic = 2;
|
||||
string Title = 3;
|
||||
string Message = 4;
|
||||
string Priority = 5;
|
||||
string RemoteTemplate = 6;
|
||||
}
|
||||
|
||||
message UpdateConfigParams {
|
||||
|
||||
@@ -50,14 +50,17 @@ type SRpcService struct {
|
||||
SendServices *ServiceMap
|
||||
socketFileDir string
|
||||
configStore _interface.IServiceConfigStore
|
||||
templateStore _interface.ITemplateStore
|
||||
}
|
||||
|
||||
// NewSRpcService create a SRpcService
|
||||
func NewSRpcService(socketFileDir string, configStore _interface.IServiceConfigStore) *SRpcService {
|
||||
func NewSRpcService(socketFileDir string, configStore _interface.IServiceConfigStore,
|
||||
tempalteStore _interface.ITemplateStore) *SRpcService {
|
||||
return &SRpcService{
|
||||
SendServices: NewServiceMap(),
|
||||
socketFileDir: socketFileDir,
|
||||
configStore: configStore,
|
||||
templateStore: tempalteStore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,20 +109,24 @@ func (self *SRpcService) StopAll() {
|
||||
// Send call the corresponding rpc server to send messager.
|
||||
func (self *SRpcService) Send(ctx context.Context, contactType, contact, topic, msg, priority string) error {
|
||||
|
||||
args := apis.SendParams{
|
||||
Contact: contact,
|
||||
Topic: topic,
|
||||
Message: msg,
|
||||
Priority: priority,
|
||||
args, err := self.templateStore.NotifyFilter(contactType, topic, msg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "templateStore.NotifyFilter")
|
||||
}
|
||||
|
||||
args.Contact = contact
|
||||
args.Priority = priority
|
||||
|
||||
f := func(service *apis.SendNotificationClient) (interface{}, error) {
|
||||
log.Debugf("send one")
|
||||
return service.Send(ctx, &args)
|
||||
}
|
||||
|
||||
_, err := self.execute(ctx, f, contactType)
|
||||
return err
|
||||
_, err = self.execute(ctx, f, contactType)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "contactType: %s", contactType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestartService can restart remote rpc server and pass config info.
|
||||
@@ -179,7 +186,7 @@ func (self *SRpcService) execute(ctx context.Context, f func(client *apis.SendNo
|
||||
}
|
||||
|
||||
if st.Message() != ErrSendServiceNotInit.Error() {
|
||||
return nil, errors.Error(fmt.Sprintf("Send message failed because that %s.", st.Message()))
|
||||
return nil, errors.Error(st.Message())
|
||||
}
|
||||
|
||||
// if NOINIT, try to restart server and send again
|
||||
@@ -197,7 +204,7 @@ func (self *SRpcService) execute(ctx context.Context, f func(client *apis.SendNo
|
||||
|
||||
return nil, errors.Wrap(ErrSendServiceNotFound, serviceName)
|
||||
}
|
||||
return nil, errors.Error(fmt.Sprintf("Send message failed because that %s.", st.Message()))
|
||||
return nil, errors.Error(st.Message())
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
|
||||
@@ -43,6 +43,9 @@ func StartService() {
|
||||
baseOpts := &options.Options.BaseOptions
|
||||
common_options.ParseOptions(opts, os.Args, "notify.conf", "notify")
|
||||
|
||||
// init email url
|
||||
models.TemplateManager.SetEmailUrl(options.Options.VerifyEmailUrl)
|
||||
|
||||
// init auth
|
||||
app.InitAuth(commonOpts, func() {
|
||||
log.Infof("Auth complete!")
|
||||
@@ -60,7 +63,7 @@ func StartService() {
|
||||
cache.RegistUserCredCacheUpdater()
|
||||
|
||||
// init notify service
|
||||
models.NotifyService = rpc.NewSRpcService(opts.SocketFileDir, models.ConfigManager)
|
||||
models.NotifyService = rpc.NewSRpcService(opts.SocketFileDir, models.ConfigManager, models.TemplateManager)
|
||||
models.NotifyService.InitAll()
|
||||
defer models.NotifyService.StopAll()
|
||||
|
||||
@@ -72,7 +75,8 @@ func StartService() {
|
||||
resend := func(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
|
||||
models.ReSend(opts.ReSendScope)
|
||||
}
|
||||
cron.AddJobAtIntervals("ReSendNotifications", time.Duration(opts.ReSendScope)*time.Minute, resend)
|
||||
cron.AddJobAtIntervals("ReSendNotifications", time.Duration(opts.ReSendScope)*time.Second, resend)
|
||||
cron.Start()
|
||||
|
||||
app.ServeForever(applicaion, baseOpts)
|
||||
}
|
||||
|
||||
1
pkg/notify/template/doc.go
Normal file
1
pkg/notify/template/doc.go
Normal file
@@ -0,0 +1 @@
|
||||
package template // import "yunion.io/x/onecloud/pkg/notify/template"
|
||||
20
pkg/notify/template/template.go
Normal file
20
pkg/notify/template/template.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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 template
|
||||
|
||||
const (
|
||||
EMAIL_VERIFY_CONTENT_PATH = "/opt/yunion/share/notify/email_verify_template"
|
||||
EMAIL_VERIFY_TITLE = "Yunion Verify"
|
||||
)
|
||||
@@ -17,11 +17,26 @@ package utils
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/notify/cache"
|
||||
)
|
||||
|
||||
func GetUserByID(ctx context.Context, id string) (*cache.SUser, error) {
|
||||
return cache.UserCacheManager.FetchUserByID(ctx, id, false)
|
||||
func GetUserByIDOrName(ctx context.Context, idStr string) (*cache.SUser, error) {
|
||||
return cache.UserCacheManager.FetchUserByIDOrName(ctx, idStr)
|
||||
}
|
||||
|
||||
func GetUsersWithoutRemote(idStr []string) ([]cache.SUser, error) {
|
||||
q := cache.UserCacheManager.Query()
|
||||
q = q.Filter(sqlchemy.OR(sqlchemy.In(q.Field("id"), idStr), sqlchemy.In(q.Field("name"), idStr)))
|
||||
users := make([]cache.SUser, 0, 1)
|
||||
err := db.FetchModelObjects(cache.UserCacheManager, q, &users)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fetch user cache failed")
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUserIdsLikeName(ctx context.Context, name string) ([]string, error) {
|
||||
@@ -49,7 +64,7 @@ func GetUsersByGroupID(ctx context.Context, gid string) ([]string, error) {
|
||||
}
|
||||
|
||||
func GetUsernameByID(ctx context.Context, id string) (string, error) {
|
||||
user, err := GetUserByID(ctx, id)
|
||||
user, err := GetUserByIDOrName(ctx, id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user