diff --git a/cmd/apimain.go b/cmd/apimain.go index 2589964..bde9723 100644 --- a/cmd/apimain.go +++ b/cmd/apimain.go @@ -101,7 +101,7 @@ func main() { } func DatabaseAutoUpdate() { - version := 243 + version := 244 db := global.DB diff --git a/docs/admin/admin_docs.go b/docs/admin/admin_docs.go index 8fb653e..3651514 100644 --- a/docs/admin/admin_docs.go +++ b/docs/admin/admin_docs.go @@ -1485,7 +1485,7 @@ const docTemplateadmin = `{ } } }, - "/admin/loginLog/delete": { + "/admin/login_log/delete": { "post": { "security": [ { @@ -1530,7 +1530,7 @@ const docTemplateadmin = `{ } } }, - "/admin/loginLog/detail/{id}": { + "/admin/login_log/detail/{id}": { "get": { "security": [ { @@ -1585,7 +1585,7 @@ const docTemplateadmin = `{ } } }, - "/admin/loginLog/list": { + "/admin/login_log/list": { "get": { "security": [ { @@ -2216,6 +2216,12 @@ const docTemplateadmin = `{ "description": "主机名", "name": "hostname", "in": "query" + }, + { + "type": "string", + "description": "uuids 用逗号分隔", + "name": "uuids", + "in": "query" } ], "responses": { @@ -3087,6 +3093,117 @@ const docTemplateadmin = `{ } } } + }, + "/admin/user_token/delete": { + "post": { + "security": [ + { + "token": [] + } + ], + "description": "登录凭证删除", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录凭证" + ], + "summary": "登录凭证删除", + "parameters": [ + { + "description": "登录凭证信息", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UserToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/user_token/list": { + "get": { + "security": [ + { + "token": [] + } + ], + "description": "登录凭证列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录凭证" + ], + "summary": "登录凭证列表", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "页大小", + "name": "page_size", + "in": "query" + }, + { + "type": "integer", + "description": "用户ID", + "name": "user_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.UserTokenList" + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } } }, "definitions": { @@ -3801,6 +3918,9 @@ const docTemplateadmin = `{ "user_id": { "type": "integer" }, + "user_token_id": { + "type": "integer" + }, "uuid": { "type": "string" } @@ -4068,6 +4188,49 @@ const docTemplateadmin = `{ } } }, + "model.UserToken": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "expired_at": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "token": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "user_id": { + "type": "integer" + } + } + }, + "model.UserTokenList": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserToken" + } + }, + "page": { + "type": "integer" + }, + "page_size": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, "response.ErrorResponse": { "type": "object", "properties": { diff --git a/docs/admin/admin_swagger.json b/docs/admin/admin_swagger.json index f41ba63..d06910a 100644 --- a/docs/admin/admin_swagger.json +++ b/docs/admin/admin_swagger.json @@ -1478,7 +1478,7 @@ } } }, - "/admin/loginLog/delete": { + "/admin/login_log/delete": { "post": { "security": [ { @@ -1523,7 +1523,7 @@ } } }, - "/admin/loginLog/detail/{id}": { + "/admin/login_log/detail/{id}": { "get": { "security": [ { @@ -1578,7 +1578,7 @@ } } }, - "/admin/loginLog/list": { + "/admin/login_log/list": { "get": { "security": [ { @@ -2209,6 +2209,12 @@ "description": "主机名", "name": "hostname", "in": "query" + }, + { + "type": "string", + "description": "uuids 用逗号分隔", + "name": "uuids", + "in": "query" } ], "responses": { @@ -3080,6 +3086,117 @@ } } } + }, + "/admin/user_token/delete": { + "post": { + "security": [ + { + "token": [] + } + ], + "description": "登录凭证删除", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录凭证" + ], + "summary": "登录凭证删除", + "parameters": [ + { + "description": "登录凭证信息", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.UserToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/user_token/list": { + "get": { + "security": [ + { + "token": [] + } + ], + "description": "登录凭证列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录凭证" + ], + "summary": "登录凭证列表", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "页大小", + "name": "page_size", + "in": "query" + }, + { + "type": "integer", + "description": "用户ID", + "name": "user_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.UserTokenList" + } + } + } + ] + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } } }, "definitions": { @@ -3794,6 +3911,9 @@ "user_id": { "type": "integer" }, + "user_token_id": { + "type": "integer" + }, "uuid": { "type": "string" } @@ -4061,6 +4181,49 @@ } } }, + "model.UserToken": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "expired_at": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "token": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "user_id": { + "type": "integer" + } + } + }, + "model.UserTokenList": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/model.UserToken" + } + }, + "page": { + "type": "integer" + }, + "page_size": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, "response.ErrorResponse": { "type": "object", "properties": { diff --git a/docs/admin/admin_swagger.yaml b/docs/admin/admin_swagger.yaml index 1008a77..03ccd53 100644 --- a/docs/admin/admin_swagger.yaml +++ b/docs/admin/admin_swagger.yaml @@ -476,6 +476,8 @@ definitions: type: string user_id: type: integer + user_token_id: + type: integer uuid: type: string type: object @@ -653,6 +655,34 @@ definitions: total: type: integer type: object + model.UserToken: + properties: + created_at: + type: string + expired_at: + type: integer + id: + type: integer + token: + type: string + updated_at: + type: string + user_id: + type: integer + type: object + model.UserTokenList: + properties: + list: + items: + $ref: '#/definitions/model.UserToken' + type: array + page: + type: integer + page_size: + type: integer + total: + type: integer + type: object response.ErrorResponse: properties: error: @@ -1546,7 +1576,7 @@ paths: summary: 登录选项 tags: - 登录 - /admin/loginLog/delete: + /admin/login_log/delete: post: consumes: - application/json @@ -1574,7 +1604,7 @@ paths: summary: 登录日志删除 tags: - 登录日志 - /admin/loginLog/detail/{id}: + /admin/login_log/detail/{id}: get: consumes: - application/json @@ -1606,7 +1636,7 @@ paths: summary: 登录日志详情 tags: - 登录日志 - /admin/loginLog/list: + /admin/login_log/list: get: consumes: - application/json @@ -1979,6 +2009,10 @@ paths: in: query name: hostname type: string + - description: uuids 用逗号分隔 + in: query + name: uuids + type: string produces: - application/json responses: @@ -2498,6 +2532,73 @@ paths: summary: 修改密码 tags: - 用户 + /admin/user_token/delete: + post: + consumes: + - application/json + description: 登录凭证删除 + parameters: + - description: 登录凭证信息 + in: body + name: body + required: true + schema: + $ref: '#/definitions/model.UserToken' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - token: [] + summary: 登录凭证删除 + tags: + - 登录凭证 + /admin/user_token/list: + get: + consumes: + - application/json + description: 登录凭证列表 + parameters: + - description: 页码 + in: query + name: page + type: integer + - description: 页大小 + in: query + name: page_size + type: integer + - description: 用户ID + in: query + name: user_id + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/model.UserTokenList' + type: object + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - token: [] + summary: 登录凭证列表 + tags: + - 登录凭证 securityDefinitions: BearerAuth: in: header diff --git a/http/controller/admin/login.go b/http/controller/admin/login.go index ffc5ad9..ad25393 100644 --- a/http/controller/admin/login.go +++ b/http/controller/admin/login.go @@ -53,10 +53,10 @@ func (ct *Login) Login(c *gin.Context) { ut := service.AllService.UserService.Login(u, &model.LoginLog{ UserId: u.Id, - Client: "webadmin", + Client: model.LoginLogClientWebAdmin, Uuid: "", //must be empty Ip: c.ClientIP(), - Type: "account", + Type: model.LoginLogTypeAccount, Platform: f.Platform, }) diff --git a/http/controller/admin/loginLog.go b/http/controller/admin/loginLog.go index f6daf1c..914ea0f 100644 --- a/http/controller/admin/loginLog.go +++ b/http/controller/admin/loginLog.go @@ -23,7 +23,7 @@ type LoginLog struct { // @Param id path int true "ID" // @Success 200 {object} response.Response{data=model.LoginLog} // @Failure 500 {object} response.Response -// @Router /admin/loginLog/detail/{id} [get] +// @Router /admin/login_log/detail/{id} [get] // @Security token func (ct *LoginLog) Detail(c *gin.Context) { id := c.Param("id") @@ -48,7 +48,7 @@ func (ct *LoginLog) Detail(c *gin.Context) { // @Param user_id query int false "用户ID" // @Success 200 {object} response.Response{data=model.LoginLogList} // @Failure 500 {object} response.Response -// @Router /admin/loginLog/list [get] +// @Router /admin/login_log/list [get] // @Security token func (ct *LoginLog) List(c *gin.Context) { query := &admin.LoginLogQuery{} @@ -78,7 +78,7 @@ func (ct *LoginLog) List(c *gin.Context) { // @Param body body model.LoginLog true "登录日志信息" // @Success 200 {object} response.Response // @Failure 500 {object} response.Response -// @Router /admin/loginLog/delete [post] +// @Router /admin/login_log/delete [post] // @Security token func (ct *LoginLog) Delete(c *gin.Context) { f := &model.LoginLog{} diff --git a/http/controller/admin/peer.go b/http/controller/admin/peer.go index 6aae4bc..fb5fa20 100644 --- a/http/controller/admin/peer.go +++ b/http/controller/admin/peer.go @@ -79,6 +79,7 @@ func (ct *Peer) Create(c *gin.Context) { // @Param time_ago query int false "时间" // @Param id query string false "ID" // @Param hostname query string false "主机名" +// @Param uuids query string false "uuids 用逗号分隔" // @Success 200 {object} response.Response{data=model.PeerList} // @Failure 500 {object} response.Response // @Router /admin/peer/list [get] @@ -104,6 +105,9 @@ func (ct *Peer) List(c *gin.Context) { if query.Hostname != "" { tx.Where("hostname like ?", "%"+query.Hostname+"%") } + if query.Uuids != "" { + tx.Where("uuid in (?)", query.Uuids) + } }) response.Success(c, res) } diff --git a/http/controller/admin/userToken.go b/http/controller/admin/userToken.go new file mode 100644 index 0000000..daddb99 --- /dev/null +++ b/http/controller/admin/userToken.go @@ -0,0 +1,83 @@ +package admin + +import ( + "Gwen/global" + "Gwen/http/request/admin" + "Gwen/http/response" + "Gwen/model" + "Gwen/service" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type UserToken struct { +} + +// List 列表 +// @Tags 登录凭证 +// @Summary 登录凭证列表 +// @Description 登录凭证列表 +// @Accept json +// @Produce json +// @Param page query int false "页码" +// @Param page_size query int false "页大小" +// @Param user_id query int false "用户ID" +// @Success 200 {object} response.Response{data=model.UserTokenList} +// @Failure 500 {object} response.Response +// @Router /admin/user_token/list [get] +// @Security token +func (ct *UserToken) List(c *gin.Context) { + query := &admin.LoginTokenQuery{} + if err := c.ShouldBindQuery(query); err != nil { + response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) + return + } + res := service.AllService.UserService.TokenList(query.Page, query.PageSize, func(tx *gorm.DB) { + if query.UserId > 0 { + tx.Where("user_id = ?", query.UserId) + } + tx.Order("id desc") + }) + response.Success(c, res) +} + +// Delete 删除 +// @Tags 登录凭证 +// @Summary 登录凭证删除 +// @Description 登录凭证删除 +// @Accept json +// @Produce json +// @Param body body model.UserToken true "登录凭证信息" +// @Success 200 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /admin/user_token/delete [post] +// @Security token +func (ct *UserToken) Delete(c *gin.Context) { + f := &model.UserToken{} + if err := c.ShouldBindJSON(f); err != nil { + response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error()) + return + } + id := f.Id + errList := global.Validator.ValidVar(c, id, "required,gt=0") + if len(errList) > 0 { + response.Fail(c, 101, errList[0]) + return + } + l := service.AllService.UserService.TokenInfoById(f.Id) + u := service.AllService.UserService.CurUser(c) + if !service.AllService.UserService.IsAdmin(u) && l.UserId != u.Id { + response.Fail(c, 101, response.TranslateMsg(c, "NoAccess")) + return + } + if l.Id > 0 { + err := service.AllService.UserService.DeleteToken(l) + if err == nil { + response.Success(c, nil) + return + } + response.Fail(c, 101, err.Error()) + return + } + response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound")) +} diff --git a/http/controller/api/login.go b/http/controller/api/login.go index 703d792..f3d9481 100644 --- a/http/controller/api/login.go +++ b/http/controller/api/login.go @@ -54,7 +54,7 @@ func (l *Login) Login(c *gin.Context) { //根据refer判断是webclient还是app ref := c.GetHeader("referer") if ref != "" { - f.DeviceInfo.Type = "webclient" + f.DeviceInfo.Type = model.LoginLogClientWeb } ut := service.AllService.UserService.Login(u, &model.LoginLog{ diff --git a/http/request/admin/login.go b/http/request/admin/login.go index 002ef78..6052ee0 100644 --- a/http/request/admin/login.go +++ b/http/request/admin/login.go @@ -11,3 +11,7 @@ type LoginLogQuery struct { IsMy int `form:"is_my"` PageQuery } +type LoginTokenQuery struct { + UserId int `form:"user_id"` + PageQuery +} diff --git a/http/request/admin/peer.go b/http/request/admin/peer.go index b045a9d..c021ce3 100644 --- a/http/request/admin/peer.go +++ b/http/request/admin/peer.go @@ -38,6 +38,7 @@ type PeerQuery struct { TimeAgo int `json:"time_ago" form:"time_ago"` Id string `json:"id" form:"id"` Hostname string `json:"hostname" form:"hostname"` + Uuids string `json:"uuids" form:"uuids"` } type SimpleDataQuery struct { diff --git a/http/router/admin.go b/http/router/admin.go index 89d76cf..368a081 100644 --- a/http/router/admin.go +++ b/http/router/admin.go @@ -30,6 +30,7 @@ func Init(g *gin.Engine) { AuditBind(adg) AddressBookCollectionBind(adg) AddressBookCollectionRuleBind(adg) + UserTokenBind(adg) rs := &admin.Rustdesk{} adg.GET("/server-config", rs.ServerConfig) adg.GET("/app-config", rs.AppConfig) @@ -181,6 +182,12 @@ func AddressBookCollectionRuleBind(rg *gin.RouterGroup) { aR.POST("/delete", cont.Delete) } } +func UserTokenBind(rg *gin.RouterGroup) { + aR := rg.Group("/user_token").Use(middleware.AdminPrivilege()) + cont := &admin.UserToken{} + aR.GET("/list", cont.List) + aR.POST("/delete", cont.Delete) +} /* func FileBind(rg *gin.RouterGroup) { diff --git a/model/loginLog.go b/model/loginLog.go index 080e1eb..51fd97f 100644 --- a/model/loginLog.go +++ b/model/loginLog.go @@ -2,13 +2,13 @@ package model type LoginLog struct { IdModel - UserId uint `json:"user_id"` - Client string `json:"client"` //webadmin,webclient,app, - Uuid string `json:"uuid"` - Ip string `json:"ip"` - Type string `json:"type"` //account,oauth - Platform string `json:"platform"` //windows,linux,mac,android,ios - + UserId uint `json:"user_id" gorm:"default:0;not null;"` + Client string `json:"client"` //webadmin,webclient,app, + Uuid string `json:"uuid"` + Ip string `json:"ip"` + Type string `json:"type"` //account,oauth + Platform string `json:"platform"` //windows,linux,mac,android,ios + UserTokenId uint `json:"user_token_id" gorm:"default:0;not null;"` TimeModel } diff --git a/model/userToken.go b/model/userToken.go index 73c3339..f80c4f0 100644 --- a/model/userToken.go +++ b/model/userToken.go @@ -7,3 +7,8 @@ type UserToken struct { ExpiredAt int64 `json:"expired_at" gorm:"default:0;not null;"` TimeModel } + +type UserTokenList struct { + UserTokens []UserToken `json:"list"` + Pagination +} diff --git a/service/user.go b/service/user.go index 5708f1a..fc3ffa5 100644 --- a/service/user.go +++ b/service/user.go @@ -70,6 +70,7 @@ func (us *UserService) Login(u *model.User, llog *model.LoginLog) *model.UserTok ExpiredAt: time.Now().Add(time.Hour * 24 * 7).Unix(), } global.DB.Create(ut) + llog.UserTokenId = ut.UserId global.DB.Create(llog) if llog.Uuid != "" { AllService.PeerService.UuidBindUserId(llog.Uuid, u.Id) @@ -356,3 +357,27 @@ func (us *UserService) Register(username string, password string) *model.User { global.DB.Create(u) return u } + +func (us *UserService) TokenList(page uint, size uint, f func(tx *gorm.DB)) *model.UserTokenList { + res := &model.UserTokenList{} + res.Page = int64(page) + res.PageSize = int64(size) + tx := global.DB.Model(&model.UserToken{}) + if f != nil { + f(tx) + } + tx.Count(&res.Total) + tx.Scopes(Paginate(page, size)) + tx.Find(&res.UserTokens) + return res +} + +func (us *UserService) TokenInfoById(id uint) *model.UserToken { + ut := &model.UserToken{} + global.DB.Where("id = ?", id).First(ut) + return ut +} + +func (us *UserService) DeleteToken(l *model.UserToken) error { + return global.DB.Delete(l).Error +}