From 57d0fc3021c3dd8ccb7dc5c8574dff0dc748be81 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Sun, 19 Nov 2023 18:59:35 +0800
Subject: [PATCH 1/7] support base64 image
---
common/image.go | 64 +++++++++++++++++++++++++++++++++
controller/midjourney.go | 18 +++++-----
controller/relay-utils.go | 29 ++++++---------
web/src/components/LogsTable.js | 11 +++---
4 files changed, 90 insertions(+), 32 deletions(-)
create mode 100644 common/image.go
diff --git a/common/image.go b/common/image.go
new file mode 100644
index 0000000..d80cbb7
--- /dev/null
+++ b/common/image.go
@@ -0,0 +1,64 @@
+package common
+
+import (
+ "bytes"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "github.com/chai2010/webp"
+ "image"
+ "io"
+ "net/http"
+ "strings"
+)
+
+func DecodeBase64ImageData(base64String string) (image.Config, error) {
+ // 去除base64数据的URL前缀(如果有)
+ if idx := strings.Index(base64String, ","); idx != -1 {
+ base64String = base64String[idx+1:]
+ }
+
+ // 将base64字符串解码为字节切片
+ decodedData, err := base64.StdEncoding.DecodeString(base64String)
+ if err != nil {
+ fmt.Println("Error: Failed to decode base64 string")
+ return image.Config{}, err
+ }
+
+ // 创建一个bytes.Buffer用于存储解码后的数据
+ reader := bytes.NewReader(decodedData)
+ config, err := getImageConfig(reader)
+ return config, err
+}
+
+func DecodeUrlImageData(imageUrl string) (image.Config, error) {
+ response, err := http.Get(imageUrl)
+ if err != nil {
+ SysLog(fmt.Sprintf("fail to get image from url: %s", err.Error()))
+ return image.Config{}, err
+ }
+
+ // 限制读取的字节数,防止下载整个图片
+ limitReader := io.LimitReader(response.Body, 8192)
+ config, err := getImageConfig(limitReader)
+ response.Body.Close()
+ return config, err
+}
+
+func getImageConfig(reader io.Reader) (image.Config, error) {
+ // 读取图片的头部信息来获取图片尺寸
+ config, _, err := image.DecodeConfig(reader)
+ if err != nil {
+ err = errors.New(fmt.Sprintf("fail to decode image config(gif, jpg, png): %s", err.Error()))
+ SysLog(err.Error())
+ config, err = webp.DecodeConfig(reader)
+ if err != nil {
+ err = errors.New(fmt.Sprintf("fail to decode image config(webp): %s", err.Error()))
+ SysLog(err.Error())
+ }
+ }
+ if err != nil {
+ return image.Config{}, err
+ }
+ return config, nil
+}
diff --git a/controller/midjourney.go b/controller/midjourney.go
index d9f4f89..a5ef8e4 100644
--- a/controller/midjourney.go
+++ b/controller/midjourney.go
@@ -19,12 +19,12 @@ import (
func UpdateMidjourneyTask() {
//revocer
imageModel := "midjourney"
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("UpdateMidjourneyTask panic: %v", err)
+ }
+ }()
for {
- defer func() {
- if err := recover(); err != nil {
- log.Printf("UpdateMidjourneyTask panic: %v", err)
- }
- }()
time.Sleep(time.Duration(15) * time.Second)
tasks := model.GetAllUnFinishTasks()
if len(tasks) != 0 {
@@ -55,7 +55,6 @@ func UpdateMidjourneyTask() {
// 设置超时时间
timeout := time.Second * 5
ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
// 使用带有超时的 context 创建新的请求
req = req.WithContext(ctx)
@@ -68,8 +67,8 @@ func UpdateMidjourneyTask() {
log.Printf("UpdateMidjourneyTask error: %v", err)
continue
}
- defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
+ resp.Body.Close()
log.Printf("responseBody: %s", string(responseBody))
var responseItem Midjourney
// err = json.NewDecoder(resp.Body).Decode(&responseItem)
@@ -83,12 +82,12 @@ func UpdateMidjourneyTask() {
if err1 == nil && err2 == nil {
jsonData, err3 := json.Marshal(responseWithoutStatus)
if err3 != nil {
- log.Fatalf("UpdateMidjourneyTask error1: %v", err3)
+ log.Printf("UpdateMidjourneyTask error1: %v", err3)
continue
}
err4 := json.Unmarshal(jsonData, &responseStatus)
if err4 != nil {
- log.Fatalf("UpdateMidjourneyTask error2: %v", err4)
+ log.Printf("UpdateMidjourneyTask error2: %v", err4)
continue
}
responseItem.Status = strconv.Itoa(responseStatus.Status)
@@ -138,6 +137,7 @@ func UpdateMidjourneyTask() {
log.Printf("UpdateMidjourneyTask error5: %v", err)
}
log.Printf("UpdateMidjourneyTask success: %v", task)
+ cancel()
}
}
}
diff --git a/controller/relay-utils.go b/controller/relay-utils.go
index 6b1322b..ce9a3bf 100644
--- a/controller/relay-utils.go
+++ b/controller/relay-utils.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "github.com/chai2010/webp"
"github.com/gin-gonic/gin"
"github.com/pkoukk/tiktoken-go"
"image"
@@ -75,29 +74,21 @@ func getImageToken(imageUrl MessageImageUrl) (int, error) {
if imageUrl.Detail == "low" {
return 85, nil
}
-
- response, err := http.Get(imageUrl.Url)
+ var config image.Config
+ var err error
+ if strings.HasPrefix(imageUrl.Url, "http") {
+ common.SysLog(fmt.Sprintf("downloading image: %s", imageUrl.Url))
+ config, err = common.DecodeUrlImageData(imageUrl.Url)
+ } else {
+ common.SysLog(fmt.Sprintf("decoding image"))
+ config, err = common.DecodeBase64ImageData(imageUrl.Url)
+ }
if err != nil {
- fmt.Println("Error: Failed to get the URL")
return 0, err
}
- // 限制读取的字节数,防止下载整个图片
- limitReader := io.LimitReader(response.Body, 8192)
-
- response.Body.Close()
-
- // 读取图片的头部信息来获取图片尺寸
- config, _, err := image.DecodeConfig(limitReader)
- if err != nil {
- common.SysLog(fmt.Sprintf("fail to decode image config(gif, jpg, png): %s", err.Error()))
- config, err = webp.DecodeConfig(limitReader)
- if err != nil {
- common.SysLog(fmt.Sprintf("fail to decode image config(webp): %s", err.Error()))
- }
- }
if config.Width == 0 || config.Height == 0 {
- return 0, errors.New(fmt.Sprintf("fail to decode image config: %s", err.Error()))
+ return 0, errors.New(fmt.Sprintf("fail to decode image config: %s", imageUrl.Url))
}
if config.Width < 512 && config.Height < 512 {
if imageUrl.Detail == "auto" || imageUrl.Detail == "" {
diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js
index e2aa8aa..8556493 100644
--- a/web/src/components/LogsTable.js
+++ b/web/src/components/LogsTable.js
@@ -106,7 +106,7 @@ const LogsTable = () => {
return (
record.type === 0 || record.type === 2 ?
- {
+ {
copyText(text)
}}> {text}
@@ -133,7 +133,7 @@ const LogsTable = () => {
return (
record.type === 0 || record.type === 2 ?
- {
+ {
copyText(text)
}}> {text}
@@ -202,11 +202,12 @@ const LogsTable = () => {
const [logType, setLogType] = useState(0);
const isAdminUser = isAdmin();
let now = new Date();
+ // 初始化start_timestamp为前一天
const [inputs, setInputs] = useState({
username: '',
token_name: '',
model_name: '',
- start_timestamp: timestamp2string(0),
+ start_timestamp: timestamp2string(now.getTime() / 1000 - 86400),
end_timestamp: timestamp2string(now.getTime() / 1000 + 3600),
channel: ''
});
@@ -338,7 +339,7 @@ const LogsTable = () => {
showSuccess('已复制:' + text);
} else {
// setSearchKeyword(text);
- Modal.error({ title: '无法复制到剪贴板,请手动复制', content: text });
+ Modal.error({title: '无法复制到剪贴板,请手动复制', content: text});
}
}
@@ -412,10 +413,12 @@ const LogsTable = () => {
name='model_name'
onChange={value => handleInputChange(value, 'model_name')}/>
handleInputChange(value, 'start_timestamp')}/>
handleInputChange(value, 'end_timestamp')}/>
From fa45f3ba7b704b3a3034020b5b70d403b32d5f03 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Tue, 21 Nov 2023 14:34:48 +0800
Subject: [PATCH 2/7] fix email bug
---
controller/relay-image.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/controller/relay-image.go b/controller/relay-image.go
index 8c16ec1..a1abd03 100644
--- a/controller/relay-image.go
+++ b/controller/relay-image.go
@@ -147,7 +147,7 @@ func relayImageHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode
var textResponse ImageResponse
defer func(ctx context.Context) {
if consumeQuota {
- err := model.PostConsumeTokenQuota(tokenId, userId, quota, 0, true)
+ err := model.PostConsumeTokenQuota(tokenId, userQuota, quota, 0, true)
if err != nil {
common.SysError("error consuming token remain quota: " + err.Error())
}
From fd57a1df0829fe68888e8b79b49d4df82517c3db Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Tue, 21 Nov 2023 16:35:51 +0800
Subject: [PATCH 3/7] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=AA=E4=BA=BA?=
=?UTF-8?q?=E4=B8=AD=E5=BF=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
controller/user.go | 59 ++
model/ability.go | 10 +
model/token.go | 42 +-
model/user.go | 54 +-
router/api-router.go | 2 +
web/src/components/PersonalSetting.js | 886 +++++++++++++++-----------
web/src/helpers/render.js | 6 +
web/src/pages/Setting/index.js | 84 ++-
web/src/pages/User/EditUser.js | 161 ++---
9 files changed, 812 insertions(+), 492 deletions(-)
diff --git a/controller/user.go b/controller/user.go
index 8fd10b8..f04f9ca 100644
--- a/controller/user.go
+++ b/controller/user.go
@@ -79,6 +79,7 @@ func setupLogin(user *model.User, c *gin.Context) {
DisplayName: user.DisplayName,
Role: user.Role,
Status: user.Status,
+ Group: user.Group,
}
c.JSON(http.StatusOK, gin.H{
"message": "",
@@ -284,6 +285,42 @@ func GenerateAccessToken(c *gin.Context) {
return
}
+type TransferAffQuotaRequest struct {
+ Quota int `json:"quota" binding:"required"`
+}
+
+func TransferAffQuota(c *gin.Context) {
+ id := c.GetInt("id")
+ user, err := model.GetUserById(id, true)
+ if err != nil {
+ c.JSON(http.StatusOK, gin.H{
+ "success": false,
+ "message": err.Error(),
+ })
+ return
+ }
+ tran := TransferAffQuotaRequest{}
+ if err := c.ShouldBindJSON(&tran); err != nil {
+ c.JSON(http.StatusOK, gin.H{
+ "success": false,
+ "message": err.Error(),
+ })
+ return
+ }
+ err = user.TransferAffQuotaToQuota(tran.Quota)
+ if err != nil {
+ c.JSON(http.StatusOK, gin.H{
+ "success": false,
+ "message": "划转失败 " + err.Error(),
+ })
+ return
+ }
+ c.JSON(http.StatusOK, gin.H{
+ "success": true,
+ "message": "划转成功",
+ })
+}
+
func GetAffCode(c *gin.Context) {
id := c.GetInt("id")
user, err := model.GetUserById(id, true)
@@ -330,6 +367,28 @@ func GetSelf(c *gin.Context) {
return
}
+func GetUserModels(c *gin.Context) {
+ id, err := strconv.Atoi(c.Param("id"))
+ if err != nil {
+ id = c.GetInt("id")
+ }
+ user, err := model.GetUserById(id, true)
+ if err != nil {
+ c.JSON(http.StatusOK, gin.H{
+ "success": false,
+ "message": err.Error(),
+ })
+ return
+ }
+ models := model.GetGroupModels(user.Group)
+ c.JSON(http.StatusOK, gin.H{
+ "success": true,
+ "message": "",
+ "data": models,
+ })
+ return
+}
+
func UpdateUser(c *gin.Context) {
var updatedUser model.User
err := json.NewDecoder(c.Request.Body).Decode(&updatedUser)
diff --git a/model/ability.go b/model/ability.go
index 3da83be..b57ae19 100644
--- a/model/ability.go
+++ b/model/ability.go
@@ -13,6 +13,16 @@ type Ability struct {
Priority *int64 `json:"priority" gorm:"bigint;default:0;index"`
}
+func GetGroupModels(group string) []string {
+ var abilities []Ability
+ DB.Where("`group` = ?", group).Find(&abilities)
+ models := make([]string, 0, len(abilities))
+ for _, ability := range abilities {
+ models = append(models, ability.Model)
+ }
+ return models
+}
+
func GetRandomSatisfiedChannel(group string, model string) (*Channel, error) {
ability := Ability{}
groupCol := "`group`"
diff --git a/model/token.go b/model/token.go
index ee6b4a9..66b8369 100644
--- a/model/token.go
+++ b/model/token.go
@@ -220,28 +220,30 @@ func PostConsumeTokenQuota(tokenId int, userQuota int, quota int, preConsumedQuo
}
if sendEmail {
- quotaTooLow := userQuota >= common.QuotaRemindThreshold && userQuota-(quota+preConsumedQuota) < common.QuotaRemindThreshold
- noMoreQuota := userQuota-(quota+preConsumedQuota) <= 0
- if quotaTooLow || noMoreQuota {
- go func() {
- email, err := GetUserEmail(token.UserId)
- if err != nil {
- common.SysError("failed to fetch user email: " + err.Error())
- }
- prompt := "您的额度即将用尽"
- if noMoreQuota {
- prompt = "您的额度已用尽"
- }
- if email != "" {
- topUpLink := fmt.Sprintf("%s/topup", common.ServerAddress)
- err = common.SendEmail(prompt, email,
- fmt.Sprintf("%s,当前剩余额度为 %d,为了不影响您的使用,请及时充值。
充值链接:%s", prompt, userQuota, topUpLink, topUpLink))
+ if (quota + preConsumedQuota) != 0 {
+ quotaTooLow := userQuota >= common.QuotaRemindThreshold && userQuota-(quota+preConsumedQuota) < common.QuotaRemindThreshold
+ noMoreQuota := userQuota-(quota+preConsumedQuota) <= 0
+ if quotaTooLow || noMoreQuota {
+ go func() {
+ email, err := GetUserEmail(token.UserId)
if err != nil {
- common.SysError("failed to send email" + err.Error())
+ common.SysError("failed to fetch user email: " + err.Error())
}
- common.SysLog("user quota is low, consumed quota: " + strconv.Itoa(quota) + ", user quota: " + strconv.Itoa(userQuota))
- }
- }()
+ prompt := "您的额度即将用尽"
+ if noMoreQuota {
+ prompt = "您的额度已用尽"
+ }
+ if email != "" {
+ topUpLink := fmt.Sprintf("%s/topup", common.ServerAddress)
+ err = common.SendEmail(prompt, email,
+ fmt.Sprintf("%s,当前剩余额度为 %d,为了不影响您的使用,请及时充值。
充值链接:%s", prompt, userQuota, topUpLink, topUpLink))
+ if err != nil {
+ common.SysError("failed to send email" + err.Error())
+ }
+ common.SysLog("user quota is low, consumed quota: " + strconv.Itoa(quota) + ", user quota: " + strconv.Itoa(userQuota))
+ }
+ }()
+ }
}
}
diff --git a/model/user.go b/model/user.go
index 31eae3b..422d268 100644
--- a/model/user.go
+++ b/model/user.go
@@ -27,6 +27,9 @@ type User struct {
RequestCount int `json:"request_count" gorm:"type:int;default:0;"` // request number
Group string `json:"group" gorm:"type:varchar(32);default:'default'"`
AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
+ AffCount int `json:"aff_count" gorm:"type:int;default:0;column:aff_count"`
+ AffQuota int `json:"aff_quota" gorm:"type:int;default:0;column:aff_quota"` // 邀请剩余额度
+ AffHistoryQuota int `json:"aff_history_quota" gorm:"type:int;default:0;column:aff_history"` // 邀请历史额度
InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
}
@@ -77,6 +80,54 @@ func DeleteUserById(id int) (err error) {
return user.Delete()
}
+func inviteUser(inviterId int) (err error) {
+ user, err := GetUserById(inviterId, true)
+ if err != nil {
+ return err
+ }
+ user.AffCount++
+ user.AffQuota += common.QuotaForInviter
+ user.AffHistoryQuota += common.QuotaForInviter
+ return DB.Save(user).Error
+}
+
+func (user *User) TransferAffQuotaToQuota(quota int) error {
+ // 检查quota是否小于最小额度
+ if float64(quota) < common.QuotaPerUnit {
+ return fmt.Errorf("转移额度最小为%s!", common.LogQuota(int(common.QuotaPerUnit)))
+ }
+
+ // 开始数据库事务
+ tx := DB.Begin()
+ if tx.Error != nil {
+ return tx.Error
+ }
+ defer tx.Rollback() // 确保在函数退出时事务能回滚
+
+ // 加锁查询用户以确保数据一致性
+ err := tx.Set("gorm:query_option", "FOR UPDATE").First(&user, user.Id).Error
+ if err != nil {
+ return err
+ }
+
+ // 再次检查用户的AffQuota是否足够
+ if user.AffQuota < quota {
+ return errors.New("邀请额度不足!")
+ }
+
+ // 更新用户额度
+ user.AffQuota -= quota
+ user.Quota += quota
+
+ // 保存用户状态
+ if err := tx.Save(user).Error; err != nil {
+ return err
+ }
+
+ // 提交事务
+ return tx.Commit().Error
+}
+
func (user *User) Insert(inviterId int) error {
var err error
if user.Password != "" {
@@ -101,8 +152,9 @@ func (user *User) Insert(inviterId int) error {
RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("使用邀请码赠送 %s", common.LogQuota(common.QuotaForInvitee)))
}
if common.QuotaForInviter > 0 {
- _ = IncreaseUserQuota(inviterId, common.QuotaForInviter)
+ //_ = IncreaseUserQuota(inviterId, common.QuotaForInviter)
RecordLog(inviterId, LogTypeSystem, fmt.Sprintf("邀请用户赠送 %s", common.LogQuota(common.QuotaForInviter)))
+ _ = inviteUser(inviterId)
}
}
return nil
diff --git a/router/api-router.go b/router/api-router.go
index 17758b0..038b750 100644
--- a/router/api-router.go
+++ b/router/api-router.go
@@ -39,6 +39,7 @@ func SetApiRouter(router *gin.Engine) {
selfRoute.Use(middleware.UserAuth())
{
selfRoute.GET("/self", controller.GetSelf)
+ selfRoute.GET("/models", controller.GetUserModels)
selfRoute.PUT("/self", controller.UpdateSelf)
selfRoute.DELETE("/self", controller.DeleteSelf)
selfRoute.GET("/token", controller.GenerateAccessToken)
@@ -46,6 +47,7 @@ func SetApiRouter(router *gin.Engine) {
selfRoute.POST("/topup", controller.TopUp)
selfRoute.POST("/pay", controller.RequestEpay)
selfRoute.POST("/amount", controller.RequestAmount)
+ selfRoute.POST("/aff_transfer", controller.TransferAffQuota)
}
adminRoute := userRoute.Group("/")
diff --git a/web/src/components/PersonalSetting.js b/web/src/components/PersonalSetting.js
index 6baf1f3..41ff804 100644
--- a/web/src/components/PersonalSetting.js
+++ b/web/src/components/PersonalSetting.js
@@ -1,376 +1,548 @@
-import React, { useContext, useEffect, useState } from 'react';
-import { Button, Divider, Form, Header, Image, Message, Modal } from 'semantic-ui-react';
-import { Link, useNavigate } from 'react-router-dom';
-import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers';
+import React, {useContext, useEffect, useState} from 'react';
+import {Form, Image, Message} from 'semantic-ui-react';
+import {Link, useNavigate} from 'react-router-dom';
+import {API, copy, isRoot, showError, showInfo, showNotice, showSuccess} from '../helpers';
import Turnstile from 'react-turnstile';
-import { UserContext } from '../context/User';
-import { onGitHubOAuthClicked } from './utils';
+import {UserContext} from '../context/User';
+import {onGitHubOAuthClicked} from './utils';
+import {
+ Avatar, Banner,
+ Button,
+ Card,
+ Descriptions,
+ Divider,
+ Input, InputNumber,
+ Layout,
+ Modal,
+ Space,
+ Tag,
+ Typography
+} from "@douyinfe/semi-ui";
+import {getQuotaPerUnit, renderQuota, renderQuotaWithPrompt, stringToColor} from "../helpers/render";
+import EditToken from "../pages/Token/EditToken";
+import EditUser from "../pages/User/EditUser";
const PersonalSetting = () => {
- const [userState, userDispatch] = useContext(UserContext);
- let navigate = useNavigate();
+ const [userState, userDispatch] = useContext(UserContext);
+ let navigate = useNavigate();
- const [inputs, setInputs] = useState({
- wechat_verification_code: '',
- email_verification_code: '',
- email: '',
- self_account_deletion_confirmation: ''
- });
- const [status, setStatus] = useState({});
- const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
- const [showEmailBindModal, setShowEmailBindModal] = useState(false);
- const [showAccountDeleteModal, setShowAccountDeleteModal] = useState(false);
- const [turnstileEnabled, setTurnstileEnabled] = useState(false);
- const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
- const [turnstileToken, setTurnstileToken] = useState('');
- const [loading, setLoading] = useState(false);
- const [disableButton, setDisableButton] = useState(false);
- const [countdown, setCountdown] = useState(30);
- const [affLink, setAffLink] = useState("");
- const [systemToken, setSystemToken] = useState("");
+ const [inputs, setInputs] = useState({
+ wechat_verification_code: '',
+ email_verification_code: '',
+ email: '',
+ self_account_deletion_confirmation: ''
+ });
+ const [status, setStatus] = useState({});
+ const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
+ const [showEmailBindModal, setShowEmailBindModal] = useState(false);
+ const [showAccountDeleteModal, setShowAccountDeleteModal] = useState(false);
+ const [turnstileEnabled, setTurnstileEnabled] = useState(false);
+ const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
+ const [turnstileToken, setTurnstileToken] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [disableButton, setDisableButton] = useState(false);
+ const [countdown, setCountdown] = useState(30);
+ const [affLink, setAffLink] = useState("");
+ const [systemToken, setSystemToken] = useState("");
+ const [models, setModels] = useState([]);
+ const [openTransfer, setOpenTransfer] = useState(false);
+ const [transferAmount, setTransferAmount] = useState(0);
- useEffect(() => {
- let status = localStorage.getItem('status');
- if (status) {
- status = JSON.parse(status);
- setStatus(status);
- if (status.turnstile_check) {
- setTurnstileEnabled(true);
- setTurnstileSiteKey(status.turnstile_site_key);
- }
- }
- }, []);
+ useEffect(() => {
+ // let user = localStorage.getItem('user');
+ // if (user) {
+ // userDispatch({ type: 'login', payload: user });
+ // }
+ // console.log(localStorage.getItem('user'))
- useEffect(() => {
- let countdownInterval = null;
- if (disableButton && countdown > 0) {
- countdownInterval = setInterval(() => {
- setCountdown(countdown - 1);
- }, 1000);
- } else if (countdown === 0) {
- setDisableButton(false);
- setCountdown(30);
- }
- return () => clearInterval(countdownInterval); // Clean up on unmount
- }, [disableButton, countdown]);
+ let status = localStorage.getItem('status');
+ if (status) {
+ status = JSON.parse(status);
+ setStatus(status);
+ if (status.turnstile_check) {
+ setTurnstileEnabled(true);
+ setTurnstileSiteKey(status.turnstile_site_key);
+ }
+ }
+ getUserData().then(
+ (res) => {
+ console.log(userState)
+ }
+ );
+ loadModels().then();
+ getAffLink().then();
+ setTransferAmount(getQuotaPerUnit())
+ }, []);
- const handleInputChange = (e, { name, value }) => {
- setInputs((inputs) => ({ ...inputs, [name]: value }));
- };
+ useEffect(() => {
+ let countdownInterval = null;
+ if (disableButton && countdown > 0) {
+ countdownInterval = setInterval(() => {
+ setCountdown(countdown - 1);
+ }, 1000);
+ } else if (countdown === 0) {
+ setDisableButton(false);
+ setCountdown(30);
+ }
+ return () => clearInterval(countdownInterval); // Clean up on unmount
+ }, [disableButton, countdown]);
- const generateAccessToken = async () => {
- const res = await API.get('/api/user/token');
- const { success, message, data } = res.data;
- if (success) {
- setSystemToken(data);
- setAffLink("");
- await copy(data);
- showSuccess(`令牌已重置并已复制到剪贴板`);
- } else {
- showError(message);
- }
- };
+ const handleInputChange = (name, value) => {
+ setInputs((inputs) => ({...inputs, [name]: value}));
+ };
- const getAffLink = async () => {
- const res = await API.get('/api/user/aff');
- const { success, message, data } = res.data;
- if (success) {
- let link = `${window.location.origin}/register?aff=${data}`;
- setAffLink(link);
- setSystemToken("");
- await copy(link);
- showSuccess(`邀请链接已复制到剪切板`);
- } else {
- showError(message);
- }
- };
+ const generateAccessToken = async () => {
+ const res = await API.get('/api/user/token');
+ const {success, message, data} = res.data;
+ if (success) {
+ setSystemToken(data);
+ await copy(data);
+ showSuccess(`令牌已重置并已复制到剪贴板`);
+ } else {
+ showError(message);
+ }
+ };
- const handleAffLinkClick = async (e) => {
- e.target.select();
- await copy(e.target.value);
- showSuccess(`邀请链接已复制到剪切板`);
- };
+ const getAffLink = async () => {
+ const res = await API.get('/api/user/aff');
+ const {success, message, data} = res.data;
+ if (success) {
+ let link = `${window.location.origin}/register?aff=${data}`;
+ setAffLink(link);
+ } else {
+ showError(message);
+ }
+ };
- const handleSystemTokenClick = async (e) => {
- e.target.select();
- await copy(e.target.value);
- showSuccess(`系统令牌已复制到剪切板`);
- };
-
- const deleteAccount = async () => {
- if (inputs.self_account_deletion_confirmation !== userState.user.username) {
- showError('请输入你的账户名以确认删除!');
- return;
+ const getUserData = async () => {
+ let res = await API.get(`/api/user/self`);
+ const {success, message, data} = res.data;
+ if (success) {
+ userDispatch({type: 'login', payload: data});
+ } else {
+ showError(message);
+ }
}
- const res = await API.delete('/api/user/self');
- const { success, message } = res.data;
-
- if (success) {
- showSuccess('账户已删除!');
- await API.get('/api/user/logout');
- userDispatch({ type: 'logout' });
- localStorage.removeItem('user');
- navigate('/login');
- } else {
- showError(message);
+ const loadModels = async () => {
+ let res = await API.get(`/api/user/models`);
+ const {success, message, data} = res.data;
+ if (success) {
+ setModels(data);
+ console.log(data)
+ } else {
+ showError(message);
+ }
}
- };
- const bindWeChat = async () => {
- if (inputs.wechat_verification_code === '') return;
- const res = await API.get(
- `/api/oauth/wechat/bind?code=${inputs.wechat_verification_code}`
+ const handleAffLinkClick = async (e) => {
+ e.target.select();
+ await copy(e.target.value);
+ showSuccess(`邀请链接已复制到剪切板`);
+ };
+
+ const handleSystemTokenClick = async (e) => {
+ e.target.select();
+ await copy(e.target.value);
+ showSuccess(`系统令牌已复制到剪切板`);
+ };
+
+ const deleteAccount = async () => {
+ if (inputs.self_account_deletion_confirmation !== userState.user.username) {
+ showError('请输入你的账户名以确认删除!');
+ return;
+ }
+
+ const res = await API.delete('/api/user/self');
+ const {success, message} = res.data;
+
+ if (success) {
+ showSuccess('账户已删除!');
+ await API.get('/api/user/logout');
+ userDispatch({type: 'logout'});
+ localStorage.removeItem('user');
+ navigate('/login');
+ } else {
+ showError(message);
+ }
+ };
+
+ const bindWeChat = async () => {
+ if (inputs.wechat_verification_code === '') return;
+ const res = await API.get(
+ `/api/oauth/wechat/bind?code=${inputs.wechat_verification_code}`
+ );
+ const {success, message} = res.data;
+ if (success) {
+ showSuccess('微信账户绑定成功!');
+ setShowWeChatBindModal(false);
+ } else {
+ showError(message);
+ }
+ };
+
+ const transfer = async () => {
+ if (transferAmount < getQuotaPerUnit()) {
+ showError('划转金额最低为' + renderQuota(getQuotaPerUnit()));
+ return;
+ }
+ const res = await API.post(
+ `/api/user/aff_transfer`,
+ {
+ quota: transferAmount
+ }
+ );
+ const {success, message} = res.data;
+ if (success) {
+ showSuccess(message);
+ setOpenTransfer(false);
+ getUserData().then();
+ } else {
+ showError(message);
+ }
+ };
+
+ const sendVerificationCode = async () => {
+ if (inputs.email === '') {
+ showError('请输入邮箱!');
+ return;
+ }
+ setDisableButton(true);
+ if (turnstileEnabled && turnstileToken === '') {
+ showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
+ return;
+ }
+ setLoading(true);
+ const res = await API.get(
+ `/api/verification?email=${inputs.email}&turnstile=${turnstileToken}`
+ );
+ const {success, message} = res.data;
+ if (success) {
+ showSuccess('验证码发送成功,请检查邮箱!');
+ } else {
+ showError(message);
+ }
+ setLoading(false);
+ };
+
+ const bindEmail = async () => {
+ if (inputs.email_verification_code === '') {
+ showError('请输入邮箱验证码!');
+ return;
+ }
+ setLoading(true);
+ const res = await API.get(
+ `/api/oauth/email/bind?email=${inputs.email}&code=${inputs.email_verification_code}`
+ );
+ const {success, message} = res.data;
+ if (success) {
+ showSuccess('邮箱账户绑定成功!');
+ setShowEmailBindModal(false);
+ userState.user.email = inputs.email;
+ } else {
+ showError(message);
+ }
+ setLoading(false);
+ };
+
+ const getUsername = () => {
+ if (userState.user) {
+ return userState.user.username;
+ } else {
+ return 'null';
+ }
+ }
+
+ const handleCancel = () => {
+ setOpenTransfer(false);
+ }
+
+ return (
+
+
+
+
+
+ {`可用额度${renderQuotaWithPrompt(userState?.user?.aff_quota)}`}
+
+
+
+
{`划转额度${renderQuotaWithPrompt(transferAmount)} 最低` + renderQuota(getQuotaPerUnit())}
+
+ setTransferAmount(value)} disabled={false}>
+
+
+
+
+
+ {typeof getUsername() === 'string' && getUsername().slice(0, 1)}
+ }
+ title={{getUsername()}}
+ description={isRoot()?管理员:普通用户}
+ >
+ }
+ headerExtraContent={
+ <>
+
+ {'ID: ' + userState?.user?.id}
+ {userState?.user?.group}
+
+ >
+ }
+ footer={
+
+ {renderQuota(userState?.user?.quota)}
+ {renderQuota(userState?.user?.used_quota)}
+ {userState.user?.request_count}
+
+ }
+ >
+ 可用模型
+
+
+ {models.map((model) => (
+
+ {model}
+
+ ))}
+
+
+
+
+
+ 邀请链接
+
+
+ }
+ >
+ 邀请信息
+
+
+
+
+ {
+ renderQuota(userState?.user?.aff_quota)
+ }
+
+
+
+ {renderQuota(userState?.user?.aff_history_quota)}
+ {userState?.user?.aff_count}
+
+
+
+
+ 个人信息
+
+
邮箱
+
+
+
+
+
+
+
+
+
+
+
微信
+
+
+
+
+
+
+
+
+
+
+
GitHub
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {systemToken && (
+
+ )}
+ {
+ status.wechat_login && (
+
+ )
+ }
+
setShowWeChatBindModal(false)}
+ // onOpen={() => setShowWeChatBindModal(true)}
+ visible={showWeChatBindModal}
+ size={'mini'}
+ >
+
+
+
+ 微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)
+
+
+
+
+
+
+
+
+ setShowEmailBindModal(false)}
+ // onOpen={() => setShowEmailBindModal(true)}
+ onOk={bindEmail}
+ visible={showEmailBindModal}
+ size={'small'}
+ centered={true}
+ maskClosable={false}
+ >
+ 绑定邮箱地址
+
+ handleInputChange('email', value)}
+ name='email'
+ type='email'
+ />
+
+
+
+ handleInputChange('email_verification_code', value)}
+ />
+
+ {turnstileEnabled ? (
+ {
+ setTurnstileToken(token);
+ }}
+ />
+ ) : (
+ <>>
+ )}
+
+ setShowAccountDeleteModal(false)}
+ visible={showAccountDeleteModal}
+ size={'small'}
+ centered={true}
+ onOk={deleteAccount}
+ >
+
+
+
+
+ handleInputChange('self_account_deletion_confirmation', value)}
+ />
+ {turnstileEnabled ? (
+ {
+ setTurnstileToken(token);
+ }}
+ />
+ ) : (
+ <>>
+ )}
+
+
+
+
+
+
+
);
- const { success, message } = res.data;
- if (success) {
- showSuccess('微信账户绑定成功!');
- setShowWeChatBindModal(false);
- } else {
- showError(message);
- }
- };
-
- const sendVerificationCode = async () => {
- setDisableButton(true);
- if (inputs.email === '') return;
- if (turnstileEnabled && turnstileToken === '') {
- showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
- return;
- }
- setLoading(true);
- const res = await API.get(
- `/api/verification?email=${inputs.email}&turnstile=${turnstileToken}`
- );
- const { success, message } = res.data;
- if (success) {
- showSuccess('验证码发送成功,请检查邮箱!');
- } else {
- showError(message);
- }
- setLoading(false);
- };
-
- const bindEmail = async () => {
- if (inputs.email_verification_code === '') return;
- setLoading(true);
- const res = await API.get(
- `/api/oauth/email/bind?email=${inputs.email}&code=${inputs.email_verification_code}`
- );
- const { success, message } = res.data;
- if (success) {
- showSuccess('邮箱账户绑定成功!');
- setShowEmailBindModal(false);
- } else {
- showError(message);
- }
- setLoading(false);
- };
-
- return (
-
-
-
- 注意,此处生成的令牌用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。
-
-
-
-
-
-
- {systemToken && (
-
- )}
- {affLink && (
-
- )}
-
-
- {
- status.wechat_login && (
-
- )
- }
-
setShowWeChatBindModal(false)}
- onOpen={() => setShowWeChatBindModal(true)}
- open={showWeChatBindModal}
- size={'mini'}
- >
-
-
-
-
-
- 微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)
-
-
-
-
-
-
-
-
- {
- status.github_oauth && (
-
- )
- }
-
-
setShowEmailBindModal(false)}
- onOpen={() => setShowEmailBindModal(true)}
- open={showEmailBindModal}
- size={'tiny'}
- style={{ maxWidth: '450px' }}
- >
- 绑定邮箱地址
-
-
-
- {disableButton ? `重新发送(${countdown})` : '获取验证码'}
-
- }
- />
-
- {turnstileEnabled ? (
- {
- setTurnstileToken(token);
- }}
- />
- ) : (
- <>>
- )}
-
-
-
-
-
-
-
-
-
-
setShowAccountDeleteModal(false)}
- onOpen={() => setShowAccountDeleteModal(true)}
- open={showAccountDeleteModal}
- size={'tiny'}
- style={{ maxWidth: '450px' }}
- >
- 危险操作
-
- 您正在删除自己的帐户,将清空所有数据且不可恢复
-
-
- {turnstileEnabled ? (
- {
- setTurnstileToken(token);
- }}
- />
- ) : (
- <>>
- )}
-
-
-
-
-
-
-
-
-
-
- );
};
export default PersonalSetting;
diff --git a/web/src/helpers/render.js b/web/src/helpers/render.js
index 16d1ed9..2a7fd4a 100644
--- a/web/src/helpers/render.js
+++ b/web/src/helpers/render.js
@@ -37,6 +37,12 @@ export function renderNumber(num) {
}
}
+export function getQuotaPerUnit() {
+ let quotaPerUnit = localStorage.getItem('quota_per_unit');
+ quotaPerUnit = parseFloat(quotaPerUnit);
+ return quotaPerUnit;
+}
+
export function renderQuota(quota, digits = 2) {
let quotaPerUnit = localStorage.getItem('quota_per_unit');
let displayInCurrency = localStorage.getItem('display_in_currency');
diff --git a/web/src/pages/Setting/index.js b/web/src/pages/Setting/index.js
index 30d0ef2..99b3e4c 100644
--- a/web/src/pages/Setting/index.js
+++ b/web/src/pages/Setting/index.js
@@ -1,55 +1,53 @@
import React from 'react';
-import { Segment, Tab } from 'semantic-ui-react';
import SystemSetting from '../../components/SystemSetting';
-import { isRoot } from '../../helpers';
+import {isRoot} from '../../helpers';
import OtherSetting from '../../components/OtherSetting';
import PersonalSetting from '../../components/PersonalSetting';
import OperationSetting from '../../components/OperationSetting';
+import {Layout, TabPane, Tabs} from "@douyinfe/semi-ui";
const Setting = () => {
- let panes = [
- {
- menuItem: '个人设置',
- render: () => (
-
-
-
- )
+ let panes = [
+ {
+ tab: '个人设置',
+ content: ,
+ itemKey: '1'
+ }
+ ];
+
+ if (isRoot()) {
+ panes.push({
+ tab: '运营设置',
+ content: ,
+ itemKey: '2'
+ });
+ panes.push({
+ tab: '系统设置',
+ content: ,
+ itemKey: '3'
+ });
+ panes.push({
+ tab: '其他设置',
+ content: ,
+ itemKey: '4'
+ });
}
- ];
- if (isRoot()) {
- panes.push({
- menuItem: '运营设置',
- render: () => (
-
-
-
- )
- });
- panes.push({
- menuItem: '系统设置',
- render: () => (
-
-
-
- )
- });
- panes.push({
- menuItem: '其他设置',
- render: () => (
-
-
-
- )
- });
- }
-
- return (
-
-
-
- );
+ return (
+
+
+
+
+ {panes.map(pane => (
+
+ {pane.content}
+
+ ))}
+
+
+
+
+ );
};
export default Setting;
diff --git a/web/src/pages/User/EditUser.js b/web/src/pages/User/EditUser.js
index 8ae0e55..6ff88fc 100644
--- a/web/src/pages/User/EditUser.js
+++ b/web/src/pages/User/EditUser.js
@@ -1,10 +1,12 @@
import React, { useEffect, useState } from 'react';
import { Button, Form, Header, Segment } from 'semantic-ui-react';
import { useParams, useNavigate } from 'react-router-dom';
-import { API, showError, showSuccess } from '../../helpers';
+import {API, isMobile, showError, showSuccess} from '../../helpers';
import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render';
+import Title from "@douyinfe/semi-ui/lib/es/typography/title";
+import {SideSheet, Space} from "@douyinfe/semi-ui";
-const EditUser = () => {
+const EditUser = (props) => {
const params = useParams();
const userId = params.id;
const [loading, setLoading] = useState(true);
@@ -84,105 +86,122 @@ const EditUser = () => {
return (
<>
-
-
+ 更新用户信息}
+ headerStyle={{borderBottom: '1px solid var(--semi-color-border)'}}
+ bodyStyle={{borderBottom: '1px solid var(--semi-color-border)'}}
+ visible={props.visiable}
+ footer={
+
+
+
+
+
+
+ }
+ closeIcon={null}
+ onCancel={() => handleCancel()}
+ width={isMobile() ? '100%' : 600}
+ >
{
- userId && <>
-
-
-
-
-
-
- >
+ userId && <>
+
+
+
+
+
+
+ >
}
-
+
+
>
);
};
From 79661397561f1f3dee6cfbd53147cf1b4d35e0f9 Mon Sep 17 00:00:00 2001
From: Calcium-Ion <61247483+Calcium-Ion@users.noreply.github.com>
Date: Tue, 21 Nov 2023 16:40:53 +0800
Subject: [PATCH 4/7] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index aa3e0b2..ba3f6da 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,8 @@
## 界面截图
+
+



From 65694d26aa4c3cc3e685db0e5beaf6567f5acb21 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Tue, 21 Nov 2023 16:49:11 +0800
Subject: [PATCH 5/7] rollback EditUser.js
---
web/src/pages/User/EditUser.js | 227 +++++++++++++++------------------
1 file changed, 104 insertions(+), 123 deletions(-)
diff --git a/web/src/pages/User/EditUser.js b/web/src/pages/User/EditUser.js
index 6ff88fc..d99d83b 100644
--- a/web/src/pages/User/EditUser.js
+++ b/web/src/pages/User/EditUser.js
@@ -1,12 +1,10 @@
import React, { useEffect, useState } from 'react';
import { Button, Form, Header, Segment } from 'semantic-ui-react';
import { useParams, useNavigate } from 'react-router-dom';
-import {API, isMobile, showError, showSuccess} from '../../helpers';
+import { API, showError, showSuccess } from '../../helpers';
import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render';
-import Title from "@douyinfe/semi-ui/lib/es/typography/title";
-import {SideSheet, Space} from "@douyinfe/semi-ui";
-const EditUser = (props) => {
+const EditUser = () => {
const params = useParams();
const userId = params.id;
const [loading, setLoading] = useState(true);
@@ -22,7 +20,7 @@ const EditUser = (props) => {
});
const [groupOptions, setGroupOptions] = useState([]);
const { username, display_name, password, github_id, wechat_id, email, quota, group } =
- inputs;
+ inputs;
const handleInputChange = (e, { name, value }) => {
setInputs((inputs) => ({ ...inputs, [name]: value }));
};
@@ -85,124 +83,107 @@ const EditUser = (props) => {
};
return (
- <>
- 更新用户信息}
- headerStyle={{borderBottom: '1px solid var(--semi-color-border)'}}
- bodyStyle={{borderBottom: '1px solid var(--semi-color-border)'}}
- visible={props.visiable}
- footer={
-
-
-
-
-
-
- }
- closeIcon={null}
- onCancel={() => handleCancel()}
- width={isMobile() ? '100%' : 600}
- >
-
-
-
-
-
-
-
-
-
- {
- userId && <>
-
-
-
-
-
-
- >
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ {
+ userId && <>
+
+
+
+
+
+
+ >
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
);
};
From f18f585fc825ee9c174a2a98e923219653c46333 Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Tue, 21 Nov 2023 16:58:31 +0800
Subject: [PATCH 6/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=AF=E7=94=A8model?=
=?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=98=BE=E7=A4=BAbug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
model/ability.go | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/model/ability.go b/model/ability.go
index b57ae19..2e8766a 100644
--- a/model/ability.go
+++ b/model/ability.go
@@ -15,7 +15,8 @@ type Ability struct {
func GetGroupModels(group string) []string {
var abilities []Ability
- DB.Where("`group` = ?", group).Find(&abilities)
+ //去重
+ DB.Where("`group` = ?", group).Distinct("model").Find(&abilities)
models := make([]string, 0, len(abilities))
for _, ability := range abilities {
models = append(models, ability.Model)
From 9d191c10a906c6bf804ccad1b22501699086c78e Mon Sep 17 00:00:00 2001
From: CaIon <1808837298@qq.com>
Date: Tue, 21 Nov 2023 18:11:07 +0800
Subject: [PATCH 7/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0pprof=E6=80=A7=E8=83=BD?=
=?UTF-8?q?=E5=88=86=E6=9E=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
common/pprof.go | 44 ++++++++++++++++++++++++++++++++++++++++++++
go.mod | 5 +++++
go.sum | 13 +++++++++++++
main.go | 6 ++++++
4 files changed, 68 insertions(+)
create mode 100644 common/pprof.go
diff --git a/common/pprof.go b/common/pprof.go
new file mode 100644
index 0000000..4bec30f
--- /dev/null
+++ b/common/pprof.go
@@ -0,0 +1,44 @@
+package common
+
+import (
+ "fmt"
+ "github.com/shirou/gopsutil/cpu"
+ "os"
+ "runtime/pprof"
+ "time"
+)
+
+// Monitor 定时监控cpu使用率,超过阈值输出pprof文件
+func Monitor() {
+ for {
+ percent, err := cpu.Percent(time.Second, false)
+ if err != nil {
+ panic(err)
+ }
+ if percent[0] > 80 {
+ fmt.Println("cpu usage too high")
+ // write pprof file
+ if _, err := os.Stat("./pprof"); os.IsNotExist(err) {
+ err := os.Mkdir("./pprof", os.ModePerm)
+ if err != nil {
+ SysLog("创建pprof文件夹失败 " + err.Error())
+ continue
+ }
+ }
+ f, err := os.Create("./pprof/" + fmt.Sprintf("cpu-%s.pprof", time.Now().Format("20060102150405")))
+ if err != nil {
+ SysLog("创建pprof文件失败 " + err.Error())
+ continue
+ }
+ err = pprof.StartCPUProfile(f)
+ if err != nil {
+ SysLog("启动pprof失败 " + err.Error())
+ continue
+ }
+ time.Sleep(10 * time.Second) // profile for 30 seconds
+ pprof.StopCPUProfile()
+ f.Close()
+ }
+ time.Sleep(30 * time.Second)
+ }
+}
diff --git a/go.mod b/go.mod
index 3a75341..d7ac8c1 100644
--- a/go.mod
+++ b/go.mod
@@ -17,6 +17,7 @@ require (
github.com/gorilla/websocket v1.5.0
github.com/pkoukk/tiktoken-go v0.1.1
github.com/samber/lo v1.38.1
+ github.com/shirou/gopsutil v3.21.11+incompatible
github.com/star-horizon/go-epay v0.0.0-20230204124159-fa2e2293fdc2
golang.org/x/crypto v0.14.0
gorm.io/driver/mysql v1.4.3
@@ -33,6 +34,7 @@ require (
github.com/dlclark/regexp2 v1.8.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
@@ -54,8 +56,11 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
+ github.com/yusufpapurcu/wmi v1.2.3 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/net v0.17.0 // indirect
diff --git a/go.sum b/go.sum
index 6e7f963..7e4cfa7 100644
--- a/go.sum
+++ b/go.sum
@@ -33,6 +33,8 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
@@ -131,6 +133,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
+github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
+github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/star-horizon/go-epay v0.0.0-20230204124159-fa2e2293fdc2 h1:avbt5a8F/zbYwFzTugrqWOBJe/K1cJj6+xpr+x1oVAI=
github.com/star-horizon/go-epay v0.0.0-20230204124159-fa2e2293fdc2/go.mod h1:SiffGCWGGMVwujne2dUQbJ5zUVD1V1Yj0hDuTfqFNEo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -146,6 +150,10 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -154,6 +162,8 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
+github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -165,6 +175,7 @@ golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -172,6 +183,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
diff --git a/main.go b/main.go
index de0b2a0..d4a0f4b 100644
--- a/main.go
+++ b/main.go
@@ -83,6 +83,12 @@ func main() {
common.SysLog("batch update enabled with interval " + strconv.Itoa(common.BatchUpdateInterval) + "s")
model.InitBatchUpdater()
}
+
+ if os.Getenv("ENABLE_PPROF") == "true" {
+ go common.Monitor()
+ common.SysLog("pprof enabled")
+ }
+
controller.InitTokenEncoders()
// Initialize HTTP server