Prechádzať zdrojové kódy

feat: able to delete logs now (close #486)

JustSong 2 rokov pred
rodič
commit
328aa68255

+ 42 - 10
controller/log.go

@@ -2,6 +2,7 @@ package controller
 
 import (
 	"github.com/gin-gonic/gin"
+	"net/http"
 	"one-api/common"
 	"one-api/model"
 	"strconv"
@@ -20,17 +21,18 @@ func GetAllLogs(c *gin.Context) {
 	modelName := c.Query("model_name")
 	logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*common.ItemsPerPage, common.ItemsPerPage)
 	if err != nil {
-		c.JSON(200, gin.H{
+		c.JSON(http.StatusOK, gin.H{
 			"success": false,
 			"message": err.Error(),
 		})
 		return
 	}
-	c.JSON(200, gin.H{
+	c.JSON(http.StatusOK, gin.H{
 		"success": true,
 		"message": "",
 		"data":    logs,
 	})
+	return
 }
 
 func GetUserLogs(c *gin.Context) {
@@ -46,34 +48,36 @@ func GetUserLogs(c *gin.Context) {
 	modelName := c.Query("model_name")
 	logs, err := model.GetUserLogs(userId, logType, startTimestamp, endTimestamp, modelName, tokenName, p*common.ItemsPerPage, common.ItemsPerPage)
 	if err != nil {
-		c.JSON(200, gin.H{
+		c.JSON(http.StatusOK, gin.H{
 			"success": false,
 			"message": err.Error(),
 		})
 		return
 	}
-	c.JSON(200, gin.H{
+	c.JSON(http.StatusOK, gin.H{
 		"success": true,
 		"message": "",
 		"data":    logs,
 	})
+	return
 }
 
 func SearchAllLogs(c *gin.Context) {
 	keyword := c.Query("keyword")
 	logs, err := model.SearchAllLogs(keyword)
 	if err != nil {
-		c.JSON(200, gin.H{
+		c.JSON(http.StatusOK, gin.H{
 			"success": false,
 			"message": err.Error(),
 		})
 		return
 	}
-	c.JSON(200, gin.H{
+	c.JSON(http.StatusOK, gin.H{
 		"success": true,
 		"message": "",
 		"data":    logs,
 	})
+	return
 }
 
 func SearchUserLogs(c *gin.Context) {
@@ -81,17 +85,18 @@ func SearchUserLogs(c *gin.Context) {
 	userId := c.GetInt("id")
 	logs, err := model.SearchUserLogs(userId, keyword)
 	if err != nil {
-		c.JSON(200, gin.H{
+		c.JSON(http.StatusOK, gin.H{
 			"success": false,
 			"message": err.Error(),
 		})
 		return
 	}
-	c.JSON(200, gin.H{
+	c.JSON(http.StatusOK, gin.H{
 		"success": true,
 		"message": "",
 		"data":    logs,
 	})
+	return
 }
 
 func GetLogsStat(c *gin.Context) {
@@ -103,7 +108,7 @@ func GetLogsStat(c *gin.Context) {
 	modelName := c.Query("model_name")
 	quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName)
 	//tokenNum := model.SumUsedToken(logType, startTimestamp, endTimestamp, modelName, username, "")
-	c.JSON(200, gin.H{
+	c.JSON(http.StatusOK, gin.H{
 		"success": true,
 		"message": "",
 		"data": gin.H{
@@ -111,6 +116,7 @@ func GetLogsStat(c *gin.Context) {
 			//"token": tokenNum,
 		},
 	})
+	return
 }
 
 func GetLogsSelfStat(c *gin.Context) {
@@ -122,7 +128,7 @@ func GetLogsSelfStat(c *gin.Context) {
 	modelName := c.Query("model_name")
 	quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName)
 	//tokenNum := model.SumUsedToken(logType, startTimestamp, endTimestamp, modelName, username, tokenName)
-	c.JSON(200, gin.H{
+	c.JSON(http.StatusOK, gin.H{
 		"success": true,
 		"message": "",
 		"data": gin.H{
@@ -130,4 +136,30 @@ func GetLogsSelfStat(c *gin.Context) {
 			//"token": tokenNum,
 		},
 	})
+	return
+}
+
+func DeleteHistoryLogs(c *gin.Context) {
+	targetTimestamp, _ := strconv.ParseInt(c.Query("target_timestamp"), 10, 64)
+	if targetTimestamp == 0 {
+		c.JSON(http.StatusOK, gin.H{
+			"success": false,
+			"message": "target timestamp is required",
+		})
+		return
+	}
+	count, err := model.DeleteOldLog(targetTimestamp)
+	if err != nil {
+		c.JSON(http.StatusOK, gin.H{
+			"success": false,
+			"message": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"success": true,
+		"message": "",
+		"data":    count,
+	})
+	return
 }

+ 5 - 0
model/log.go

@@ -169,3 +169,8 @@ func SumUsedToken(logType int, startTimestamp int64, endTimestamp int64, modelNa
 	tx.Where("type = ?", LogTypeConsume).Scan(&token)
 	return token
 }
+
+func DeleteOldLog(targetTimestamp int64) (int64, error) {
+	result := DB.Where("created_at < ?", targetTimestamp).Delete(&Log{})
+	return result.RowsAffected, result.Error
+}

+ 1 - 0
router/api-router.go

@@ -98,6 +98,7 @@ func SetApiRouter(router *gin.Engine) {
 		}
 		logRoute := apiRouter.Group("/log")
 		logRoute.GET("/", middleware.AdminAuth(), controller.GetAllLogs)
+		logRoute.DELETE("/", middleware.AdminAuth(), controller.DeleteHistoryLogs)
 		logRoute.GET("/stat", middleware.AdminAuth(), controller.GetLogsStat)
 		logRoute.GET("/self/stat", middleware.UserAuth(), controller.GetLogsSelfStat)
 		logRoute.GET("/search", middleware.AdminAuth(), controller.SearchAllLogs)

+ 37 - 8
web/src/components/OperationSetting.js

@@ -1,8 +1,9 @@
 import React, { useEffect, useState } from 'react';
 import { Divider, Form, Grid, Header } from 'semantic-ui-react';
-import { API, showError, verifyJSON } from '../helpers';
+import { API, showError, showSuccess, timestamp2string, verifyJSON } from '../helpers';
 
 const OperationSetting = () => {
+  let now = new Date();
   let [inputs, setInputs] = useState({
     QuotaForNewUser: 0,
     QuotaForInviter: 0,
@@ -20,10 +21,11 @@ const OperationSetting = () => {
     DisplayInCurrencyEnabled: '',
     DisplayTokenStatEnabled: '',
     ApproximateTokenEnabled: '',
-    RetryTimes: 0,
+    RetryTimes: 0
   });
   const [originInputs, setOriginInputs] = useState({});
   let [loading, setLoading] = useState(false);
+  let [historyTimestamp, setHistoryTimestamp] = useState(timestamp2string(now.getTime() / 1000 - 30 * 24 * 3600)); // a month ago
 
   const getOptions = async () => {
     const res = await API.get('/api/option/');
@@ -130,6 +132,17 @@ const OperationSetting = () => {
     }
   };
 
+  const deleteHistoryLogs = async () => {
+    console.log(inputs);
+    const res = await API.delete(`/api/log/?target_timestamp=${Date.parse(historyTimestamp) / 1000}`);
+    const { success, message, data } = res.data;
+    if (success) {
+      showSuccess(`${data} 条日志已清理!`);
+      return;
+    }
+    showError('日志清理失败:' + message);
+  };
+
   return (
     <Grid columns={1}>
       <Grid.Column>
@@ -179,12 +192,6 @@ const OperationSetting = () => {
             />
           </Form.Group>
           <Form.Group inline>
-            <Form.Checkbox
-              checked={inputs.LogConsumeEnabled === 'true'}
-              label='启用额度消费日志记录'
-              name='LogConsumeEnabled'
-              onChange={handleInputChange}
-            />
             <Form.Checkbox
               checked={inputs.DisplayInCurrencyEnabled === 'true'}
               label='以货币形式显示额度'
@@ -208,6 +215,28 @@ const OperationSetting = () => {
             submitConfig('general').then();
           }}>保存通用设置</Form.Button>
           <Divider />
+          <Header as='h3'>
+            日志设置
+          </Header>
+          <Form.Group inline>
+            <Form.Checkbox
+              checked={inputs.LogConsumeEnabled === 'true'}
+              label='启用额度消费日志记录'
+              name='LogConsumeEnabled'
+              onChange={handleInputChange}
+            />
+          </Form.Group>
+          <Form.Group widths={4}>
+            <Form.Input label='目标时间' value={historyTimestamp} type='datetime-local'
+                        name='history_timestamp'
+                        onChange={(e, { name, value }) => {
+                          setHistoryTimestamp(value);
+                        }} />
+          </Form.Group>
+          <Form.Button onClick={() => {
+            deleteHistoryLogs().then();
+          }}>清理历史日志</Form.Button>
+          <Divider />
           <Header as='h3'>
             监控设置
           </Header>