mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
Merge pull request #4103 from rainzm/feature/notify_template
feat(notify): Available uname in api & Unified template management
This commit is contained in:
@@ -12,4 +12,5 @@ RUN apk update && \
|
||||
|
||||
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
|
||||
COPY ./build/notify/root/opt/ /opt/
|
||||
ADD ./_output/bin/notify /opt/yunion/bin/notify
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Vendored
+14
-116
@@ -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)
|
||||
|
||||
+130
-29
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+17
-10
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package template // import "yunion.io/x/onecloud/pkg/notify/template"
|
||||
@@ -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