feat: save and query request client ip for relay router; fix air theme bugs at log page;
This commit is contained in:
parent
0eb2272bb7
commit
cab01a8ebb
@ -20,7 +20,8 @@ func GetAllLogs(c *gin.Context) {
|
|||||||
tokenName := c.Query("token_name")
|
tokenName := c.Query("token_name")
|
||||||
modelName := c.Query("model_name")
|
modelName := c.Query("model_name")
|
||||||
channel, _ := strconv.Atoi(c.Query("channel"))
|
channel, _ := strconv.Atoi(c.Query("channel"))
|
||||||
logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*config.ItemsPerPage, config.ItemsPerPage, channel)
|
clientIP := c.Query("client_ip")
|
||||||
|
logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*config.ItemsPerPage, config.ItemsPerPage, channel, clientIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
|
11
model/log.go
11
model/log.go
@ -3,6 +3,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
@ -24,6 +25,7 @@ type Log struct {
|
|||||||
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
|
PromptTokens int `json:"prompt_tokens" gorm:"default:0"`
|
||||||
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
|
CompletionTokens int `json:"completion_tokens" gorm:"default:0"`
|
||||||
ChannelId int `json:"channel" gorm:"index"`
|
ChannelId int `json:"channel" gorm:"index"`
|
||||||
|
ClientIP string `json:"client_ip" gorm:"index;default:''"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -51,7 +53,7 @@ func RecordLog(userId int, logType int, content string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int64, content string) {
|
func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int64, content string, clientIP string) {
|
||||||
logger.Info(ctx, fmt.Sprintf("record consume log: userId=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content))
|
logger.Info(ctx, fmt.Sprintf("record consume log: userId=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content))
|
||||||
if !config.LogConsumeEnabled {
|
if !config.LogConsumeEnabled {
|
||||||
return
|
return
|
||||||
@ -68,6 +70,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
|
|||||||
ModelName: modelName,
|
ModelName: modelName,
|
||||||
Quota: int(quota),
|
Quota: int(quota),
|
||||||
ChannelId: channelId,
|
ChannelId: channelId,
|
||||||
|
ClientIP: clientIP,
|
||||||
}
|
}
|
||||||
err := LOG_DB.Create(log).Error
|
err := LOG_DB.Create(log).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -75,7 +78,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int) (logs []*Log, err error) {
|
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int, clientIP string) (logs []*Log, err error) {
|
||||||
var tx *gorm.DB
|
var tx *gorm.DB
|
||||||
if logType == LogTypeUnknown {
|
if logType == LogTypeUnknown {
|
||||||
tx = LOG_DB
|
tx = LOG_DB
|
||||||
@ -100,6 +103,10 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName
|
|||||||
if channel != 0 {
|
if channel != 0 {
|
||||||
tx = tx.Where("channel_id = ?", channel)
|
tx = tx.Where("channel_id = ?", channel)
|
||||||
}
|
}
|
||||||
|
if clientIP != "" {
|
||||||
|
tx = tx.Where("client_ip = ?", clientIP)
|
||||||
|
}
|
||||||
|
|
||||||
err = tx.Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error
|
err = tx.Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error
|
||||||
return logs, err
|
return logs, err
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
@ -16,9 +20,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
"github.com/songquanpeng/one-api/relay/util"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatusCode {
|
func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatusCode {
|
||||||
@ -99,6 +100,9 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
|
|
||||||
baseURL := common.ChannelBaseURLs[channelType]
|
baseURL := common.ChannelBaseURLs[channelType]
|
||||||
requestURL := c.Request.URL.String()
|
requestURL := c.Request.URL.String()
|
||||||
|
clientIP := c.ClientIP()
|
||||||
|
fmt.Println(clientIP)
|
||||||
|
|
||||||
if c.GetString("base_url") != "" {
|
if c.GetString("base_url") != "" {
|
||||||
baseURL = c.GetString("base_url")
|
baseURL = c.GetString("base_url")
|
||||||
}
|
}
|
||||||
@ -204,7 +208,7 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
}
|
}
|
||||||
quotaDelta := quota - preConsumedQuota
|
quotaDelta := quota - preConsumedQuota
|
||||||
defer func(ctx context.Context) {
|
defer func(ctx context.Context) {
|
||||||
go util.PostConsumeQuota(ctx, tokenId, quotaDelta, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName)
|
go util.PostConsumeQuota(ctx, tokenId, quotaDelta, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName, clientIP)
|
||||||
}(c.Request.Context())
|
}(c.Request.Context())
|
||||||
|
|
||||||
for k, v := range resp.Header {
|
for k, v := range resp.Header {
|
||||||
|
@ -4,6 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
@ -13,8 +16,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
"github.com/songquanpeng/one-api/relay/util"
|
||||||
"math"
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getAndValidateTextRequest(c *gin.Context, relayMode int) (*relaymodel.GeneralOpenAIRequest, error) {
|
func getAndValidateTextRequest(c *gin.Context, relayMode int) (*relaymodel.GeneralOpenAIRequest, error) {
|
||||||
@ -144,7 +145,7 @@ func preConsumeQuota(ctx context.Context, textRequest *relaymodel.GeneralOpenAIR
|
|||||||
return preConsumedQuota, nil
|
return preConsumedQuota, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *util.RelayMeta, textRequest *relaymodel.GeneralOpenAIRequest, ratio float64, preConsumedQuota int64, modelRatio float64, groupRatio float64) {
|
func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *util.RelayMeta, textRequest *relaymodel.GeneralOpenAIRequest, ratio float64, preConsumedQuota int64, modelRatio float64, groupRatio float64, clientIP string) {
|
||||||
if usage == nil {
|
if usage == nil {
|
||||||
logger.Error(ctx, "usage is nil, which is unexpected")
|
logger.Error(ctx, "usage is nil, which is unexpected")
|
||||||
return
|
return
|
||||||
@ -173,7 +174,7 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *util.R
|
|||||||
logger.Error(ctx, "error update user quota cache: "+err.Error())
|
logger.Error(ctx, "error update user quota cache: "+err.Error())
|
||||||
}
|
}
|
||||||
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f", modelRatio, groupRatio, completionRatio)
|
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f", modelRatio, groupRatio, completionRatio)
|
||||||
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent)
|
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent, clientIP)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
||||||
model.UpdateChannelUsedQuota(meta.ChannelId, quota)
|
model.UpdateChannelUsedQuota(meta.ChannelId, quota)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
@ -13,9 +17,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
"github.com/songquanpeng/one-api/relay/util"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -132,7 +133,7 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
if quota != 0 {
|
if quota != 0 {
|
||||||
tokenName := c.GetString("token_name")
|
tokenName := c.GetString("token_name")
|
||||||
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
|
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
|
||||||
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, 0, 0, imageRequest.Model, tokenName, quota, logContent)
|
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, 0, 0, imageRequest.Model, tokenName, quota, logContent, c.ClientIP())
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
model.UpdateChannelUsedQuota(channelId, quota)
|
model.UpdateChannelUsedQuota(channelId, quota)
|
||||||
|
@ -4,6 +4,10 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
@ -12,9 +16,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/helper"
|
"github.com/songquanpeng/one-api/relay/helper"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
"github.com/songquanpeng/one-api/relay/util"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
||||||
@ -99,6 +100,6 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
return respErr
|
return respErr
|
||||||
}
|
}
|
||||||
// post-consume quota
|
// post-consume quota
|
||||||
go postConsumeQuota(ctx, usage, meta, textRequest, ratio, preConsumedQuota, modelRatio, groupRatio)
|
go postConsumeQuota(ctx, usage, meta, textRequest, ratio, preConsumedQuota, modelRatio, groupRatio, c.ClientIP())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -155,7 +156,7 @@ func GetFullRequestURL(baseURL string, requestURL string, channelType int) strin
|
|||||||
return fullRequestURL
|
return fullRequestURL
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int64, totalQuota int64, userId int, channelId int, modelRatio float64, groupRatio float64, modelName string, tokenName string) {
|
func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int64, totalQuota int64, userId int, channelId int, modelRatio float64, groupRatio float64, modelName string, tokenName string, clientIP string) {
|
||||||
// quotaDelta is remaining quota to be consumed
|
// quotaDelta is remaining quota to be consumed
|
||||||
err := model.PostConsumeTokenQuota(tokenId, quotaDelta)
|
err := model.PostConsumeTokenQuota(tokenId, quotaDelta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -168,7 +169,7 @@ func PostConsumeQuota(ctx context.Context, tokenId int, quotaDelta int64, totalQ
|
|||||||
// totalQuota is total quota consumed
|
// totalQuota is total quota consumed
|
||||||
if totalQuota != 0 {
|
if totalQuota != 0 {
|
||||||
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
|
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
|
||||||
model.RecordConsumeLog(ctx, userId, channelId, int(totalQuota), 0, modelName, tokenName, totalQuota, logContent)
|
model.RecordConsumeLog(ctx, userId, channelId, int(totalQuota), 0, modelName, tokenName, totalQuota, logContent, clientIP)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, totalQuota)
|
||||||
model.UpdateChannelUsedQuota(channelId, totalQuota)
|
model.UpdateChannelUsedQuota(channelId, totalQuota)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { API, copy, isAdmin, showError, showSuccess, timestamp2string } from '../helpers';
|
import { API, copy, isAdmin, showError, showSuccess, timestamp2string, safeGetLocalTimestamp } from '../helpers';
|
||||||
|
|
||||||
import { Avatar, Button, Form, Layout, Modal, Select, Space, Spin, Table, Tag } from '@douyinfe/semi-ui';
|
import { Avatar, Button, Form, Layout, Modal, Select, Space, Spin, Table, Tag } from '@douyinfe/semi-ui';
|
||||||
import { ITEMS_PER_PAGE } from '../constants';
|
import { ITEMS_PER_PAGE } from '../constants';
|
||||||
@ -128,6 +128,10 @@ const LogsTable = () => {
|
|||||||
{renderQuota(text, 6)}
|
{renderQuota(text, 6)}
|
||||||
</div> : <></>);
|
</div> : <></>);
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
title: '请求端IP', dataIndex: 'client_ip', render: (text, record, index) => {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
title: '详情', dataIndex: 'content', render: (text, record, index) => {
|
title: '详情', dataIndex: 'content', render: (text, record, index) => {
|
||||||
return <Paragraph ellipsis={{ rows: 2, showTooltip: { type: 'popover', opts: { style: { width: 240 } } } }}
|
return <Paragraph ellipsis={{ rows: 2, showTooltip: { type: 'popover', opts: { style: { width: 240 } } } }}
|
||||||
@ -156,9 +160,10 @@ const LogsTable = () => {
|
|||||||
model_name: '',
|
model_name: '',
|
||||||
start_timestamp: timestamp2string(now.getTime() / 1000 - 86400),
|
start_timestamp: timestamp2string(now.getTime() / 1000 - 86400),
|
||||||
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
|
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
|
||||||
channel: ''
|
channel: '',
|
||||||
|
client_ip: ''
|
||||||
});
|
});
|
||||||
const { username, token_name, model_name, start_timestamp, end_timestamp, channel } = inputs;
|
const { username, token_name, model_name, start_timestamp, end_timestamp, channel, client_ip } = inputs;
|
||||||
|
|
||||||
const [stat, setStat] = useState({
|
const [stat, setStat] = useState({
|
||||||
quota: 0, token: 0
|
quota: 0, token: 0
|
||||||
@ -169,9 +174,9 @@ const LogsTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getLogSelfStat = async () => {
|
const getLogSelfStat = async () => {
|
||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = safeGetLocalTimestamp(start_timestamp);
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = safeGetLocalTimestamp(end_timestamp);
|
||||||
let res = await API.get(`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`);
|
let res = await API.get(`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&client_ip=${client_ip}`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setStat(data);
|
setStat(data);
|
||||||
@ -181,9 +186,9 @@ const LogsTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getLogStat = async () => {
|
const getLogStat = async () => {
|
||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = safeGetLocalTimestamp(start_timestamp);
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = safeGetLocalTimestamp(end_timestamp);
|
||||||
let res = await API.get(`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`);
|
let res = await API.get(`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}&client_ip=${client_ip}`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setStat(data);
|
setStat(data);
|
||||||
@ -238,12 +243,12 @@ const LogsTable = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
let url = '';
|
let url = '';
|
||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = safeGetLocalTimestamp(start_timestamp);
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = safeGetLocalTimestamp(end_timestamp);
|
||||||
if (isAdminUser) {
|
if (isAdminUser) {
|
||||||
url = `/api/log/?p=${startIdx}&page_size=${pageSize}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`;
|
url = `/api/log/?p=${startIdx}&page_size=${pageSize}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}&client_ip=${client_ip}`;
|
||||||
} else {
|
} else {
|
||||||
url = `/api/log/self/?p=${startIdx}&page_size=${pageSize}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
url = `/api/log/self/?p=${startIdx}&page_size=${pageSize}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&client_ip=${client_ip}`;
|
||||||
}
|
}
|
||||||
const res = await API.get(url);
|
const res = await API.get(url);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
@ -349,6 +354,10 @@ const LogsTable = () => {
|
|||||||
placeholder="可选值"
|
placeholder="可选值"
|
||||||
name="model_name"
|
name="model_name"
|
||||||
onChange={value => handleInputChange(value, 'model_name')} />
|
onChange={value => handleInputChange(value, 'model_name')} />
|
||||||
|
<Form.Input field="model_name" label="请求端IP" style={{ width: 176 }} value={client_ip}
|
||||||
|
placeholder="可选值"
|
||||||
|
name="client_ip"
|
||||||
|
onChange={value => handleInputChange(value, 'client_ip')} />
|
||||||
<Form.DatePicker field="start_timestamp" label="起始时间" style={{ width: 272 }}
|
<Form.DatePicker field="start_timestamp" label="起始时间" style={{ width: 272 }}
|
||||||
initValue={start_timestamp}
|
initValue={start_timestamp}
|
||||||
value={start_timestamp} type="dateTime"
|
value={start_timestamp} type="dateTime"
|
||||||
@ -369,7 +378,7 @@ const LogsTable = () => {
|
|||||||
</>}
|
</>}
|
||||||
<Form.Section>
|
<Form.Section>
|
||||||
<Button label="查询" type="primary" htmlType="submit" className="btn-margin-right"
|
<Button label="查询" type="primary" htmlType="submit" className="btn-margin-right"
|
||||||
onClick={refresh} loading={loading}>查询</Button>
|
onClick={()=>refresh(logType)} loading={loading}>查询</Button>
|
||||||
</Form.Section>
|
</Form.Section>
|
||||||
</>
|
</>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -230,4 +230,8 @@ export function shouldShowPrompt(id) {
|
|||||||
|
|
||||||
export function setPromptShown(id) {
|
export function setPromptShown(id) {
|
||||||
localStorage.setItem(`prompt-${id}`, 'true');
|
localStorage.setItem(`prompt-${id}`, 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function safeGetLocalTimestamp(timeString) {
|
||||||
|
return timeString ? Date.parse(timeString) / 1000 : ''
|
||||||
}
|
}
|
@ -14,6 +14,7 @@ const LogTableHead = ({ userIsAdmin }) => {
|
|||||||
<TableCell>提示</TableCell>
|
<TableCell>提示</TableCell>
|
||||||
<TableCell>补全</TableCell>
|
<TableCell>补全</TableCell>
|
||||||
<TableCell>额度</TableCell>
|
<TableCell>额度</TableCell>
|
||||||
|
<TableCell>请求端IP</TableCell>
|
||||||
<TableCell>详情</TableCell>
|
<TableCell>详情</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
|
@ -57,6 +57,7 @@ export default function LogTableRow({ item, userIsAdmin }) {
|
|||||||
<TableCell>{item.prompt_tokens || ''}</TableCell>
|
<TableCell>{item.prompt_tokens || ''}</TableCell>
|
||||||
<TableCell>{item.completion_tokens || ''}</TableCell>
|
<TableCell>{item.completion_tokens || ''}</TableCell>
|
||||||
<TableCell>{item.quota ? renderQuota(item.quota, 6) : ''}</TableCell>
|
<TableCell>{item.quota ? renderQuota(item.quota, 6) : ''}</TableCell>
|
||||||
|
<TableCell>{item.client_ip || ''}</TableCell>
|
||||||
<TableCell>{item.content}</TableCell>
|
<TableCell>{item.content}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</>
|
</>
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
IconKey,
|
IconKey,
|
||||||
IconBrandGithubCopilot,
|
IconBrandGithubCopilot,
|
||||||
IconSitemap,
|
IconSitemap,
|
||||||
|
IconMapPin
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import {
|
import {
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
@ -81,6 +82,30 @@ export default function TableToolBar({
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl>
|
||||||
|
<InputLabel htmlFor="channel-client_ip-label">请求端IP</InputLabel>
|
||||||
|
<OutlinedInput
|
||||||
|
id="client_ip"
|
||||||
|
name="client_ip"
|
||||||
|
sx={{
|
||||||
|
minWidth: "100%",
|
||||||
|
}}
|
||||||
|
label="请求端IP"
|
||||||
|
value={filterName.client_ip}
|
||||||
|
onChange={handleFilterName}
|
||||||
|
placeholder="请求端IP"
|
||||||
|
startAdornment={
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<IconMapPin
|
||||||
|
stroke={1.5}
|
||||||
|
size="20px"
|
||||||
|
color={grey500}
|
||||||
|
/>
|
||||||
|
</InputAdornment>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<LocalizationProvider
|
<LocalizationProvider
|
||||||
dateAdapter={AdapterDayjs}
|
dateAdapter={AdapterDayjs}
|
||||||
|
@ -28,7 +28,8 @@ export default function Log() {
|
|||||||
start_timestamp: 0,
|
start_timestamp: 0,
|
||||||
end_timestamp: new Date().getTime() / 1000 + 3600,
|
end_timestamp: new Date().getTime() / 1000 + 3600,
|
||||||
type: 0,
|
type: 0,
|
||||||
channel: ''
|
channel: '',
|
||||||
|
client_ip: ''
|
||||||
};
|
};
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState([]);
|
||||||
const [activePage, setActivePage] = useState(0);
|
const [activePage, setActivePage] = useState(0);
|
||||||
|
@ -57,9 +57,10 @@ const LogsTable = () => {
|
|||||||
model_name: '',
|
model_name: '',
|
||||||
start_timestamp: timestamp2string(0),
|
start_timestamp: timestamp2string(0),
|
||||||
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
|
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
|
||||||
channel: ''
|
channel: '',
|
||||||
|
client_ip: ''
|
||||||
});
|
});
|
||||||
const { username, token_name, model_name, start_timestamp, end_timestamp, channel } = inputs;
|
const { username, token_name, model_name, start_timestamp, end_timestamp, channel, client_ip } = inputs;
|
||||||
|
|
||||||
const [stat, setStat] = useState({
|
const [stat, setStat] = useState({
|
||||||
quota: 0,
|
quota: 0,
|
||||||
@ -73,7 +74,9 @@ const LogsTable = () => {
|
|||||||
const getLogSelfStat = async () => {
|
const getLogSelfStat = async () => {
|
||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
||||||
let res = await API.get(`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`);
|
let res = await API.get(
|
||||||
|
`/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`
|
||||||
|
);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setStat(data);
|
setStat(data);
|
||||||
@ -85,7 +88,9 @@ const LogsTable = () => {
|
|||||||
const getLogStat = async () => {
|
const getLogStat = async () => {
|
||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
||||||
let res = await API.get(`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`);
|
let res = await API.get(
|
||||||
|
`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`
|
||||||
|
);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setStat(data);
|
setStat(data);
|
||||||
@ -110,9 +115,9 @@ const LogsTable = () => {
|
|||||||
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
let localStartTimestamp = Date.parse(start_timestamp) / 1000;
|
||||||
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
let localEndTimestamp = Date.parse(end_timestamp) / 1000;
|
||||||
if (isAdminUser) {
|
if (isAdminUser) {
|
||||||
url = `/api/log/?p=${startIdx}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`;
|
url = `/api/log/?p=${startIdx}&type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`;
|
||||||
} else {
|
} else {
|
||||||
url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${token_name}&model_name=${model_name}&client_ip=${client_ip}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
||||||
}
|
}
|
||||||
const res = await API.get(url);
|
const res = await API.get(url);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
@ -211,6 +216,9 @@ const LogsTable = () => {
|
|||||||
<Form.Input fluid label='模型名称' width={3} value={model_name} placeholder='可选值'
|
<Form.Input fluid label='模型名称' width={3} value={model_name} placeholder='可选值'
|
||||||
name='model_name'
|
name='model_name'
|
||||||
onChange={handleInputChange} />
|
onChange={handleInputChange} />
|
||||||
|
<Form.Input fluid label='请求端IP' width={3} value={client_ip} placeholder='可选值'
|
||||||
|
name='client_ip'
|
||||||
|
onChange={handleInputChange} />
|
||||||
<Form.Input fluid label='起始时间' width={4} value={start_timestamp} type='datetime-local'
|
<Form.Input fluid label='起始时间' width={4} value={start_timestamp} type='datetime-local'
|
||||||
name='start_timestamp'
|
name='start_timestamp'
|
||||||
onChange={handleInputChange} />
|
onChange={handleInputChange} />
|
||||||
@ -321,6 +329,15 @@ const LogsTable = () => {
|
|||||||
>
|
>
|
||||||
额度
|
额度
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
sortLog('client_ip');
|
||||||
|
}}
|
||||||
|
width={1}
|
||||||
|
>
|
||||||
|
请求端IP
|
||||||
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
Loading…
Reference in New Issue
Block a user