diff --git a/core/app/api/v2/auth.go b/core/app/api/v2/auth.go
index dde64a2db..2839e40a2 100644
--- a/core/app/api/v2/auth.go
+++ b/core/app/api/v2/auth.go
@@ -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
diff --git a/core/app/dto/logs.go b/core/app/dto/logs.go
index 189ce6ab1..16002b7b5 100644
--- a/core/app/dto/logs.go
+++ b/core/app/dto/logs.go
@@ -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"`
diff --git a/core/app/model/logs.go b/core/app/model/logs.go
index 511ebad44..f4fc71b52 100644
--- a/core/app/model/logs.go
+++ b/core/app/model/logs.go
@@ -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"`
diff --git a/core/app/repo/logs.go b/core/app/repo/logs.go
index fbc097109..a70c7e487 100644
--- a/core/app/repo/logs.go
+++ b/core/app/repo/logs.go
@@ -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)
}
}
diff --git a/core/app/service/logs.go b/core/app/service/logs.go
index d43068138..0c87efc74 100644
--- a/core/app/service/logs.go
+++ b/core/app/service/logs.go
@@ -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))
diff --git a/core/cmd/server/docs/docs.go b/core/cmd/server/docs/docs.go
index 78ac4b6f6..e6aad283d 100644
--- a/core/cmd/server/docs/docs.go
+++ b/core/cmd/server/docs/docs.go
@@ -34284,7 +34284,7 @@ const docTemplate = `{
},
"dto.SearchLgLogWithPage": {
"properties": {
- "ip": {
+ "info": {
"type": "string"
},
"page": {
diff --git a/core/cmd/server/docs/swagger.json b/core/cmd/server/docs/swagger.json
index 63acdb717..6f9ea21ac 100644
--- a/core/cmd/server/docs/swagger.json
+++ b/core/cmd/server/docs/swagger.json
@@ -34280,7 +34280,7 @@
},
"dto.SearchLgLogWithPage": {
"properties": {
- "ip": {
+ "info": {
"type": "string"
},
"page": {
diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go
index 7c263be8e..64009678b 100644
--- a/core/init/migration/migrate.go
+++ b/core/init/migration/migrate.go
@@ -47,6 +47,7 @@ func Init() {
migrations.AddAIProxyMenu,
migrations.AddSkillsHubMenu,
migrations.AddOperationLogUser,
+ migrations.AddLoginLogUser,
migrations.AddIsOfflineSetting,
})
if err := m.Migrate(); err != nil {
diff --git a/core/init/migration/migrations/init.go b/core/init/migration/migrations/init.go
index 2ae703bc8..3de9a9f9d 100644
--- a/core/init/migration/migrations/init.go
+++ b/core/init/migration/migrations/init.go
@@ -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 {
diff --git a/frontend/src/api/interface/log.ts b/frontend/src/api/interface/log.ts
index ee907ab08..5a9b89488 100644
--- a/frontend/src/api/interface/log.ts
+++ b/frontend/src/api/interface/log.ts
@@ -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;
diff --git a/frontend/src/views/log/login/index.vue b/frontend/src/views/log/login/index.vue
index 23562fa29..304d1e787 100644
--- a/frontend/src/views/log/login/index.vue
+++ b/frontend/src/views/log/login/index.vue
@@ -16,13 +16,14 @@
-
+
+
@@ -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('');
+const searchInfo = ref('');
const searchStatus = ref('');
const search = async () => {
let params = {
- ip: searchIP.value,
+ info: searchInfo.value,
status: searchStatus.value,
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,