feat: add login user display to login records (#12870)

This commit is contained in:
ssongliu
2026-05-27 13:44:14 +08:00
committed by GitHub
parent 612e67e4cc
commit b9dff99d63
11 changed files with 49 additions and 16 deletions
+18 -4
View File
@@ -61,7 +61,7 @@ func (b *BaseApi) Login(c *gin.Context) {
user, msgKey, err := xpack.AuthProvider.Login(c, req, string(entrance))
if user == nil || user.MfaStatus != constant.StatusEnable {
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
go saveLoginLogs(c, req.Name, wrapLoginErr(msgKey, err))
}
if msgKey == "ErrAuth" || msgKey == "ErrEntrance" {
if msgKey == "ErrAuth" {
@@ -108,7 +108,7 @@ func (b *BaseApi) MFALogin(c *gin.Context) {
}
user, msgKey, err := xpack.AuthProvider.MFALogin(c, req, string(entrance))
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
go saveLoginLogs(c, loginLogUserName(user, req.SessionID), wrapLoginErr(msgKey, err))
if msgKey == "ErrMFA" {
global.IPTracker.RecordFailure(ip)
failures := initauth.GetMFASessionStore().RecordFailure(req.SessionID)
@@ -163,7 +163,7 @@ func (b *BaseApi) PasskeyFinishLogin(c *gin.Context) {
sessionID := c.GetHeader("Passkey-Session")
entrance := loadEntranceFromRequest(c)
user, msgKey, err := xpack.AuthProvider.PasskeyFinishLogin(c, sessionID, entrance)
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
go saveLoginLogs(c, loginLogUserName(user, ""), wrapLoginErr(msgKey, err))
if msgKey == "ErrAuth" || msgKey == "ErrEntrance" {
if msgKey == "ErrAuth" {
global.IPTracker.SetNeedCaptcha(common.GetRealClientIP(c))
@@ -500,7 +500,7 @@ func (b *BaseApi) ResetPassword(c *gin.Context) {
helper.Success(c)
}
func saveLoginLogs(c *gin.Context, err error) {
func saveLoginLogs(c *gin.Context, userName string, err error) {
var logs model.LoginLog
if err != nil {
logs.Status = constant.StatusFailed
@@ -509,10 +509,24 @@ func saveLoginLogs(c *gin.Context, err error) {
logs.Status = constant.StatusSuccess
}
logs.IP = c.ClientIP()
logs.User = userName
logs.Agent = c.GetHeader("User-Agent")
_ = logService.CreateLoginLog(logs)
}
func loginLogUserName(user *dto.UserLoginInfo, mfaSessionID string) string {
if user != nil {
return user.Name
}
if mfaSessionID == "" {
return ""
}
if session, ok := initauth.GetMFASessionStore().Get(mfaSessionID); ok {
return session.Name
}
return ""
}
func wrapLoginErr(msgKey string, err error) error {
if err != nil {
return err
+2 -1
View File
@@ -33,13 +33,14 @@ type SearchOpLogWithPage struct {
type SearchLgLogWithPage struct {
PageInfo
IP string `json:"ip"`
Info string `json:"info"`
Status string `json:"status"`
}
type LoginLog struct {
ID uint `json:"id"`
IP string `json:"ip"`
User string `json:"user"`
Address string `json:"address"`
Agent string `json:"agent"`
Status string `json:"status"`
+1
View File
@@ -25,6 +25,7 @@ type OperationLog struct {
type LoginLog struct {
BaseModel
IP string `json:"ip"`
User string `json:"user"`
Address string `json:"address"`
Agent string `json:"agent"`
Status string `json:"status"`
+7 -3
View File
@@ -17,7 +17,7 @@ type ILogRepo interface {
CreateOperationLog(user *model.OperationLog) error
PageOperationLog(limit, offset int, opts ...global.DBOption) (int64, []model.OperationLog, error)
WithByIP(ip string) global.DBOption
WithByInfo(info string) global.DBOption
WithBySource(source string) global.DBOption
WithByLikeOperation(operation string) global.DBOption
}
@@ -82,9 +82,13 @@ func (c *LogRepo) WithBySource(source string) global.DBOption {
return g.Where("source = ?", source)
}
}
func (c *LogRepo) WithByIP(ip string) global.DBOption {
func (c *LogRepo) WithByInfo(info string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("ip LIKE ?", "%"+ip+"%")
if len(info) == 0 {
return g
}
infoStr := "%" + info + "%"
return g.Where("ip LIKE ? OR user LIKE ?", infoStr, infoStr)
}
}
+2 -2
View File
@@ -47,8 +47,8 @@ func (u *LogService) PageLoginLog(ctx *gin.Context, req dto.SearchLgLogWithPage)
options := []global.DBOption{
repo.WithOrderDesc("created_at"),
}
if len(req.IP) != 0 {
options = append(options, logRepo.WithByIP(req.IP))
if len(req.Info) != 0 {
options = append(options, logRepo.WithByInfo(req.Info))
}
if len(req.Status) != 0 {
options = append(options, repo.WithByStatus(req.Status))
+1 -1
View File
@@ -34284,7 +34284,7 @@ const docTemplate = `{
},
"dto.SearchLgLogWithPage": {
"properties": {
"ip": {
"info": {
"type": "string"
},
"page": {
+1 -1
View File
@@ -34280,7 +34280,7 @@
},
"dto.SearchLgLogWithPage": {
"properties": {
"ip": {
"info": {
"type": "string"
},
"page": {
+1
View File
@@ -47,6 +47,7 @@ func Init() {
migrations.AddAIProxyMenu,
migrations.AddSkillsHubMenu,
migrations.AddOperationLogUser,
migrations.AddLoginLogUser,
migrations.AddIsOfflineSetting,
})
if err := m.Migrate(); err != nil {
+7
View File
@@ -1328,6 +1328,13 @@ var AddOperationLogUser = &gormigrate.Migration{
},
}
var AddLoginLogUser = &gormigrate.Migration{
ID: "20260527-add-login-log-user",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.LoginLog{})
},
}
var AddIsOfflineSetting = &gormigrate.Migration{
ID: "20260429-add-is-offline-setting",
Migrate: func(tx *gorm.DB) error {
+2 -1
View File
@@ -24,11 +24,12 @@ export namespace Log {
operation: string;
}
export interface SearchLgLog extends ReqPage {
ip: string;
info: string;
status: string;
}
export interface LoginLogs {
ip: string;
user: string;
address: string;
agent: string;
status: string;
+7 -3
View File
@@ -16,13 +16,14 @@
<el-option :label="$t('commons.status.success')" value="Success"></el-option>
<el-option :label="$t('commons.status.failed')" value="Failed"></el-option>
</el-select>
<TableSearch @search="search()" v-model:searchName="searchIP" />
<TableSearch @search="search()" v-model:searchName="searchInfo" />
<TableRefresh @search="search()" />
<TableSetting title="login-log-refresh" @search="search()" />
</template>
<template #main>
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search" :heightDiff="370">
<el-table-column :label="$t('logs.loginIP')" prop="ip" />
<el-table-column v-if="isEnterprise" :label="$t('commons.login.username')" prop="user" />
<el-table-column :label="$t('logs.loginAddress')" prop="address" />
<el-table-column :label="$t('logs.loginAgent')" show-overflow-tooltip prop="agent" />
<el-table-column :label="$t('logs.loginStatus')" prop="status">
@@ -51,6 +52,9 @@ import { cleanLogs, getLoginLogs } from '@/api/modules/log';
import { onMounted, reactive, ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { useGlobalStore } from '@/composables/useGlobalStore';
const { isEnterprise } = useGlobalStore();
const loading = ref();
const data = ref();
@@ -61,12 +65,12 @@ const paginationConfig = reactive({
pageSize: Number(localStorage.getItem('login-log-page-size')) || 20,
total: 0,
});
const searchIP = ref<string>('');
const searchInfo = ref<string>('');
const searchStatus = ref<string>('');
const search = async () => {
let params = {
ip: searchIP.value,
info: searchInfo.value,
status: searchStatus.value,
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,