├── Dockerfile ├── Readme.md ├── alertCenter ├── conf ├── app.conf ├── dev.conf ├── prod.conf └── test.conf ├── controllers ├── APIBaseController.go ├── APIController.go ├── AlertController.go ├── IgnoreRuleAPIControll.go ├── PrometheusAPI.go ├── TeamAPIController.go ├── TeamController.go ├── TokenAPIController.go ├── TokenController.go ├── UserController.go ├── baseController.go ├── default.go └── session │ ├── session.go │ └── static.go ├── core ├── MessageCenter.go ├── MessageCenter_test.go ├── db │ ├── MongoDB.go │ └── MongoDB_test.go ├── gitlab │ ├── gitlab.go │ ├── gitlab_access_token.go │ ├── gitlab_admin_api.go │ ├── gitlab_model.go │ ├── gitlab_test.go │ └── gitlab_user_api.go ├── notice │ ├── Mail.go │ ├── Mail_test.go │ ├── NoticeCenter.go │ ├── NoticeServer.go │ ├── WeChat.go │ ├── noticeChan.go │ └── noticeControl.go ├── service │ ├── AlertService.go │ ├── GlobalConfigService.go │ ├── IgnoreRuleService.go │ ├── TeamService.go │ └── TokenService.go └── user │ ├── GetUsers.go │ ├── Gitlab.go │ ├── LDAP.go │ ├── Receiver.go │ └── UserInterface.go ├── data ├── alert.json ├── alerts.json ├── team.json └── wechat.json ├── main.go ├── makefile ├── models ├── Token.go ├── alert.go ├── relations.go └── types.go ├── routers └── router.go ├── static ├── css │ ├── .DS_Store │ ├── bootstrap-switch.css │ ├── bootstrap.css │ └── site.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ ├── default.jpg │ ├── load.gif │ └── nav1.png └── js │ ├── bootstrap-switch.js │ ├── bootstrap.js │ ├── jquery.min.js │ └── site.js ├── tests ├── alertsApi.sh ├── default_test.go └── test.go ├── util ├── Json.go └── Utils.go └── views ├── alertList.html ├── alertListAll.html ├── historyList.html ├── index.html ├── mail.html ├── menu.html ├── teams.html └── userHome.html /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.4 2 | MAINTAINER Barnett 3 | 4 | RUN mkdir /alert 5 | COPY ./conf /alert/conf 6 | COPY ./static /alert/static 7 | COPY ./views /alert/views 8 | COPY ./alertCenter /alert/alertCenter 9 | 10 | WORKDIR /alert 11 | 12 | RUN chmod 655 /alert/alertCenter 13 | 14 | EXPOSE 8888 15 | 16 | ENTRYPOINT ["/alert/alertCenter"] 17 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # 使用说明 2 | 如果你想要使用本项目,请联系我:qq:576501057 3 | 我很高兴为你解答遇到的问题。 4 | 5 | # 安装要求 6 | 项目 | 要求说明 7 | ---- | --- 8 | go | 版本1.6.3及以上 9 | beego | 版本1.7.0及以上 10 | gitlab | 账号体系依赖gitlab应用。 11 | mongodb | 存储服务。 12 | # 功能说明 13 | 1. 兼容prometheus报警信息,比alertmanager更完善的功能 14 | 2. 多种安全验证方式的API 15 | 3. 支持报警信息分类存储,查询 16 | 4. 支持gitlab,ldap用户管理 17 | 5. 支持邮件,微信报警 18 | 6. 支持多维度报警忽略 19 | 7. 支持报警历史查询 20 | 8. 简洁完善的ui 21 | # 应用一键试用 22 | 本应用已发布到好雨云市,你可以一键安装。 23 | http://app.goodrain.com/detail/109/ 24 | -------------------------------------------------------------------------------- /alertCenter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/alertCenter -------------------------------------------------------------------------------- /conf/app.conf: -------------------------------------------------------------------------------- 1 | appname = alertCenter 2 | httpport = 8888 3 | runmode = dev 4 | copyrequestbody = true 5 | url=http://127.0.0.1:8888 6 | # cloudURI=http://103.235.250.11:8002 7 | sessionon=true 8 | autoRefreshTime=1h 9 | 10 | 11 | #Get notice Server,can choose iterms:'mail','wexin',user ',' connect, example: mail,weixin 12 | NoticeServer = mail 13 | weURI = http://10.12.1.129:18081 14 | weToken = f6974BCU7Btd1eZ83yX17TLOmZBHI9DZ8Km06JM1v0X7stj7 15 | weAgentId = 3 16 | weCount = 20 17 | weReCount = 3 18 | 19 | mailServer = smtp.exmail.qq.com 20 | mailPort = 465 21 | mailUser = monitor@yiyun.pro 22 | mailPassword = test 23 | mailFrom = monitor@yiyun.pro 24 | mailCount = 20 25 | mailReCount = 3 26 | 27 | # send message interval. The unit is minute. 28 | sendMsgInterval_0 = 1h 29 | sendMsgInterval_1 = 30m 30 | sendMsgInterval_2 = 15m 31 | sendMsgInterval_3 = 5m 32 | 33 | # Get User Source, can choose iterms:'ldap','gitlab', user ',' connect, example: ldap,gitlab 34 | UserSource = gitlab 35 | 36 | LADPServer=127.0.0.1 37 | LDAPPort=8389 38 | LDAPDN = cn=admin,dc=yunpro,dc=cn 39 | LDAPPass=admin123 40 | 41 | include "dev.conf" 42 | include "prod.conf" 43 | include "test.conf" -------------------------------------------------------------------------------- /conf/dev.conf: -------------------------------------------------------------------------------- 1 | [dev] 2 | # Mongo config 3 | mongoURI = 127.0.0.1:27017 4 | mongoDB = admin 5 | mongoUser = root 6 | mongoPass = root 7 | 8 | # Get User Source, can choose iterms:'ldap','gitlab', user ',' connect, example: ldap,gitlab 9 | UserSource = gitlab 10 | 11 | LADPServer=127.0.0.1 12 | LDAPPort=8389 13 | LDAPDN = cn=admin,dc=yunpro,dc=cn 14 | LDAPPass=admin123 15 | 16 | 17 | # send message interval. The unit is minute. 18 | sendMsgInterval_0 = 1h 19 | sendMsgInterval_1 = 30m 20 | sendMsgInterval_2 = 15m 21 | sendMsgInterval_3 = 5m 22 | 23 | 24 | # ignoreSend 时间段来结束信号就不发邮件 25 | ignoreSend = 10s 26 | # stopSend 时间内不来邮件,结束报警 27 | stopSend = 30m 28 | 29 | # GitLab 30 | Gitlab = http://80.grf0ef26.7804f67d.ali-sh.goodrain.net:10080/ 31 | GitlabAccessToken = 1Gmz8M5y4ZRwskCWroPy 32 | GitlabOAuthClientId = 9898a915c89c7be860a13f45060b4f25bc1e37876a6227dc92907db0e85d0dce 33 | GitlabOAuthSercet = 5eec28518de97509109c720094822855e29ae506b828f141a0f6f776a41446ae 34 | GitlabCallBackUrl = http://alert.yiyun.pro 35 | -------------------------------------------------------------------------------- /conf/prod.conf: -------------------------------------------------------------------------------- 1 | [prod] 2 | 3 | url=http://ac.v2.yoo.yunpro.cn 4 | cloudURI=http://10.50.1.25:8002 5 | # Mongo config 6 | mongoURI = 10.50.1.92:1501 7 | mongoDB = admin 8 | mongoUser = root 9 | mongoPass = root 10 | 11 | 12 | # Get User Source, can choose iterms:'ldap','gitlab', user ',' connect, example: ldap,gitlab 13 | UserSource = gitlab 14 | 15 | LADPServer=127.0.0.1 16 | LDAPPort=8389 17 | LDAPDN = cn=admin,dc=yunpro,dc=cn 18 | LDAPPass=admin123 19 | 20 | Gitlab = https://git.yunpro.cn 21 | GitlabAccessToken = CcNLSBS1TsmW6jy8Ayyz 22 | GitlabOAuthClientId = a49cc2e5246ac9268e59200fa533110729373535b9c44c07b85dd5a6cfc55a0c 23 | GitlabOAuthSercet = 3b4548b2e704cd4f92de7a3869b07dd256e076c0bd34ee5551d027182df73b87 24 | GitlabCallBackUrl = http://ac.v2.yoo.yunpro.cn 25 | 26 | 27 | # send message interval. The unit is minute. 28 | sendMsgInterval_0 = 1h 29 | sendMsgInterval_1 = 30m 30 | sendMsgInterval_2 = 15m 31 | sendMsgInterval_3 = 5m 32 | 33 | # ignoreSend 时间段来结束信号就不发邮件 34 | ignoreSend = 10s 35 | # stopSend 时间内不来邮件,结束报警 36 | stopSend = 30m -------------------------------------------------------------------------------- /conf/test.conf: -------------------------------------------------------------------------------- 1 | [test] 2 | # Mongo config 3 | mongoURI = 127.0.0.1:27017 4 | mongoDB = admin 5 | mongoUser = root 6 | mongoPass = root 7 | 8 | 9 | # Get User Source, can choose iterms:'ldap','gitlab', user ',' connect, example: ldap,gitlab 10 | UserSource = gitlab 11 | 12 | LADPServer=127.0.0.1 13 | LDAPPort=8389 14 | LDAPDN = cn=admin,dc=yunpro,dc=cn 15 | LDAPPass=admin123 16 | 17 | 18 | # send message interval. The unit is minute. 19 | sendMsgInterval_0 = 20s 20 | sendMsgInterval_1 = 30m 21 | sendMsgInterval_2 = 15m 22 | sendMsgInterval_3 = 5m 23 | 24 | 25 | # ignoreSend 时间段来结束信号就不发邮件 26 | ignoreSend = 10s 27 | # stopSend 时间内不来邮件,结束报警 28 | stopSend = 30m 29 | 30 | Gitlab = http://10.50.1.56:10080 31 | GitlabAccessToken = s4PXhysUoxrkH65FUZWu 32 | GitlabOAuthClientId = f7e820cfd9f216301fc2c9a5dc447eb21fcac76265ea222cd203a3da7f388fe0 33 | GitlabOAuthSercet = d960b57c99e9b95020477ad37217959d19a587ded868c55f8f7230e4b5b3bca4 34 | GitlabCallBackUrl = http://localhost:8888 35 | 36 | -------------------------------------------------------------------------------- /controllers/APIBaseController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/core/service" 5 | 6 | "alertCenter/util" 7 | 8 | "github.com/astaxie/beego" 9 | ) 10 | 11 | type APIBaseController struct { 12 | beego.Controller 13 | Username string 14 | } 15 | 16 | func (this *APIBaseController) Prepare() { 17 | // start := time.Now() 18 | // defer fmt.Println("check token time:", time.Now().Sub(start)) 19 | 20 | token := this.Ctx.Input.Header("token") 21 | user := this.Ctx.Input.Header("user") 22 | tokenService := &service.TokenService{ 23 | Session: nil, 24 | } 25 | beego.Debug("check token:" + token + ",user:" + user) 26 | if ok := tokenService.CheckToken(token, user); !ok { 27 | this.Data["json"] = util.GetErrorJson("Security verification failed") 28 | this.ServeJSON() 29 | } 30 | this.Username = user 31 | } 32 | -------------------------------------------------------------------------------- /controllers/APIController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/core" 5 | "alertCenter/core/db" 6 | "alertCenter/core/service" 7 | "alertCenter/core/user" 8 | "alertCenter/models" 9 | "alertCenter/util" 10 | "encoding/json" 11 | "strconv" 12 | "time" 13 | 14 | "github.com/astaxie/beego" 15 | mgo "gopkg.in/mgo.v2" 16 | "gopkg.in/mgo.v2/bson" 17 | ) 18 | 19 | type APIController struct { 20 | //beego.Controller 21 | APIBaseController 22 | session *db.MongoSession 23 | alertService *service.AlertService 24 | teamServcie *service.TeamService 25 | } 26 | 27 | func (e *APIController) Receive() { 28 | data := e.Ctx.Input.RequestBody 29 | if data != nil && len(data) > 0 { 30 | var Alerts []*models.Alert 31 | err := json.Unmarshal(data, &Alerts) 32 | if err == nil { 33 | core.HandleAlerts(Alerts) 34 | e.Data["json"] = util.GetSuccessJson("receive alert success") 35 | } else { 36 | e.Data["json"] = util.GetErrorJson("receive a unknow data") 37 | } 38 | } else { 39 | e.Data["json"] = util.GetErrorJson("receive a unknow data") 40 | } 41 | 42 | e.ServeJSON() 43 | } 44 | 45 | func (e *APIController) GetAlerts() { 46 | pageSizeStr := e.Ctx.Request.FormValue("pageSize") 47 | pageSize, err := strconv.Atoi(pageSizeStr) 48 | if err != nil { 49 | pageSize = 20 50 | } 51 | 52 | pageStr := e.Ctx.Request.FormValue("page") 53 | 54 | page, err := strconv.Atoi(pageStr) 55 | if err != nil { 56 | page = 1 57 | } 58 | blogStr := e.Ctx.Request.FormValue("blog") 59 | blog, err := strconv.ParseBool(blogStr) 60 | if err != nil { 61 | blog = false 62 | } 63 | receiver := e.APIBaseController.Username 64 | 65 | // if admin. Should show all the alerts. 66 | //user, err := gitlab.GetUserByUsername(receiver) 67 | relation := user.Relation{} 68 | user := relation.GetUserByName(receiver) 69 | if user != nil && user.IsAdmin { 70 | receiver = "all" 71 | } 72 | 73 | e.session = db.GetMongoSession() 74 | if e.session != nil { 75 | defer e.session.Close() 76 | } 77 | 78 | if e.session == nil { 79 | e.Data["json"] = util.GetFailJson("get database session faild.") 80 | goto over 81 | } else { 82 | e.alertService = service.GetAlertService(e.session) 83 | if len(receiver) != 0 && receiver != "all" { 84 | alerts, err := e.alertService.FindByUser(receiver, pageSize, page, blog) 85 | beego.Info("Get", len(alerts), " alerts") 86 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 87 | e.Data["json"] = util.GetFailJson("get alerts error.") 88 | goto over 89 | } else { 90 | e.Data["json"] = util.GetSuccessReJson(alerts) 91 | goto over 92 | } 93 | } else if receiver == "all" { 94 | alerts, err := e.alertService.FindAll(pageSize, page, blog) 95 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 96 | e.Data["json"] = util.GetFailJson("get alerts error.") 97 | goto over 98 | } else { 99 | e.Data["json"] = util.GetSuccessReJson(alerts) 100 | goto over 101 | } 102 | } else { 103 | e.Data["json"] = util.GetErrorJson("api use error,please provide receiver") 104 | goto over 105 | } 106 | } 107 | over: 108 | e.ServeJSON() 109 | } 110 | func (e *APIController) GetHistorys() { 111 | pageSizeStr := e.Ctx.Request.FormValue("pageSize") 112 | pageSize, err := strconv.Atoi(pageSizeStr) 113 | if err != nil { 114 | pageSize = 20 115 | } 116 | 117 | pageStr := e.Ctx.Request.FormValue("page") 118 | page, err := strconv.Atoi(pageStr) 119 | if err != nil { 120 | page = 1 121 | } 122 | mark := e.GetString(":mark") 123 | if mark == "" { 124 | e.Data["json"] = util.GetFailJson("mark is empty") 125 | goto over 126 | } 127 | e.session = db.GetMongoSession() 128 | if e.session != nil { 129 | defer e.session.Close() 130 | } 131 | if e.session == nil { 132 | e.Data["json"] = util.GetFailJson("get database session faild.") 133 | goto over 134 | } else { 135 | e.alertService = service.GetAlertService(e.session) 136 | historys, err := e.alertService.GetHistoryByMark(mark, pageSize, page) 137 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 138 | e.Data["json"] = util.GetFailJson("get historys error.") 139 | goto over 140 | } else { 141 | e.Data["json"] = util.GetSuccessReJson(historys) 142 | goto over 143 | } 144 | } 145 | over: 146 | e.ServeJSON() 147 | } 148 | 149 | //SetNoticeMode 控制是否发送邮件 150 | func (e *APIController) SetNoticeMode() { 151 | userName := e.Ctx.Input.Header("user") 152 | status, err := e.GetBool(":status") 153 | if err != nil { 154 | e.Data["json"] = util.GetFailJson("api error,status not provide") 155 | } else { 156 | relation := user.Relation{} 157 | user := relation.GetUserByName(userName) 158 | if user == nil || !user.IsAdmin { 159 | e.Data["json"] = util.GetFailJson("Do not allow the operation") 160 | } else { 161 | session := db.GetMongoSession() 162 | if session != nil { 163 | defer session.Close() 164 | } 165 | if session == nil { 166 | e.Data["json"] = util.GetErrorJson("get mongo session error when init NoticeOn ") 167 | } else { 168 | service := &service.GlobalConfigService{ 169 | Session: session, 170 | } 171 | config, err := service.GetConfig("noticeOn") 172 | if config == nil && err.Error() == mgo.ErrNotFound.Error() { 173 | config = &models.GlobalConfig{} 174 | config.Name = "noticeOn" 175 | config.Value = status 176 | config.AddTime = time.Now() 177 | service.Insert(config) 178 | } else { 179 | config.Value = status 180 | service.Update(config) 181 | } 182 | e.Data["json"] = util.GetSuccessJson("noticeon update success") 183 | } 184 | } 185 | } 186 | e.ServeJSON() 187 | } 188 | 189 | func (e *APIController) GetNoticeMode() { 190 | userName := e.Ctx.Input.Header("user") 191 | relation := user.Relation{} 192 | user := relation.GetUserByName(userName) 193 | if user == nil || !user.IsAdmin { 194 | e.Data["json"] = util.GetFailJson("Do not allow the operation") 195 | } else { 196 | session := db.GetMongoSession() 197 | if session != nil { 198 | defer session.Close() 199 | } 200 | if session == nil { 201 | e.Data["json"] = util.GetErrorJson("get mongo session error when init NoticeOn ") 202 | } else { 203 | service := &service.GlobalConfigService{ 204 | Session: session, 205 | } 206 | config, err := service.GetConfig("noticeOn") 207 | if config == nil && err.Error() == mgo.ErrNotFound.Error() { 208 | config = &models.GlobalConfig{} 209 | config.Name = "noticeOn" 210 | config.Value = true 211 | config.AddTime = time.Now() 212 | session.Insert("GlobalConfig", config) 213 | 214 | } 215 | e.Data["json"] = util.GetSuccessReJson(config) 216 | } 217 | } 218 | e.ServeJSON() 219 | } 220 | 221 | func (e *APIController) AddTrustIP() { 222 | 223 | userName := e.Ctx.Input.Header("user") 224 | relation := user.Relation{} 225 | user := relation.GetUserByName(userName) 226 | if user == nil || !user.IsAdmin { 227 | e.Data["json"] = util.GetFailJson("Do not allow the operation") 228 | } else { 229 | data := e.Ctx.Input.RequestBody 230 | var config = models.GlobalConfig{} 231 | err := json.Unmarshal(data, &config) 232 | if err != nil { 233 | e.Data["json"] = util.GetErrorJson("data parse error") 234 | } else { 235 | session := db.GetMongoSession() 236 | if session != nil { 237 | defer session.Close() 238 | } 239 | if session == nil { 240 | e.Data["json"] = util.GetErrorJson("get mongo session error when add trust ip ") 241 | } else { 242 | service := &service.GlobalConfigService{ 243 | Session: session, 244 | } 245 | if ok, err := service.CheckExist("TrustIP", config.Value); !ok && (err == nil || err.Error() == mgo.ErrNotFound.Error()) { 246 | service.Insert(&models.GlobalConfig{ 247 | Name: "TrustIP", 248 | Value: config.Value, 249 | AddTime: time.Now(), 250 | ID: bson.NewObjectId(), 251 | }) 252 | } 253 | re, err := service.GetConfigA("TrustIP", config.Value) 254 | if re != nil { 255 | e.Data["json"] = util.GetSuccessReJson(re) 256 | } else if err.Error() == mgo.ErrNotFound.Error() { 257 | e.Data["json"] = util.GetFailJson("insert trust ip faild") 258 | } else { 259 | e.Data["json"] = util.GetFailJson("An unknown error when get trust ip") 260 | } 261 | 262 | } 263 | } 264 | } 265 | e.ServeJSON() 266 | } 267 | 268 | func (e *APIController) GetTrustIP() { 269 | userName := e.Ctx.Input.Header("user") 270 | relation := user.Relation{} 271 | user := relation.GetUserByName(userName) 272 | if user == nil || !user.IsAdmin { 273 | e.Data["json"] = util.GetFailJson("Do not allow the operation") 274 | } else { 275 | session := db.GetMongoSession() 276 | if session != nil { 277 | defer session.Close() 278 | } 279 | if session == nil { 280 | e.Data["json"] = util.GetErrorJson("get mongo session error when add trust ip ") 281 | } else { 282 | service := &service.GlobalConfigService{ 283 | Session: session, 284 | } 285 | 286 | re, err := service.GetAllConfig("TrustIP") 287 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 288 | e.Data["json"] = util.GetFailJson("get trust ip error") 289 | } else { 290 | e.Data["json"] = util.GetSuccessReJson(re) 291 | } 292 | 293 | } 294 | } 295 | e.ServeJSON() 296 | } 297 | func (e *APIController) DeleteTrustIP() { 298 | userName := e.Ctx.Input.Header("user") 299 | relation := user.Relation{} 300 | user := relation.GetUserByName(userName) 301 | if user == nil || !user.IsAdmin { 302 | e.Data["json"] = util.GetFailJson("Do not allow the operation") 303 | } else { 304 | ID := e.GetString(":ID") 305 | if ID == "" { 306 | e.Data["json"] = util.GetErrorJson("Trust ip id is not provide") 307 | } else { 308 | session := db.GetMongoSession() 309 | if session != nil { 310 | defer session.Close() 311 | } 312 | if session == nil { 313 | e.Data["json"] = util.GetErrorJson("get mongo session error when add trust ip ") 314 | } else { 315 | service := &service.GlobalConfigService{ 316 | Session: session, 317 | } 318 | re := service.DeleteByID(ID) 319 | if re { 320 | e.Data["json"] = util.GetSuccessJson("remove trust ip success") 321 | } else { 322 | e.Data["json"] = util.GetFailJson("get trust ip faild") 323 | } 324 | 325 | } 326 | } 327 | } 328 | e.ServeJSON() 329 | } 330 | 331 | //RefreshCache 更新缓存的用户和组信息app信息 332 | func (e *APIController) RefreshCache() { 333 | userName := e.Ctx.Input.Header("user") 334 | relation := user.Relation{} 335 | user := relation.GetUserByName(userName) 336 | if user != nil && user.IsAdmin { 337 | globalServcie := &service.GlobalConfigService{ 338 | Session: db.GetMongoSession(), 339 | } 340 | if globalServcie.Session != nil { 341 | defer globalServcie.Session.Close() 342 | } 343 | //更新全局配置缓存 344 | globalServcie.RefreshGlobalCnfig() 345 | //更新用户信息,必须在全局配置以后更新 346 | relation.RefreshCache() 347 | e.Data["json"] = util.GetSuccessJson("fresh cache success") 348 | } else { 349 | e.Data["json"] = util.GetFailJson("Do not allow the operation") 350 | } 351 | e.ServeJSON() 352 | } 353 | -------------------------------------------------------------------------------- /controllers/AlertController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/core/service" 6 | ) 7 | 8 | type AlertController struct { 9 | BaseController 10 | } 11 | 12 | func (e *AlertController) AlertList() { 13 | e.TplName = "alertListAll.html" 14 | } 15 | 16 | func (e *AlertController) AlertsCurrent() { 17 | e.TplName = "alertList.html" 18 | } 19 | 20 | func (e *AlertController) HistoryList() { 21 | mark := e.GetString(":mark") 22 | if mark == "" { 23 | e.Abort("404") 24 | } 25 | alertService := &service.AlertService{ 26 | Session: db.GetMongoSession(), 27 | } 28 | if alertService.Session != nil { 29 | defer alertService.Session.Close() 30 | } 31 | alert, _ := alertService.GetAlertByMark(mark) 32 | if alert == nil { 33 | e.Abort("404") 34 | } else { 35 | e.Data["alert"] = alert 36 | e.Data["mark"] = mark 37 | e.Data["description"] = alert.Annotations.LabelSet["description"] 38 | e.Data["alertName"] = alert.Labels.LabelSet["alertname"] 39 | e.Data["startsAt"] = alert.StartsAt.Format("2006-01-02T15:04:05") 40 | e.Data["endsAt"] = alert.EndsAt.Format("2006-01-02T15:04:05") 41 | labels := make(map[string]string, 0) 42 | for k := range alert.Labels.LabelSet { 43 | labels[string(k)] = string(alert.Labels.LabelSet[k]) 44 | } 45 | e.Data["labels"] = labels 46 | e.TplName = "historyList.html" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /controllers/IgnoreRuleAPIControll.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/core/service" 6 | "alertCenter/models" 7 | "alertCenter/util" 8 | "encoding/json" 9 | "time" 10 | 11 | "github.com/astaxie/beego" 12 | mgo "gopkg.in/mgo.v2" 13 | ) 14 | 15 | type IgnoreRuleAPIControll struct { 16 | APIBaseController 17 | } 18 | 19 | //AddRule 添加自定义忽略规则 20 | func (e *IgnoreRuleAPIControll) AddRule() { 21 | data := e.Ctx.Input.RequestBody 22 | if data != nil && len(data) > 0 { 23 | beego.Debug("ignoreRule:" + string(data)) 24 | var rule *models.UserIgnoreRule = &models.UserIgnoreRule{} 25 | err := json.Unmarshal(data, rule) 26 | if err == nil { 27 | session := db.GetMongoSession() 28 | if session != nil { 29 | defer session.Close() 30 | } 31 | 32 | ruleService := &service.IgnoreRuleService{ 33 | Session: session, 34 | } 35 | ruleService.AddRule(rule) 36 | e.Data["json"] = util.GetSuccessReJson(rule) 37 | } else { 38 | beego.Error("Parse the received user ignore rule faild." + err.Error()) 39 | e.Data["json"] = util.GetFailJson("Parse the received user ignore rule faild.") 40 | } 41 | } else { 42 | beego.Error("receive a unknow data") 43 | e.Data["jaon"] = util.GetErrorJson("receive a unknow data") 44 | } 45 | e.ServeJSON() 46 | } 47 | 48 | //GetRulesByUser 获取用户的规则 49 | func (e *IgnoreRuleAPIControll) GetRulesByUser() { 50 | user := e.Ctx.Input.Header("user") 51 | if user == "" { 52 | e.Data["json"] = util.GetErrorJson("please certification") 53 | e.ServeJSON() 54 | } else { 55 | session := db.GetMongoSession() 56 | if session != nil { 57 | defer session.Close() 58 | } 59 | ruleService := &service.IgnoreRuleService{ 60 | Session: session, 61 | } 62 | rules, err := ruleService.FindRuleByUser(user) 63 | if rules != nil || err == nil || (err != nil && err == mgo.ErrNotFound) { 64 | e.Data["json"] = util.GetSuccessReJson(rules) 65 | } else if err != nil { 66 | e.Data["json"] = util.GetFailJson("get rules error." + err.Error()) 67 | } 68 | } 69 | e.ServeJSON() 70 | } 71 | 72 | //AddRuleByAlert 添加某alert的忽略规则 73 | func (e *IgnoreRuleAPIControll) AddRuleByAlert() { 74 | ID := e.GetString(":mark") 75 | user := e.Ctx.Input.Header("user") 76 | if ID == "" { 77 | e.Data["json"] = util.GetErrorJson("api error,mark is not provided") 78 | } else { 79 | session := db.GetMongoSession() 80 | if session != nil { 81 | defer session.Close() 82 | } 83 | alertService := &service.AlertService{ 84 | Session: session, 85 | } 86 | alert, err := alertService.FindByID(ID) 87 | if alert == nil || err != nil { 88 | e.Data["json"] = util.GetErrorJson("alertID is not exit") 89 | } else { 90 | rule := &models.UserIgnoreRule{ 91 | Labels: alert.Labels, 92 | StartsAt: time.Now(), 93 | UserName: user, 94 | } 95 | ruleService := &service.IgnoreRuleService{ 96 | Session: session, 97 | } 98 | ruleService.AddRule(rule) 99 | e.Data["json"] = util.GetSuccessJson("add user ignore rule success") 100 | } 101 | } 102 | e.ServeJSON() 103 | } 104 | 105 | //DeleteRule 删除rule 106 | func (e *IgnoreRuleAPIControll) DeleteRule() { 107 | user := e.Ctx.Input.Header("user") 108 | ruleID := e.GetString(":ruleID") 109 | beego.Debug("delete ignoreRule,user:" + user) 110 | if user == "" { 111 | e.Data["json"] = util.GetErrorJson("please certification") 112 | e.ServeJSON() 113 | } else { 114 | service := &service.IgnoreRuleService{ 115 | Session: db.GetMongoSession(), 116 | } 117 | if service.Session != nil { 118 | defer service.Session.Close() 119 | } 120 | 121 | if ok := service.DeleteRule(ruleID, user); ok { 122 | e.Data["json"] = util.GetSuccessJson("Delete rule success") 123 | } else { 124 | e.Data["json"] = util.GetFailJson("Delete rule faild") 125 | } 126 | e.ServeJSON() 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /controllers/PrometheusAPI.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/core" 5 | "alertCenter/core/db" 6 | "alertCenter/core/service" 7 | "alertCenter/models" 8 | "alertCenter/util" 9 | "encoding/json" 10 | 11 | "github.com/astaxie/beego" 12 | ) 13 | 14 | type PrometheusAPI struct { 15 | beego.Controller 16 | } 17 | 18 | //ReceivePrometheus 单独验证prometheus 19 | func (e *PrometheusAPI) ReceivePrometheus() { 20 | ip := e.Ctx.Input.IP() 21 | configService := &service.GlobalConfigService{ 22 | Session: db.GetMongoSession(), 23 | } 24 | if configService.Session != nil { 25 | defer configService.Session.Close() 26 | } 27 | if ok, _ := configService.CheckExist("TrustIP", ip); ok { 28 | data := e.Ctx.Input.RequestBody 29 | if data != nil && len(data) > 0 { 30 | var Alerts []*models.Alert 31 | err := json.Unmarshal(data, &Alerts) 32 | if err == nil { 33 | core.HandleAlerts(Alerts) 34 | e.Data["json"] = util.GetSuccessJson("receive alert success") 35 | } else { 36 | e.Data["json"] = util.GetErrorJson("receive a unknow data") 37 | } 38 | } 39 | } else { 40 | beego.Debug(ip + " is not trust ip") 41 | e.Data["json"] = util.GetFailJson("Have no right to access") 42 | } 43 | e.ServeJSON() 44 | 45 | } 46 | -------------------------------------------------------------------------------- /controllers/TeamAPIController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/core/user" 6 | "alertCenter/models" 7 | "alertCenter/util" 8 | "encoding/json" 9 | 10 | "github.com/astaxie/beego" 11 | ) 12 | 13 | type TeamAPIController struct { 14 | APIBaseController 15 | session *db.MongoSession 16 | } 17 | 18 | func (e *TeamAPIController) GetTeams() { 19 | e.session = db.GetMongoSession() 20 | if e.session != nil { 21 | defer e.session.Close() 22 | } 23 | if e.session == nil { 24 | e.Data["json"] = util.GetFailJson("get database session faild.") 25 | } else { 26 | relation := &user.Relation{} 27 | teams := relation.GetAllTeam() 28 | if teams == nil { 29 | e.Data["json"] = util.GetFailJson("There is no info of team") 30 | } else { 31 | e.Data["json"] = util.GetSuccessReJson(teams) 32 | } 33 | } 34 | e.ServeJSON() 35 | } 36 | 37 | func (e *TeamAPIController) AddTeam() { 38 | data := e.Ctx.Input.RequestBody 39 | if data != nil && len(data) > 0 { 40 | var team *models.Team = &models.Team{} 41 | err := json.Unmarshal(data, team) 42 | if err == nil { 43 | relation := &user.Relation{} 44 | relation.SetTeam(team) 45 | e.Data["json"] = util.GetSuccessJson("receive team info success") 46 | } else { 47 | beego.Error("Parse the received message to teams faild." + err.Error()) 48 | e.Data["json"] = util.GetFailJson("Parse the received message to teams faild.") 49 | } 50 | } else { 51 | beego.Error("receive a unknow data") 52 | e.Data["jaon"] = util.GetErrorJson("receive a unknow data") 53 | } 54 | e.ServeJSON() 55 | } 56 | 57 | //type TeamUser struct { 58 | // Team *models.Team 59 | // User []*models.User 60 | //} 61 | // 62 | //func (e *TeamAPIController) GetTeamUsers() { 63 | // e.session = db.GetMongoSession() 64 | // if e.session == nil { 65 | // e.Data["json"] = util.GetFailJson("get database session faild.") 66 | // } else { 67 | // relation := &user.Relation{} 68 | // teams := relation.GetAllTeam() 69 | // if teams == nil { 70 | // e.Data["json"] = util.GetFailJson("There is no info of team") 71 | // } else { 72 | // var result []*TeamUser 73 | // for _, team := range teams { 74 | // users := relation.GetUsersByTeam(team.Name) 75 | // teamUser := &TeamUser{ 76 | // Team: team, 77 | // User: users, 78 | // } 79 | // result = append(result, teamUser) 80 | // } 81 | // e.Data["json"] = util.GetSuccessReJson(result) 82 | // } 83 | // } 84 | // e.ServeJSON() 85 | //} 86 | -------------------------------------------------------------------------------- /controllers/TeamController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/models" 5 | "alertCenter/core/gitlab" 6 | "alertCenter/util" 7 | "fmt" 8 | ) 9 | 10 | type TeamController struct { 11 | BaseController 12 | } 13 | 14 | func (e *TeamController) GetTeams() { 15 | e.TplName = "teams.html" 16 | } 17 | 18 | type TeamUser struct { 19 | Team *models.Team 20 | User []*models.User 21 | } 22 | 23 | func (e *TeamController) GetTeamUsers() { 24 | groups, err := gitlab.GetGroupsByUsername(e.BaseController.Username) 25 | if err != nil { 26 | 27 | } 28 | var result []*TeamUser 29 | for _, group := range groups { 30 | team := gitlab.ConvertGitlabGroupToAlertModel(group) 31 | users, _ := gitlab.GetUsersByTeam(e.BaseController.Username, team.ID) 32 | us := gitlab.ConvertGitlabUsers(users) 33 | teamUser := &TeamUser{ 34 | Team: team, 35 | User: us, 36 | } 37 | result = append(result, teamUser) 38 | } 39 | 40 | fmt.Println("in GetTeamUsers and result;",result) 41 | 42 | e.Data["json"] = util.GetSuccessReJson(result) 43 | e.ServeJSON() 44 | } 45 | -------------------------------------------------------------------------------- /controllers/TokenAPIController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/core/service" 6 | "alertCenter/util" 7 | 8 | "github.com/astaxie/beego" 9 | ) 10 | 11 | type TokenAPIController struct { 12 | APIBaseController 13 | } 14 | 15 | func (e *TokenAPIController) GetAllToken() { 16 | 17 | user := e.Ctx.Input.Header("user") 18 | if user == "" { 19 | e.Data["json"] = util.GetErrorJson("please certification") 20 | e.ServeJSON() 21 | } else { 22 | service := &service.TokenService{ 23 | Session: db.GetMongoSession(), 24 | } 25 | if service.Session != nil { 26 | defer service.Session.Close() 27 | } 28 | tokens := service.GetAllToken(user) 29 | e.Data["json"] = util.GetSuccessReJson(tokens) 30 | e.ServeJSON() 31 | } 32 | } 33 | 34 | func (e *TokenAPIController) DeleteToken() { 35 | user := e.Ctx.Input.Header("user") 36 | project := e.GetString(":project") 37 | beego.Debug("delete token,user:" + user) 38 | if user == "" { 39 | e.Data["json"] = util.GetErrorJson("please certification") 40 | e.ServeJSON() 41 | } else { 42 | service := &service.TokenService{ 43 | Session: db.GetMongoSession(), 44 | } 45 | if service.Session != nil { 46 | defer service.Session.Close() 47 | } 48 | if ok := service.DeleteToken(project, user); ok { 49 | e.Data["json"] = util.GetSuccessJson("Delete project success") 50 | } else { 51 | e.Data["json"] = util.GetFailJson("Delete project faild") 52 | } 53 | e.ServeJSON() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /controllers/TokenController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/controllers/session" 5 | "alertCenter/core/db" 6 | "alertCenter/core/service" 7 | "alertCenter/models" 8 | "alertCenter/util" 9 | "encoding/json" 10 | 11 | "github.com/astaxie/beego" 12 | ) 13 | 14 | type TokenController struct { 15 | BaseController 16 | } 17 | 18 | func (e *TokenController) AddToken() { 19 | sess, err := session.GetSession(e.Ctx) 20 | if err != nil { 21 | beego.Error("get session error:", err) 22 | e.Data["json"] = util.GetErrorJson("please certification") 23 | e.ServeJSON() 24 | } 25 | user := sess.Get(session.SESSION_USER) 26 | if user == nil { 27 | e.Data["json"] = util.GetErrorJson("please certification") 28 | e.ServeJSON() 29 | } else { 30 | u := user.(*models.User) 31 | data := e.Ctx.Input.RequestBody 32 | var token = &models.Token{} 33 | err := json.Unmarshal(data, token) 34 | if err != nil { 35 | e.Data["json"] = util.GetErrorJson("json data error") 36 | e.ServeJSON() 37 | } else { 38 | if token != nil && token.Project == "" { 39 | e.Data["json"] = util.GetErrorJson("projectName can't be empty") 40 | e.ServeJSON() 41 | } else { 42 | service := &service.TokenService{ 43 | Session: db.GetMongoSession(), 44 | } 45 | if service.Session != nil { 46 | defer service.Session.Close() 47 | } 48 | token := service.CreateToken(token.Project, u.Name) 49 | e.Data["json"] = util.GetSuccessReJson(token) 50 | e.ServeJSON() 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /controllers/UserController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/controllers/session" 5 | "alertCenter/core/user" 6 | "alertCenter/models" 7 | 8 | "github.com/astaxie/beego" 9 | ) 10 | 11 | type UserController struct { 12 | BaseController 13 | } 14 | 15 | func (e *UserController) UserHome() { 16 | userName := e.GetString(":userName") 17 | relaction := user.Relation{} 18 | user := relaction.GetUserByName(userName) 19 | sess, err := session.GetSession(e.Ctx) 20 | var self interface{} 21 | if err != nil { 22 | beego.Error("get session error:", err) 23 | } else { 24 | self = sess.Get(session.SESSION_USER) 25 | } 26 | if user == nil { 27 | e.Abort("404") 28 | } else { 29 | e.Data["userInfo"] = user 30 | if self != nil && user.Name == self.(*models.User).Name { 31 | e.Data["self"] = true 32 | } 33 | e.Data["isAdmin"] = user.IsAdmin 34 | e.TplName = "userHome.html" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /controllers/baseController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/controllers/session" 5 | "alertCenter/core/db" 6 | "alertCenter/core/gitlab" 7 | "alertCenter/core/service" 8 | "alertCenter/core/user" 9 | "net/http" 10 | 11 | "github.com/astaxie/beego" 12 | ) 13 | 14 | type BaseController struct { 15 | beego.Controller 16 | Username string 17 | } 18 | 19 | func (this *BaseController) Prepare() { 20 | 21 | sess, err := session.GetSession(this.Ctx) 22 | if err != nil { 23 | beego.Error(err) 24 | return 25 | } 26 | defer sess.SessionRelease(this.Ctx.ResponseWriter) 27 | 28 | sessUsername := sess.Get(session.SESSION_USERNAME) 29 | paramCode := this.GetString("code") 30 | //fmt.Printf("paramCode: %s,session: %#v\n", paramCode, sess) 31 | 32 | if sessUsername == nil && paramCode == "" { 33 | 34 | sess.Set("redirect", this.Ctx.Request.URL.String()) //为了再次访问的重定向 35 | //fmt.Println("in sessUsername == nil && paramCode == nil") 36 | redirct := gitlab.GetGitlabOAuthUrl() 37 | http.Redirect(this.Ctx.ResponseWriter, this.Ctx.Request, redirct, http.StatusTemporaryRedirect) 38 | return 39 | } else if sessUsername == nil && paramCode != "" { 40 | beego.Debug("in sessUsername == nil && paramCode != nil, paramCode:", paramCode, "session:", sessUsername) 41 | access, err := gitlab.GetGitlabAccessToken(paramCode) 42 | if err != nil { 43 | beego.Error(err) 44 | return 45 | } 46 | u, err := gitlab.GetCurrentUserWithToken(access.AccessToken) 47 | if err != nil { 48 | beego.Error(err) 49 | return 50 | } 51 | //beego.Debug("access token:", access, "user:", u) 52 | 53 | username := u.Username 54 | beego.Info("user login username:", username) 55 | //加载用户默认token 56 | tokenService := &service.TokenService{ 57 | Session: db.GetMongoSession(), 58 | } 59 | if tokenService.Session != nil { 60 | defer tokenService.Session.Close() 61 | } 62 | defaultToken := tokenService.GetDefaultToken(username) 63 | if defaultToken != nil { 64 | this.Data["token"] = defaultToken.Value 65 | } 66 | //查询用户信息 67 | relation := user.Relation{} 68 | relationUser := relation.GetUserByName(username) 69 | if relationUser == nil { 70 | beego.Error("relationUser is nil.") 71 | return 72 | } 73 | this.Data["user"] = relationUser 74 | this.Data["userName"] = username 75 | err = sess.Set(session.SESSION_USER, relationUser) 76 | if err != nil { 77 | beego.Error(err) 78 | return 79 | } 80 | // check if the code is right. 81 | err = sess.Set(session.SESSION_USERNAME, username) 82 | if err != nil { 83 | beego.Error(err) 84 | return 85 | } 86 | gitlab.Tokens.Add(username, access) 87 | 88 | beego.Info("Login ... ", username, "access token:", access.AccessToken) 89 | redirectUrl := sess.Get("redirect") 90 | if redirectUrl != nil { 91 | if r, ok := redirectUrl.(string); ok && r != "" { 92 | sess.Delete("redirect") 93 | http.Redirect(this.Ctx.ResponseWriter, this.Ctx.Request, r, 301) 94 | return 95 | } 96 | } 97 | } else { 98 | //fmt.Println("in sessUsername != nil && paramCode != nil, paramCode:", paramCode, "session:", sessUsername) 99 | //全局模版变量 100 | this.Data["userName"] = sessUsername 101 | relation := user.Relation{} 102 | relationUser := relation.GetUserByName(sessUsername.(string)) 103 | beego.Debug("relationUser:" + relationUser.Name) 104 | this.Data["user"] = relationUser 105 | //加载用户默认token 106 | tokenService := &service.TokenService{ 107 | Session: db.GetMongoSession(), 108 | } 109 | if tokenService.Session != nil { 110 | defer tokenService.Session.Close() 111 | } 112 | defaultToken := tokenService.GetDefaultToken(sessUsername.(string)) 113 | if defaultToken != nil { 114 | this.Data["token"] = defaultToken.Value 115 | } 116 | 117 | //beego.Debug("redirect in else user:", relationUser, "username:", sessUsername, "token:", defaultToken.Value) 118 | if name, ok := sessUsername.(string); ok { 119 | this.Username = name 120 | } 121 | // Already login 122 | //beego.Debug("Have code.", "sessCode",sessCode,"paramCode",paramCode) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /controllers/default.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "alertCenter/controllers/session" 5 | "net/http" 6 | 7 | "github.com/astaxie/beego" 8 | "alertCenter/util" 9 | ) 10 | 11 | type MainController struct { 12 | BaseController 13 | } 14 | 15 | 16 | func (c *MainController) Logout(){ 17 | sess, err := session.GetSession(c.Ctx) 18 | if err!=nil{ 19 | beego.Error(err) 20 | return 21 | } 22 | defer sess.SessionRelease(c.Ctx.ResponseWriter) 23 | 24 | sess.Delete(session.SESSION_USERNAME) 25 | sess.Delete(session.SESSION_USER) 26 | 27 | c.Data["json"] = util.GetSuccessJson("Logout success.") 28 | c.ServeJSON() 29 | 30 | return 31 | } 32 | 33 | func (c *MainController) Get() { 34 | beego.Debug("in /") 35 | code := c.GetString("code") 36 | beego.Debug("code is :", code) 37 | if code != "" { 38 | beego.Debug("code is:", code) 39 | } else { 40 | beego.Debug("do not have code.") 41 | } 42 | 43 | //c.Data["Website"] = "beego.me" 44 | c.TplName = "index.html" 45 | } 46 | 47 | type Transit struct { 48 | Response http.ResponseWriter 49 | Request *http.Request 50 | Redirct string 51 | code int 52 | } 53 | 54 | func (c *MainController) Transit() { 55 | sess, err := session.GetSession(c.Ctx) 56 | if err == nil { 57 | transit := sess.Get(session.SESSION_TRANSIT) 58 | if transit != nil { 59 | http.Redirect(transit.(*Transit).Response, transit.(*Transit).Request, transit.(*Transit).Redirct, http.StatusTemporaryRedirect) 60 | } 61 | } 62 | redirct := beego.AppConfig.String("url") 63 | http.Redirect(c.Ctx.ResponseWriter, c.Ctx.Request, redirct, http.StatusTemporaryRedirect) 64 | } 65 | -------------------------------------------------------------------------------- /controllers/session/session.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "github.com/astaxie/beego/session" 5 | "github.com/astaxie/beego/context" 6 | "github.com/astaxie/beego" 7 | ) 8 | 9 | func init() { 10 | mangeConfig := &session.ManagerConfig{CookieLifeTime:3600, CookieName:"gosessionid", Gclifetime:3600, EnableSetCookie:true} 11 | GlobalSessions, _ = session.NewManager("memory", mangeConfig) 12 | go GlobalSessions.GC() 13 | } 14 | 15 | func GetSession(ctx *context.Context) (session.Store, error) { 16 | sess, err := GlobalSessions.SessionStart(ctx.ResponseWriter, ctx.Request) 17 | if err != nil { 18 | beego.Error("get session error:", err) 19 | return sess, err 20 | } 21 | return sess, nil 22 | } -------------------------------------------------------------------------------- /controllers/session/static.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "github.com/astaxie/beego/session" 5 | ) 6 | 7 | var GlobalSessions *session.Manager 8 | 9 | const ( 10 | SESSION_USER = "user" 11 | SESSION_USERNAME = "username" 12 | SESSION_TRANSIT = "transit" 13 | ) 14 | -------------------------------------------------------------------------------- /core/MessageCenter.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/astaxie/beego" 7 | 8 | "alertCenter/core/db" 9 | "alertCenter/core/notice" 10 | "alertCenter/core/service" 11 | "alertCenter/core/user" 12 | "alertCenter/models" 13 | 14 | "gopkg.in/mgo.v2/bson" 15 | 16 | mgo "gopkg.in/mgo.v2" 17 | ) 18 | 19 | //HandleMessage 处理alertmanager回来的数据 20 | // func HandleMessage(message *models.AlertReceive) { 21 | // session := db.GetMongoSession() 22 | // if session != nil { 23 | // defer session.Close() 24 | // } 25 | // alertService := &service.AlertService{ 26 | // Session: session, 27 | // } 28 | // ok := SaveMessage(message, session) 29 | // if !ok { 30 | // beego.Error("save a message fail,message receiver:" + message.Receiver) 31 | // } 32 | // for _, alert := range message.Alerts { 33 | // old := alertService.GetAlertByLabels(&alert) 34 | // if old != nil { 35 | // old.AlertCount = old.AlertCount + 1 36 | // old = old.Merge(&alert) 37 | // if !old.EndsAt.IsZero() { 38 | // old.IsHandle = 2 39 | // old.HandleDate = time.Now() 40 | // old.HandleMessage = "报警已自动恢复" 41 | // } 42 | // alertService.Update(old) 43 | // } else { 44 | // alert.AlertCount = 1 45 | // alert.IsHandle = 0 46 | // alert.Mark = alert.Fingerprint().String() 47 | // alert.Receiver = user.GetReceiverByTeam(message.Receiver) 48 | // now := time.Now() 49 | // // Ensure StartsAt is set. 50 | // if alert.StartsAt.IsZero() { 51 | // alert.StartsAt = now 52 | // } 53 | // if !alert.EndsAt.IsZero() { 54 | // alert.IsHandle = 2 55 | // alert.HandleDate = time.Now() 56 | // alert.HandleMessage = "报警已自动恢复" 57 | // } 58 | // alertService.Save(&alert) 59 | // } 60 | // } 61 | 62 | // } 63 | 64 | //HandleAlerts 处理prometheus回来的数据 65 | func HandleAlerts(alerts []*models.Alert) { 66 | session := db.GetMongoSession() 67 | if session != nil { 68 | defer session.Close() 69 | } 70 | alertService := &service.AlertService{ 71 | Session: session, 72 | } 73 | for _, alert := range alerts { 74 | //start := time.Now() 75 | old, err := alertService.GetAlertByMark(alert.Labels.Fingerprint().String()) 76 | if err != nil { 77 | if err.Error() == mgo.ErrNotFound.Error() { 78 | SaveAlert(alertService, alert) 79 | } else { 80 | continue 81 | } 82 | } else { 83 | //fmt.Println("get label:", time.Now().Sub(start)) 84 | if old != nil && old.EndsAt.IsZero() { 85 | old.AlertCount = old.AlertCount + 1 86 | alert.UpdatedAt = time.Now() 87 | old = old.Merge(alert) 88 | //old已更新时间信息 89 | if !old.EndsAt.IsZero() { 90 | old.IsHandle = 2 91 | old.HandleDate = time.Now() 92 | old.HandleMessage = "报警已自动恢复" 93 | SaveHistory(alertService, old) 94 | } 95 | old.UpdatedAt = time.Now() 96 | Notice(old) 97 | alertService.Update(old) 98 | } else if old != nil && !old.EndsAt.IsZero() { 99 | //此报警曾出现过并已结束 100 | if alert.StartsAt.After(old.EndsAt) { 101 | //报警开始时间在原报警之后,我们认为这是新报警 102 | //old更新状态信息 103 | old = old.Reset(alert) 104 | if old.IsHandle == 2 { 105 | SaveHistory(alertService, old) 106 | } 107 | Notice(old) 108 | alertService.Update(old) 109 | } else if alert.StartsAt.Before(old.EndsAt) && alert.EndsAt.After(old.EndsAt) { 110 | // 新的结束时间 111 | history, err := alertService.FindHistory(old) 112 | if history != nil && err == nil { 113 | old.EndsAt = alert.EndsAt 114 | history.EndsAt = alert.EndsAt 115 | alertService.Update(old) 116 | alertService.UpdateHistory(history) 117 | } 118 | } 119 | } 120 | } 121 | 122 | //fmt.Println("alert cost:", time.Now().Sub(start)) 123 | } 124 | } 125 | 126 | //Notice 发送报警通知信息 127 | func Notice(alert *models.Alert) { 128 | //全局通知开关关闭 129 | service := &service.GlobalConfigService{ 130 | Session: db.GetMongoSession(), 131 | } 132 | if service.Session != nil { 133 | defer service.Session.Close() 134 | } 135 | noticeOn, err := service.GetConfig("noticeOn") 136 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 137 | beego.Debug("get noticeOn from database error." + err.Error()) 138 | return 139 | } 140 | if noticeOn != nil && !noticeOn.Value.(bool) { 141 | beego.Debug("notice center closed.") 142 | return 143 | } 144 | if users, ok := CheckRules(alert); ok { 145 | alert.Receiver.UserNames = users 146 | mark := alert.Fingerprint().String() 147 | 148 | ch, err := notice.GetChannelByMark(mark) 149 | if err == nil && ch != nil { 150 | ch <- alert 151 | } else { 152 | err := notice.CreateChanByMark(alert.Fingerprint().String()) 153 | if err != nil { 154 | beego.Error(err) 155 | } 156 | go notice.NoticControl(alert) 157 | } 158 | } 159 | } 160 | 161 | //CheckRules 检验是否为用户忽略的报警 162 | func CheckRules(alert *models.Alert) ([]string, bool) { 163 | if alert != nil && alert.Receiver != nil { 164 | userNames := alert.Receiver.UserNames 165 | //没有接收对象,不需要发送 166 | if userNames == nil || len(userNames) < 1 { 167 | beego.Debug("no receiver") 168 | return nil, false 169 | } 170 | relation := user.Relation{} 171 | var users []string 172 | session := db.GetMongoSession() 173 | if session != nil { 174 | defer session.Close() 175 | } 176 | ruleService := &service.IgnoreRuleService{ 177 | Session: session, 178 | } 179 | for _, userName := range userNames { 180 | user := relation.GetUserByName(userName) 181 | var ignore bool 182 | if user != nil { 183 | rules, err := ruleService.FindRuleByUser(user.Name) 184 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 185 | continue 186 | } 187 | if rules != nil && len(rules) > 0 { 188 | for _, rule := range rules { 189 | //判断是否已过期 190 | if rule.EndsAt.After(time.Now()) && rule.StartsAt.Before(time.Now()) { 191 | if alert.Labels.Contains(rule.Labels) { 192 | ignore = true 193 | } 194 | } 195 | } 196 | } 197 | } else { 198 | ignore = true 199 | } 200 | if !ignore { 201 | users = append(users, user.Name) 202 | } 203 | } 204 | if len(users) == 0 { 205 | //beego.Debug("no receiver after check rule") 206 | return nil, false 207 | } 208 | return users, true 209 | } 210 | beego.Debug("alert is nil") 211 | return nil, false 212 | } 213 | 214 | //SaveHistory 存快照纪录 215 | func SaveHistory(alertService *service.AlertService, alert *models.Alert) { 216 | history := &models.AlertHistory{ 217 | ID: bson.NewObjectId(), 218 | Mark: alert.Fingerprint().String(), 219 | AddTime: time.Now(), 220 | StartsAt: alert.StartsAt, 221 | EndsAt: alert.EndsAt, 222 | Duration: alert.EndsAt.Sub(alert.StartsAt), 223 | Value: string(alert.Annotations.LabelSet["value"]), 224 | Message: string(alert.Annotations.LabelSet["description"]), 225 | } 226 | alertService.Session.Insert("AlertHistory", history) 227 | } 228 | 229 | //SaveAlert 保存alert信息 230 | func SaveAlert(alertService *service.AlertService, alert *models.Alert) { 231 | alert.AlertCount = 1 232 | alert.IsHandle = 0 233 | alert.Mark = alert.Fingerprint().String() 234 | alert.Receiver = user.GetReceiver(alert.Labels) 235 | now := time.Now() 236 | // Ensure StartsAt is set. 237 | if alert.StartsAt.IsZero() { 238 | alert.StartsAt = now 239 | } 240 | alert.UpdatedAt = now 241 | if !alert.EndsAt.IsZero() { 242 | alert.IsHandle = 2 243 | alert.HandleDate = time.Now() 244 | alert.HandleMessage = "报警已自动恢复" 245 | SaveHistory(alertService, alert) 246 | } 247 | Notice(alert) 248 | alertService.Save(alert) 249 | } 250 | 251 | //SaveMessage 储存alertmanager的消息 252 | func SaveMessage(message *models.AlertReceive, session *db.MongoSession) bool { 253 | ok := session.Insert("AlertReceive", message) 254 | return ok 255 | } 256 | -------------------------------------------------------------------------------- /core/MessageCenter_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/core/service" 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func Test_CheckRule(t *testing.T) { 11 | SESSION := db.GetMongoSession() 12 | service := &service.AlertService{ 13 | Session: SESSION, 14 | } 15 | alerts := service.FindByUser("root", 1, 1) 16 | us, ok := CheckRules(alerts[0]) 17 | fmt.Println("ok:", ok) 18 | fmt.Println("us:", us) 19 | } 20 | -------------------------------------------------------------------------------- /core/db/MongoDB.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "os" 8 | 9 | "fmt" 10 | 11 | "github.com/astaxie/beego" 12 | mgo "gopkg.in/mgo.v2" 13 | ) 14 | 15 | var Session *mgo.Session 16 | 17 | func init() { 18 | session, err := Open() 19 | if err != nil || session == nil { 20 | beego.Error("mongodb init error!" + err.Error()) 21 | os.Exit(1) 22 | } 23 | defer session.Close() 24 | } 25 | func Open() (*mgo.Session, error) { 26 | var mongoHost string 27 | if os.Getenv("MONGODB_HOST") != "" && os.Getenv("MONGODB_PORT") != "" { 28 | mongoHost = fmt.Sprintf("%s:%s", os.Getenv("MONGODB_HOST"), os.Getenv("MONGODB_PORT")) 29 | } else { 30 | mongoHost = beego.AppConfig.String("mongoURI") 31 | } 32 | beego.Info("mongoHost:", mongoHost) 33 | if Session == nil { 34 | login := &mgo.DialInfo{ 35 | Addrs: []string{mongoHost}, 36 | Timeout: 60 * time.Second, 37 | Database: beego.AppConfig.String("mongoDB"), 38 | // Username: beego.AppConfig.String("mongoUser"), 39 | // Password: beego.AppConfig.String("mongoPass"), 40 | } 41 | //log.Debug("Connectting mongodb, host:", login.Addrs, "user:", login.Username, ", password:", login.Password, ", db:", login.Database) 42 | session, err := mgo.DialWithInfo(login) 43 | if err != nil { 44 | beego.Error(err) 45 | //log.Error(err.Error()) 46 | return nil, err 47 | } 48 | Session = session 49 | return session.Clone(), err 50 | } 51 | return Session, nil 52 | } 53 | 54 | func GetSession() (*mgo.Session, error) { 55 | if Session == nil { 56 | return Open() 57 | } 58 | return Session.Clone(), nil 59 | } 60 | 61 | func CloseSession(session *mgo.Session) { 62 | if session != nil { 63 | session.Close() 64 | } 65 | } 66 | 67 | func GetDB(session *mgo.Session) (*mgo.Database, error) { 68 | if session == nil { 69 | var err error 70 | session, err = GetSession() 71 | if err != nil { 72 | return nil, err 73 | } 74 | } 75 | db := session.DB("") 76 | return db, nil 77 | } 78 | 79 | func GetCollection(collection string, db *mgo.Database) (*mgo.Collection, error) { 80 | if len(collection) == 0 { 81 | return nil, errors.New("Don't use empty collection name") 82 | } 83 | if db == nil { 84 | var err error 85 | db, err = GetDB(nil) 86 | 87 | if err != nil { 88 | return nil, err 89 | } 90 | } 91 | co := db.C(collection) 92 | return co, nil 93 | } 94 | 95 | type MongoSession struct { 96 | Session *mgo.Session 97 | DB *mgo.Database 98 | } 99 | 100 | func GetMongoSession() *MongoSession { 101 | session, err := GetSession() 102 | if err != nil { 103 | return nil 104 | } 105 | db, err := GetDB(session) 106 | if err != nil { 107 | return nil 108 | } 109 | return &MongoSession{ 110 | Session: session, 111 | DB: db, 112 | } 113 | } 114 | 115 | func (e *MongoSession) Insert(collection string, data ...interface{}) bool { 116 | coll, err := GetCollection(collection, e.DB) 117 | if err != nil { 118 | return false 119 | } 120 | err = coll.Insert(data...) 121 | if err != nil { 122 | beego.Error("insert data in " + collection + " error," + err.Error()) 123 | return false 124 | } 125 | return true 126 | } 127 | 128 | func (e *MongoSession) GetCollection(collection string) *mgo.Collection { 129 | coll, err := GetCollection(collection, e.DB) 130 | if err != nil { 131 | return nil 132 | } 133 | return coll 134 | } 135 | func (e *MongoSession) RemoveAll(collection string) bool { 136 | coll, err := GetCollection(collection, e.DB) 137 | if err != nil { 138 | return false 139 | } 140 | _, err = coll.RemoveAll(nil) 141 | if err == nil { 142 | return true 143 | } 144 | return false 145 | } 146 | 147 | func (e *MongoSession) Close() { 148 | if e.Session != nil { 149 | CloseSession(e.Session) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /core/db/MongoDB_test.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import "testing" 4 | 5 | type Person struct { 6 | NAME string 7 | PHONE string 8 | } 9 | 10 | func Test_Insert(t *testing.T) { 11 | session := GetMongoSession() 12 | if session != nil { 13 | defer session.Close() 14 | } 15 | if ok := session.Insert("Person", &Person{PHONE: "18811577546", 16 | NAME: "barnett"}); ok { 17 | } 18 | 19 | session.Close() 20 | } 21 | -------------------------------------------------------------------------------- /core/gitlab/gitlab.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "alertCenter/models" 5 | "net/url" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/astaxie/beego" 11 | ) 12 | 13 | func GetGitlabUrl() string { 14 | if os.Getenv("GITLAB_URL") != "" { 15 | return strings.TrimSuffix(os.Getenv("GITLAB_URL"), "/") 16 | } 17 | return strings.TrimSuffix(beego.AppConfig.String("Gitlab"), "/") 18 | } 19 | 20 | func GetAdminAccessToken() string { 21 | if os.Getenv("GITLAB_ACCESS_TOKEN") != "" { 22 | return os.Getenv("GITLAB_ACCESS_TOKEN") 23 | } 24 | return beego.AppConfig.String("GitlabAccessToken") 25 | } 26 | 27 | // call back url 28 | func GetCallBackUrl() string { 29 | if os.Getenv("GITLAB_CALLBACK_URL") != "" { 30 | return os.Getenv("GITLAB_CALLBACK_URL") 31 | } 32 | return beego.AppConfig.String("GitlabCallBackUrl") 33 | } 34 | 35 | // call back urll encoding 36 | func GetCallBackUrlEncode() string { 37 | u := GetCallBackUrl() 38 | return url.QueryEscape(u) 39 | } 40 | 41 | // clientId 42 | func GetGitlabClientId() string { 43 | if os.Getenv("GITLAB_OAUTH_APPLICATION_ID") != "" { 44 | return os.Getenv("GITLAB_OAUTH_APPLICATION_ID") 45 | } 46 | return beego.AppConfig.String("GitlabOAuthClientId") 47 | } 48 | 49 | // sercetId 50 | func GetGitlabSercetId() string { 51 | if os.Getenv("GITLAB_OAUTH_APPLICATION_SECRET") != "" { 52 | return os.Getenv("GITLAB_OAUTH_APPLICATION_SECRET") 53 | } 54 | return beego.AppConfig.String("GitlabOAuthSercet") 55 | } 56 | 57 | // redirect gitlab url 58 | func GetGitlabOAuthUrl() string { 59 | return GetGitlabUrl() + "/oauth/authorize?response_type=code&client_id=" + GetGitlabClientId() + "&redirect_uri=" + GetCallBackUrlEncode() 60 | } 61 | 62 | func ConvertGitlabGroupToAlertModel(gitlab GitlabGroup) *models.Team { 63 | team := &models.Team{} 64 | team.ID = strconv.Itoa(gitlab.Id) 65 | team.Name = gitlab.Name 66 | return team 67 | } 68 | 69 | func ConvertGitlabUserToAlertModel(gitlab GitlabUser) *models.User { 70 | user := &models.User{} 71 | user.ID = strconv.Itoa(gitlab.Id) 72 | user.Name = gitlab.Username 73 | user.AvatarURL = gitlab.AvatarUrl 74 | user.Mail = gitlab.Email 75 | //user.Phone = gitlab. 76 | user.RealName = gitlab.Name 77 | return user 78 | } 79 | 80 | func ConvertGitlabUsers(gitlab []*GitlabUser) []*models.User { 81 | users := make([]*models.User, len(gitlab)) 82 | for i, u := range gitlab { 83 | users[i] = ConvertGitlabUserToAlertModel(*u) 84 | } 85 | return users 86 | } 87 | -------------------------------------------------------------------------------- /core/gitlab/gitlab_access_token.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | "time" 10 | 11 | "github.com/astaxie/beego" 12 | ) 13 | 14 | var Tokens GitlabTokens 15 | 16 | //统一用username作为key,因为gitlab username是唯一的,name是可以重复的 17 | type GitlabTokens map[string]Token 18 | 19 | func init() { 20 | Tokens = make(map[string]Token) 21 | } 22 | 23 | func (t GitlabTokens) Add(username string, at *GitlabAccessToken) { 24 | token := Token{AccessToken: at.AccessToken, Username: username} 25 | token.Expire = time.Unix(at.CreatedAt, 0).Add(time.Hour * 2) 26 | t[username] = token 27 | } 28 | 29 | func (t GitlabTokens) Delete(username string) { 30 | delete(t, username) 31 | } 32 | 33 | func (t GitlabTokens) Get(username string) (Token, error) { 34 | token, ok := t[username] 35 | if !ok { 36 | return token, fmt.Errorf("Token not found") 37 | } 38 | 39 | if token.Expire.Before(time.Now()) { 40 | return token, fmt.Errorf("Token is expired.") 41 | } 42 | 43 | return token, nil 44 | } 45 | 46 | func (t GitlabTokens) Update(username string, at *GitlabAccessToken) { 47 | t.Delete(username) 48 | t.Add(username, at) 49 | } 50 | 51 | // get access token 52 | func GetGitlabAccessToken(code string) (*GitlabAccessToken, error) { 53 | u, err := url.Parse("/oauth/token") 54 | if err != nil { 55 | beego.Error(err) 56 | return nil, err 57 | } 58 | query := u.Query() 59 | query.Add("client_id", GetGitlabClientId()) 60 | query.Add("client_secret", GetGitlabSercetId()) 61 | query.Add("code", code) 62 | query.Add("grant_type", "authorization_code") 63 | query.Add("redirect_uri", GetCallBackUrl()) 64 | 65 | q := query.Encode() 66 | uri := u.Path + "?" + q 67 | 68 | targetUrl := beego.AppConfig.String("Gitlab") + uri 69 | //beego.Debug("GetGitlabAccessToken url:", targetUrl, "gitlab:", beego.AppConfig.String("Gitlab")) 70 | 71 | resp, err := http.Post(targetUrl, "application/json", nil) 72 | if err != nil { 73 | beego.Error(err) 74 | return nil, err 75 | } 76 | 77 | body, err := ioutil.ReadAll(resp.Body) 78 | if err != nil { 79 | beego.Error(err) 80 | return nil, err 81 | } 82 | defer resp.Body.Close() 83 | 84 | accToken := &GitlabAccessToken{} 85 | err = json.Unmarshal(body, &accToken) 86 | if err != nil { 87 | beego.Error(err) 88 | return nil, err 89 | } 90 | //fmt.Printf("body is %#v\n", string(body)) 91 | 92 | return accToken, nil 93 | } 94 | -------------------------------------------------------------------------------- /core/gitlab/gitlab_admin_api.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "strings" 10 | 11 | "github.com/astaxie/beego" 12 | ) 13 | 14 | const ( 15 | ApiVersion = "/api/v3" 16 | GetUser = "/users" 17 | GetGroups = "/groups" 18 | GetGroupUsers = "/groups/:id/members" 19 | currentUser = "/user" 20 | SearchUserByName = "/users?username=:username" 21 | ) 22 | 23 | func GitlabApi(method, url string, body []byte) ([]byte, error) { 24 | client := http.Client{} 25 | 26 | b := bytes.NewBuffer(body) 27 | req, err := http.NewRequest(method, url, b) 28 | if err != nil { 29 | beego.Error(err.Error()) 30 | return []byte{}, err 31 | } 32 | 33 | //if way == "" 34 | req.Header.Set("PRIVATE-TOKEN", GetAdminAccessToken()) 35 | //fmt.Println("debug:", "private token:", GetAdminAccessToken(), "url:", url) 36 | //req.Header.Set("Authorization", "Bearer " + accessToken) 37 | resp, err := client.Do(req) 38 | if err != nil { 39 | beego.Error(err.Error()) 40 | return []byte{}, err 41 | } 42 | 43 | respBody, err := ioutil.ReadAll(resp.Body) 44 | if err != nil { 45 | beego.Error(err.Error()) 46 | return []byte{}, err 47 | } 48 | defer resp.Body.Close() 49 | return respBody, nil 50 | } 51 | 52 | func GetCurrentUserWithToken(token string) (*GitlabUser, error) { 53 | url := GetGitlabUrl() + ApiVersion + currentUser 54 | body, err := RequestGitlabWithToken(token, url, "GET", nil) 55 | if err != nil { 56 | beego.Error(err) 57 | return nil, err 58 | } 59 | 60 | user := &GitlabUser{} 61 | err = json.Unmarshal(body, &user) 62 | if err != nil { 63 | beego.Error(err) 64 | return nil, err 65 | } 66 | return user, nil 67 | } 68 | 69 | func GetUserByUsername(username string) (*GitlabUser, error) { 70 | userUrl := strings.Replace(SearchUserByName, ":username", username, -1) 71 | 72 | url := GetGitlabUrl() + ApiVersion + userUrl 73 | //fmt.Println("url:", url) 74 | body, err := GitlabApi("GET", url, nil) 75 | if err != nil { 76 | beego.Error(err) 77 | return nil, err 78 | } 79 | 80 | user := []*GitlabUser{} 81 | err = json.Unmarshal(body, &user) 82 | if err != nil { 83 | beego.Debug("Unmarshal:", string(body)) 84 | beego.Error(err) 85 | return nil, err 86 | } 87 | if len(user) == 1 { 88 | return user[0], nil 89 | } else { 90 | return nil, fmt.Errorf("Search more then one user.") 91 | } 92 | } 93 | 94 | func SearchUserByUsername(username string) (*GitlabUser, error) { 95 | url := GetGitlabUrl() + GetUser + "?username=" + username 96 | 97 | token := GetAdminAccessToken() 98 | resp, err := RequestGitlabWithToken(token, url, "GET", nil) 99 | if err != nil { 100 | beego.Error(err) 101 | 102 | return nil, err 103 | } 104 | 105 | user := &GitlabUser{} 106 | err = json.Unmarshal(resp, &user) 107 | if err != nil { 108 | beego.Error(err) 109 | return nil, err 110 | } 111 | return user, nil 112 | } 113 | -------------------------------------------------------------------------------- /core/gitlab/gitlab_model.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import "time" 4 | 5 | type GitlabAccessToken struct { 6 | AccessToken string `json:"access_token"` 7 | TokenType string `json:"token_type"` 8 | RefreshToken string `json:"refresh_token"` 9 | Scope string `json:"scope"` 10 | CreatedAt int64 `json:"created_at"` 11 | } 12 | 13 | // save this in map 14 | type Token struct { 15 | Username string 16 | AccessToken string `json:"access_token"` 17 | Expire time.Time 18 | } 19 | 20 | type GitlabUser struct { 21 | Id int 22 | Name string 23 | Username string 24 | State string 25 | AvatarUrl string `json:"avatar_url"` 26 | IsAdmin bool `json:"is_admin"` 27 | Bio interface{} 28 | Email string 29 | ProjectsLimit int `json:"projects_limit"` 30 | Identities []interface{} `json:"identities"` 31 | CanCreateGroup bool `json:"can_create_group"` 32 | CanCreateProject bool `json:"can_create_project"` 33 | Private_token string `json:"private_token"` 34 | } 35 | 36 | type GitlabGroup struct { 37 | Id int 38 | Name string 39 | Path string 40 | Description string 41 | } 42 | -------------------------------------------------------------------------------- /core/gitlab/gitlab_test.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | "github.com/astaxie/beego" 7 | ) 8 | 9 | func Test_GetGitlabAccessToken(t *testing.T) { 10 | err := beego.LoadAppConfig("ini", "/Users/qwding/gopath/src/alertCenter/conf/app.conf") 11 | if err!=nil{ 12 | t.Error(err) 13 | return 14 | } 15 | access, err := GetGitlabAccessToken("3ef631e677a61af5bb0cea434746b8f665584858ec45201dd8c9b37e8abcfa5c") 16 | if err != nil { 17 | t.Error(err) 18 | } else { 19 | fmt.Printf("access token %#v\n", access) 20 | } 21 | } -------------------------------------------------------------------------------- /core/gitlab/gitlab_user_api.go: -------------------------------------------------------------------------------- 1 | package gitlab 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/astaxie/beego" 11 | ) 12 | 13 | func RequestGitlab(username, method, url string, body []byte) ([]byte, error) { 14 | token, err := Tokens.Get(username) 15 | if err != nil { 16 | beego.Error(err) 17 | return nil, err 18 | } 19 | return RequestGitlabWithToken(token.AccessToken, url, method, body) 20 | } 21 | 22 | func RequestGitlabWithToken(token, url, method string, body []byte) ([]byte, error) { 23 | client := http.Client{} 24 | 25 | req, err := http.NewRequest(method, url, bytes.NewBuffer(body)) 26 | if err != nil { 27 | beego.Error(err) 28 | return nil, nil 29 | } 30 | req.Header.Add("Authorization", "Bearer "+token) 31 | 32 | resp, err := client.Do(req) 33 | if err != nil { 34 | beego.Error(err) 35 | return nil, err 36 | } 37 | 38 | b, err := ioutil.ReadAll(resp.Body) 39 | if err != nil { 40 | beego.Error(err) 41 | return nil, err 42 | } 43 | 44 | defer resp.Body.Close() 45 | 46 | return b, nil 47 | } 48 | 49 | func GetGroupsByUsername(username string) ([]GitlabGroup, error) { 50 | url := GetGitlabUrl() + ApiVersion + GetGroups 51 | //fmt.Println("url:", url) 52 | body, err := RequestGitlab(username, "GET", url, nil) 53 | if err != nil { 54 | beego.Error(err) 55 | return nil, err 56 | } 57 | 58 | groups := []GitlabGroup{} 59 | err = json.Unmarshal(body, &groups) 60 | if err != nil { 61 | beego.Debug("Unmarshal:", string(body)) 62 | beego.Error(err) 63 | return nil, err 64 | } 65 | 66 | return groups, nil 67 | } 68 | 69 | func GetUsersByTeam(username, groupid string) ([]*GitlabUser, error) { 70 | gurl := strings.Replace(GetGroupUsers, ":id", groupid, -1) 71 | url := GetGitlabUrl() + ApiVersion + gurl 72 | //fmt.Println("url:", url) 73 | body, err := RequestGitlab(username, "GET", url, nil) 74 | if err != nil { 75 | beego.Error(err) 76 | return nil, err 77 | } 78 | 79 | users := []*GitlabUser{} 80 | err = json.Unmarshal(body, &users) 81 | if err != nil { 82 | beego.Debug("Unmarshal:", string(body)) 83 | beego.Error(err) 84 | return nil, err 85 | } 86 | 87 | return users, nil 88 | } 89 | -------------------------------------------------------------------------------- /core/notice/Mail.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import ( 4 | "crypto/tls" 5 | "path/filepath" 6 | "time" 7 | 8 | "alertCenter/core/user" 9 | "alertCenter/models" 10 | 11 | "io/ioutil" 12 | 13 | "strings" 14 | 15 | "strconv" 16 | 17 | "github.com/astaxie/beego" 18 | "gopkg.in/gomail.v2" 19 | ) 20 | 21 | type MailNoticeServer struct { 22 | mailChan chan *MailMessage 23 | stopChan chan bool 24 | } 25 | 26 | //GetMailDialer 获取邮箱服务器代理 27 | func (e *MailNoticeServer) GetMailDialer() *gomail.Dialer { 28 | mailServer := beego.AppConfig.String("mailServer") 29 | mailPort, _ := beego.AppConfig.Int("mailPort") 30 | mailUser := beego.AppConfig.String("mailUser") 31 | mailPassword := beego.AppConfig.String("mailPassword") 32 | d := gomail.NewDialer(mailServer, mailPort, mailUser, mailPassword) 33 | d.TLSConfig = &tls.Config{InsecureSkipVerify: mailPort == 465} 34 | return d 35 | } 36 | 37 | //SendMail 发送邮件 38 | func (e *MailNoticeServer) SendMail(message ...*gomail.Message) { 39 | d := e.GetMailDialer() 40 | d.DialAndSend(message...) 41 | } 42 | 43 | //StartWork 开始工作 44 | func (e *MailNoticeServer) StartWork() error { 45 | beego.Info("mail notice server init start") 46 | defer beego.Info("mail notice server init over") 47 | mailCount, err := beego.AppConfig.Int("mailCount") 48 | if err != nil { 49 | beego.Error("mailCount's type is not int ." + err.Error()) 50 | return err 51 | } 52 | mailReCount, err := beego.AppConfig.Int("mailReCount") 53 | if err != nil { 54 | beego.Error("mailReCount's type is not int ." + err.Error()) 55 | return err 56 | } 57 | if e.mailChan == nil { 58 | e.mailChan = make(chan *MailMessage, mailCount) 59 | } 60 | if e.stopChan == nil { 61 | e.stopChan = make(chan bool) 62 | } 63 | go func() { 64 | d := e.GetMailDialer() 65 | var s gomail.SendCloser 66 | var err error 67 | open := false 68 | for { 69 | select { 70 | case m, ok := <-e.mailChan: 71 | if !ok { 72 | return 73 | } 74 | if !open { 75 | if s, err = d.Dial(); err != nil { 76 | beego.Error("Get mail dial error." + err.Error()) 77 | } 78 | open = true 79 | } 80 | if err := gomail.Send(s, m.message); err != nil { 81 | beego.Error("send mail message error." + err.Error()) 82 | m.errCount++ 83 | if m.errCount < mailReCount { 84 | //5秒后重试 85 | beego.Debug("mail errCount:", m.errCount) 86 | go func(m *MailMessage) { 87 | time.Sleep(time.Second * 5) 88 | e.mailChan <- m 89 | }(m) 90 | } 91 | } 92 | case stop := <-e.stopChan: 93 | if stop { 94 | goto exit 95 | } 96 | // Close the connection to the SMTP server if no email was sent in 97 | // the last 30 seconds. 98 | case <-time.After(30 * time.Second): 99 | if open { 100 | if err := s.Close(); err != nil { 101 | panic(err) 102 | } 103 | open = false 104 | } 105 | } 106 | } 107 | exit: 108 | beego.Info("mail work stop success") 109 | }() 110 | beego.Info("mail notice server start success") 111 | return nil 112 | } 113 | 114 | //StopWork 结束工作 115 | func (e *MailNoticeServer) StopWork() error { 116 | if e.stopChan != nil { 117 | e.stopChan <- true 118 | close(e.stopChan) 119 | } 120 | if e.mailChan != nil { 121 | close(e.mailChan) 122 | } 123 | return nil 124 | } 125 | 126 | type MailMessage struct { 127 | message *gomail.Message 128 | errCount int 129 | alert *models.Alert 130 | } 131 | 132 | //GetMessage 构建邮件消息 133 | func (e *MailNoticeServer) GetMessage(body string, subject string, receiver ...string) *MailMessage { 134 | m := gomail.NewMessage() 135 | m.SetHeader("From", beego.AppConfig.String("mailFrom")) 136 | m.SetHeader("To", receiver...) 137 | m.SetHeader("Subject", subject) 138 | m.SetBody("text/html", body) 139 | return &MailMessage{ 140 | message: m, 141 | errCount: 0, 142 | } 143 | } 144 | 145 | //GetMessageByAlert 通过alert获取邮件消息 146 | func (e *MailNoticeServer) GetMessageByAlert(alert *models.Alert) (messages []*MailMessage) { 147 | userNames := alert.Receiver.UserNames 148 | relation := user.Relation{} 149 | for _, userName := range userNames { 150 | user := relation.GetUserByName(userName) 151 | if user != nil && user.Mail != "" { 152 | m := e.GetMessage(e.GetBody(alert), string(alert.Labels.LabelSet["alertname"])+"("+strconv.Itoa(alert.AlertCount)+")", user.Mail) 153 | m.alert = alert 154 | messages = append(messages, m) 155 | } else { 156 | beego.Debug("send mail to " + userName + ",user is not exit") 157 | } 158 | } 159 | return 160 | } 161 | 162 | //GetBody 创建邮件内容 163 | func (e *MailNoticeServer) GetBody(alert *models.Alert) string { 164 | path, _ := filepath.Abs("views/mail.html") 165 | buffer, err := ioutil.ReadFile(path) 166 | if err != nil { 167 | beego.Error("get mail template file error." + err.Error()) 168 | } 169 | mail := string(buffer) 170 | mail = strings.Replace(mail, "[TITLE]", string(alert.Labels.LabelSet["alertname"]), -1) 171 | mail = strings.Replace(mail, "[URL]", beego.AppConfig.String("url")+"/alertsCurrent", -1) 172 | mail = strings.Replace(mail, "[DESCRIPTION]", string(alert.Annotations.LabelSet["description"]), -1) 173 | return mail 174 | } 175 | 176 | //SendAlert 发送报警邮件实现 177 | func (e *MailNoticeServer) SendAlert(alert *models.Alert) error { 178 | //beego.Debug("start SendAlert ") 179 | messages := e.GetMessageByAlert(alert) 180 | //beego.Debug("mail count:" + strconv.Itoa(len(messages))) 181 | if len(messages) > 0 { 182 | for _, m := range messages { 183 | e.mailChan <- m 184 | } 185 | } 186 | return nil 187 | } 188 | -------------------------------------------------------------------------------- /core/notice/Mail_test.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/astaxie/beego" 8 | ) 9 | 10 | func init() { 11 | // beego.AppConfigPath = "/Users/Goyoo/gowork/src/alertCenter/conf/app.conf" 12 | beego.AppPath = "/Users/Goyoo/gowork/src/alertCenter" 13 | } 14 | 15 | var mail = `
16 | 17 | 18 | 53 | 54 | 55 |
19 |
20 | 21 | 22 | 25 | 26 | 27 | 42 | 43 |
23 | {{TITLE}} 24 |
28 | 29 | 30 | 33 | 34 | 35 | 39 | 40 |
31 | View in AlertCenter 32 |
36 | Description:
37 | {{DESCRIPTION}}
38 |
41 |
44 | 45 |
46 | 47 | 48 | 49 | 50 |
Sent by AlertCenter
51 |
52 |
56 |
` 57 | 58 | func Test_SendMail(t *testing.T) { 59 | fmt.Println("in SendMail_Test") 60 | server := &MailNoticeServer{} 61 | message := server.GetMessage(mail, "hhhhhh", "zengqingguo@goyoo.com") 62 | server.SendMail(message.message) 63 | } 64 | 65 | // func Test_GetBody(t *testing.T) { 66 | // server := &MailNoticeServer{} 67 | // fmt.Println(server.GetBody(&models.Alert{})) 68 | // } 69 | -------------------------------------------------------------------------------- /core/notice/NoticeCenter.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import "github.com/astaxie/beego" 4 | import "strings" 5 | 6 | var ( 7 | cacheServer map[string]NoticeServer 8 | ) 9 | 10 | //StartCenter 初始化并开始发送通知操作 11 | func StartCenter() (err error) { 12 | beego.Info("Starting NoticeServer is begin") 13 | server := beego.AppConfig.String("NoticeServer") 14 | servers := strings.Split(server, ",") 15 | cacheServer = make(map[string]NoticeServer, 0) 16 | for _, s := range servers { 17 | noticeServer := GetNoticeServer(s) 18 | if noticeServer != nil { 19 | err = noticeServer.StartWork() 20 | if err != nil { 21 | return 22 | } 23 | cacheServer[s] = noticeServer 24 | } 25 | } 26 | beego.Info("Starting NoticeServer success") 27 | 28 | return 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /core/notice/NoticeServer.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import "alertCenter/models" 4 | 5 | //NoticeServer 发送通知插件接口 6 | type NoticeServer interface { 7 | StartWork() error 8 | StopWork() error 9 | SendAlert(alert *models.Alert) error 10 | } 11 | 12 | //GetNoticeServer 获取通知插件 13 | func GetNoticeServer(name string) NoticeServer { 14 | if name == "mail" { 15 | return &MailNoticeServer{} 16 | } 17 | if name == "wexin" { 18 | return &WeNoticeServer{} 19 | } 20 | return nil 21 | } 22 | 23 | -------------------------------------------------------------------------------- /core/notice/WeChat.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "io/ioutil" 8 | "net/http" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "alertCenter/core/db" 14 | "alertCenter/core/user" 15 | "alertCenter/models" 16 | 17 | "github.com/astaxie/beego" 18 | "gopkg.in/mgo.v2/bson" 19 | ) 20 | 21 | type WeNoticeServer struct { 22 | weChan chan *WeMessage 23 | stopChan chan bool 24 | } 25 | 26 | var WeTag map[string]*models.WeTag 27 | 28 | //SendAlert 从一个alert构建wemessage送往发送通道 29 | func (e *WeNoticeServer) SendAlert(alert *models.Alert) error { 30 | url := beego.AppConfig.String("url") 31 | userNames := alert.Receiver.UserNames 32 | relation := user.Relation{} 33 | for _, userName := range userNames { 34 | user := relation.GetUserByName(userName) 35 | if user != nil && user.WeID != "" { 36 | me := e.GetWeAlertByUser(user.WeID) 37 | message := `` 38 | message += string(alert.Annotations.LabelSet["description"]) + "\n\t" 39 | message += `------------------\n\r` 40 | message += `[点击查看详情]` 41 | me = strings.Replace(me, "CONTENT", message, -1) 42 | e.weChan <- &WeMessage{ 43 | message: me, 44 | alert: alert, 45 | errCount: 0, 46 | } 47 | } 48 | } 49 | return nil 50 | } 51 | 52 | //StartWork 开始工作 53 | func (e *WeNoticeServer) StartWork() error { 54 | beego.Info("Wexin notice server init begin") 55 | defer beego.Info("Wexin notice server init over") 56 | 57 | weCount, err := beego.AppConfig.Int("weCount") 58 | if err != nil { 59 | beego.Error("weCount's type is not int ." + err.Error()) 60 | return err 61 | } 62 | weReCount, err := beego.AppConfig.Int("weReCount") 63 | if err != nil { 64 | beego.Error("weReCount's type is not int ." + err.Error()) 65 | return err 66 | } 67 | if WeTag == nil { 68 | WeTag = make(map[string]*models.WeTag, 0) 69 | if ok := e.GetAllTags(); !ok { 70 | return errors.New("get all weTags faild") 71 | } 72 | } 73 | if e.weChan == nil { 74 | e.weChan = make(chan *WeMessage, weCount) 75 | } 76 | if e.stopChan == nil { 77 | e.stopChan = make(chan bool) 78 | } 79 | go func() { 80 | for { 81 | select { 82 | case m, ok := <-e.weChan: 83 | if !ok { 84 | return 85 | } 86 | if err := e.sendWeChatMessage(m.message); err != nil { 87 | m.errCount++ 88 | if m.errCount < weReCount { 89 | //5秒后重试 90 | go func(m *WeMessage) { 91 | time.Sleep(time.Second * 5) 92 | e.weChan <- m 93 | }(m) 94 | } 95 | } 96 | case stop := <-e.stopChan: 97 | if stop { 98 | goto exit 99 | } 100 | } 101 | } 102 | exit: 103 | beego.Info("mail work stop success") 104 | }() 105 | beego.Info("wexin notice server start success") 106 | return nil 107 | } 108 | 109 | //StopWork 停止工作 110 | func (e *WeNoticeServer) StopWork() error { 111 | if e.stopChan != nil { 112 | e.stopChan <- true 113 | close(e.stopChan) 114 | } 115 | if e.weChan != nil { 116 | close(e.weChan) 117 | } 118 | return nil 119 | } 120 | 121 | //SendWeChatMessage 发送微信消息 122 | func (e *WeNoticeServer) sendWeChatMessage(mestr string) error { 123 | //util.Debug("send weiChat message :" + mestr) 124 | MessageURI := beego.AppConfig.String("weURI") + "/cgi-bin/message/send?access_token=ACCESS_TOKEN" 125 | body := bytes.NewBufferString(mestr) //.NewReader(me) 126 | client := &http.Client{} 127 | req, err := http.NewRequest("POST", MessageURI, body) 128 | if err != nil { 129 | beego.Error("create wechat request faild." + err.Error()) 130 | return err 131 | } 132 | req.Header.Set("Content-Type", "application/json;charset=utf-8") 133 | req.Header.Set("token", beego.AppConfig.String("weToken")) 134 | resp, err := client.Do(req) 135 | if err != nil { 136 | beego.Error("Send weChat message error," + err.Error()) 137 | return err 138 | } 139 | content, err := ioutil.ReadAll(resp.Body) 140 | if err != nil { 141 | beego.Error("Send weChat message error," + err.Error()) 142 | return err 143 | } else { 144 | beego.Info("Send WeChat result feedback:" + string(content)) 145 | } 146 | return nil 147 | } 148 | 149 | //GetWeAlertByTag 发送给标签组的消息 150 | func (e *WeNoticeServer) GetWeAlertByTag(tags string) string { 151 | AgentID := beego.AppConfig.String("weiAgentId") 152 | tagID, err := e.GetTagIDByTag(tags) 153 | if err == nil { 154 | return `{"totag": " ` + strconv.Itoa(tagID) + ` ","msgtype": "text","agentid": ` + AgentID + `,"text": {"content": "CONTENT"},"safe":0}` 155 | } 156 | return "" 157 | } 158 | 159 | //GetWeAlertByUser 发送给用户的消息 160 | func (e *WeNoticeServer) GetWeAlertByUser(WeID string) string { 161 | AgentID := beego.AppConfig.String("weiAgentId") 162 | return `{"touser": " ` + WeID + ` ","msgtype": "text","agentid": ` + AgentID + `,"text": {"content": "CONTENT"},"safe":0}` 163 | } 164 | 165 | //GetTagIDByTag 通过tag name获取tag id 166 | func (e *WeNoticeServer) GetTagIDByTag(tag string) (int, error) { 167 | session := db.GetMongoSession() 168 | if session != nil { 169 | defer session.Close() 170 | } 171 | 172 | coll := session.GetCollection("WeiTag") 173 | if coll == nil { 174 | return 0, errors.New("get collection WeiTag faild") 175 | } 176 | weTag := &models.WeTag{} 177 | err := coll.Find(bson.M{"tagname": tag}).Select(nil).One(&weTag) 178 | if err != nil { 179 | beego.Error("get weiTag by name error." + err.Error()) 180 | return 0, err 181 | } 182 | return weTag.TagId, nil 183 | } 184 | 185 | //GetAllTags 获取微信服务器全部tag 186 | func (e *WeNoticeServer) GetAllTags() bool { 187 | TagListURI := beego.AppConfig.String("weURI") + "/cgi-bin/tag/list?access_token=ACCESS_TOKEN" 188 | client := &http.Client{} 189 | req, err := http.NewRequest("GET", TagListURI, nil) 190 | if err != nil { 191 | beego.Error("Get mongo session error." + err.Error()) 192 | return false 193 | } 194 | req.Header.Set("Content-Type", "application/json") 195 | req.Header.Set("token", beego.AppConfig.String("weToken")) 196 | resp, err := client.Do(req) 197 | if err != nil { 198 | beego.Error("GET taglist error," + err.Error()) 199 | return false 200 | } 201 | content, err := ioutil.ReadAll(resp.Body) 202 | if err != nil { 203 | beego.Error("GET taglist error," + err.Error()) 204 | return false 205 | } 206 | WeiTagResult := &models.WeiTagResult{} 207 | err = json.Unmarshal(content, WeiTagResult) 208 | if err != nil { 209 | beego.Error("Parse the taglist data error ." + err.Error()) 210 | return false 211 | } 212 | for _, tag := range WeiTagResult.TagList { 213 | WeTag[tag.TagName] = &tag 214 | } 215 | 216 | session := db.GetMongoSession() 217 | if session != nil { 218 | defer session.Close() 219 | } 220 | if ok := session.RemoveAll("WeTag"); ok { 221 | 222 | var data []interface{} 223 | for _, tag := range WeiTagResult.TagList { 224 | data = append(data, tag) 225 | } 226 | beego.Debug("Got the wetag number is " + strconv.Itoa(len(data))) 227 | return session.Insert("WeTag", data...) 228 | } 229 | return false 230 | } 231 | 232 | type WeMessage struct { 233 | alert *models.Alert 234 | errCount int 235 | message string 236 | } 237 | 238 | func GetWeTagByName(name string) *models.WeTag { 239 | if WeTag != nil { 240 | return WeTag[name] 241 | } 242 | return nil 243 | } 244 | -------------------------------------------------------------------------------- /core/notice/noticeChan.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import ( 4 | "alertCenter/models" 5 | "fmt" 6 | ) 7 | 8 | var NoticChans map[string]chan *models.Alert 9 | 10 | func init() { 11 | NoticChans = make(map[string]chan *models.Alert) 12 | } 13 | 14 | //GetChannelByMark 获取发送报警通道 15 | func GetChannelByMark(mark string) (chan *models.Alert, error) { 16 | result, ok := NoticChans[mark] 17 | if ok { 18 | return result,nil 19 | }else{ 20 | return nil, fmt.Errorf("Can not find the channel.") 21 | 22 | } 23 | 24 | } 25 | 26 | func CreateChanByMark(mark string) error { 27 | if _, ok := NoticChans[mark]; ok { 28 | return fmt.Errorf("Channel already exist.") 29 | } else { 30 | NoticChans[mark] = make(chan *models.Alert) 31 | } 32 | return nil 33 | } 34 | 35 | func DeleteChanByMark(mark string) { 36 | delete(NoticChans, mark) 37 | } 38 | -------------------------------------------------------------------------------- /core/notice/noticeControl.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import ( 4 | //"fmt" 5 | "alertCenter/models" 6 | "time" 7 | 8 | "github.com/astaxie/beego" 9 | ) 10 | 11 | var IgnoreSend = 10 * time.Second 12 | var StopSend = 30 * time.Minute 13 | 14 | var SendMsgInterval_0 = time.Hour * 1 15 | var SendMsgInterval_1 = time.Minute * 30 16 | var SendMsgInterval_2 = time.Minute * 15 17 | var SendMsgInterval_3 = time.Minute * 5 18 | 19 | func init() { 20 | Lv0, err := time.ParseDuration(beego.AppConfig.String("sendMsgInterval_0")) 21 | if err == nil { 22 | SendMsgInterval_0 = Lv0 23 | } else { 24 | beego.Error(err) 25 | } 26 | Lv1, err := time.ParseDuration(beego.AppConfig.String("sendMsgInterval_1")) 27 | if err == nil { 28 | SendMsgInterval_1 = Lv1 29 | } else { 30 | beego.Error(err) 31 | } 32 | Lv2, err := time.ParseDuration(beego.AppConfig.String("sendMsgInterval_2")) 33 | if err == nil { 34 | SendMsgInterval_2 = Lv2 35 | } else { 36 | beego.Error(err) 37 | } 38 | Lv3, err := time.ParseDuration(beego.AppConfig.String("sendMsgInterval_3")) 39 | if err == nil { 40 | SendMsgInterval_3 = Lv3 41 | } else { 42 | beego.Error(err) 43 | } 44 | 45 | ignoreSend, err := time.ParseDuration(beego.AppConfig.String("ignoreSend")) 46 | if err == nil { 47 | IgnoreSend = ignoreSend 48 | } else { 49 | beego.Error(err) 50 | } 51 | 52 | stopSend, err := time.ParseDuration(beego.AppConfig.String("stopSend")) 53 | if err == nil { 54 | StopSend = stopSend 55 | } else { 56 | beego.Error(err) 57 | } 58 | 59 | } 60 | 61 | func NoticControl(alert *models.Alert) { 62 | beego.Info("start alert:", alert.Mark) 63 | defer beego.Info("end alert:", alert.Mark) 64 | 65 | ch, err := GetChannelByMark(alert.Fingerprint().String()) 66 | if err != nil { 67 | beego.Error(err) 68 | return 69 | } 70 | defer DeleteChanByMark(alert.Fingerprint().String()) 71 | 72 | var timeout = time.NewTimer(IgnoreSend) 73 | noNeedFlag := false 74 | 75 | NoNeedSend: 76 | for { 77 | select { 78 | case tmp := <-ch: 79 | if tmp.EndsAt.After(alert.StartsAt) { 80 | beego.Info("No need send this alert.") 81 | } 82 | noNeedFlag = true 83 | case <-timeout.C: 84 | break NoNeedSend 85 | } 86 | } 87 | 88 | if noNeedFlag { 89 | return 90 | } 91 | 92 | var timer = time.NewTimer(0 * time.Second) 93 | var stopSend = time.NewTimer(StopSend) 94 | 95 | for { 96 | 97 | select { 98 | case al := <-ch: 99 | _ = alert 100 | if al.EndsAt.After(alert.StartsAt) { 101 | beego.Info("Alert has been fix") 102 | return 103 | } 104 | stopSend.Reset(StopSend) 105 | case <-stopSend.C: 106 | beego.Info("Have not get this alert for long time. Stop sending email.") 107 | return 108 | case <-timer.C: 109 | //if beego.AppConfig.String("runmode") != "dev" { 110 | for _, server := range cacheServer { 111 | if server != nil { 112 | server.SendAlert(alert) 113 | } 114 | } 115 | //} 116 | 117 | timer.Reset(GetSendMsgInterval(alert.Level)) 118 | } 119 | } 120 | 121 | } 122 | 123 | func GetSendMsgInterval(level int) time.Duration { 124 | switch level { 125 | case 0: 126 | return SendMsgInterval_0 127 | case 1: 128 | return SendMsgInterval_1 129 | case 2: 130 | return SendMsgInterval_2 131 | case 3: 132 | return SendMsgInterval_3 133 | default: 134 | return SendMsgInterval_0 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /core/service/AlertService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/models" 6 | 7 | "github.com/astaxie/beego" 8 | 9 | "fmt" 10 | 11 | mgo "gopkg.in/mgo.v2" 12 | "gopkg.in/mgo.v2/bson" 13 | ) 14 | 15 | type AlertService struct { 16 | Session *db.MongoSession 17 | } 18 | 19 | //GetAlertService 获取servcie 20 | func GetAlertService(session *db.MongoSession) *AlertService { 21 | return &AlertService{ 22 | Session: session, 23 | } 24 | } 25 | 26 | //GetAlertByLabels 获取报警根据labels 27 | func (e *AlertService) GetAlertByLabels(alert *models.Alert) (result *models.Alert, err error) { 28 | //start := time.Now() 29 | //defer fmt.Println("cost:",time.Now().Sub(start)) 30 | mark := alert.Fingerprint().String() 31 | coll := e.Session.GetCollection("Alert") 32 | if coll == nil { 33 | return nil, fmt.Errorf("get alert collection error") 34 | } 35 | err = coll.Find(bson.M{"mark": mark}).Select(nil).One(&result) 36 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 37 | beego.Error("Get alert by Mark " + mark + " error." + err.Error()) 38 | return nil, err 39 | } 40 | return 41 | } 42 | 43 | //GetAlertByMark 获取报警根据mark 44 | func (e *AlertService) GetAlertByMark(mark string) (result *models.Alert, err error) { 45 | //start := time.Now() 46 | //defer fmt.Println("cost:",time.Now().Sub(start)) 47 | coll := e.Session.GetCollection("Alert") 48 | if coll == nil { 49 | return nil, fmt.Errorf("get alert collection error") 50 | } 51 | err = coll.Find(bson.M{"mark": mark}).Select(nil).One(&result) 52 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 53 | beego.Error("Get alert by Mark " + mark + " error." + err.Error()) 54 | return nil, err 55 | } 56 | return 57 | } 58 | 59 | //Update 更新可变信息 60 | func (e *AlertService) Update(alert *models.Alert) bool { 61 | coll := e.Session.GetCollection("Alert") 62 | if coll == nil { 63 | return false 64 | } 65 | err := coll.Update(bson.M{"mark": alert.Mark}, bson.M{ 66 | "$set": bson.M{ 67 | "alertcount": alert.AlertCount, 68 | "ishandle": alert.IsHandle, 69 | "handledate": alert.HandleDate, 70 | "handlemessage": alert.HandleMessage, 71 | "endsat": alert.EndsAt, 72 | "startsat": alert.StartsAt, 73 | "updatedat": alert.UpdatedAt, 74 | "annotations": alert.Annotations, 75 | }, 76 | }) 77 | if err != nil { 78 | beego.Error("Update the alert Error By Mark " + alert.Mark + "," + err.Error()) 79 | return false 80 | } 81 | return true 82 | } 83 | 84 | // Save 存储报警 85 | func (e *AlertService) Save(alert *models.Alert) bool { 86 | //return e.Session.Insert("Alert", alert) 87 | coll := e.Session.GetCollection("Alert") 88 | if coll == nil { 89 | return false 90 | } 91 | _, err := coll.Upsert(bson.M{"mark": alert.Mark}, alert) 92 | if err == nil { 93 | return true 94 | } 95 | return false 96 | } 97 | 98 | //FindByUser 根据receiver的name或者id获取报警信息 99 | func (e *AlertService) FindByUser(user string, pageSize int, page int, blog bool) (alerts []*models.Alert, err error) { 100 | coll := e.Session.GetCollection("Alert") 101 | if coll == nil { 102 | return nil, fmt.Errorf("get alert collection error") 103 | } 104 | if blog { 105 | err = coll.Find(bson.M{"receiver.usernames": user}).Skip(pageSize * (page - 1)).Limit(pageSize).Select(nil).All(&alerts) 106 | } else { 107 | err = coll.Find(bson.M{"receiver.usernames": user, "ishandle": bson.M{"$ne": 2}}).Skip(pageSize * (page - 1)).Limit(pageSize).Select(nil).All(&alerts) 108 | } 109 | return 110 | } 111 | 112 | //FindByID 根据mark获取报警 113 | func (e *AlertService) FindByID(ID string) (alert *models.Alert, err error) { 114 | coll := e.Session.GetCollection("Alert") 115 | if coll == nil { 116 | return nil, nil 117 | } 118 | err = coll.Find(bson.M{"mark": ID}).One(&alert) 119 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 120 | beego.Debug("find alert by id faild." + err.Error()) 121 | } 122 | return 123 | } 124 | 125 | //FindAll 获取全部报警 126 | func (e *AlertService) FindAll(pageSize int, page int, blog bool) (alerts []*models.Alert, err error) { 127 | coll := e.Session.GetCollection("Alert") 128 | if coll == nil { 129 | return nil, fmt.Errorf("get alert collection error") 130 | } 131 | if blog { 132 | err = coll.Find(nil).Skip(pageSize * (page - 1)).Limit(pageSize).Select(nil).All(&alerts) 133 | } else { 134 | err = coll.Find(bson.M{"ishandle": bson.M{"$ne": 2}}).Skip(pageSize * (page - 1)).Limit(pageSize).Select(nil).All(&alerts) 135 | } 136 | return 137 | } 138 | 139 | //FindHistory 获取history通过alert 140 | func (e *AlertService) FindHistory(alert *models.Alert) (history *models.AlertHistory, err error) { 141 | coll := e.Session.GetCollection("AlertHistory") 142 | if coll == nil { 143 | return nil, fmt.Errorf("get AlertHistory collection error") 144 | } 145 | err = coll.Find(bson.M{"mark": alert.Fingerprint().String(), "startsat": alert.StartsAt}).One(&history) 146 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 147 | beego.Error("find alerthistory by mark and startsAt faild." + err.Error()) 148 | } 149 | return 150 | } 151 | 152 | //UpdateHistory 更新history时间信息 153 | func (e *AlertService) UpdateHistory(history *models.AlertHistory) { 154 | coll := e.Session.GetCollection("AlertHistory") 155 | if coll == nil { 156 | return 157 | } 158 | err := coll.UpdateId(history.ID, history) 159 | if err != nil { 160 | beego.Error("update alerthistory by mark and startsAt faild." + err.Error()) 161 | } 162 | } 163 | 164 | //GetHistoryByMark 获取报警历史 165 | func (e *AlertService) GetHistoryByMark(mark string, pageSize int, page int) (historys []*models.AlertHistory, err error) { 166 | coll := e.Session.GetCollection("AlertHistory") 167 | if coll == nil { 168 | return nil, fmt.Errorf("get AlertHistory collection error") 169 | } 170 | err = coll.Find(bson.M{"mark": mark}).Skip(pageSize * (page - 1)).Limit(pageSize).All(&historys) 171 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 172 | beego.Error("find alerthistory by mark and startsAt faild." + err.Error()) 173 | } 174 | return 175 | } 176 | -------------------------------------------------------------------------------- /core/service/GlobalConfigService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/models" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/astaxie/beego" 10 | mgo "gopkg.in/mgo.v2" 11 | "gopkg.in/mgo.v2/bson" 12 | ) 13 | 14 | type GlobalConfigService struct { 15 | Session *db.MongoSession 16 | } 17 | 18 | var globalConfigMap map[string]*models.GlobalConfig 19 | var globalConfigs []*models.GlobalConfig 20 | 21 | //RefreshGlobalCnfig 更新全局配置缓存 22 | func (e *GlobalConfigService) RefreshGlobalCnfig() { 23 | beego.Debug("Start refresh global configs.") 24 | if e.Session == nil { 25 | return 26 | } 27 | coll := e.Session.GetCollection("GlobalConfig") 28 | if coll == nil { 29 | beego.Error("get GlobalConfig collection error when init NoticeOn ") 30 | } else { 31 | var configs []*models.GlobalConfig 32 | err := coll.Find(nil).Select(nil).All(&configs) 33 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 34 | beego.Error("get GlobalConfigs error " + err.Error()) 35 | } 36 | if configs != nil { 37 | globalConfigs = configs 38 | globalConfigTmp := make(map[string]*models.GlobalConfig, 0) 39 | for _, config := range configs { 40 | globalConfigTmp[config.Name] = config 41 | } 42 | globalConfigMap = globalConfigTmp 43 | beego.Debug("refresh global configs success.size:", len(globalConfigMap)) 44 | } 45 | } 46 | 47 | } 48 | func (e *GlobalConfigService) Init() error { 49 | globalConfigMap = make(map[string]*models.GlobalConfig, 0) 50 | globalConfigs = make([]*models.GlobalConfig, 0) 51 | if config, _ := e.GetConfig("noticeOn"); config == nil { 52 | e.Insert(&models.GlobalConfig{ 53 | ID: bson.NewObjectId(), 54 | Name: "noticeOn", 55 | Value: false, 56 | AddTime: time.Now(), 57 | }) 58 | } 59 | e.RefreshGlobalCnfig() 60 | return nil 61 | } 62 | 63 | //GetConfig 获取全局配置,重复名称的配置不适合此方法 64 | func (e *GlobalConfigService) GetConfig(name string) (config *models.GlobalConfig, err error) { 65 | if config := globalConfigMap[name]; config != nil { 66 | return config, nil 67 | } 68 | coll := e.Session.GetCollection("GlobalConfig") 69 | if coll == nil { 70 | beego.Error("get GlobalConfig collection error when init NoticeOn ") 71 | err = fmt.Errorf("get GlobalConfig collection error") 72 | } else { 73 | err := coll.Find(bson.M{"name": name}).Select(nil).One(&config) 74 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 75 | beego.Error("get GlobalConfig with name " + name + " error " + err.Error()) 76 | } 77 | } 78 | return 79 | } 80 | 81 | //GetConfigA 获取全局配置 82 | func (e *GlobalConfigService) GetConfigA(name string, value interface{}) (config *models.GlobalConfig, err error) { 83 | if globalConfigs != nil { 84 | for _, config := range globalConfigs { 85 | if config.Name == name && config.Value == value { 86 | return config, nil 87 | } 88 | } 89 | } 90 | coll := e.Session.GetCollection("GlobalConfig") 91 | if coll == nil { 92 | beego.Error("get GlobalConfig collection error when init NoticeOn ") 93 | err = fmt.Errorf("get GlobalConfig collection error") 94 | } else { 95 | err = coll.Find(bson.M{"name": name, "value": value}).Select(nil).One(&config) 96 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 97 | beego.Error("get GlobalConfig with name " + name + " error " + err.Error()) 98 | } 99 | } 100 | return 101 | } 102 | 103 | //GetAllConfig 获取同名全部配置 104 | func (e *GlobalConfigService) GetAllConfig(name string) (configs []*models.GlobalConfig, err error) { 105 | if globalConfigs != nil { 106 | for _, config := range globalConfigs { 107 | if config.Name == name { 108 | configs = append(configs, config) 109 | } 110 | } 111 | return 112 | } 113 | coll := e.Session.GetCollection("GlobalConfig") 114 | if coll == nil { 115 | beego.Error("get GlobalConfig collection error when init NoticeOn ") 116 | err = fmt.Errorf("get GlobalConfig collection error") 117 | } else { 118 | err = coll.Find(bson.M{"name": name}).Select(nil).All(&configs) 119 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 120 | beego.Error("get GlobalConfig with name " + name + " error " + err.Error()) 121 | } 122 | } 123 | return 124 | } 125 | 126 | //CheckExist 判断指定变量是否存在, 适用于value类型为string的 127 | func (e *GlobalConfigService) CheckExist(name string, value interface{}) (bool, error) { 128 | if globalConfigs != nil { 129 | for _, config := range globalConfigs { 130 | //beego.Debug(config.Name, name, config.Value, value) 131 | if config.Name == name && config.Value.(string) == value.(string) { 132 | return true, nil 133 | } 134 | } 135 | return false, nil 136 | } 137 | coll := e.Session.GetCollection("GlobalConfig") 138 | var err error 139 | if coll == nil { 140 | beego.Error("get GlobalConfig collection error when init NoticeOn ") 141 | err = fmt.Errorf("get GlobalConfig collection error") 142 | } else { 143 | var config *models.GlobalConfig 144 | err = coll.Find(bson.M{"name": name, "value": value}).Select(nil).One(&config) 145 | if err != nil && err.Error() != mgo.ErrNotFound.Error() { 146 | beego.Error("get config error," + err.Error()) 147 | return false, err 148 | } 149 | if err != nil && err.Error() == mgo.ErrNotFound.Error() { 150 | return false, nil 151 | } 152 | if &config != nil { 153 | return true, nil 154 | } 155 | } 156 | return false, err 157 | } 158 | 159 | //Update 更新全局配置 160 | func (e *GlobalConfigService) Update(config *models.GlobalConfig) bool { 161 | if config == nil { 162 | return false 163 | } 164 | coll := e.Session.GetCollection("GlobalConfig") 165 | if coll == nil { 166 | beego.Error("get GlobalConfig collection error when init NoticeOn ") 167 | } else { 168 | err := coll.Update(bson.M{"name": config.Name}, bson.M{"$set": bson.M{"value": config.Value}}) 169 | if err != nil { 170 | beego.Error("update GlobalConfig with name " + config.Name + " error " + err.Error()) 171 | return false 172 | } 173 | if globalConfigMap != nil { 174 | globalConfigMap[config.Name] = config 175 | } 176 | if globalConfigs != nil { 177 | for _, c := range globalConfigs { 178 | if c.Name == config.Name { 179 | c.Value = config.Value 180 | } 181 | } 182 | } 183 | return true 184 | } 185 | return false 186 | } 187 | 188 | //DeleteByID 删除配置通过id 189 | func (e *GlobalConfigService) DeleteByID(id string) bool { 190 | coll := e.Session.GetCollection("GlobalConfig") 191 | if coll == nil { 192 | beego.Error("get GlobalConfig collection error when init NoticeOn ") 193 | } else { 194 | err := coll.RemoveId(bson.ObjectIdHex(id)) 195 | if err != nil { 196 | beego.Error("remove GlobalConfig by ID(" + id + ") error ." + err.Error()) 197 | return false 198 | } 199 | var co *models.GlobalConfig 200 | var cos []*models.GlobalConfig 201 | if globalConfigs != nil { 202 | for _, c := range globalConfigs { 203 | if c.ID.Hex() == id { 204 | co = c 205 | } else { 206 | cos = append(cos, c) 207 | } 208 | } 209 | globalConfigs = cos 210 | beego.Debug("delete config id:", id, "globalConfigs length :", len(globalConfigs)) 211 | } 212 | if globalConfigMap != nil && co != nil { 213 | delete(globalConfigMap, co.Name) 214 | } 215 | return true 216 | } 217 | return false 218 | } 219 | 220 | //Insert 添加全局配置 221 | func (e *GlobalConfigService) Insert(config *models.GlobalConfig) bool { 222 | if e.Session == nil || config == nil { 223 | return false 224 | } 225 | if ok := e.Session.Insert("GlobalConfig", config); ok { 226 | globalConfigMap[config.Name] = config 227 | globalConfigs = append(globalConfigs, config) 228 | return true 229 | } 230 | return false 231 | } 232 | -------------------------------------------------------------------------------- /core/service/IgnoreRuleService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/models" 6 | "fmt" 7 | "time" 8 | 9 | mgo "gopkg.in/mgo.v2" 10 | "gopkg.in/mgo.v2/bson" 11 | 12 | "github.com/astaxie/beego" 13 | uuid "github.com/satori/go.uuid" 14 | ) 15 | 16 | type IgnoreRuleService struct { 17 | Session *db.MongoSession 18 | } 19 | 20 | //FindRuleByUser 获取规则 21 | func (e *IgnoreRuleService) FindRuleByUser(userName string) (rules []*models.UserIgnoreRule, err error) { 22 | coll := e.Session.GetCollection("IgnoreRule") 23 | if coll == nil { 24 | return nil, fmt.Errorf("get IgnoreRule collection error") 25 | } 26 | err = coll.Find(bson.M{"username": userName}).Select(nil).All(&rules) 27 | return 28 | } 29 | 30 | //FindRuleByMark 通过mark获取规则 31 | func (e *IgnoreRuleService) FindRuleByMark(mark string) (rule *models.UserIgnoreRule, err error) { 32 | coll := e.Session.GetCollection("IgnoreRule") 33 | if coll == nil { 34 | return nil, fmt.Errorf("get IgnoreRule collection error") 35 | } 36 | err = coll.Find(bson.M{"mark": mark}).Select(nil).One(rule) 37 | return 38 | } 39 | 40 | //AddRule 添加规则 41 | func (e *IgnoreRuleService) AddRule(rule *models.UserIgnoreRule) { 42 | rule.Mark = rule.Labels.FastFingerprint().String() 43 | _, err := e.FindRuleByMark(rule.Mark) 44 | if err != nil && err.Error() == mgo.ErrNotFound.Error() { 45 | rule.AddTime = time.Now() 46 | rule.RuleID = uuid.NewV4().String() 47 | rule.IsLive = true 48 | e.Session.Insert("IgnoreRule", rule) 49 | } 50 | } 51 | 52 | //DeleteRule 删除规则 53 | func (e *IgnoreRuleService) DeleteRule(ruleID string, user string) bool { 54 | col := e.Session.GetCollection("IgnoreRule") 55 | if col == nil { 56 | beego.Error("get collection IgnoreRule error ") 57 | return false 58 | } 59 | err := col.Remove(bson.M{"ruleid": ruleID, "username": user}) 60 | if err != nil { 61 | beego.Error("delete IgnoreRule error ,", err.Error()) 62 | return false 63 | } 64 | return true 65 | } 66 | -------------------------------------------------------------------------------- /core/service/TeamService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/models" 6 | ) 7 | 8 | type TeamService struct { 9 | Session *db.MongoSession 10 | } 11 | 12 | func GetTeamService(session *db.MongoSession) *TeamService { 13 | return &TeamService{ 14 | Session: session, 15 | } 16 | } 17 | 18 | func (e *TeamService) FindAll() (teams []*models.Team) { 19 | coll := e.Session.GetCollection("team") 20 | if coll == nil { 21 | return nil 22 | } 23 | coll.Find(nil).Select(nil).All(&teams) 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /core/service/TokenService.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/models" 6 | "time" 7 | 8 | "gopkg.in/mgo.v2/bson" 9 | 10 | "github.com/astaxie/beego" 11 | uuid "github.com/satori/go.uuid" 12 | ) 13 | 14 | type TokenService struct { 15 | Session *db.MongoSession 16 | } 17 | 18 | var cacheToken map[string][]*models.Token 19 | 20 | func init() { 21 | if cacheToken == nil { 22 | cacheToken = make(map[string][]*models.Token, 0) 23 | Session := db.GetMongoSession() 24 | if Session != nil { 25 | defer Session.Close() 26 | } 27 | col := Session.GetCollection("Token") 28 | if col == nil { 29 | beego.Error("get collection token error ") 30 | } 31 | var result []*models.Token 32 | err := col.Find(nil).All(&result) 33 | if err != nil { 34 | beego.Error("find all token error." + err.Error()) 35 | } 36 | for _, item := range result { 37 | items := cacheToken[item.UserName] 38 | if items == nil { 39 | items = make([]*models.Token, 0) 40 | } 41 | items = append(items, item) 42 | cacheToken[item.UserName] = items 43 | } 44 | } 45 | } 46 | 47 | //GetDefaultToken 获取默认token 48 | func (e *TokenService) GetDefaultToken(user string) *models.Token { 49 | 50 | tokens := cacheToken[user] 51 | for _, token := range tokens { 52 | if token.Project == "default" { 53 | return token 54 | } 55 | } 56 | return nil 57 | } 58 | 59 | //CreateToken 创建token 60 | func (e *TokenService) CreateToken(project string, userName string) *models.Token { 61 | beego.Debug("create token project:" + project + " ,user:" + userName) 62 | old := e.GetToken(project, userName) 63 | if old != nil { 64 | return old 65 | } 66 | token := &models.Token{ 67 | Value: uuid.NewV4().String(), 68 | CreateTime: time.Now(), 69 | Project: project, 70 | UserName: userName, 71 | } 72 | items := cacheToken[userName] 73 | if items == nil { 74 | items = make([]*models.Token, 0) 75 | } 76 | items = append(items, token) 77 | cacheToken[userName] = items 78 | e.Session.Insert("Token", token) 79 | return token 80 | } 81 | 82 | //CheckToken 验证token 83 | func (e *TokenService) CheckToken(token string, user string) bool { 84 | // start := time.Now() 85 | // defer fmt.Println("checkToken time:", time.Now().Sub(start)) 86 | for _, v := range cacheToken[user] { 87 | if v.Value == token { 88 | return true 89 | } 90 | } 91 | return false 92 | } 93 | 94 | //DeleteToken 删除token 95 | func (e *TokenService) DeleteToken(project string, user string) bool { 96 | col := e.Session.GetCollection("Token") 97 | if col == nil { 98 | beego.Error("get collection token error ") 99 | return false 100 | } 101 | err := col.Remove(bson.M{"project": project, "username": user}) 102 | if err != nil { 103 | beego.Error("delete token error ,", err.Error()) 104 | return false 105 | } 106 | //删除slice成员 107 | var items []*models.Token 108 | for _, v := range cacheToken[user] { 109 | if v.Project != project { 110 | items = append(items, v) 111 | } 112 | } 113 | cacheToken[user] = items 114 | return true 115 | } 116 | 117 | //GetToken 获取token 118 | func (e *TokenService) GetToken(project string, user string) *models.Token { 119 | tokens := cacheToken[user] 120 | for _, token := range tokens { 121 | if token.Project == project { 122 | return token 123 | } 124 | } 125 | return nil 126 | } 127 | 128 | //GetAllToken 获取用户所有token 129 | func (e *TokenService) GetAllToken(userName string) (result []*models.Token) { 130 | for _, v := range cacheToken[userName] { 131 | if v.Project != "default" { 132 | result = append(result, v) 133 | } 134 | } 135 | return 136 | } 137 | -------------------------------------------------------------------------------- /core/user/GetUsers.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "fmt" 4 | 5 | func GetUserBySource(source string) (UserInterface,error) { 6 | switch source{ 7 | case "ldap": 8 | return &LDAPServer{},nil 9 | case "gitlab": 10 | return &GitlabServer{},nil 11 | default: 12 | return nil,fmt.Errorf(fmt.Sprintf("Can not get the user server by source %s\n",source)) 13 | } 14 | } -------------------------------------------------------------------------------- /core/user/Gitlab.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "alertCenter/models" 5 | "encoding/json" 6 | "strconv" 7 | "strings" 8 | 9 | "alertCenter/core/gitlab" 10 | 11 | "github.com/astaxie/beego" 12 | ) 13 | 14 | //type GitlabUser struct { 15 | // Id int 16 | // Username string 17 | // Email string 18 | // Name string 19 | // State string 20 | // Is_admin bool 21 | // Bio string 22 | //} 23 | // 24 | //type GitlabGroup struct { 25 | // Id int 26 | // Name string 27 | // Path string 28 | // Description string 29 | //} 30 | 31 | type GitlabServer struct { 32 | } 33 | 34 | func (e *GitlabServer) SearchTeams() ([]*models.Team, error) { 35 | beego.Info("In Gitlab Server SearchTeam") 36 | 37 | teams := []*models.Team{} 38 | 39 | page := 1 40 | for { 41 | url := gitlab.GetGitlabUrl() + gitlab.ApiVersion + gitlab.GetGroups + "?per_page=100&page=" + strconv.Itoa(page) 42 | resp, err := gitlab.GitlabApi("GET", url, nil) 43 | if err != nil { 44 | beego.Error(err.Error()) 45 | return nil, err 46 | } 47 | 48 | //gitlabGroup := []GitlabGroup{} 49 | gitlabGroup := []gitlab.GitlabGroup{} 50 | //fmt.Println("debug:", string(resp)) 51 | 52 | err = json.Unmarshal(resp, &gitlabGroup) 53 | if err != nil { 54 | beego.Error(err.Error()) 55 | break 56 | } 57 | if len(gitlabGroup) == 0 { 58 | break 59 | } 60 | 61 | for _, g := range gitlabGroup { 62 | tmp := &models.Team{} 63 | tmp.ID = strconv.Itoa(g.Id) 64 | tmp.Name = g.Name 65 | teams = append(teams, tmp) 66 | } 67 | page = page + 1 68 | } 69 | 70 | return teams, nil 71 | } 72 | 73 | func (e *GitlabServer) SearchUsers() ([]*models.User, error) { 74 | beego.Info("In Gitlab Server SearchUsers") 75 | users := []*models.User{} 76 | 77 | page := 1 78 | for { 79 | url := gitlab.GetGitlabUrl() + gitlab.ApiVersion + gitlab.GetUser + "?per_page=100&page=" + strconv.Itoa(page) 80 | 81 | resp, err := gitlab.GitlabApi("GET", url, nil) 82 | if err != nil { 83 | beego.Error(err.Error()) 84 | return nil, err 85 | } 86 | 87 | //gitlabusers := [] GitlabUser{} 88 | gitlabusers := []gitlab.GitlabUser{} 89 | 90 | //fmt.Println("debug:", string(resp)) 91 | err = json.Unmarshal(resp, &gitlabusers) 92 | if err != nil { 93 | //fmt.Println(string(resp)) 94 | beego.Error(err.Error()) 95 | break 96 | } 97 | beego.Debug("SearchUsers, this loop get user:", len(gitlabusers)) 98 | if len(gitlabusers) == 0 { 99 | break 100 | } 101 | 102 | for _, u := range gitlabusers { 103 | if u.State != "active" { 104 | continue 105 | } 106 | tmp := &models.User{} 107 | tmp.ID = strconv.Itoa(u.Id) 108 | tmp.Name = u.Username 109 | tmp.Mail = u.Email 110 | tmp.AvatarURL = u.AvatarUrl 111 | tmp.IsAdmin = u.IsAdmin 112 | users = append(users, tmp) 113 | } 114 | page = page + 1 115 | 116 | } 117 | 118 | return users, nil 119 | } 120 | 121 | func (e *GitlabServer) GetUserByTeam(id string) ([]*models.User, error) { 122 | beego.Info("In Gitlab Server GetUserByTeam") 123 | users := []*models.User{} 124 | 125 | page := 1 126 | for { 127 | 128 | guUrl := strings.Replace(gitlab.GetGroupUsers, ":id", id, -1) 129 | 130 | url := gitlab.GetGitlabUrl() + gitlab.ApiVersion + guUrl + "?per_page=100&page=" + strconv.Itoa(page) 131 | resp, err := gitlab.GitlabApi("GET", url, nil) 132 | if err != nil { 133 | beego.Error(err.Error()) 134 | return nil, err 135 | } 136 | 137 | //gitlabusers := []GitlabUser{} 138 | gitlabusers := []gitlab.GitlabUser{} 139 | 140 | //fmt.Println("debug:", string(resp)) 141 | err = json.Unmarshal(resp, &gitlabusers) 142 | if err != nil { 143 | beego.Error(err.Error()) 144 | break 145 | } 146 | 147 | for _, u := range gitlabusers { 148 | if u.State != "active" { 149 | continue 150 | } 151 | tmp := &models.User{} 152 | tmp.ID = strconv.Itoa(u.Id) 153 | tmp.Name = u.Username 154 | tmp.AvatarURL = u.AvatarUrl 155 | users = append(users, tmp) 156 | } 157 | page = page + 1 158 | //此处不严谨,根据api版本可能不成立 159 | if len(gitlabusers) < 100 { 160 | break 161 | } 162 | } 163 | 164 | return users, nil 165 | } 166 | -------------------------------------------------------------------------------- /core/user/LDAP.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "fmt" 5 | 6 | "alertCenter/models" 7 | 8 | "github.com/astaxie/beego" 9 | 10 | ldap "gopkg.in/ldap.v2" 11 | ) 12 | 13 | var ldapServer string 14 | var ldapDN string 15 | var ldapPass string 16 | var ldapPort int 17 | var err error 18 | 19 | func init() { 20 | ldapServer = beego.AppConfig.String("LADPServer") 21 | ldapPort, err = beego.AppConfig.Int("LDAPPort") 22 | ldapDN = beego.AppConfig.String("LDAPDN") 23 | ldapPass = beego.AppConfig.String("LDAPPass") 24 | } 25 | 26 | type LDAPServer struct { 27 | } 28 | 29 | func (e *LDAPServer) SearchTeams() (teams []*models.Team, err error) { 30 | if err != nil { 31 | beego.Error(err) 32 | return nil, err 33 | } 34 | l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) 35 | if err != nil { 36 | beego.Error(err) 37 | return nil, err 38 | } 39 | defer l.Close() 40 | err = l.Bind(ldapDN, ldapPass) 41 | if err != nil { 42 | beego.Error(err) 43 | return nil, err 44 | } 45 | searchRequest := ldap.NewSearchRequest( 46 | "dc=yunpro,dc=cn", // The base dn to search 47 | ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, 48 | "(&(objectClass=posixGroup))", // The filter to apply 49 | nil, // A list attributes to retrieve 50 | nil, 51 | ) 52 | 53 | sr, err := l.Search(searchRequest) 54 | if err != nil { 55 | beego.Error(err) 56 | return nil, err 57 | } 58 | 59 | for _, entry := range sr.Entries { 60 | team := &models.Team{ 61 | ID: entry.GetAttributeValue("gidNumber"), 62 | Name: entry.GetAttributeValue("cn"), 63 | } 64 | teams = append(teams, team) 65 | } 66 | return teams, nil 67 | } 68 | 69 | func (e *LDAPServer) SearchUsers() (users []*models.User, err error) { 70 | if err != nil { 71 | beego.Error(err) 72 | return nil, err 73 | } 74 | l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) 75 | if err != nil { 76 | beego.Error(err) 77 | return nil, err 78 | } 79 | defer l.Close() 80 | err = l.Bind(ldapDN, ldapPass) 81 | if err != nil { 82 | beego.Error(err) 83 | return nil, err 84 | } 85 | searchRequest := ldap.NewSearchRequest( 86 | "dc=yunpro,dc=cn", // The base dn to search 87 | ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, 88 | "(&(objectClass=posixAccount))", // The filter to apply 89 | nil, // A list attributes to retrieve 90 | nil, 91 | ) 92 | 93 | sr, err := l.Search(searchRequest) 94 | if err != nil { 95 | beego.Error(err) 96 | return nil, err 97 | } 98 | 99 | for _, entry := range sr.Entries { 100 | user := &models.User{ 101 | ID: entry.GetAttributeValue("uidNumber"), 102 | Name: entry.GetAttributeValue("cn"), 103 | TeamID: entry.GetAttributeValue("gidNumber"), 104 | Phone: entry.GetAttributeValue("mobile"), 105 | Mail: entry.GetAttributeValue("Email"), 106 | } 107 | users = append(users, user) 108 | } 109 | return users, nil 110 | } 111 | 112 | func (e *LDAPServer) GetUserByTeam(id string) ([]*models.User, error) { 113 | if err != nil { 114 | beego.Error(err) 115 | return nil, err 116 | } 117 | l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) 118 | if err != nil { 119 | beego.Error(err) 120 | return nil, err 121 | } 122 | defer l.Close() 123 | err = l.Bind(ldapDN, ldapPass) 124 | if err != nil { 125 | beego.Error(err) 126 | return nil, err 127 | } 128 | searchRequest := ldap.NewSearchRequest( 129 | "dc=yunpro,dc=cn", // The base dn to search 130 | ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, 131 | "(&(objectClass=posixAccount))", // The filter to apply 132 | nil, // A list attributes to retrieve 133 | nil, 134 | ) 135 | 136 | sr, err := l.Search(searchRequest) 137 | if err != nil { 138 | beego.Error(err) 139 | return nil, err 140 | } 141 | var users []*models.User 142 | for _, entry := range sr.Entries { 143 | if entry.GetAttributeValue("gidNumber") == id { 144 | user := &models.User{ 145 | ID: entry.GetAttributeValue("uidNumber"), 146 | Name: entry.GetAttributeValue("cn"), 147 | TeamID: entry.GetAttributeValue("gidNumber"), 148 | Phone: entry.GetAttributeValue("mobile"), 149 | Mail: entry.GetAttributeValue("Email"), 150 | } 151 | users = append(users, user) 152 | } 153 | } 154 | return users, nil 155 | } 156 | -------------------------------------------------------------------------------- /core/user/Receiver.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | // 2016.9.20去除了receiver缓存,添加缓存自动刷新线程 4 | 5 | import ( 6 | "alertCenter/core/db" 7 | "alertCenter/core/service" 8 | "alertCenter/models" 9 | "encoding/json" 10 | "io/ioutil" 11 | "net/http" 12 | "strconv" 13 | "strings" 14 | "time" 15 | 16 | "github.com/astaxie/beego" 17 | uuid "github.com/satori/go.uuid" 18 | ) 19 | 20 | var ( 21 | cacheTeams map[string]*models.Team 22 | cacheApps map[string]*models.APP 23 | cacheUsers map[string]*models.User 24 | cacheTeamUsers map[string][]string 25 | autoRefreshDura time.Duration 26 | ) 27 | 28 | //Init 初始化用户关系缓存 29 | func (r *Relation) Init() error { 30 | beego.Info("Relation init begin") 31 | defer beego.Info("Relation init over") 32 | err := initUser() 33 | if err != nil { 34 | return err 35 | } 36 | err = checkToken() 37 | if err != nil { 38 | return err 39 | } 40 | err = initApp() 41 | if err != nil { 42 | return err 43 | } 44 | 45 | autoRefreshTime, err := time.ParseDuration(beego.AppConfig.String("autoRefreshTime")) 46 | if err == nil { 47 | autoRefreshDura = autoRefreshTime 48 | } else { 49 | beego.Error(err) 50 | return err 51 | } 52 | go autoRefresh() 53 | return nil 54 | } 55 | 56 | func autoRefresh() { 57 | beego.Debug("open auto refresh users and teams work") 58 | var ticker = time.NewTicker(autoRefreshDura) 59 | defer ticker.Stop() 60 | for { 61 | select { 62 | case <-ticker.C: 63 | beego.Debug("start auto refresh users and teams") 64 | initUser() 65 | checkToken() 66 | initApp() 67 | beego.Debug("end auto refresh users and teams") 68 | } 69 | } 70 | } 71 | 72 | //初始化用户及群组数据 73 | func initUser() error { 74 | ts := []*models.Team{} 75 | us := []*models.User{} 76 | source := beego.AppConfig.String("UserSource") 77 | sources := strings.Split(source, ",") 78 | 79 | var userServer UserInterface 80 | var err error 81 | for _, s := range sources { 82 | userServer, err = GetUserBySource(s) 83 | if err != nil { 84 | beego.Error(err.Error()) 85 | return err 86 | } 87 | tmpTS, err := userServer.SearchTeams() 88 | if err != nil { 89 | beego.Error(err.Error()) 90 | return err 91 | } 92 | 93 | tmpUS, err := userServer.SearchUsers() 94 | if err != nil { 95 | beego.Error(err.Error()) 96 | return err 97 | } 98 | 99 | ts = append(ts, tmpTS...) 100 | us = append(us, tmpUS...) 101 | } 102 | beego.Info("load users number is " + strconv.Itoa(len(us))) 103 | beego.Info("load teams number is " + strconv.Itoa(len(ts))) 104 | if us != nil { 105 | cacheUsersTmp := make(map[string]*models.User, 0) 106 | 107 | for _, user := range us { 108 | if user.AvatarURL == "" { 109 | user.AvatarURL = "/static/img/default.jpg" 110 | } 111 | if user.Name != "" { 112 | globalConfigService := service.GlobalConfigService{ 113 | Session: db.GetMongoSession(), 114 | } 115 | if globalConfigService.Session != nil { 116 | defer globalConfigService.Session.Close() 117 | } 118 | if ok, _ := globalConfigService.CheckExist("IsAdmin", user.Name); ok { 119 | user.IsAdmin = true 120 | } 121 | cacheUsersTmp[user.Name] = user 122 | } 123 | } 124 | cacheUsers = cacheUsersTmp 125 | } 126 | if ts != nil { 127 | 128 | cacheTeamsTmp := make(map[string]*models.Team, 0) 129 | cacheTeamUsersTmp := make(map[string][]string, 0) 130 | for _, team := range ts { 131 | //beego.Debug("load team " + team.Name) 132 | if team.Name != "" { 133 | cacheTeamsTmp[team.Name] = team 134 | users, err := userServer.GetUserByTeam(team.ID) 135 | if err != nil { 136 | beego.Error(err.Error()) 137 | return err 138 | } 139 | var completeUsers []string 140 | for _, user := range users { 141 | completeUsers = append(completeUsers, user.Name) 142 | beego.Debug("load user " + user.Name + " in team " + team.Name) 143 | } 144 | cacheTeamUsersTmp[team.Name] = completeUsers 145 | } 146 | } 147 | cacheTeams = cacheTeamsTmp 148 | cacheTeamUsers = cacheTeamUsersTmp 149 | } 150 | return nil 151 | } 152 | 153 | //初始化app数据 154 | func initApp() error { 155 | if beego.AppConfig.String("cloudURI") == "" { 156 | return nil 157 | } 158 | as, err := GetAllAppInfo() 159 | if err != nil { 160 | return err 161 | } 162 | beego.Info("load apps number is " + strconv.Itoa(len(as))) 163 | 164 | cacheAppsTmp := make(map[string]*models.APP, 0) 165 | 166 | for _, app := range as { 167 | if app.ID != "" { 168 | cacheAppsTmp[app.ID] = app 169 | } 170 | } 171 | cacheApps = cacheAppsTmp 172 | return nil 173 | } 174 | 175 | //检查用户默认token是否存在 176 | func checkToken() error { 177 | service := &service.TokenService{ 178 | Session: db.GetMongoSession(), 179 | } 180 | if service.Session != nil { 181 | defer service.Session.Close() 182 | } 183 | for _, user := range cacheUsers { 184 | token := service.GetDefaultToken(user.Name) 185 | if token == nil { 186 | service.CreateToken("default", user.Name) 187 | } 188 | } 189 | return nil 190 | } 191 | 192 | //GetAllAppInfo 获取全部app信息 193 | func GetAllAppInfo() (apps []*models.APP, err error) { 194 | client := &http.Client{} 195 | req, err := http.NewRequest("GET", beego.AppConfig.String("cloudURI")+"/cloud-api/alert/apps", nil) 196 | if err != nil { 197 | beego.Error("create get appInfo request faild." + err.Error()) 198 | return nil, err 199 | } 200 | req.Header.Set("Content-Type", "application/json") 201 | resp, err := client.Do(req) 202 | if err != nil { 203 | beego.Error("GET appinfo error," + err.Error()) 204 | return nil, err 205 | } 206 | content, err := ioutil.ReadAll(resp.Body) 207 | if err != nil { 208 | beego.Error("GET appinfo error," + err.Error()) 209 | return nil, err 210 | } 211 | var AppInfo = make([]*models.APP, 0) 212 | err = json.Unmarshal(content, &AppInfo) 213 | if err != nil { 214 | beego.Error("Parse the appinfo data error ." + err.Error()) 215 | return nil, err 216 | } 217 | return AppInfo, nil 218 | } 219 | 220 | //GetAppInfoByID 通过id查询app信息 221 | func GetAppInfoByID(id string) (app *models.APP, err error) { 222 | client := &http.Client{} 223 | req, err := http.NewRequest("GET", beego.AppConfig.String("cloudURI")+"cloud-api/alert/app/"+id, nil) 224 | if err != nil { 225 | beego.Error("create get appInfo request faild." + err.Error()) 226 | return nil, err 227 | } 228 | req.Header.Set("Content-Type", "application/json") 229 | resp, err := client.Do(req) 230 | if err != nil { 231 | beego.Error("GET appinfo error," + err.Error()) 232 | return nil, err 233 | } 234 | content, err := ioutil.ReadAll(resp.Body) 235 | if err != nil { 236 | beego.Error("GET appinfo error," + err.Error()) 237 | return nil, err 238 | } 239 | var AppInfo = models.APP{} 240 | err = json.Unmarshal(content, &AppInfo) 241 | if err != nil { 242 | beego.Error("Parse the appinfo data error ." + err.Error()) 243 | return nil, err 244 | } 245 | return &AppInfo, nil 246 | } 247 | 248 | type Relation struct { 249 | session *db.MongoSession 250 | } 251 | 252 | //RefreshCache 更新缓存 253 | func (r *Relation) RefreshCache() { 254 | beego.Debug("start refresh users and teams") 255 | initUser() 256 | checkToken() 257 | initApp() 258 | beego.Debug("end refresh users and teams") 259 | } 260 | 261 | //SetTeam 老方法添加team 262 | func (r *Relation) SetTeam(team *models.Team) { 263 | team.ID = uuid.NewV4().String() 264 | // if team.WeTag == nil || team.WeTag.TagName == "" { 265 | // team.WeTag = GetWeTagByName(team.Name) 266 | // } 267 | cacheTeams[team.Name] = team 268 | r.session = db.GetMongoSession() 269 | if r.session != nil { 270 | defer r.session.Close() 271 | } 272 | r.session.Insert("team", team) 273 | } 274 | 275 | //GetAllTeam 获取全部团队 276 | func (r *Relation) GetAllTeam() (result []*models.Team) { 277 | if cacheTeams != nil { 278 | for _, v := range cacheTeams { 279 | result = append(result, v) 280 | } 281 | } 282 | return 283 | } 284 | 285 | //GetAllUser 获取全部用户 286 | func (r *Relation) GetAllUser() (result []*models.User) { 287 | if cacheUsers != nil { 288 | for _, v := range cacheUsers { 289 | result = append(result, v) 290 | } 291 | } 292 | return 293 | } 294 | 295 | //GetUsersByTeam 根据组名获取用户 296 | func (r *Relation) GetUsersByTeam(name string) (users []*models.User) { 297 | if cacheTeamUsers != nil { 298 | userNames := cacheTeamUsers[name] 299 | for _, userName := range userNames { 300 | user := cacheUsers[userName] 301 | if user != nil { 302 | users = append(users, user) 303 | } 304 | } 305 | } 306 | return 307 | } 308 | 309 | //GetUserByName 获取指定用户 310 | func (r *Relation) GetUserByName(name string) *models.User { 311 | if name == "" { 312 | return nil 313 | } 314 | user := cacheUsers[name] 315 | if user != nil { 316 | return user 317 | } else { 318 | //暂时不实现 319 | // return &models.User{ 320 | // Name: name, 321 | // } 322 | return nil 323 | } 324 | } 325 | 326 | //FindUserByMail 通过邮箱获取用户 327 | func FindUserByMail(mail string) *models.User { 328 | if mail == "" { 329 | return nil 330 | } 331 | if cacheUsers != nil { 332 | for _, v := range cacheUsers { 333 | if v.Mail == mail { 334 | return v 335 | } 336 | } 337 | } 338 | return nil 339 | } 340 | 341 | //GetReceiverByAPPID 通过appid获取receiver 342 | func GetReceiverByAPPID(appID string) (receiver *models.Receiver) { 343 | if appID == "" { 344 | return 345 | } 346 | app := cacheApps[appID] 347 | if app == nil { 348 | var err error 349 | app, err = GetAppInfoByID(appID) 350 | if err != nil { 351 | return nil 352 | } 353 | } 354 | if app != nil { 355 | var us []string 356 | for _, mail := range app.Mails { 357 | user := FindUserByMail(mail) 358 | us = append(us, user.Name) 359 | } 360 | receiver = &models.Receiver{ 361 | ID: uuid.NewV4().String(), 362 | Name: appID, 363 | UserNames: us, 364 | } 365 | 366 | } 367 | return 368 | } 369 | 370 | //GetReceiverByTeam 通过team name 获取receiver 371 | func GetReceiverByTeam(team string) (receiver *models.Receiver) { 372 | t := cacheTeams[team] 373 | if t != nil { 374 | us := cacheTeamUsers[t.Name] 375 | receiver = &models.Receiver{ 376 | ID: uuid.NewV4().String(), 377 | Name: team, 378 | UserNames: us, 379 | } 380 | return 381 | } 382 | return nil 383 | 384 | } 385 | 386 | //GetReceiverByUser 通过user name 获取receiver 387 | func GetReceiverByUser(user string) (receiver *models.Receiver) { 388 | 389 | users := strings.Split(user, ",") 390 | var us []string 391 | for _, u := range users { 392 | t := cacheUsers[u] 393 | if t != nil { 394 | us = append(us, t.Name) 395 | } 396 | } 397 | receiver = &models.Receiver{ 398 | ID: uuid.NewV4().String(), 399 | Name: "user_" + user, 400 | UserNames: us, 401 | } 402 | return 403 | } 404 | 405 | //GetReceiver 获取receiver 406 | func GetReceiver(labels models.Label) (receiver *models.Receiver) { 407 | 408 | if v, ok := labels.LabelSet["user"]; ok { 409 | return GetReceiverByUser(string(v)) 410 | } 411 | if v, ok := labels.LabelSet["container_label_app_id"]; ok { 412 | return GetReceiverByAPPID(string(v)) 413 | } 414 | if v, ok := labels.LabelSet["team"]; ok { 415 | return GetReceiverByTeam(string(v)) 416 | } 417 | return nil 418 | } 419 | -------------------------------------------------------------------------------- /core/user/UserInterface.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "alertCenter/models" 4 | 5 | type UserInterface interface { 6 | //查询所有团队 7 | SearchTeams() ([]*models.Team, error) 8 | //查询所有用户 9 | SearchUsers() ([]*models.User, error) 10 | //根据团队查询用户 11 | GetUserByTeam(id string) ([]*models.User, error) 12 | //根据名称获取用户 13 | //GetUserByUserName() (*models.User, error) 14 | } 15 | 16 | //UserAuthentication 用户管理之用户权限认真 17 | type UserAuthentication interface { 18 | 19 | //API权限验证方法 20 | APIFilter() 21 | //页面请求权限验证方法 22 | HTTPFilter() 23 | } 24 | -------------------------------------------------------------------------------- /data/alert.json: -------------------------------------------------------------------------------- 1 | { 2 | "receiver": "team-X-mails", 3 | "status": "firing", 4 | "alerts": [ 5 | { 6 | "status": "firing", 7 | "labels": { 8 | "__name__": "up", 9 | "alertname": "MONITOR_DOWN", 10 | "instance": "10.50.1.45:8088", 11 | "job": "container", 12 | "monitor": "container-prometheus-monitor", 13 | "prometheus": "10.50.1.92", 14 | "team": "common", 15 | "zone": "yz-swarm-01" 16 | }, 17 | "annotations": { 18 | "description": "10.50.1.45:8088 ,server may be down,job=container,this message from 10.50.1.92" 19 | }, 20 | "startsAt": "2016-08-22T15:56:12.879+08:00", 21 | "endsAt": "0001-01-01T00:00:00Z", 22 | "generatorURL": "http://8a84196a1f3c:9090/graph#%5B%7B%22expr%22%3A%22up%20%3D%3D%200%22%2C%22tab%22%3A0%7D%5D" 23 | }, 24 | { 25 | "status": "firing", 26 | "labels": { 27 | "__name__": "up", 28 | "alertname": "MONITOR_DOWN", 29 | "instance": "10.50.1.45:9100", 30 | "job": "node", 31 | "monitor": "node-prometheus-monitor", 32 | "prometheus": "10.50.1.92", 33 | "team": "common", 34 | "zone": "yz-swarm-01" 35 | }, 36 | "annotations": { 37 | "description": "10.50.1.45:9100 ,server may be down,job=node,this message from 10.50.1.92" 38 | }, 39 | "startsAt": "2016-08-22T15:56:12.804+08:00", 40 | "endsAt": "0001-01-01T00:00:00Z", 41 | "generatorURL": "http://bb8d60463c8f:9090/graph#%5B%7B%22expr%22%3A%22up%20%3D%3D%200%22%2C%22tab%22%3A0%7D%5D" 42 | }, 43 | { 44 | "status": "firing", 45 | "labels": { 46 | "__name__": "up", 47 | "alertname": "MONITOR_DOWN", 48 | "instance": "10.50.1.44:9100", 49 | "job": "node", 50 | "monitor": "node-prometheus-monitor", 51 | "prometheus": "10.50.1.92", 52 | "team": "common", 53 | "zone": "yz-swarm-01" 54 | }, 55 | "annotations": { 56 | "description": "10.50.1.44:9100 ,server may be down,job=node,this message from 10.50.1.92" 57 | }, 58 | "startsAt": "2016-08-22T15:59:27.804+08:00", 59 | "endsAt": "0001-01-01T00:00:00Z", 60 | "generatorURL": "http://bb8d60463c8f:9090/graph#%5B%7B%22expr%22%3A%22up%20%3D%3D%200%22%2C%22tab%22%3A0%7D%5D" 61 | }, 62 | { 63 | "status": "firing", 64 | "labels": { 65 | "__name__": "up", 66 | "alertname": "MONITOR_DOWN", 67 | "instance": "10.50.1.44:8088", 68 | "job": "container", 69 | "monitor": "container-prometheus-monitor", 70 | "prometheus": "10.50.1.92", 71 | "team": "common", 72 | "zone": "yz-swarm-01" 73 | }, 74 | "annotations": { 75 | "description": "10.50.1.44:8088 ,server may be down,job=container,this message from 10.50.1.92" 76 | }, 77 | "startsAt": "2016-08-22T15:59:27.88+08:00", 78 | "endsAt": "0001-01-01T00:00:00Z", 79 | "generatorURL": "http://8a84196a1f3c:9090/graph#%5B%7B%22expr%22%3A%22up%20%3D%3D%200%22%2C%22tab%22%3A0%7D%5D" 80 | } 81 | ], 82 | "groupLabels": { 83 | "team": "common" 84 | }, 85 | "commonLabels": { 86 | "__name__": "up", 87 | "alertname": "MONITOR_DOWN", 88 | "prometheus": "10.50.1.92", 89 | "team": "common", 90 | "zone": "yz-swarm-01" 91 | }, 92 | "commonAnnotations": {}, 93 | "externalURL": "http://bb8510aa9971:9093", 94 | "version": "3", 95 | "groupKey": 9927269329099545789 96 | } -------------------------------------------------------------------------------- /data/alerts.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "labels": { 3 | "__name__": "node_memory_Active", 4 | "alertname": "MemoryActiveOutB", 5 | "instance": "10.10.12.18:9100", 6 | "job": "node", 7 | "monitor": "codelab-monitor", 8 | "team": "cloud", 9 | "asdasfasfasfasfasfasfasfasfasfasfafs": "asfasfasfasfasfasfasfasfasfasfasfasfafs" 10 | }, 11 | "annotations": { 12 | "description": "10.10.12.18:9100 of node memory active out " 13 | }, 14 | "startsAt": "2016-08-29T07:39:52.806Z", 15 | "endsAt": "0001-01-01T00:00:00Z", 16 | "generatorURL": "http://987a66675dda:9090/graph#%5B%7B%22expr%22%3A%22node_memory_Active%7Binstance%3D%5C%2210.10.12.18%3A9100%5C%22%7D%20%3E%202000000%22%2C%22tab%22%3A0%7D%5D" 17 | }, 18 | { 19 | "labels": { 20 | "__name__": "node_memory_Active", 21 | "alertname": "MemoryActiveOutB", 22 | "instance": "10.10.12.18:9100", 23 | "job": "node", 24 | "monitor": "codelab-monitor", 25 | "team": "cloud" 26 | }, 27 | "annotations": { 28 | "description": "10.10.12.18:9100 of node memory active out " 29 | }, 30 | "startsAt": "2016-08-29T07:39:52.806Z", 31 | "endsAt": "2016-08-29T07:59:52.806Z", 32 | "generatorURL": "http://987a66675dda:9090/graph#%5B%7B%22expr%22%3A%22node_memory_Active%7Binstance%3D%5C%2210.10.12.18%3A9100%5C%22%7D%20%3E%202000000%22%2C%22tab%22%3A0%7D%5D" 33 | }, 34 | { 35 | "labels": { 36 | "__name__": "node_memory_Active", 37 | "alertname": "MemoryActiveOutB", 38 | "instance": "10.10.12.18:9100", 39 | "job": "node", 40 | "monitor": "codelab-monitor", 41 | "team": "cloud" 42 | }, 43 | "annotations": { 44 | "description": "10.10.12.18:9100 of node memory active out " 45 | }, 46 | "startsAt": "2016-08-29T07:39:52.806Z", 47 | "endsAt": "2016-08-29T07:42:52.806Z", 48 | "generatorURL": "http://987a66675dda:9090/graph#%5B%7B%22expr%22%3A%22node_memory_Active%7Binstance%3D%5C%2210.10.12.18%3A9100%5C%22%7D%20%3E%202000000%22%2C%22tab%22%3A0%7D%5D" 49 | }] -------------------------------------------------------------------------------- /data/team.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"goyoo-cloud", 3 | "mail":["zengqingguo@goyoo.com"], 4 | "wetag":{ 5 | "tagid":1, 6 | "tagname":"goyoo-cloud" 7 | } 8 | } -------------------------------------------------------------------------------- /data/wechat.json: -------------------------------------------------------------------------------- 1 | { 2 | "touser": "UserID1|UserID2|UserID3", 3 | "toparty": " PartyID1 | PartyID2 ", 4 | "totag": " TagID1 | TagID2 ", 5 | "msgtype": "text", 6 | "agentid": 1, 7 | "text": { 8 | "content": "Holiday Request For Pony(http://xxxxx)" 9 | }, 10 | "safe":0 11 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "alertCenter/core/db" 5 | "alertCenter/core/notice" 6 | "alertCenter/core/user" 7 | _ "alertCenter/routers" 8 | _ "net/http/pprof" 9 | 10 | "alertCenter/core/service" 11 | 12 | "github.com/astaxie/beego" 13 | ) 14 | 15 | func main() { 16 | re := user.Relation{} 17 | beego.AddAPPStartHook(func() error { 18 | return re.Init() 19 | }) 20 | beego.AddAPPStartHook(func() error { 21 | return notice.StartCenter() 22 | }) 23 | //初始化检查全局配置 24 | beego.AddAPPStartHook(func() error { 25 | service := &service.GlobalConfigService{ 26 | Session: db.GetMongoSession(), 27 | } 28 | if service.Session != nil { 29 | defer service.Session.Close() 30 | } 31 | return service.Init() 32 | }) 33 | //beego.SetLogger("file", `{"filename":"log/test.log","level":10}`) 34 | beego.Run() 35 | } 36 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | GO_LDFLAGS=-ldflags " -w" 2 | 3 | TAG=0415 4 | # PREFIX=barnettzqg/alert-center 5 | PREFIX = goodrain.me/8439cf79b5c6_barnettZQG_alertCenter 6 | build: ## build the go packages 7 | @echo "🐳 $@" 8 | @CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo ${GO_LDFLAGS} . 9 | 10 | image: clean 11 | @echo "🐳 $@" 12 | @CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo ${GO_LDFLAGS} . 13 | @docker build -t $(PREFIX):$(TAG) . 14 | @docker push $(PREFIX):$(TAG) 15 | @rm -f alertCenter 16 | clean: 17 | @rm -f alertCenter -------------------------------------------------------------------------------- /models/Token.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type Token struct { 6 | Value string 7 | CreateTime time.Time 8 | Project string 9 | UserName string 10 | } 11 | -------------------------------------------------------------------------------- /models/alert.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/prometheus/common/model" 7 | "gopkg.in/mgo.v2/bson" 8 | ) 9 | 10 | type Alert struct { 11 | Labels Label `json:"labels"` 12 | Annotations Label `json:"annotations"` 13 | StartsAt time.Time `json:"startsAt,omitempty"` 14 | EndsAt time.Time `json:"endsAt,omitempty"` 15 | GeneratorURL string `json:"generatorURL"` 16 | Mark string `json:"mark" bson:"mark"` 17 | Receiver *Receiver `json:"receiver"` 18 | AlertCount int 19 | IsHandle int 20 | HandleDate time.Time `json:"handleDate,omitempty"` 21 | HandleMessage string 22 | UpdatedAt time.Time `json:"updatedAt,omitempty"` 23 | Level int `json:"level,omitempty"` 24 | } 25 | 26 | type Label struct { 27 | model.LabelSet 28 | } 29 | 30 | //Contains a是否包含source 31 | func (a Label) Contains(source Label) bool { 32 | for k, v := range source.LabelSet { 33 | if va, ok := a.LabelSet[k]; ok { 34 | if v != va { 35 | return false 36 | } 37 | } else { 38 | return false 39 | } 40 | } 41 | return true 42 | } 43 | 44 | func (a *Alert) Fingerprint() model.Fingerprint { 45 | return a.Labels.Fingerprint() 46 | } 47 | 48 | // Merge merges the timespan of two alerts based and overwrites annotations 49 | // based on the authoritative timestamp. A new alert is returned, the labels 50 | // are assumed to be equal. 51 | func (a *Alert) Merge(o *Alert) *Alert { 52 | // Let o always be the younger alert. 53 | if o.UpdatedAt.Before(a.UpdatedAt) { 54 | return o.Merge(a) 55 | } 56 | 57 | res := *a 58 | 59 | // Always pick the earliest starting time. 60 | if a.StartsAt.After(o.StartsAt) { 61 | res.StartsAt = o.StartsAt 62 | } 63 | 64 | // A non-timeout resolved timestamp always rules. 65 | // The latest explicit resolved timestamp wins. 66 | if a.EndsAt.Before(o.EndsAt) { 67 | res.EndsAt = o.EndsAt 68 | } 69 | res.Annotations = o.Annotations 70 | return &res 71 | } 72 | 73 | //Reset 重置alert状态 74 | func (a *Alert) Reset(o *Alert) *Alert { 75 | res := *a 76 | res.StartsAt = o.StartsAt 77 | res.EndsAt = o.EndsAt 78 | res.Annotations = o.Annotations 79 | if res.EndsAt.IsZero() { 80 | res.AlertCount = 1 81 | res.IsHandle = 0 82 | res.HandleDate = time.Now() 83 | res.HandleMessage = "报警再次产生" 84 | } else { 85 | res.IsHandle = 2 86 | res.HandleDate = time.Now() 87 | res.HandleMessage = "报警已自动恢复" 88 | } 89 | res.UpdatedAt = time.Now() 90 | return &res 91 | } 92 | 93 | type AlertHistory struct { 94 | ID bson.ObjectId `bson:"_id"` 95 | Mark string `json:"mark"` 96 | AddTime time.Time `json:"addTime"` 97 | StartsAt time.Time `json:"startsat"` 98 | EndsAt time.Time `json:"endsat"` 99 | Duration time.Duration `json:"duration"` 100 | Message string `json:"message"` 101 | Value string `json:"value"` 102 | } 103 | -------------------------------------------------------------------------------- /models/relations.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type Receiver struct { 6 | ID string `json:"id"` 7 | Name string `json:"name"` 8 | UserNames []string `json:"userNames"` 9 | WeGroupID []string `json:"weiGroupId"` 10 | } 11 | type Team struct { 12 | Name string `json:"name"` 13 | ID string `json:"id"` 14 | WeTeamID string `json:"weId"` 15 | ParentTeamID string `json:"parentId"` 16 | } 17 | 18 | type APP struct { 19 | ID string `json:"app_id"` 20 | Name string `json:"app_name"` 21 | Users []*User 22 | Teams []*Team 23 | Mails []string `json:"emails"` 24 | IDC string `json:"idc"` 25 | Domain string `json:"domain"` 26 | BusinessLine string `json:"business_line"` 27 | AvatarUrl string `json:"avatar_url"` 28 | } 29 | 30 | type User struct { 31 | ID string `json:"id"` 32 | Name string `json:"Name"` 33 | RealName string `json:"realName"` 34 | TeamID string `json:"teamId"` 35 | Phone string `json:"phone"` 36 | Mail string `json:"mail"` 37 | WeID string `json:"weId"` 38 | AvatarURL string `json:"avatar_url"` 39 | IsAdmin bool `json:"isAdmin"` 40 | } 41 | type UserIgnoreRule struct { 42 | RuleID string `json:"ruleID"` 43 | UserName string `json:"userName"` 44 | Labels Label `json:"labels"` 45 | StartsAt time.Time `json:"startsAt"` 46 | EndsAt time.Time `json:"endsAt"` 47 | AddTime time.Time `json:"addTime"` 48 | IsLive bool `json:"isLive"` 49 | Mark string `json:"mark"` 50 | } 51 | -------------------------------------------------------------------------------- /models/types.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | import "gopkg.in/mgo.v2/bson" 5 | 6 | type GlobalConfig struct { 7 | ID bson.ObjectId `bson:"_id"` 8 | Name string 9 | Value interface{} 10 | AddTime time.Time 11 | } 12 | type AlertReceive struct { 13 | Version string `json:"version"` 14 | Status string `json:"status"` 15 | Alerts []Alert `json:"alerts"` 16 | Receiver string `json:"receiver"` 17 | GroupLabels map[string]string `json:"groupLabels"` 18 | CommonLabels map[string]string `json:"commonLabels"` 19 | CommonAnnotations map[string]string `json:"commonAnnotations"` 20 | ExternalURL string `json:"externalURL"` 21 | } 22 | 23 | type WeAlert struct { 24 | // ToUser string `json:"touser"` 25 | // ToParty string `json:"toparty"` 26 | ToTag string `json:"totag"` 27 | MsgType string `json:"msgtype"` 28 | AgentID int `json:"agentid"` 29 | Text map[string]string `json:"text"` 30 | Safe int `json:"safe"` 31 | } 32 | 33 | type WeTag struct { 34 | TagId int `json:"tagid"` 35 | TagName string `json:"tagname"` 36 | } 37 | 38 | type WeiTagResult struct { 39 | TagList []WeTag `json:"taglist"` 40 | ErrCode int `json:"errcode"` 41 | ErrMsg string `json:"errmsg"` 42 | } 43 | -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "alertCenter/controllers" 5 | 6 | "github.com/astaxie/beego" 7 | ) 8 | 9 | func init() { 10 | beego.Router("/", &controllers.MainController{}) 11 | beego.Router("/logout", &controllers.MainController{}, "post:Logout") 12 | beego.Router("/transit", &controllers.MainController{}, "get:Transit") 13 | beego.Router("/alerts", &controllers.AlertController{}, "get:AlertList") 14 | beego.Router("/alertsCurrent", &controllers.AlertController{}, "get:AlertsCurrent") 15 | beego.Router("/teams", &controllers.TeamController{}, "get:GetTeams") 16 | beego.Router("teamUsers", &controllers.TeamController{}, "get:GetTeamUsers") 17 | beego.Router("/user/:userName", &controllers.UserController{}, "get:UserHome") 18 | beego.Router("/token/addToken", &controllers.TokenController{}, "post:AddToken") 19 | beego.Router("/history/:mark", &controllers.AlertController{}, "get:HistoryList") 20 | 21 | beego.Router("/api/teams", &controllers.TeamAPIController{}, "get:GetTeams") 22 | beego.Router("/api/addTeam", &controllers.TeamAPIController{}, "post:AddTeam") 23 | beego.Router("/api/receive", &controllers.APIController{}, "post:Receive") 24 | beego.Router("/api/v1/alerts", &controllers.PrometheusAPI{}, "post:ReceivePrometheus") 25 | //beego.Router("/api/getTag", &controllers.APIController{}, "get:AddTag") 26 | 27 | beego.Router("/api/ignoreRules", &controllers.IgnoreRuleAPIControll{}, "get:GetRulesByUser") 28 | beego.Router("/api/ignoreRule/:ruleID", &controllers.IgnoreRuleAPIControll{}, "delete:DeleteRule") 29 | beego.Router("/api/alerts", &controllers.APIController{}, "get:GetAlerts") 30 | beego.Router("/api/history/:mark", &controllers.APIController{}, "get:GetHistorys") 31 | beego.Router("/api/addIgnoreRule", &controllers.IgnoreRuleAPIControll{}, "post:AddRule") 32 | beego.Router("/api/ignoreAlert/:mark", &controllers.IgnoreRuleAPIControll{}, "post:AddRuleByAlert") 33 | beego.Router("/api/projects", &controllers.TokenAPIController{}, "get:GetAllToken") 34 | beego.Router("/api/project/:project", &controllers.TokenAPIController{}, "delete:DeleteToken") 35 | //外部通知开关控制 36 | beego.Router("/api/noticeOn/:status", &controllers.APIController{}, "post:SetNoticeMode") 37 | beego.Router("/api/noticeOn", &controllers.APIController{}, "get:GetNoticeMode") 38 | //白名单ip控制 39 | beego.Router("/api/trustIP", &controllers.APIController{}, "post:AddTrustIP") 40 | beego.Router("/api/trustIP", &controllers.APIController{}, "get:GetTrustIP") 41 | beego.Router("/api/trustIP/:ID", &controllers.APIController{}, "delete:DeleteTrustIP") 42 | 43 | beego.Router("/api/refreshCache", &controllers.APIController{}, "post:RefreshCache") 44 | } 45 | -------------------------------------------------------------------------------- /static/css/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/css/.DS_Store -------------------------------------------------------------------------------- /static/css/bootstrap-switch.css: -------------------------------------------------------------------------------- 1 | /* ======================================================================== 2 | * bootstrap-switch - v3.3.2 3 | * http://www.bootstrap-switch.org 4 | * ======================================================================== 5 | * Copyright 2012-2013 Mattia Larentis 6 | * 7 | * ======================================================================== 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * ======================================================================== 20 | */ 21 | 22 | .bootstrap-switch { 23 | display: inline-block; 24 | direction: ltr; 25 | cursor: pointer; 26 | border-radius: 4px; 27 | border: 1px solid; 28 | border-color: #cccccc; 29 | position: relative; 30 | text-align: left; 31 | overflow: hidden; 32 | line-height: 8px; 33 | z-index: 0; 34 | -webkit-user-select: none; 35 | -moz-user-select: none; 36 | -ms-user-select: none; 37 | user-select: none; 38 | vertical-align: middle; 39 | -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 40 | -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 41 | transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 42 | } 43 | .bootstrap-switch .bootstrap-switch-container { 44 | display: inline-block; 45 | top: 0; 46 | border-radius: 4px; 47 | -webkit-transform: translate3d(0, 0, 0); 48 | transform: translate3d(0, 0, 0); 49 | } 50 | .bootstrap-switch .bootstrap-switch-handle-on, 51 | .bootstrap-switch .bootstrap-switch-handle-off, 52 | .bootstrap-switch .bootstrap-switch-label { 53 | -webkit-box-sizing: border-box; 54 | -moz-box-sizing: border-box; 55 | box-sizing: border-box; 56 | cursor: pointer; 57 | display: inline-block !important; 58 | height: 100%; 59 | padding: 6px 12px; 60 | font-size: 14px; 61 | line-height: 20px; 62 | } 63 | .bootstrap-switch .bootstrap-switch-handle-on, 64 | .bootstrap-switch .bootstrap-switch-handle-off { 65 | text-align: center; 66 | z-index: 1; 67 | } 68 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary, 69 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary { 70 | color: #fff; 71 | background: #337ab7; 72 | } 73 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info, 74 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info { 75 | color: #fff; 76 | background: #5bc0de; 77 | } 78 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success, 79 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success { 80 | color: #fff; 81 | background: #5cb85c; 82 | } 83 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning, 84 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning { 85 | background: #f0ad4e; 86 | color: #fff; 87 | } 88 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger, 89 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger { 90 | color: #fff; 91 | background: #d9534f; 92 | } 93 | .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default, 94 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default { 95 | color: #000; 96 | background: #eeeeee; 97 | } 98 | .bootstrap-switch .bootstrap-switch-label { 99 | text-align: center; 100 | margin-top: -1px; 101 | margin-bottom: -1px; 102 | z-index: 100; 103 | color: #333333; 104 | background: #ffffff; 105 | } 106 | .bootstrap-switch .bootstrap-switch-handle-on { 107 | border-bottom-left-radius: 3px; 108 | border-top-left-radius: 3px; 109 | } 110 | .bootstrap-switch .bootstrap-switch-handle-off { 111 | border-bottom-right-radius: 3px; 112 | border-top-right-radius: 3px; 113 | } 114 | .bootstrap-switch input[type='radio'], 115 | .bootstrap-switch input[type='checkbox'] { 116 | position: absolute !important; 117 | top: 0; 118 | left: 0; 119 | margin: 0; 120 | z-index: -1; 121 | opacity: 0; 122 | filter: alpha(opacity=0); 123 | } 124 | .bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on, 125 | .bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off, 126 | .bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label { 127 | padding: 1px 5px; 128 | font-size: 12px; 129 | line-height: 1.5; 130 | } 131 | .bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on, 132 | .bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off, 133 | .bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label { 134 | padding: 5px 10px; 135 | font-size: 12px; 136 | line-height: 1.5; 137 | } 138 | .bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on, 139 | .bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off, 140 | .bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label { 141 | padding: 6px 16px; 142 | font-size: 18px; 143 | line-height: 1.3333333; 144 | } 145 | .bootstrap-switch.bootstrap-switch-disabled, 146 | .bootstrap-switch.bootstrap-switch-readonly, 147 | .bootstrap-switch.bootstrap-switch-indeterminate { 148 | cursor: default !important; 149 | } 150 | .bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on, 151 | .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on, 152 | .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on, 153 | .bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off, 154 | .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off, 155 | .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off, 156 | .bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label, 157 | .bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label, 158 | .bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label { 159 | opacity: 0.5; 160 | filter: alpha(opacity=50); 161 | cursor: default !important; 162 | } 163 | .bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container { 164 | -webkit-transition: margin-left 0.5s; 165 | -o-transition: margin-left 0.5s; 166 | transition: margin-left 0.5s; 167 | } 168 | .bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on { 169 | border-bottom-left-radius: 0; 170 | border-top-left-radius: 0; 171 | border-bottom-right-radius: 3px; 172 | border-top-right-radius: 3px; 173 | } 174 | .bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off { 175 | border-bottom-right-radius: 0; 176 | border-top-right-radius: 0; 177 | border-bottom-left-radius: 3px; 178 | border-top-left-radius: 3px; 179 | } 180 | .bootstrap-switch.bootstrap-switch-focused { 181 | border-color: #66afe9; 182 | outline: 0; 183 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); 184 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); 185 | } 186 | .bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label, 187 | .bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label { 188 | border-bottom-right-radius: 3px; 189 | border-top-right-radius: 3px; 190 | } 191 | .bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label, 192 | .bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label { 193 | border-bottom-left-radius: 3px; 194 | border-top-left-radius: 3px; 195 | } 196 | -------------------------------------------------------------------------------- /static/css/site.css: -------------------------------------------------------------------------------- 1 | body{font-family:"Helvetica Neue",Helvetica,Arial,"Hiragino Sans GB","Hiragino Sans GB W3","WenQuanYi Micro Hei",sans-serif}h1,.h1,h2,.h2,h3,.h3,h4,.h4,.lead{font-family:"Helvetica Neue",Helvetica,Arial,"Hiragino Sans GB","Hiragino Sans GB W3","Microsoft YaHei UI","Microsoft YaHei","WenQuanYi Micro Hei",sans-serif}body{padding-top:30px}@media (min-width:768px){.navbar{min-height:40px}.navbar-nav>li>a{font-size:14px;padding-top:11px;padding-bottom:11px}.navbar-brand{padding-top:0;padding-bottom:0;line-height:42px;height:42px}}.jumbotron{position:relative;padding:40px 0;color:#fff;text-align:center;text-shadow:0 1px 3px rgba(0,0,0,.4),0 0 30px rgba(0,0,0,.075);background:#020031;background:-moz-linear-gradient(45deg,#020031 0,#6d3353 100%);background:-webkit-gradient(linear,left bottom,right top,color-stop(0%,#020031),color-stop(100%,#6d3353));background:-webkit-linear-gradient(45deg,#020031 0,#6d3353 100%);background:-o-linear-gradient(45deg,#020031 0,#6d3353 100%);background:-ms-linear-gradient(45deg,#020031 0,#6d3353 100%);background:linear-gradient(45deg,#020031 0,#6d3353 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#020031', endColorstr='#6d3353', GradientType=1);-webkit-box-shadow:inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2);-moz-box-shadow:inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2);box-shadow:inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2)}.jumbotron a{color:#fff;color:rgba(255,255,255,.5);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.jumbotron aa:hover{color:#fff;text-shadow:0 0 10px rgba(255,255,255,.25)}.jumbotron .container{position:relative;z-index:2}.jumbotron:after{content:'';display:block;position:absolute;top:0;right:0;bottom:0;left:0;background:url(../img/bs-docs-masthead-pattern.png) repeat center center;opacity:.4}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min--moz-device-pixel-ratio:2),only screen and (-o-min-device-pixel-ratio:2/1){.jumbotron:after{background-size:150px 150px}}.masthead{padding:60px 0 80px;margin-bottom:0;color:#fff}@media screen and (min-width:768px){.masthead{padding:90px 0 110px}}.masthead h1{font-size:60px;line-height:1;letter-spacing:-2px;font-weight:700}@media screen and (min-width:768px){.masthead h1{font-size:90px}}@media screen and (min-width:992px){.masthead h1{font-size:100px}}.masthead h2{font-size:18px;font-weight:200;line-height:1.25}@media screen and (min-width:768px){.masthead h2{font-size:24px}}@media screen and (min-width:992px){.masthead h2{font-size:30px}}.masthead p{font-size:40px;font-weight:200;line-height:1.25}.masthead .masthead-button-links{margin-top:30px}.masthead-links{margin:0;padding:0;list-style:none}.masthead-links li{display:inline;padding:0 10px;color:rgba(255,255,255,.25)}.masthead-links li a:hover{color:#fff}.subhead{text-align:center;border-bottom:1px solid #ddd}@media screen and (min-width:768px){.subhead{text-align:left}}.subhead h1{font-size:60px}.subhead p{margin-bottom:20px}@media screen and (min-width:768px){.subhead p{text-align:left}}.btn-primary.btn-shadow{-webkit-box-shadow:inset 0 -4px 0 #2a6496;box-shadow:inset 0 -4px 0 #2a6496;border:0;color:#fff}.btn-lg.btn-shadow{padding:13px 35px 17px}.bc-social{padding:15px 0;text-align:center;background-color:#f5f5f5;border-top:1px solid #fff;border-bottom:1px solid #ddd}.bc-social-buttons{margin-left:0;margin-bottom:0;padding-left:0;list-style:none}.bc-social-buttons li{display:inline-block;line-height:1;color:#555}.bc-social-buttons li .fa{font-size:18px;margin-right:3px}.bc-social-buttons li .fa-weibo{font-size:20px}.bc-social-buttons li a{color:#555}.bc-social-buttons li.social-qq:hover{color:#428bca}.bc-social-buttons li.social-weibo a:hover{color:#d9534f}.bc-social-buttons>li+li:before{padding:0 10px;color:#ccc;content:"|"}.projects .thumbnail{display:block;margin-left:auto;margin-right:auto;text-align:center;max-width:310px;margin-bottom:30px;border-radius:0}.projects .thumbnail .caption{height:200px;overflow-y:hidden;color:#555}.projects .thumbnail .caption a:hover,.projects .thumbnail .caption a:focus{text-decoration:none}.projects .thumbnail img{max-width:100%;height:auto}.projects-header{width:60%;text-align:center;margin:60px 0 10px;font-weight:200;margin-bottom:40px;display:block;margin-left:auto;margin-right:auto}.projects-header h2{font-size:30px;letter-spacing:-1px}@media screen and (min-width:768px){.projects-header h2{font-size:42px}}.nav-sub{padding-top:10px;padding-bottom:10px;margin-top:70px;border-top:1px solid #eee}.footer{color:#777;padding:30px 0;border-top:1px solid #e5e5e5;}.footer a{color:#777}.footer-top .about>div{height:110px;margin-bottom:10px}.footer-top .about>div h4{color:#563d7c;font-size:16px}.footer-bottom{font-size:13px}.footer-bottom ul>li{padding:0}.footer-bottom ul>li+li:before{padding:0 10px;color:#ccc;content:"|"}#scrollUp{background-color:#777;color:#eee;font-size:40px;line-height:1;text-align:center;text-decoration:none;bottom:20px;right:20px;overflow:hidden;width:46px;height:46px;border:none;opacity:.8}#scrollUp:hover{background-color:#333}@media screen and (min-width:992px){#scrollUp{bottom:100px}}.bc-sidebar{margin-top:30px}.bc-sidebar>ul>li>a{display:block;margin:0 0 -1px;padding:8px 14px;border:1px solid #e5e5e5}.excerpt-list{margin-top:60px}.excerpt{min-height:120px;border:1px solid #eee;position:relative;margin-bottom:10px;padding:20px 20px 20px 24px}.excerpt-title{font-size:24px;margin-top:0}.excerpt-title a{color:#555}.excerpt-title a:hover,.excerpt-title a:active{color:#3071a9}.excerpt-meta{position:absolute;bottom:12px}.excerpt-tags{color:#777}.excerpt-tags .glyphicon{position:relative;top:2px;color:#eee}.excerpt-tags a,.excerpt-tags span{color:#777;font-size:12px}.post{position:relative;margin-top:60px;max-width:680px;display:block;margin-left:auto;margin-right:auto}.post-header h1,.post-header h2{font-size:32px;margin:0 0 45px 0;position:relative;text-align:center}@media (min-width:768px){.post-header h1,.post-header h2{font-size:36px}}.post-header h1:after,.post-header h2:after{border-top:1px solid #e5e5e5;bottom:0;content:"";left:50%;margin:0 0 0 -30%;position:absolute;width:60%}.post-header h1 a,.post-header h2 a{color:#363636;display:block;padding:65px 0 20px;position:relative}.post-header h1 a:hover,.post-header h2 a:hover{color:#428bca}.post-header h1 a:before,.post-header h2 a:before{border-top:1px solid #e5e5e5;bottom:-4px;content:"";left:50%;margin:0 0 0 -27%;position:absolute;width:60%}.post-header h1 a:after,.post-header h2 a:after{border-top:1px solid #e5e5e5;bottom:-3px;content:"";left:50%;margin:0 0 0 -28%;position:absolute;width:60%}@media (min-width:768px){.post-header h1 a,.post-header h2 a{padding-left:65px;padding-right:65px}}.post-content{font-size:16px;line-height:1.8;padding-top:20px;padding-bottom:20px}.post-content p,.post-content pre,.post-content ul,.post-content ol,.post-content dl,.post-content form,.post-content hr,.post-content table,.post-content blockquote{margin-bottom:1.8em}.post-content blockquote{font-size:16px}.post-content pre{margin-top:-20px}.post-content li>p{margin-bottom:5px}.post-content img,.post-content video,.post-content embed,.post-content iframe{max-width:100%}.post-content img{height:auto}article.page{margin-top:0;max-width:none}article.page .post-content{padding-top:0}article.page .post-content h2{font-size:36px;padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}#btn-jike-video:after{content:"新";color:#fff;position:absolute;top:1px;right:0;padding:3px 3px 3px 3px;z-index:9999999;background:#d9534f;border-radius:50%;font-size:12px;line-height:1;border:1px solid #d43f3a} 2 | .top_avatovr img{ 3 | width: 34px; 4 | height: 34px; 5 | border-radius: 50%; 6 | } 7 | .top_avatovr{ 8 | padding: 4px 0px 4px 0px !important; 9 | } -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/img/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/img/default.jpg -------------------------------------------------------------------------------- /static/img/load.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/img/load.gif -------------------------------------------------------------------------------- /static/img/nav1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/static/img/nav1.png -------------------------------------------------------------------------------- /static/js/site.js: -------------------------------------------------------------------------------- 1 | jQuery.extend({ 2 | alert: function (dom, result) { 3 | if (result.status != "success") { 4 | html = '
' + 5 | ' × ' + 6 | ''+result.status+'! ' + result.message 7 | '
' 8 | $("#"+dom).html(html) 9 | // setTimeout(function(){ 10 | // $("#"+dom).slideUp() 11 | // $("#"+dom).html("") 12 | // $("#"+dom).slideDown() 13 | // },5000) 14 | }else{ 15 | console.log("alert success") 16 | html = '
' + 17 | ' × ' + 18 | ''+result.status+'! ' + result.message 19 | '
' 20 | $("#"+dom).html(html) 21 | setTimeout(function(){ 22 | $("#"+dom).slideUp() 23 | $("#"+dom).html("") 24 | $("#"+dom).slideDown() 25 | },5000) 26 | } 27 | }, 28 | alertFail: function (dom, message) { 29 | html = '
' + 30 | ' × ' + 31 | 'Fail! ' + message 32 | '
' 33 | $("#"+dom).html(html) 34 | // setTimeout(function(){ 35 | // $("#"+dom).slideUp() 36 | // $("#"+dom).html("") 37 | // $("#"+dom).slideDown() 38 | // },5000) 39 | }, 40 | get:function(token,user,url,callback){ 41 | $.ajax({ 42 | url:url, 43 | type:"GET", 44 | headers: { 45 | "token" : token, 46 | "user": user 47 | }, 48 | contentType:"application/json; charset=utf-8", 49 | success:callback, 50 | error:callback, 51 | }) 52 | }, 53 | post:function(token,user,url,data,callback){ 54 | $.ajax({ 55 | url:url, 56 | type:"POST", 57 | headers: { 58 | "token" : token, 59 | "user" : user 60 | }, 61 | contentType:"application/json; charset=utf-8", 62 | data:data, 63 | dataType:"json", 64 | success:callback, 65 | error:callback, 66 | }) 67 | }, 68 | delete:function(token,user,url,callback){ 69 | $.ajax({ 70 | url:url, 71 | type:"DELETE", 72 | headers: { 73 | "token" : token, 74 | "user": user 75 | }, 76 | contentType:"application/json; charset=utf-8", 77 | success:callback, 78 | error:callback, 79 | }) 80 | } 81 | 82 | }) 83 | 84 | $('#logout').click(function(){ 85 | $.post("","","/logout",null,function(result){ 86 | console.log("result:",result); 87 | if(result.status == "success"){ 88 | 89 | console.log("debug, logout success.") 90 | window.location.href="/" 91 | }else{ 92 | $.alert("alert",result) 93 | } 94 | }) 95 | }) 96 | var patterns = new Object(); 97 | 98 | //匹配ip地址 99 | patterns.ip = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/; 100 | 101 | //匹配邮件地址 102 | patterns.email = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/; 103 | 104 | //匹配日期格式2008-01-31,但不匹配2008-13-00 105 | patterns.date = /^\d{4}-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2]\d|3[0-1])$/; 106 | 107 | /*匹配时间格式00:15:39,但不匹配24:60:00,下面使用RegExp对象的构造方法 108 | 来创建RegExp对象实例,注意正则表达式模式文本中的“\”要写成“\\”*/ 109 | patterns.time = new RegExp("^([0-1]\\d|2[0-3]):[0-5]\\d:[0-5]\\d$"); 110 | 111 | /*verify – 校验一个字符串是否符合某种模式 112 | *str – 要进行校验的字符串 113 | *pat – 与patterns中的某个正则表达式模式对应的属性名称 114 | */ 115 | function verify(str,pat) 116 | { 117 | thePat = patterns[pat]; 118 | if(thePat.test(str)) 119 | { 120 | return true; 121 | } 122 | else 123 | { 124 | return false; 125 | } 126 | } -------------------------------------------------------------------------------- /tests/alertsApi.sh: -------------------------------------------------------------------------------- 1 | alerts1='[ 2 | { 3 | "labels": { 4 | "__name__": "node_memory_Active", 5 | "alertname": "Memory报警测试一", 6 | "instance": "10.10.12.18:9100", 7 | "job": "node", 8 | "monitor": "codelab-monitor", 9 | "team":"yiyun", 10 | "asdasfasfasfasfasfasfasfasfasfasfafs":"asfasfasfasfasfasfasfasfasfasfasfasfafs" 11 | }, 12 | "annotations": { 13 | "description": "10.10.12.18:9100 of node memory active out " 14 | }, 15 | "startsAt": "2017-04-17T07:39:52.806Z", 16 | "endsAt": "0001-01-01T00:00:00Z", 17 | "generatorURL": "http://987a66675dda:9090/graph#%5B%7B%22expr%22%3A%22node_memory_Active%7Binstance%3D%5C%2210.10.12.18%3A9100%5C%22%7D%20%3E%202000000%22%2C%22tab%22%3A0%7D%5D" 18 | }, 19 | { 20 | "labels": { 21 | "__name__": "node_memory_Active", 22 | "alertname": "Memory报警测试二", 23 | "instance": "10.10.12.18:9100", 24 | "job": "node", 25 | "monitor": "codelab-monitor", 26 | "team":"yiyun" 27 | }, 28 | "annotations": { 29 | "description": "10.10.12.18:9100 of node memory active out " 30 | }, 31 | "startsAt": "2017-04-17T07:39:52.806Z", 32 | "endsAt": "0001-01-01T00:00:00Z", 33 | "generatorURL": "http://987a66675dda:9090/graph#%5B%7B%22expr%22%3A%22node_memory_Active%7Binstance%3D%5C%2210.10.12.18%3A9100%5C%22%7D%20%3E%202000000%22%2C%22tab%22%3A0%7D%5D" 34 | }, 35 | { 36 | "labels": { 37 | "__name__": "node_memory_Active", 38 | "alertname": "Memory报警测试三", 39 | "instance": "10.10.12.18:9100", 40 | "job": "node", 41 | "monitor": "codelab-monitor", 42 | "team":"yiyun" 43 | }, 44 | "annotations": { 45 | "description": "10.10.12.18:9100 of node memory active out " 46 | }, 47 | "startsAt": "2017-04-17T07:39:52.806Z", 48 | "endsAt": "0001-01-01T00:00:00Z", 49 | "generatorURL": "http://987a66675dda:9090/graph#%5B%7B%22expr%22%3A%22node_memory_Active%7Binstance%3D%5C%2210.10.12.18%3A9100%5C%22%7D%20%3E%202000000%22%2C%22tab%22%3A0%7D%5D" 50 | } 51 | 52 | ]' 53 | curl -XPOST -d"$alerts1" http://alert.yiyun.pro/api/v1/alerts 54 | curl -XPOST -d"$alerts1" http://alert.yiyun.pro/api/v1/alerts 55 | curl -XPOST -d"$alerts1" http://alert.yiyun.pro/api/v1/alerts -------------------------------------------------------------------------------- /tests/default_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | "runtime" 8 | "path/filepath" 9 | _ "github.com/barnettZQG/alertCenter/alertCenter/routers" 10 | 11 | "github.com/astaxie/beego" 12 | . "github.com/smartystreets/goconvey/convey" 13 | ) 14 | 15 | func init() { 16 | _, file, _, _ := runtime.Caller(1) 17 | apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) 18 | beego.TestBeegoInit(apppath) 19 | } 20 | 21 | 22 | // TestMain is a sample to run an endpoint test 23 | func TestMain(t *testing.T) { 24 | r, _ := http.NewRequest("GET", "/", nil) 25 | w := httptest.NewRecorder() 26 | beego.BeeApp.Handlers.ServeHTTP(w, r) 27 | 28 | beego.Trace("testing", "TestMain", "Code[%d]\n%s", w.Code, w.Body.String()) 29 | 30 | Convey("Subject: Test Station Endpoint\n", t, func() { 31 | Convey("Status Code Should Be 200", func() { 32 | So(w.Code, ShouldEqual, 200) 33 | }) 34 | Convey("The Result Should Not Be Empty", func() { 35 | So(w.Body.Len(), ShouldBeGreaterThan, 0) 36 | }) 37 | }) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /tests/test.go: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barnettZQG/alertCenter/9a97c09db3a819da298900d3c057475eaca7d172/tests/test.go -------------------------------------------------------------------------------- /util/Json.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | type ResultMessage struct { 4 | Status string `json:"status"` 5 | Message string `json:"message"` 6 | Version int `json:"version"` 7 | Data interface{} `json:"data"` 8 | } 9 | 10 | func GetSuccessJson(message string) (result *ResultMessage) { 11 | result = &ResultMessage{ 12 | Status: "success", 13 | Message: message, 14 | Version: 1, 15 | } 16 | return 17 | } 18 | func GetSuccessReJson(data interface{}) (result *ResultMessage) { 19 | result = &ResultMessage{ 20 | Status: "success", 21 | Data: data, 22 | Version: 1, 23 | } 24 | return 25 | } 26 | 27 | func GetErrorJson(message string) (result *ResultMessage) { 28 | result = &ResultMessage{ 29 | Status: "error", 30 | Message: message, 31 | Version: 1, 32 | } 33 | return 34 | } 35 | 36 | func GetFailJson(message string) (result *ResultMessage) { 37 | result = &ResultMessage{ 38 | Status: "fail", 39 | Message: message, 40 | Version: 1, 41 | } 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /util/Utils.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "sort" 4 | 5 | func GetLabelString(labels map[string]string) string { 6 | var data []string 7 | for k, _ := range labels { 8 | data = append(data, k) 9 | } 10 | sort.Strings(data) 11 | var result string 12 | for _, k := range data { 13 | result += k + labels[k] 14 | } 15 | return result 16 | } 17 | 18 | func FormatTime(oldTime string) (newTime string) { 19 | newTime = Substr(oldTime, 0, 19) 20 | return 21 | } 22 | 23 | func Substr(str string, start, length int) string { 24 | rs := []rune(str) 25 | rl := len(rs) 26 | end := 0 27 | 28 | if start < 0 { 29 | start = rl - 1 + start 30 | } 31 | end = start + length 32 | 33 | if start > end { 34 | start, end = end, start 35 | } 36 | 37 | if start < 0 { 38 | start = 0 39 | } 40 | if start > rl { 41 | start = rl 42 | } 43 | if end < 0 { 44 | end = 0 45 | } 46 | if end > rl { 47 | end = rl 48 | } 49 | return string(rs[start:end]) 50 | } 51 | -------------------------------------------------------------------------------- /views/alertList.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 一云监控报警处理平台 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 47 | 93 | 94 | 95 | 96 | 97 | {{template "menu.html" .}} 98 |
99 | 100 | 101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | 109 | 110 | 111 | 118 | 119 | 121 | 122 | 123 | 124 | 125 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /views/alertListAll.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 一云监控报警处理平台 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 67 | 68 | 69 | 73 | 74 | 75 | 76 | 77 | 78 | 81 | 82 | 130 | 131 | 132 | 133 | {{template "menu.html" .}} 134 |
135 | 136 | 137 |
138 |
139 |
140 | 显示已停止的报警: 141 | 142 |
143 | 导出Excel 144 |
145 |
146 |
147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 |
报警名报警次数目标主机开始时间结束时间处理团队处理状态操作
164 |
165 |
166 |
167 |
168 | 169 | 170 | 178 | 179 | 181 | 182 | 183 | 184 | 185 | 186 | 272 | 273 | 274 | -------------------------------------------------------------------------------- /views/historyList.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 一云监控报警处理平台-报警历史 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 106 | 107 | 108 | 112 | 113 | 114 | 115 | 116 | 117 | 120 | 121 | 122 | 123 | {{template "menu.html" .}} 124 |
125 | 126 |
127 |
128 |
129 |
130 | {{.alertName}} 131 |
132 |
133 |
{{.description}}
134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | {{range $key,$val := .labels}} 145 | 146 | 147 | 148 | 149 | {{end}} 150 | 151 | 152 |
153 |
154 |
155 |
告警> >告警历史
156 |
157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 |
发送日期开始时间结束时间持续时长报警标志量
171 |
172 |
173 |
174 |
175 |
176 | 177 | 184 | 185 | 187 | 188 | 189 | 190 | 191 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 一云监控报警处理平台 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | {{template "menu.html" .}} 38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 | 46 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /views/mail.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 40 | 41 | 42 |
6 |
7 | 8 | 9 | 12 | 13 | 14 | 29 | 30 |
10 | [TITLE] 11 |
15 | 16 | 17 | 20 | 21 | 22 | 26 | 27 |
18 | View in AlertCenter 19 |
23 | Description:
24 | [DESCRIPTION]
25 |
28 |
31 | 32 |
33 | 34 | 35 | 36 | 37 |
Sent by AlertCenter
38 |
39 |
43 |
-------------------------------------------------------------------------------- /views/menu.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/teams.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 一云监控报警处理平台-团队信息管理 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 74 | 75 | 76 | 80 | 81 | 82 | 83 | 84 | 85 | 88 | 89 | 90 | 91 | {{template "menu.html" .}} 92 |
93 | 94 |
95 |
96 | 97 |
98 |
99 | 100 | 107 | 108 | 110 | 111 | 112 | 113 | 114 | 150 | 151 | 152 | --------------------------------------------------------------------------------