user_act_log.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package dbdata
  2. import (
  3. "net/url"
  4. "regexp"
  5. "strings"
  6. "github.com/bjdgyc/anylink/base"
  7. "github.com/ivpusic/grpool"
  8. "github.com/spf13/cast"
  9. "xorm.io/xorm"
  10. )
  11. const (
  12. UserAuthFail = 0 // 认证失败
  13. UserAuthSuccess = 1 // 认证成功
  14. UserConnected = 2 // 连线成功
  15. UserLogout = 3 // 用户登出
  16. UserLogoutLose = 0 // 用户掉线
  17. UserLogoutBanner = 1 // 用户banner弹窗取消
  18. UserLogoutClient = 2 // 用户主动登出
  19. UserLogoutTimeout = 3 // 用户超时登出
  20. UserLogoutAdmin = 4 // 账号被管理员踢下线
  21. UserLogoutExpire = 5 // 账号过期被踢下线
  22. )
  23. type UserActLogProcess struct {
  24. Pool *grpool.Pool
  25. StatusOps []string
  26. OsOps []string
  27. ClientOps []string
  28. InfoOps []string
  29. }
  30. var (
  31. UserActLogIns = &UserActLogProcess{
  32. Pool: grpool.NewPool(1, 100),
  33. StatusOps: []string{ // 操作类型
  34. UserAuthFail: "认证失败",
  35. UserAuthSuccess: "认证成功",
  36. UserConnected: "连接成功",
  37. UserLogout: "用户登出",
  38. },
  39. OsOps: []string{ // 操作系统
  40. 0: "Unknown",
  41. 1: "Windows",
  42. 2: "macOS",
  43. 3: "Linux",
  44. 4: "Android",
  45. 5: "iOS",
  46. },
  47. ClientOps: []string{ // 客户端
  48. 0: "Unknown",
  49. 1: "AnyConnect",
  50. 2: "OpenConnect",
  51. 3: "AnyLink",
  52. },
  53. InfoOps: []string{ // 信息
  54. UserLogoutLose: "用户掉线",
  55. UserLogoutBanner: "用户取消弹窗/客户端发起的logout",
  56. UserLogoutClient: "用户/客户端主动断开",
  57. UserLogoutTimeout: "Session过期被踢下线",
  58. UserLogoutAdmin: "账号被管理员踢下线",
  59. UserLogoutExpire: "账号过期被踢下线",
  60. },
  61. }
  62. )
  63. // 异步写入用户操作日志
  64. func (ua *UserActLogProcess) Add(u UserActLog, userAgent string) {
  65. // os, client, ver
  66. os_idx, client_idx, ver := ua.ParseUserAgent(userAgent)
  67. u.Os = os_idx
  68. u.Client = client_idx
  69. u.Version = ver
  70. u.RemoteAddr = strings.Split(u.RemoteAddr, ":")[0]
  71. // remove extra characters
  72. infoSlice := strings.Split(u.Info, " ")
  73. infoLen := len(infoSlice)
  74. if infoLen > 1 {
  75. if u.Username == infoSlice[0] {
  76. u.Info = strings.Join(infoSlice[1:], " ")
  77. }
  78. // delete - char
  79. if infoLen > 2 && infoSlice[1] == "-" {
  80. u.Info = u.Info[2:]
  81. }
  82. }
  83. // limit the max length of char
  84. u.Version = substr(u.Version, 0, 15)
  85. u.DeviceType = substr(u.DeviceType, 0, 128)
  86. u.PlatformVersion = substr(u.PlatformVersion, 0, 128)
  87. u.Info = substr(u.Info, 0, 255)
  88. UserActLogIns.Pool.JobQueue <- func() {
  89. err := Add(u)
  90. if err != nil {
  91. base.Error("Add UserActLog error: ", err)
  92. }
  93. }
  94. }
  95. // 转义操作类型, 方便vue显示
  96. func (ua *UserActLogProcess) GetStatusOpsWithTag() interface{} {
  97. type StatusTag struct {
  98. Key int `json:"key"`
  99. Value string `json:"value"`
  100. Tag string `json:"tag"`
  101. }
  102. var res []StatusTag
  103. for k, v := range ua.StatusOps {
  104. tag := "info"
  105. switch k {
  106. case UserAuthFail:
  107. tag = "danger"
  108. case UserAuthSuccess:
  109. tag = "success"
  110. case UserConnected:
  111. tag = ""
  112. }
  113. res = append(res, StatusTag{k, v, tag})
  114. }
  115. return res
  116. }
  117. func (ua *UserActLogProcess) GetInfoOpsById(id uint8) string {
  118. return ua.InfoOps[id]
  119. }
  120. // 解析user agent
  121. func (ua *UserActLogProcess) ParseUserAgent(userAgent string) (os_idx, client_idx uint8, ver string) {
  122. // Unknown
  123. if len(userAgent) == 0 {
  124. return 0, 0, ""
  125. }
  126. // OS
  127. os_idx = 0
  128. if strings.Contains(userAgent, "windows") {
  129. os_idx = 1
  130. } else if strings.Contains(userAgent, "mac os") || strings.Contains(userAgent, "darwin_i386") {
  131. os_idx = 2
  132. } else if strings.Contains(userAgent, "darwin_arm") || strings.Contains(userAgent, "apple") {
  133. os_idx = 5
  134. } else if strings.Contains(userAgent, "android") {
  135. os_idx = 4
  136. } else if strings.Contains(userAgent, "linux") {
  137. os_idx = 3
  138. }
  139. // Client
  140. client_idx = 0
  141. if strings.Contains(userAgent, "anyconnect") {
  142. client_idx = 1
  143. } else if strings.Contains(userAgent, "openconnect") {
  144. client_idx = 2
  145. } else if strings.Contains(userAgent, "anylink") {
  146. client_idx = 3
  147. }
  148. // Version
  149. uaSlice := strings.Split(userAgent, " ")
  150. ver = uaSlice[len(uaSlice)-1]
  151. if ver[0] == 'v' {
  152. ver = ver[1:]
  153. }
  154. if !regexp.MustCompile(`^(\d+\.?)+$`).MatchString(ver) {
  155. ver = ""
  156. }
  157. return
  158. }
  159. // 清除用户操作日志
  160. func (ua *UserActLogProcess) ClearUserActLog(ts string) (int64, error) {
  161. affected, err := xdb.Where("created_at < '" + ts + "'").Delete(&UserActLog{})
  162. return affected, err
  163. }
  164. // 后台筛选用户操作日志
  165. func (ua *UserActLogProcess) GetSession(values url.Values) *xorm.Session {
  166. session := xdb.Where("1=1")
  167. if values.Get("username") != "" {
  168. session.And("username = ?", values.Get("username"))
  169. }
  170. if values.Get("sdate") != "" {
  171. session.And("created_at >= ?", values.Get("sdate")+" 00:00:00'")
  172. }
  173. if values.Get("edate") != "" {
  174. session.And("created_at <= ?", values.Get("edate")+" 23:59:59'")
  175. }
  176. if values.Get("status") != "" {
  177. session.And("status = ?", cast.ToUint8(values.Get("status"))-1)
  178. }
  179. if values.Get("os") != "" {
  180. session.And("os = ?", cast.ToUint8(values.Get("os"))-1)
  181. }
  182. if values.Get("sort") == "1" {
  183. session.OrderBy("id desc")
  184. } else {
  185. session.OrderBy("id asc")
  186. }
  187. return session
  188. }
  189. // 截取字符串
  190. func substr(s string, pos, length int) string {
  191. runes := []rune(s)
  192. l := pos + length
  193. if l > len(runes) {
  194. l = len(runes)
  195. }
  196. return string(runes[pos:l])
  197. }