diff --git a/constant/user_setting.go b/constant/user_setting.go index 055884f7..7e79035e 100644 --- a/constant/user_setting.go +++ b/constant/user_setting.go @@ -7,6 +7,7 @@ var ( UserSettingWebhookSecret = "webhook_secret" // WebhookSecret webhook密钥 UserSettingNotificationEmail = "notification_email" // NotificationEmail 通知邮箱地址 UserAcceptUnsetRatioModel = "accept_unset_model_ratio_model" // AcceptUnsetRatioModel 是否接受未设置价格的模型 + UserSettingRecordIpLog = "record_ip_log" // 是否记录请求和错误日志IP ) var ( diff --git a/controller/user.go b/controller/user.go index fd53e743..d7eb42d7 100644 --- a/controller/user.go +++ b/controller/user.go @@ -943,6 +943,7 @@ type UpdateUserSettingRequest struct { WebhookSecret string `json:"webhook_secret,omitempty"` NotificationEmail string `json:"notification_email,omitempty"` AcceptUnsetModelRatioModel bool `json:"accept_unset_model_ratio_model"` + RecordIpLog bool `json:"record_ip_log"` } func UpdateUserSetting(c *gin.Context) { @@ -1019,6 +1020,7 @@ func UpdateUserSetting(c *gin.Context) { constant.UserSettingNotifyType: req.QuotaWarningType, constant.UserSettingQuotaWarningThreshold: req.QuotaWarningThreshold, "accept_unset_model_ratio_model": req.AcceptUnsetModelRatioModel, + constant.UserSettingRecordIpLog: req.RecordIpLog, } // 如果是webhook类型,添加webhook相关设置 diff --git a/model/log.go b/model/log.go index 0a891fcd..3df961e1 100644 --- a/model/log.go +++ b/model/log.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "one-api/common" + "one-api/constant" "os" "strings" "time" @@ -32,6 +33,7 @@ type Log struct { ChannelName string `json:"channel_name" gorm:"->"` TokenId int `json:"token_id" gorm:"default:0;index"` Group string `json:"group" gorm:"index"` + Ip string `json:"ip" gorm:"index;default:''"` Other string `json:"other"` } @@ -95,6 +97,15 @@ func RecordErrorLog(c *gin.Context, userId int, channelId int, modelName string, common.LogInfo(c, fmt.Sprintf("record error log: userId=%d, channelId=%d, modelName=%s, tokenName=%s, content=%s", userId, channelId, modelName, tokenName, content)) username := c.GetString("username") otherStr := common.MapToJsonStr(other) + // 判断是否需要记录 IP + needRecordIp := false + if settingMap, err := GetUserSetting(userId, false); err == nil { + if v, ok := settingMap[constant.UserSettingRecordIpLog]; ok { + if vb, ok := v.(bool); ok && vb { + needRecordIp = true + } + } + } log := &Log{ UserId: userId, Username: username, @@ -111,6 +122,7 @@ func RecordErrorLog(c *gin.Context, userId int, channelId int, modelName string, UseTime: useTimeSeconds, IsStream: isStream, Group: group, + Ip: func() string { if needRecordIp { return c.ClientIP() }; return "" }(), Other: otherStr, } err := LOG_DB.Create(log).Error @@ -128,6 +140,15 @@ func RecordConsumeLog(c *gin.Context, userId int, channelId int, promptTokens in } username := c.GetString("username") otherStr := common.MapToJsonStr(other) + // 判断是否需要记录 IP + needRecordIp := false + if settingMap, err := GetUserSetting(userId, false); err == nil { + if v, ok := settingMap[constant.UserSettingRecordIpLog]; ok { + if vb, ok := v.(bool); ok && vb { + needRecordIp = true + } + } + } log := &Log{ UserId: userId, Username: username, @@ -144,6 +165,7 @@ func RecordConsumeLog(c *gin.Context, userId int, channelId int, promptTokens in UseTime: useTimeSeconds, IsStream: isStream, Group: group, + Ip: func() string { if needRecordIp { return c.ClientIP() }; return "" }(), Other: otherStr, } err := LOG_DB.Create(log).Error diff --git a/web/src/components/settings/PersonalSetting.js b/web/src/components/settings/PersonalSetting.js index 3228d184..95821332 100644 --- a/web/src/components/settings/PersonalSetting.js +++ b/web/src/components/settings/PersonalSetting.js @@ -103,6 +103,7 @@ const PersonalSetting = () => { webhookSecret: '', notificationEmail: '', acceptUnsetModelRatioModel: false, + recordIpLog: false, }); const [modelsLoading, setModelsLoading] = useState(true); const [showWebhookDocs, setShowWebhookDocs] = useState(true); @@ -147,6 +148,7 @@ const PersonalSetting = () => { notificationEmail: settings.notification_email || '', acceptUnsetModelRatioModel: settings.accept_unset_model_ratio_model || false, + recordIpLog: settings.record_ip_log || false, }); } }, [userState?.user?.setting]); @@ -346,7 +348,7 @@ const PersonalSetting = () => { const handleNotificationSettingChange = (type, value) => { setNotificationSettings((prev) => ({ ...prev, - [type]: value.target ? value.target.value : value, // 处理 Radio 事件对象 + [type]: value.target ? value.target.value !== undefined ? value.target.value : value.target.checked : value, // handle checkbox properly })); }; @@ -362,6 +364,7 @@ const PersonalSetting = () => { notification_email: notificationSettings.notificationEmail, accept_unset_model_ratio_model: notificationSettings.acceptUnsetModelRatioModel, + record_ip_log: notificationSettings.recordIpLog, }); if (res.data.success) { @@ -1063,7 +1066,7 @@ const PersonalSetting = () => { tab={