Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
so-operation-api
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
smart-operation
so-operation-api
Commits
6d17138f
You need to sign in or sign up before continuing.
Commit
6d17138f
authored
Jul 18, 2023
by
李科
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 修改预警列表字段名称
parent
6610b00d
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
197 additions
and
54 deletions
+197
-54
src/bean/entity/alert.go
src/bean/entity/alert.go
+4
-3
src/bean/vo/request/alert.go
src/bean/vo/request/alert.go
+6
-6
src/bean/vo/response/alert.go
src/bean/vo/response/alert.go
+13
-13
src/controller/alert.go
src/controller/alert.go
+12
-12
src/router/alertoverviewrouter.go.go
src/router/alertoverviewrouter.go.go
+7
-2
src/router/alertrouter.go.go
src/router/alertrouter.go.go
+20
-0
src/router/workorderrouter.go
src/router/workorderrouter.go
+3
-3
src/service/alert_list.go
src/service/alert_list.go
+15
-15
src/service/alert_webhook.go
src/service/alert_webhook.go
+117
-0
No files found.
src/bean/entity/alert
_list
.go
→
src/bean/entity/alert.go
View file @
6d17138f
...
...
@@ -4,7 +4,8 @@ import (
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime"
)
type
AlertList
struct
{
// Alert 预警表
type
Alert
struct
{
Id
int
`json:"id"`
// 预警列表ID,主键,自增长
AlertPoint
string
`json:"alert_point"`
// 预警点
AlertRulesId
string
`json:"alert_rules_id"`
// 告警规则id
...
...
@@ -42,6 +43,6 @@ type DisposedList struct {
DisposalTime
jsontime
.
Time
`json:"disposal_time"`
// 处置时间(工单管理,结果反馈)
}
func
(
a
*
Alert
List
)
TableName
()
string
{
return
"alert
_list
"
func
(
a
*
Alert
)
TableName
()
string
{
return
"alert"
}
src/bean/vo/request/alert
_list
.go
→
src/bean/vo/request/alert.go
View file @
6d17138f
...
...
@@ -4,31 +4,31 @@ import (
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity"
)
type
DetailAlert
List
struct
{
type
DetailAlert
struct
{
Id
int
`json:"id" form:"id" binding:"required"`
}
type
UpdateAlert
List
struct
{
type
UpdateAlert
struct
{
RiskLevel
int
`json:"risk_level" form:"risk_level" binding:"omitempty,oneof=1 2 3 4"`
// 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险
Status
int
`json:"status" form:"status" binding:"omitempty,oneof=1 2 3"`
// 状态,1:已恢复 2:未恢复 3:已关闭
Keyword
string
`json:"keyword" form:"keyword"`
// 预警点/分类/指标
}
type
BatchPushAlert
List
struct
{
type
BatchPushAlert
struct
{
Id
string
`json:"id" form:"id"`
Ids
[]
string
`json:"ids" form:"ids" binding:"required_without=Id"`
// 预警ids
NotifyMethod
[]
string
`json:"notify_method" form:"notify_method" binding:"max=2,dive,oneof=dingtalk sms"`
// 预警通知方式 dingtalk sms
NotifyRecipients
[]
entity
.
NotifyRecipients
`json:"notify_recipients" form:"notify_recipients" binding:"dive"`
// 预警推送用户
}
type
BatchCloseAlert
List
struct
{
type
BatchCloseAlert
struct
{
Id
string
`json:"id" form:"id"`
Ids
[]
string
`json:"ids" form:"ids" binding:"required_without=Id"`
// 预警ids
CloseRemark
string
`json:"close_remark" form:"close_remark" binding:"required"`
// 关闭备注
DeferPush
int
`json:"defer_push" form:"defer_push" binding:"omitempty,oneof=0 1"`
// 延迟三天推送: 0:否 1:是 三天内将不再自动推送该告警信息给处置人员,可手动推送,但告警数据依然会出现
}
type
ListAlert
List
struct
{
type
ListAlert
struct
{
Id
int
`json:"id" form:"id"`
RiskLevel
int
`json:"risk_level" form:"risk_level" binding:"omitempty,oneof=1 2 3 4"`
// 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险
Status
int
`json:"status" form:"status" binding:"omitempty,oneof=1 2 3"`
// 状态,1:已恢复 2:未恢复 3:已关闭
...
...
@@ -38,7 +38,7 @@ type ListAlertList struct {
Pagination
}
type
DisposeAlert
List
struct
{
type
DisposeAlert
struct
{
Id
int
`json:"id" form:"id" binding:"required"`
Status
int
`json:"status" form:"status" binding:"oneof=1 2"`
// 状态,1:已恢复 2:未恢复 3:已关闭
DisposalContent
string
`json:"disposal_content" binding:"required"`
// 处置内容(工单管理,结果反馈)
...
...
src/bean/vo/response/alert
_list
.go
→
src/bean/vo/response/alert.go
View file @
6d17138f
...
...
@@ -4,15 +4,15 @@ import (
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity"
)
type
Alert
List
Item
struct
{
entity
.
Alert
List
`xorm:"extends"`
AlertCondition
entity
.
AlertCondition
`json:"alert_condition" xorm:"alert_condition"`
PushRecords
[]
PushRecordItem
`json:"push_records"`
type
AlertItem
struct
{
entity
.
Alert
`xorm:"extends"`
AlertCondition
entity
.
AlertCondition
`json:"alert_condition" xorm:"alert_condition"`
PushRecords
[]
PushRecordItem
`json:"push_records"`
}
type
AlertList
List
struct
{
TotalCount
int64
`json:"total_count"`
List
[]
Alert
List
Item
`json:"list"`
type
AlertList
struct
{
TotalCount
int64
`json:"total_count"`
List
[]
AlertItem
`json:"list"`
}
type
CatIndices
struct
{
...
...
@@ -46,12 +46,12 @@ type Total struct {
}
type
SubHits
struct
{
Index
string
`json:"_index"`
Type
string
`json:"_type"`
ID
string
`json:"_id"`
Score
float64
`json:"_score"`
Ignored
[]
string
`json:"_ignored"`
Source
Alert
List
Item
`json:"_source"`
Index
string
`json:"_index"`
Type
string
`json:"_type"`
ID
string
`json:"_id"`
Score
float64
`json:"_score"`
Ignored
[]
string
`json:"_ignored"`
Source
AlertItem
`json:"_source"`
}
type
Hits
struct
{
Total
Total
`json:"total"`
...
...
src/controller/alert
_list
.go
→
src/controller/alert.go
View file @
6d17138f
...
...
@@ -9,8 +9,8 @@ import (
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/service"
)
func
UpdateAlert
List
(
c
*
gin
.
Context
)
{
var
req
request
.
UpdateAlert
List
func
UpdateAlert
(
c
*
gin
.
Context
)
{
var
req
request
.
UpdateAlert
if
err
:=
c
.
ShouldBind
(
&
req
);
err
!=
nil
{
SendJsonResponse
(
c
,
resp
.
InvalidParam
.
TranslateError
(
err
),
nil
)
return
...
...
@@ -30,8 +30,8 @@ func UpdateAlertList(c *gin.Context) {
SendJsonResponse
(
c
,
resp
.
OK
,
nil
)
}
func
BatchPushAlert
List
(
c
*
gin
.
Context
)
{
var
req
request
.
BatchPushAlert
List
func
BatchPushAlert
(
c
*
gin
.
Context
)
{
var
req
request
.
BatchPushAlert
if
err
:=
c
.
ShouldBind
(
&
req
);
err
!=
nil
{
SendJsonResponse
(
c
,
resp
.
InvalidParam
.
TranslateError
(
err
),
nil
)
return
...
...
@@ -51,8 +51,8 @@ func BatchPushAlertList(c *gin.Context) {
SendJsonResponse
(
c
,
resp
.
OK
,
nil
)
}
func
BatchCloseAlert
List
(
c
*
gin
.
Context
)
{
var
req
request
.
BatchCloseAlert
List
func
BatchCloseAlert
(
c
*
gin
.
Context
)
{
var
req
request
.
BatchCloseAlert
if
err
:=
c
.
ShouldBind
(
&
req
);
err
!=
nil
{
SendJsonResponse
(
c
,
resp
.
InvalidParam
.
TranslateError
(
err
),
nil
)
return
...
...
@@ -72,8 +72,8 @@ func BatchCloseAlertList(c *gin.Context) {
SendJsonResponse
(
c
,
resp
.
OK
,
nil
)
}
func
DetailAlert
List
(
c
*
gin
.
Context
)
{
var
req
request
.
DetailAlert
List
func
DetailAlert
(
c
*
gin
.
Context
)
{
var
req
request
.
DetailAlert
if
err
:=
c
.
ShouldBind
(
&
req
);
err
!=
nil
{
SendJsonResponse
(
c
,
resp
.
InvalidParam
.
TranslateError
(
err
),
nil
)
return
...
...
@@ -88,8 +88,8 @@ func DetailAlertList(c *gin.Context) {
SendJsonResponse
(
c
,
resp
.
OK
,
data
)
}
func
ListAlert
List
(
c
*
gin
.
Context
)
{
var
req
request
.
ListAlert
List
func
ListAlert
(
c
*
gin
.
Context
)
{
var
req
request
.
ListAlert
if
err
:=
c
.
ShouldBind
(
&
req
);
err
!=
nil
{
SendJsonResponse
(
c
,
resp
.
InvalidParam
.
TranslateError
(
err
),
nil
)
return
...
...
@@ -103,8 +103,8 @@ func ListAlertList(c *gin.Context) {
SendJsonResponse
(
c
,
resp
.
OK
,
data
)
}
func
DisposeAlert
List
(
c
*
gin
.
Context
)
{
var
req
request
.
DisposeAlert
List
func
DisposeAlert
(
c
*
gin
.
Context
)
{
var
req
request
.
DisposeAlert
if
err
:=
c
.
ShouldBind
(
&
req
);
err
!=
nil
{
SendJsonResponse
(
c
,
resp
.
InvalidParam
.
TranslateError
(
err
),
nil
)
return
...
...
src/router/alertoverviewrouter.go.go
View file @
6d17138f
...
...
@@ -9,8 +9,13 @@ import (
// InitAlertOverviewRouter 初始化预警总览配置路由
func
InitAlertOverviewRouter
(
e
*
gin
.
Engine
)
{
g
roup
:=
e
.
Group
(
fmt
.
Sprintf
(
"%s/alert_overview"
,
conf
.
Options
.
Prefix
))
overviewG
roup
:=
e
.
Group
(
fmt
.
Sprintf
(
"%s/alert_overview"
,
conf
.
Options
.
Prefix
))
{
group
.
GET
(
""
,
controller
.
DetailAlertOverview
)
overviewGroup
.
GET
(
""
,
controller
.
DetailAlertOverview
)
}
screensGroup
:=
e
.
Group
(
fmt
.
Sprintf
(
"%s/screens"
,
conf
.
Options
.
Prefix
))
{
screensGroup
.
GET
(
""
,
controller
.
DetailAlert
)
screensGroup
.
GET
(
"list"
,
controller
.
ListAlert
)
}
}
src/router/alert
list
router.go.go
→
src/router/alertrouter.go.go
View file @
6d17138f
...
...
@@ -9,12 +9,12 @@ import (
// InitAlertListRouter 初始化预警列表配置路由
func
InitAlertListRouter
(
e
*
gin
.
Engine
)
{
group
:=
e
.
Group
(
fmt
.
Sprintf
(
"%s/alert
_list
"
,
conf
.
Options
.
Prefix
))
group
:=
e
.
Group
(
fmt
.
Sprintf
(
"%s/alert"
,
conf
.
Options
.
Prefix
))
{
group
.
GET
(
""
,
controller
.
DetailAlert
List
)
group
.
GET
(
"list"
,
controller
.
ListAlert
List
)
group
.
PUT
(
""
,
controller
.
UpdateAlert
List
)
group
.
PUT
(
"batch/push"
,
controller
.
BatchPushAlert
List
)
group
.
PUT
(
"batch/close"
,
controller
.
BatchCloseAlert
List
)
group
.
GET
(
""
,
controller
.
DetailAlert
)
group
.
GET
(
"list"
,
controller
.
ListAlert
)
group
.
PUT
(
""
,
controller
.
UpdateAlert
)
group
.
PUT
(
"batch/push"
,
controller
.
BatchPushAlert
)
group
.
PUT
(
"batch/close"
,
controller
.
BatchCloseAlert
)
}
}
src/router/workorderrouter.go
View file @
6d17138f
...
...
@@ -14,9 +14,9 @@ func InitWorkOrderRouter(e *gin.Engine) {
//预警工单管理
alert
:=
so
.
Group
(
"/alert"
,
header
.
SetContext
)
{
alert
.
GET
(
""
,
controller
.
DetailAlert
List
)
// 详情
alert
.
GET
(
"/list"
,
controller
.
ListAlert
List
)
// 列表
alert
.
PUT
(
"/dispose"
,
controller
.
DisposeAlert
List
)
// 处置反馈
alert
.
GET
(
""
,
controller
.
DetailAlert
)
// 详情
alert
.
GET
(
"/list"
,
controller
.
ListAlert
)
// 列表
alert
.
PUT
(
"/dispose"
,
controller
.
DisposeAlert
)
// 处置反馈
}
//业务工单管理
...
...
src/service/alert_list.go
View file @
6d17138f
...
...
@@ -238,7 +238,7 @@ func (a *AlertListSvc) Indices() (indices []string, err error) {
return
}
func
(
a
*
AlertListSvc
)
IndexSearch
(
req
request
.
ListAlert
List
)
(
resp
response
.
AlertLis
tList
,
err
error
)
{
func
(
a
*
AlertListSvc
)
IndexSearch
(
req
request
.
ListAlert
)
(
resp
response
.
Aler
tList
,
err
error
)
{
var
(
sources
response
.
OpenSearchSource
)
...
...
@@ -312,9 +312,9 @@ func (a *AlertListSvc) IndexSearch(req request.ListAlertList) (resp response.Ale
return
}
func
(
a
*
AlertListSvc
)
Update
(
session
*
xorm
.
Session
,
req
request
.
UpdateAlert
List
)
error
{
func
(
a
*
AlertListSvc
)
Update
(
session
*
xorm
.
Session
,
req
request
.
UpdateAlert
)
error
{
now
:=
jsontime
.
Now
()
data
:=
entity
.
Alert
List
{
data
:=
entity
.
Alert
{
UpdatedBy
:
a
.
User
.
SystemAccount
,
UpdatedAt
:
now
,
}
...
...
@@ -326,7 +326,7 @@ func (a *AlertListSvc) Update(session *xorm.Session, req request.UpdateAlertList
return
nil
}
func
(
a
*
AlertListSvc
)
BatchPushAlertList
(
session
*
xorm
.
Session
,
req
request
.
BatchPushAlert
List
)
error
{
func
(
a
*
AlertListSvc
)
BatchPushAlertList
(
session
*
xorm
.
Session
,
req
request
.
BatchPushAlert
)
error
{
now
:=
jsontime
.
Now
()
_
=
now
// TODO 批量推送用户告警
...
...
@@ -334,7 +334,7 @@ func (a *AlertListSvc) BatchPushAlertList(session *xorm.Session, req request.Bat
return
nil
}
func
(
a
*
AlertListSvc
)
BatchCloseAlertList
(
session
*
xorm
.
Session
,
req
request
.
BatchCloseAlert
List
)
error
{
func
(
a
*
AlertListSvc
)
BatchCloseAlertList
(
session
*
xorm
.
Session
,
req
request
.
BatchCloseAlert
)
error
{
now
:=
jsontime
.
Now
()
_
=
now
// TODO 批量推送用户告警
...
...
@@ -342,10 +342,10 @@ func (a *AlertListSvc) BatchCloseAlertList(session *xorm.Session, req request.Ba
return
nil
}
func
(
a
*
AlertListSvc
)
GetDataById
(
req
request
.
DetailAlert
List
)
(
resp
response
.
AlertLis
tItem
,
err
error
)
{
func
(
a
*
AlertListSvc
)
GetDataById
(
req
request
.
DetailAlert
)
(
resp
response
.
Aler
tItem
,
err
error
)
{
/*now := jsontime.Now()
data := response.Alert
List
Item{
Alert
List: entity.AlertLis
t{
data := response.AlertItem{
Alert
: entity.Aler
t{
Id: 123,
AlertPoint: "容器云/kube-apiserver",
AlertRulesId: "762ed641-6c0e-4c39-bf7c-7463abb0f8a2",
...
...
@@ -423,17 +423,17 @@ func (a *AlertListSvc) GetDataById(req request.DetailAlertList) (resp response.A
},
},
}*/
list
,
err
:=
a
.
IndexSearch
(
request
.
ListAlert
List
{
Id
:
req
.
Id
})
list
,
err
:=
a
.
IndexSearch
(
request
.
ListAlert
{
Id
:
req
.
Id
})
if
len
(
list
.
List
)
>
0
{
resp
=
list
.
List
[
0
]
}
return
}
func
(
a
*
AlertListSvc
)
List
(
req
request
.
ListAlert
List
)
(
resp
response
.
AlertLis
tList
,
err
error
)
{
func
(
a
*
AlertListSvc
)
List
(
req
request
.
ListAlert
)
(
resp
response
.
Aler
tList
,
err
error
)
{
/*now := jsontime.Now()
data1 := response.Alert
List
Item{
Alert
List: entity.AlertLis
t{
data1 := response.AlertItem{
Alert
: entity.Aler
t{
Id: 123,
AlertPoint: "容器云/kube-apiserver",
AlertRulesId: "762ed641-6c0e-4c39-bf7c-7463abb0f8a2",
...
...
@@ -517,8 +517,8 @@ func (a *AlertListSvc) List(req request.ListAlertList) (resp response.AlertListL
},
},
}
data2 := response.Alert
List
Item{
Alert
List: entity.AlertLis
t{
data2 := response.AlertItem{
Alert
: entity.Aler
t{
Id: 125,
AlertPoint: "容器云/apaas",
AlertRulesId: "762ed641-6c0e-4c39-bf7c-7463abb0f8a2",
...
...
@@ -605,7 +605,7 @@ func (a *AlertListSvc) List(req request.ListAlertList) (resp response.AlertListL
return
}
func
(
a
*
AlertListSvc
)
DisposeAlertList
(
session
*
xorm
.
Session
,
req
request
.
DisposeAlert
List
)
error
{
func
(
a
*
AlertListSvc
)
DisposeAlertList
(
session
*
xorm
.
Session
,
req
request
.
DisposeAlert
)
error
{
now
:=
jsontime
.
Now
()
_
=
now
// TODO 我的预警工单处置
...
...
src/service/alert_webhook.go
0 → 100644
View file @
6d17138f
package
service
import
(
"github.com/jinzhu/copier"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client"
"gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime"
"xorm.io/xorm"
)
type
AlertWebhookSvc
struct
{
User
entity
.
SystemUserInfo
}
// TODO AlertManager 回调接口
func
(
a
*
AlertWebhookSvc
)
WebHook
(
session
*
xorm
.
Session
,
req
request
.
AddPushRecord
)
(
id
string
,
err
error
)
{
// 报警消息
now
:=
jsontime
.
Now
()
data
:=
entity
.
PushRecord
{
CreatedBy
:
a
.
User
.
SystemAccount
,
CreatedAt
:
now
,
UpdatedBy
:
a
.
User
.
SystemAccount
,
UpdatedAt
:
now
,
}
_
=
copier
.
Copy
(
&
data
,
&
req
)
_
,
err
=
session
.
Insert
(
&
data
)
if
err
!=
nil
{
return
}
return
}
func
(
a
*
AlertWebhookSvc
)
Update
(
session
*
xorm
.
Session
,
req
request
.
UpdatePushRecord
)
error
{
now
:=
jsontime
.
Now
()
data
:=
entity
.
PushRecord
{
UpdatedBy
:
a
.
User
.
SystemAccount
,
UpdatedAt
:
now
,
}
_
=
copier
.
Copy
(
&
data
,
&
req
)
_
,
err
:=
session
.
ID
(
req
.
Id
)
.
Update
(
&
data
)
if
err
!=
nil
{
return
err
}
return
nil
}
func
(
a
*
AlertWebhookSvc
)
GetDataById
(
req
request
.
DetailPushRecord
)
(
resp
response
.
PushRecordItem
,
err
error
)
{
now
:=
jsontime
.
Time
{}
data
:=
response
.
PushRecordItem
{
PushRecord
:
entity
.
PushRecord
{
Id
:
1
,
AlertRulesId
:
"83343ef6-4a99-47bd-abb4-bcff52feb2ec"
,
RiskLevel
:
1
,
SystemAccount
:
"xiaowang"
,
PushTime
:
now
,
PushType
:
1
,
Status
:
1
,
CreatedBy
:
"admin"
,
CreatedAt
:
now
,
UpdatedBy
:
"admin"
,
UpdatedAt
:
now
,
},
NotifyMethod
:
[]
string
{
"dingtalk"
,
"sms"
},
}
resp
=
data
return
}
func
(
a
*
AlertWebhookSvc
)
List
(
req
request
.
ListPushRecord
)
(
resp
response
.
PushRecordList
,
err
error
)
{
now
:=
jsontime
.
Time
{}
data1
:=
response
.
PushRecordItem
{
PushRecord
:
entity
.
PushRecord
{
Id
:
1
,
AlertRulesId
:
"83343ef6-4a99-47bd-abb4-bcff52feb2ec"
,
RiskLevel
:
1
,
SystemAccount
:
"xiaowang"
,
PushTime
:
now
,
PushType
:
1
,
Status
:
1
,
CreatedBy
:
"admin"
,
CreatedAt
:
now
,
UpdatedBy
:
"admin"
,
UpdatedAt
:
now
,
},
NotifyMethod
:
[]
string
{
"dingtalk"
,
"sms"
},
}
data2
:=
response
.
PushRecordItem
{
PushRecord
:
entity
.
PushRecord
{
Id
:
1
,
AlertRulesId
:
"83343ef6-4a99-47bd-abb4-bcff52feb2ec"
,
RiskLevel
:
2
,
SystemAccount
:
"xiaozhang"
,
PushTime
:
now
,
PushType
:
2
,
Status
:
2
,
CreatedBy
:
"admin"
,
CreatedAt
:
now
,
UpdatedBy
:
"admin"
,
UpdatedAt
:
now
,
},
NotifyMethod
:
[]
string
{
"dingtalk"
,
"sms"
},
}
resp
.
List
=
append
(
resp
.
List
,
data1
,
data2
)
resp
.
TotalCount
=
int64
(
len
(
resp
.
List
))
return
}
func
(
a
*
AlertWebhookSvc
)
Delete
(
ids
[]
string
)
(
err
error
)
{
db
,
err
:=
client
.
GetDbClient
()
if
err
!=
nil
{
return
}
_
,
err
=
db
.
NewSession
()
.
In
(
"id"
,
ids
)
.
Delete
(
new
(
entity
.
PushRecord
))
return
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment