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 @@ ## 界面截图 +![image](https://github.com/Calcium-Ion/new-api/assets/61247483/d1ac216e-0804-4105-9fdc-66b35022d861) + ![image](https://github.com/Calcium-Ion/new-api/assets/61247483/3ca0b282-00ff-4c96-bf9d-e29ef615c605) ![image](https://github.com/Calcium-Ion/new-api/assets/61247483/f4f40ed4-8ccb-43d7-a580-90677827646d) ![image](https://github.com/Calcium-Ion/new-api/assets/61247483/90d7d763-6a77-4b36-9f76-2bb30f18583d) 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