Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions controller/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@ import (
// action 的 params 填充。本地化展示文案在前端 i18n 模板中维护,本表是语言中立的
// 英文基线——调用方因此无需在每个埋点处手写句子(避免与 params 重复书写同一份值)。
var auditContentTemplates = map[string]string{
"user.create": "Created user ${username} (role ${role})",
"user.update": "Updated user ${username} (ID: ${id})",
"user.delete": "Deleted user ${username} (ID: ${id})",
"user.manage": "Performed ${action} on user ${username} (ID: ${id})",
"user.quota_add": "Increased user quota by ${quota}",
"user.quota_subtract": "Decreased user quota by ${quota}",
"user.quota_override": "Overrode user quota from ${from} to ${to}",
"user.binding_clear": "Cleared ${bindingType} binding for user ${username}",
"user.2fa_disable": "Force-disabled two-factor authentication for the user",
"user.passkey_register": "Registered a passkey",
"user.passkey_delete": "Deleted a passkey",
"user.reset_passkey": "Reset the user passkey",
"option.update": "Updated system setting ${key}",
"user.create": "Created user ${username} (role ${role})",
"user.update": "Updated user ${username} (ID: ${id})",
"user.delete": "Deleted user ${username} (ID: ${id})",
"user.manage": "Performed ${action} on user ${username} (ID: ${id})",
"user.quota_add": "Increased user quota by ${quota}",
"user.quota_subtract": "Decreased user quota by ${quota}",
"user.quota_override": "Overrode user quota from ${from} to ${to}",
"user.binding_clear": "Cleared ${bindingType} binding for user ${username}",
"user.2fa_disable": "Force-disabled two-factor authentication for the user",
"user.passkey_register": "Registered a passkey",
"user.passkey_delete": "Deleted a passkey",
"user.reset_passkey": "Reset the user passkey",
"option.update": "Updated system setting ${key}",
"savings.official_price_update": "Updated savings official price setting",

"channel.create": "Created channel ${name} (type ${type}, count ${count})",
"channel.update": "Updated channel ${name} (ID: ${id})",
Expand Down
16 changes: 15 additions & 1 deletion controller/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/QuantumNous/new-api/setting/ratio_setting"
"github.com/QuantumNous/new-api/setting/savings_setting"
"github.com/QuantumNous/new-api/setting/system_setting"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -299,6 +300,15 @@ func UpdateOption(c *gin.Context) {
})
return
}
case savings_setting.OptionKey:
err = savings_setting.ValidateSettingJSONString(option.Value.(string))
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "节省估算设置失败: " + err.Error(),
})
return
}
case "ModelRequestRateLimitGroup":
err = setting.CheckModelRequestRateLimitGroup(option.Value.(string))
if err != nil {
Expand Down Expand Up @@ -369,7 +379,11 @@ func UpdateOption(c *gin.Context) {
return
}
// 出于安全考虑只记录被修改的配置项名称,不记录配置值(可能含密钥等敏感信息)。
recordManageAudit(c, "option.update", map[string]interface{}{
auditAction := "option.update"
if option.Key == savings_setting.OptionKey {
auditAction = "savings.official_price_update"
}
recordManageAudit(c, auditAction, map[string]interface{}{
"key": option.Key,
})
c.JSON(http.StatusOK, gin.H{
Expand Down
84 changes: 84 additions & 0 deletions controller/savings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package controller

import (
"net/http"
"strconv"

"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/service"

"github.com/gin-gonic/gin"
)

func GetUserSavingsSummary(c *gin.Context) {
startTimestamp, endTimestamp, err := parseSavingsTimeRange(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "必须传入开始和结束时间",
})
return
}
effectiveEndTimestamp, err := service.NormalizeSavingsSummaryWindow(startTimestamp, endTimestamp)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": err.Error(),
})
return
}

summary, err := service.GetUserSavingsSummary(c.GetInt("id"), startTimestamp, effectiveEndTimestamp)
if err != nil {
common.ApiError(c, err)
return
}
common.ApiSuccess(c, summary)
}

func GetUserSavingsTrend(c *gin.Context) {
startTimestamp, endTimestamp, err := parseSavingsTimeRange(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "必须传入开始和结束时间",
})
return
}
granularity := c.Query("granularity")
utcOffsetMinutes, err := strconv.Atoi(c.Query("utc_offset_minutes"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "必须传入时区偏移",
})
return
}
effectiveEndTimestamp, err := service.NormalizeSavingsTrendWindow(startTimestamp, endTimestamp, granularity, utcOffsetMinutes)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": err.Error(),
})
return
}

trend, err := service.GetUserSavingsTrend(c.GetInt("id"), startTimestamp, effectiveEndTimestamp, granularity, utcOffsetMinutes)
if err != nil {
common.ApiError(c, err)
return
}
common.ApiSuccess(c, trend)
}

func parseSavingsTimeRange(c *gin.Context) (int64, int64, error) {
startTimestamp, err := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
if err != nil {
return 0, 0, err
}
endTimestamp, err := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
if err != nil {
return 0, 0, err
}
return startTimestamp, endTimestamp, nil
}
Loading