Commit f73ecc57 by CaIon

移除不必要的功能

parent e25bcce4
...@@ -13,6 +13,10 @@ var StartTime = time.Now().Unix() // unit: second ...@@ -13,6 +13,10 @@ var StartTime = time.Now().Unix() // unit: second
var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change
var SystemName = "One API" var SystemName = "One API"
var ServerAddress = "http://localhost:3000" var ServerAddress = "http://localhost:3000"
var PayAddress = ""
var EpayId = ""
var EpayKey = ""
var Price = 7
var Footer = "" var Footer = ""
var Logo = "" var Logo = ""
var TopUpLink = "" var TopUpLink = ""
...@@ -94,10 +98,6 @@ var RequestInterval = time.Duration(requestInterval) * time.Second ...@@ -94,10 +98,6 @@ var RequestInterval = time.Duration(requestInterval) * time.Second
var SyncFrequency = 10 * 60 // unit is second, will be overwritten by SYNC_FREQUENCY var SyncFrequency = 10 * 60 // unit is second, will be overwritten by SYNC_FREQUENCY
var NormalPrice = 1.5
var StablePrice = 6.0
var BasePrice = 1.5
const ( const (
RoleGuestUser = 0 RoleGuestUser = 0
RoleCommonUser = 1 RoleCommonUser = 1
......
...@@ -19,90 +19,84 @@ func UpdateMidjourneyTask() { ...@@ -19,90 +19,84 @@ func UpdateMidjourneyTask() {
for { for {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
log.Printf("UpdateMidjourneyTask: %v", err) log.Printf("UpdateMidjourneyTask panic: %v", err)
} }
}() }()
time.Sleep(time.Duration(15) * time.Second) time.Sleep(time.Duration(15) * time.Second)
tasks := model.GetAllUnFinishTasks() tasks := model.GetAllUnFinishTasks()
if len(tasks) != 0 { if len(tasks) != 0 {
//log.Printf("UpdateMidjourneyTask: %v", time.Now())
ids := make([]string, 0)
for _, task := range tasks { for _, task := range tasks {
ids = append(ids, task.MjId) midjourneyChannel, err := model.GetChannelById(task.ChannelId, true)
} if err != nil {
requestUrl := "http://107.173.171.147:8080/mj/task/list-by-condition" log.Printf("UpdateMidjourneyTask: %v", err)
requestBody := map[string]interface{}{ task.FailReason = fmt.Sprintf("获取渠道信息失败,请联系管理员,渠道ID:%d", task.ChannelId)
"ids": ids, task.Status = "FAILURE"
} task.Progress = "100%"
jsonStr, err := json.Marshal(requestBody) err := task.Update()
if err != nil { if err != nil {
log.Printf("UpdateMidjourneyTask: %v", err) log.Printf("UpdateMidjourneyTask error: %v", err)
continue }
} continue
req, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(jsonStr))
if err != nil {
log.Printf("UpdateMidjourneyTask: %v", err)
continue
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("mj-api-secret", "uhiftyuwadbkjshbiklahcuitguasguzhxliawodawdu")
resp, err := httpClient.Do(req)
if err != nil {
log.Printf("UpdateMidjourneyTask: %v", err)
continue
}
defer resp.Body.Close()
var response []Midjourney
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
log.Printf("UpdateMidjourneyTask: %v", err)
continue
}
for _, responseItem := range response {
var midjourneyTask *model.Midjourney
for _, mj := range tasks {
mj.MjId = responseItem.MjId
midjourneyTask = model.GetMjByuId(mj.Id)
} }
if midjourneyTask != nil { requestUrl := fmt.Sprintf("%s/mj/task/%s/fetch", midjourneyChannel.BaseURL, task.MjId)
midjourneyTask.Code = 1
midjourneyTask.Progress = responseItem.Progress req, err := http.NewRequest("GET", requestUrl, bytes.NewBuffer([]byte("")))
midjourneyTask.PromptEn = responseItem.PromptEn if err != nil {
midjourneyTask.State = responseItem.State log.Printf("UpdateMidjourneyTask error: %v", err)
midjourneyTask.SubmitTime = responseItem.SubmitTime continue
midjourneyTask.StartTime = responseItem.StartTime }
midjourneyTask.FinishTime = responseItem.FinishTime
midjourneyTask.ImageUrl = responseItem.ImageUrl req.Header.Set("Content-Type", "application/json")
midjourneyTask.Status = responseItem.Status req.Header.Set("mj-api-secret", midjourneyChannel.Key)
midjourneyTask.FailReason = responseItem.FailReason resp, err := httpClient.Do(req)
if midjourneyTask.Progress != "100%" && responseItem.FailReason != "" { if err != nil {
log.Println(midjourneyTask.MjId + " 构建失败," + midjourneyTask.FailReason) log.Printf("UpdateMidjourneyTask error: %v", err)
midjourneyTask.Progress = "100%" continue
err = model.CacheUpdateUserQuota(midjourneyTask.UserId) }
if err != nil { defer resp.Body.Close()
log.Println("error update user quota cache: " + err.Error()) var responseItem Midjourney
} else { err = json.NewDecoder(resp.Body).Decode(&responseItem)
modelRatio := common.GetModelRatio(imageModel) if err != nil {
groupRatio := common.GetGroupRatio("default") log.Printf("UpdateMidjourneyTask error: %v", err)
ratio := modelRatio * groupRatio continue
quota := int(ratio * 1 * 1000) }
if quota != 0 { task.Code = 1
err := model.IncreaseUserQuota(midjourneyTask.UserId, quota) task.Progress = responseItem.Progress
if err != nil { task.PromptEn = responseItem.PromptEn
log.Println("fail to increase user quota") task.State = responseItem.State
} task.SubmitTime = responseItem.SubmitTime
logContent := fmt.Sprintf("%s 构图失败,补偿 %s", midjourneyTask.MjId, common.LogQuota(quota)) task.StartTime = responseItem.StartTime
model.RecordLog(midjourneyTask.UserId, 1, logContent) task.FinishTime = responseItem.FinishTime
task.ImageUrl = responseItem.ImageUrl
task.Status = responseItem.Status
task.FailReason = responseItem.FailReason
if task.Progress != "100%" && responseItem.FailReason != "" {
log.Println(task.MjId + " 构建失败," + task.FailReason)
task.Progress = "100%"
err = model.CacheUpdateUserQuota(task.UserId)
if err != nil {
log.Println("error update user quota cache: " + err.Error())
} else {
modelRatio := common.GetModelRatio(imageModel)
groupRatio := common.GetGroupRatio("default")
ratio := modelRatio * groupRatio
quota := int(ratio * 1 * 1000)
if quota != 0 {
err := model.IncreaseUserQuota(task.UserId, quota)
if err != nil {
log.Println("fail to increase user quota")
} }
logContent := fmt.Sprintf("%s 构图失败,补偿 %s", task.MjId, common.LogQuota(quota))
model.RecordLog(task.UserId, 1, logContent)
} }
} }
}
err = midjourneyTask.Update() err = task.Update()
if err != nil { if err != nil {
log.Printf("UpdateMidjourneyTaskFail: %v", err) log.Printf("UpdateMidjourneyTask error: %v", err)
}
log.Printf("UpdateMidjourneyTask: %v", midjourneyTask)
} }
log.Printf("UpdateMidjourneyTask success: %v", task)
} }
} }
} }
......
...@@ -27,15 +27,16 @@ func GetStatus(c *gin.Context) { ...@@ -27,15 +27,16 @@ func GetStatus(c *gin.Context) {
"wechat_qrcode": common.WeChatAccountQRCodeImageURL, "wechat_qrcode": common.WeChatAccountQRCodeImageURL,
"wechat_login": common.WeChatAuthEnabled, "wechat_login": common.WeChatAuthEnabled,
"server_address": common.ServerAddress, "server_address": common.ServerAddress,
"pay_address": common.PayAddress,
"epay_id": common.EpayId,
"epay_key": common.EpayKey,
"price": common.Price,
"turnstile_check": common.TurnstileCheckEnabled, "turnstile_check": common.TurnstileCheckEnabled,
"turnstile_site_key": common.TurnstileSiteKey, "turnstile_site_key": common.TurnstileSiteKey,
"top_up_link": common.TopUpLink, "top_up_link": common.TopUpLink,
"chat_link": common.ChatLink, "chat_link": common.ChatLink,
"quota_per_unit": common.QuotaPerUnit, "quota_per_unit": common.QuotaPerUnit,
"display_in_currency": common.DisplayInCurrencyEnabled, "display_in_currency": common.DisplayInCurrencyEnabled,
"normal_price": common.NormalPrice,
"stable_price": common.StablePrice,
"base_price": common.BasePrice,
}, },
}) })
return return
......
...@@ -356,6 +356,7 @@ func relayMidjourneySubmit(c *gin.Context, relayMode int) *MidjourneyResponse { ...@@ -356,6 +356,7 @@ func relayMidjourneySubmit(c *gin.Context, relayMode int) *MidjourneyResponse {
Status: "", Status: "",
Progress: "0%", Progress: "0%",
FailReason: "", FailReason: "",
ChannelId: c.GetInt("channel_id"),
} }
if midjResponse.Code == 4 || midjResponse.Code == 24 { if midjResponse.Code == 4 || midjResponse.Code == 24 {
midjourneyTask.FailReason = midjResponse.Description midjourneyTask.FailReason = midjResponse.Description
......
...@@ -105,7 +105,6 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode { ...@@ -105,7 +105,6 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
case common.ChannelTypeXunfei: case common.ChannelTypeXunfei:
apiType = APITypeXunfei apiType = APITypeXunfei
} }
isStable := c.GetBool("stable")
baseURL := common.ChannelBaseURLs[channelType] baseURL := common.ChannelBaseURLs[channelType]
requestURL := c.Request.URL.String() requestURL := c.Request.URL.String()
...@@ -189,15 +188,10 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode { ...@@ -189,15 +188,10 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
preConsumedTokens = promptTokens + textRequest.MaxTokens preConsumedTokens = promptTokens + textRequest.MaxTokens
} }
modelRatio := common.GetModelRatio(textRequest.Model) modelRatio := common.GetModelRatio(textRequest.Model)
stableRatio := modelRatio
groupRatio := common.GetGroupRatio(group) groupRatio := common.GetGroupRatio(group)
ratio := modelRatio * groupRatio ratio := modelRatio * groupRatio
preConsumedQuota := int(float64(preConsumedTokens) * ratio) preConsumedQuota := int(float64(preConsumedTokens) * ratio)
userQuota, err := model.CacheGetUserQuota(userId) userQuota, err := model.CacheGetUserQuota(userId)
if isStable {
stableRatio = (common.StablePrice / common.BasePrice) * modelRatio
ratio = stableRatio * groupRatio
}
if err != nil { if err != nil {
return errorWrapper(err, "get_user_quota_failed", http.StatusInternalServerError) return errorWrapper(err, "get_user_quota_failed", http.StatusInternalServerError)
} }
......
...@@ -24,31 +24,23 @@ type AmountRequest struct { ...@@ -24,31 +24,23 @@ type AmountRequest struct {
TopUpCode string `json:"top_up_code"` TopUpCode string `json:"top_up_code"`
} }
//var client, _ = epay.NewClientWithUrl(&epay.Config{ func GetEpayClient() *epay.Client {
// PartnerID: "1096", if common.PayAddress == "" || common.EpayId == "" || common.EpayKey == "" {
// Key: "n08V9LpE8JffA3NPP893689u8p39NV9J", return nil
//}, "https://api.lempay.org") }
withUrl, err := epay.NewClientWithUrl(&epay.Config{
var client, _ = epay.NewClientWithUrl(&epay.Config{ PartnerID: common.EpayId,
PartnerID: "1064", Key: common.EpayKey,
Key: "nqrrZ5RjR86mKP8rKkyrOY5Pg8NmYfKR", }, common.PayAddress)
}, "https://pay.yunjuw.cn") if err != nil {
return nil
func GetAmount(id int, count float64, topUpCode string) float64 {
amount := count * 1.5
if topUpCode != "" {
if topUpCode == "nekoapi" {
if id == 89 {
amount = count * 0.8
} else if id == 105 || id == 107 {
amount = count * 1.2
} else if id == 1 {
amount = count * 1
} else if id == 98 {
amount = count * 1.1
}
}
} }
return withUrl
}
func GetAmount(count float64) float64 {
// 别问为什么用float64,问就是这么点钱没必要
amount := count * float64(common.Price)
return amount return amount
} }
...@@ -60,38 +52,24 @@ func RequestEpay(c *gin.Context) { ...@@ -60,38 +52,24 @@ func RequestEpay(c *gin.Context) {
return return
} }
id := c.GetInt("id") id := c.GetInt("id")
amount := GetAmount(id, float64(req.Amount), req.TopUpCode) amount := GetAmount(float64(req.Amount))
if id != 1 {
if req.Amount < 10 {
c.JSON(200, gin.H{"message": "最小充值10元", "data": amount, "count": 10})
return
}
}
if req.PaymentMethod == "zfb" { if req.PaymentMethod == "zfb" {
if amount > 2000 {
c.JSON(200, gin.H{"message": "支付宝最大充值2000元", "data": amount, "count": 2000})
return
}
req.PaymentMethod = "alipay" req.PaymentMethod = "alipay"
} }
if req.PaymentMethod == "wx" { if req.PaymentMethod == "wx" {
if amount > 2000 {
c.JSON(200, gin.H{"message": "微信最大充值2000元", "data": amount, "count": 2000})
return
}
req.PaymentMethod = "wxpay" req.PaymentMethod = "wxpay"
} }
returnUrl, _ := url.Parse("https://nekoapi.com/log") returnUrl, _ := url.Parse(common.ServerAddress + "/log")
notifyUrl, _ := url.Parse("https://nekoapi.com/api/user/epay/notify") notifyUrl, _ := url.Parse(common.ServerAddress + "/api/user/epay/notify")
tradeNo := strconv.FormatInt(time.Now().Unix(), 10) tradeNo := strconv.FormatInt(time.Now().Unix(), 10)
payMoney := amount payMoney := amount
//if payMoney < 400 { client := GetEpayClient()
// payMoney = amount * 0.99 if client == nil {
// if amount-payMoney > 2 { c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"})
// payMoney = amount - 2 return
// } }
//}
uri, params, err := client.Purchase(&epay.PurchaseArgs{ uri, params, err := client.Purchase(&epay.PurchaseArgs{
Type: epay.PurchaseType(req.PaymentMethod), Type: epay.PurchaseType(req.PaymentMethod),
ServiceTradeNo: "A" + tradeNo, ServiceTradeNo: "A" + tradeNo,
...@@ -126,6 +104,14 @@ func EpayNotify(c *gin.Context) { ...@@ -126,6 +104,14 @@ func EpayNotify(c *gin.Context) {
r[t] = c.Request.URL.Query().Get(t) r[t] = c.Request.URL.Query().Get(t)
return r return r
}, map[string]string{}) }, map[string]string{})
client := GetEpayClient()
if client == nil {
log.Println("易支付回调失败 未找到配置信息")
_, err := c.Writer.Write([]byte("fail"))
if err != nil {
log.Println("易支付回调写入失败")
}
}
verifyInfo, err := client.Verify(params) verifyInfo, err := client.Verify(params)
if err == nil && verifyInfo.VerifyStatus { if err == nil && verifyInfo.VerifyStatus {
_, err := c.Writer.Write([]byte("success")) _, err := c.Writer.Write([]byte("success"))
...@@ -168,20 +154,9 @@ func RequestAmount(c *gin.Context) { ...@@ -168,20 +154,9 @@ func RequestAmount(c *gin.Context) {
var req AmountRequest var req AmountRequest
err := c.ShouldBindJSON(&req) err := c.ShouldBindJSON(&req)
if err != nil { if err != nil {
c.JSON(200, gin.H{"message": err.Error(), "data": 10}) c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
return return
} }
id := c.GetInt("id")
if id != 1 {
if req.Amount < 10 {
c.JSON(200, gin.H{"message": "最小充值10刀", "data": GetAmount(id, 10, req.TopUpCode), "count": 10})
return
}
//if req.Amount > 1500 {
// c.JSON(200, gin.H{"message": "最大充值1000刀", "data": GetAmount(id, 1000, req.TopUpCode), "count": 1500})
// return
//}
}
c.JSON(200, gin.H{"message": "success", "data": GetAmount(id, float64(req.Amount), req.TopUpCode)}) c.JSON(200, gin.H{"message": "success", "data": GetAmount(float64(req.Amount))})
} }
...@@ -95,51 +95,21 @@ func Distribute() func(c *gin.Context) { ...@@ -95,51 +95,21 @@ func Distribute() func(c *gin.Context) {
modelRequest.Model = "dall-e" modelRequest.Model = "dall-e"
} }
} }
isStable := false
channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, modelRequest.Model) channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, modelRequest.Model)
c.Set("stable", false)
if err != nil { if err != nil {
message := fmt.Sprintf("当前分组 %s 下对于模型 %s 无可用渠道", userGroup, modelRequest.Model) message := fmt.Sprintf("当前分组 %s 下对于模型 %s 无可用渠道", userGroup, modelRequest.Model)
if strings.HasPrefix(modelRequest.Model, "gpt-4") { if channel != nil {
common.SysLog("GPT-4低价渠道宕机,正在尝试转换") common.SysError(fmt.Sprintf("渠道不存在:%d", channel.Id))
nowUser, err := model.GetUserById(userId, false) message = "数据库一致性已被破坏,请联系管理员"
if err == nil {
if nowUser.StableMode {
userGroup = "svip"
//stableRatio = (common.StablePrice / common.BasePrice) * modelRatio
userMaxPrice, _ := strconv.ParseFloat(nowUser.MaxPrice, 64)
if userMaxPrice < common.StablePrice {
message = "当前低价通道不可用,稳定渠道价格为" + strconv.FormatFloat(common.StablePrice, 'f', -1, 64) + "R/刀"
} else {
//common.SysLog(fmt.Sprintf("用户 %s 使用稳定渠道", nowUser.Username))
channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, modelRequest.Model)
if err != nil {
message = "稳定渠道已经宕机,请联系管理员"
}
isStable = true
common.SysLog(fmt.Sprintf("用户 %s 使用稳定渠道 %v", nowUser.Username, channel))
c.Set("stable", true)
}
} else {
message = "当前低价通道不可用,请稍后再试,或者在后台开启稳定渠道模式"
}
}
}
//if channel == nil {
// common.SysError(fmt.Sprintf("渠道不存在:%d", channel.Id))
// message = "数据库一致性已被破坏,请联系管理员"
//}
if !isStable {
c.JSON(http.StatusInternalServerError, gin.H{
"error": gin.H{
"message": message,
"type": "one_api_error",
},
})
c.Abort()
return
} }
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": gin.H{
"message": message,
"type": "one_api_error",
},
})
c.Abort()
return
} }
} }
c.Set("channel", channel.Type) c.Set("channel", channel.Type)
......
...@@ -17,6 +17,7 @@ type Midjourney struct { ...@@ -17,6 +17,7 @@ type Midjourney struct {
Status string `json:"status"` Status string `json:"status"`
Progress string `json:"progress"` Progress string `json:"progress"`
FailReason string `json:"fail_reason"` FailReason string `json:"fail_reason"`
ChannelId int `json:"channel_id"`
} }
func GetAllUserTask(userId int, startIdx int, num int) []*Midjourney { func GetAllUserTask(userId int, startIdx int, num int) []*Midjourney {
......
...@@ -53,6 +53,10 @@ func InitOptionMap() { ...@@ -53,6 +53,10 @@ func InitOptionMap() {
common.OptionMap["SystemName"] = common.SystemName common.OptionMap["SystemName"] = common.SystemName
common.OptionMap["Logo"] = common.Logo common.OptionMap["Logo"] = common.Logo
common.OptionMap["ServerAddress"] = "" common.OptionMap["ServerAddress"] = ""
common.OptionMap["PayAddress"] = ""
common.OptionMap["EpayId"] = ""
common.OptionMap["EpayKey"] = ""
common.OptionMap["Price"] = strconv.Itoa(common.Price)
common.OptionMap["GitHubClientId"] = "" common.OptionMap["GitHubClientId"] = ""
common.OptionMap["GitHubClientSecret"] = "" common.OptionMap["GitHubClientSecret"] = ""
common.OptionMap["WeChatServerAddress"] = "" common.OptionMap["WeChatServerAddress"] = ""
...@@ -71,9 +75,6 @@ func InitOptionMap() { ...@@ -71,9 +75,6 @@ func InitOptionMap() {
common.OptionMap["ChatLink"] = common.ChatLink common.OptionMap["ChatLink"] = common.ChatLink
common.OptionMap["QuotaPerUnit"] = strconv.FormatFloat(common.QuotaPerUnit, 'f', -1, 64) common.OptionMap["QuotaPerUnit"] = strconv.FormatFloat(common.QuotaPerUnit, 'f', -1, 64)
common.OptionMap["RetryTimes"] = strconv.Itoa(common.RetryTimes) common.OptionMap["RetryTimes"] = strconv.Itoa(common.RetryTimes)
common.OptionMap["NormalPrice"] = strconv.FormatFloat(common.NormalPrice, 'f', -1, 64)
common.OptionMap["StablePrice"] = strconv.FormatFloat(common.StablePrice, 'f', -1, 64)
common.OptionMap["BasePrice"] = strconv.FormatFloat(common.BasePrice, 'f', -1, 64)
common.OptionMapRWMutex.Unlock() common.OptionMapRWMutex.Unlock()
loadOptionsFromDatabase() loadOptionsFromDatabase()
...@@ -157,8 +158,6 @@ func updateOptionMap(key string, value string) (err error) { ...@@ -157,8 +158,6 @@ func updateOptionMap(key string, value string) (err error) {
common.LogConsumeEnabled = boolValue common.LogConsumeEnabled = boolValue
case "DisplayInCurrencyEnabled": case "DisplayInCurrencyEnabled":
common.DisplayInCurrencyEnabled = boolValue common.DisplayInCurrencyEnabled = boolValue
case "DisplayTokenStatEnabled":
common.DisplayTokenStatEnabled = boolValue
} }
} }
switch key { switch key {
...@@ -177,6 +176,14 @@ func updateOptionMap(key string, value string) (err error) { ...@@ -177,6 +176,14 @@ func updateOptionMap(key string, value string) (err error) {
common.SMTPToken = value common.SMTPToken = value
case "ServerAddress": case "ServerAddress":
common.ServerAddress = value common.ServerAddress = value
case "PayAddress":
common.PayAddress = value
case "EpayId":
common.EpayId = value
case "EpayKey":
common.EpayKey = value
case "Price":
common.Price, _ = strconv.Atoi(value)
case "GitHubClientId": case "GitHubClientId":
common.GitHubClientId = value common.GitHubClientId = value
case "GitHubClientSecret": case "GitHubClientSecret":
...@@ -217,12 +224,6 @@ func updateOptionMap(key string, value string) (err error) { ...@@ -217,12 +224,6 @@ func updateOptionMap(key string, value string) (err error) {
common.TopUpLink = value common.TopUpLink = value
case "ChatLink": case "ChatLink":
common.ChatLink = value common.ChatLink = value
case "NormalPrice":
common.NormalPrice, _ = strconv.ParseFloat(value, 64)
case "BasePrice":
common.BasePrice, _ = strconv.ParseFloat(value, 64)
case "StablePrice":
common.StablePrice, _ = strconv.ParseFloat(value, 64)
case "ChannelDisableThreshold": case "ChannelDisableThreshold":
common.ChannelDisableThreshold, _ = strconv.ParseFloat(value, 64) common.ChannelDisableThreshold, _ = strconv.ParseFloat(value, 64)
case "QuotaPerUnit": case "QuotaPerUnit":
......
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import {Button, Input, Checkbox, Divider, Form, Header, Image, Message, Modal} from 'semantic-ui-react'; import { Button, Divider, Form, Header, Image, Message, Modal } from 'semantic-ui-react';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers'; import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers';
import Turnstile from 'react-turnstile'; import Turnstile from 'react-turnstile';
import { UserContext } from '../context/User'; import { UserContext } from '../context/User';
const PersonalSetting = () => { const PersonalSetting = () => {
const [userState, userDispatch] = useContext(UserContext); const [userState, userDispatch] = useContext(UserContext);
let navigate = useNavigate(); let navigate = useNavigate();
const [inputs, setInputs] = useState({ const [inputs, setInputs] = useState({
wechat_verification_code: '', wechat_verification_code: '',
...@@ -15,10 +15,6 @@ const PersonalSetting = () => { ...@@ -15,10 +15,6 @@ const PersonalSetting = () => {
email: '', email: '',
self_account_deletion_confirmation: '' self_account_deletion_confirmation: ''
}); });
const [stableMode, setStableMode] = useState({
stableMode: false,
maxPrice: 7,
});
const [status, setStatus] = useState({}); const [status, setStatus] = useState({});
const [showWeChatBindModal, setShowWeChatBindModal] = useState(false); const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
const [showEmailBindModal, setShowEmailBindModal] = useState(false); const [showEmailBindModal, setShowEmailBindModal] = useState(false);
...@@ -29,12 +25,8 @@ const PersonalSetting = () => { ...@@ -29,12 +25,8 @@ const PersonalSetting = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [disableButton, setDisableButton] = useState(false); const [disableButton, setDisableButton] = useState(false);
const [countdown, setCountdown] = useState(30); const [countdown, setCountdown] = useState(30);
const [affLink, setAffLink] = useState(""); const [affLink, setAffLink] = useState("");
const [systemToken, setSystemToken] = useState(""); const [systemToken, setSystemToken] = useState("");
// setStableMode(userState.user.stableMode, userState.user.maxPrice);
console.log(userState.user)
useEffect(() => { useEffect(() => {
let status = localStorage.getItem('status'); let status = localStorage.getItem('status');
...@@ -46,9 +38,6 @@ const PersonalSetting = () => { ...@@ -46,9 +38,6 @@ const PersonalSetting = () => {
setTurnstileSiteKey(status.turnstile_site_key); setTurnstileSiteKey(status.turnstile_site_key);
} }
} }
// if (userState.user !== undefined) {
// setStableMode(userState.user.stable_mode, userState.user.max_price);
// }
}, []); }, []);
useEffect(() => { useEffect(() => {
...@@ -64,30 +53,17 @@ const PersonalSetting = () => { ...@@ -64,30 +53,17 @@ const PersonalSetting = () => {
return () => clearInterval(countdownInterval); // Clean up on unmount return () => clearInterval(countdownInterval); // Clean up on unmount
}, [disableButton, countdown]); }, [disableButton, countdown]);
useEffect(() => { const handleInputChange = (e, { name, value }) => {
if (userState.user !== undefined) { setInputs((inputs) => ({ ...inputs, [name]: value }));
setStableMode({
stableMode: userState.user.stable_mode,
maxPrice: userState.user.max_price
})
// if (stableMode.localMaxPrice !== userState.user.max_price) {
// setStableMode({
// localMaxPrice: userState.user.max_price
// })
// }
}
}, [userState]);
const handleInputChange = (e, {name, value}) => {
setInputs((inputs) => ({...inputs, [name]: value}));
}; };
const generateAccessToken = async () => { const generateAccessToken = async () => {
const res = await API.get('/api/user/token'); const res = await API.get('/api/user/token');
const {success, message, data} = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
setSystemToken(data); setSystemToken(data);
setAffLink("");await copy(data); setAffLink("");
await copy(data);
showSuccess(`令牌已重置并已复制到剪贴板`); showSuccess(`令牌已重置并已复制到剪贴板`);
} else { } else {
showError(message); showError(message);
...@@ -96,11 +72,12 @@ const PersonalSetting = () => { ...@@ -96,11 +72,12 @@ const PersonalSetting = () => {
const getAffLink = async () => { const getAffLink = async () => {
const res = await API.get('/api/user/aff'); const res = await API.get('/api/user/aff');
const {success, message, data} = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
let link = `${window.location.origin}/register?aff=${data}`; let link = `${window.location.origin}/register?aff=${data}`;
setAffLink(link); setAffLink(link);
setSystemToken("");await copy(link); setSystemToken("");
await copy(link);
showSuccess(`邀请链接已复制到剪切板`); showSuccess(`邀请链接已复制到剪切板`);
} else { } else {
showError(message); showError(message);
...@@ -108,95 +85,95 @@ const PersonalSetting = () => { ...@@ -108,95 +85,95 @@ const PersonalSetting = () => {
}; };
const handleAffLinkClick = async (e) => { const handleAffLinkClick = async (e) => {
e.target.select(); e.target.select();
await copy(e.target.value); await copy(e.target.value);
showSuccess(`邀请链接已复制到剪切板`); showSuccess(`邀请链接已复制到剪切板`);
}; };
const handleSystemTokenClick = async (e) => {
e.target.select();
await copy(e.target.value);
showSuccess(`系统令牌已复制到剪切板`);
};
const handleSystemTokenClick = async (e) => { const deleteAccount = async () => {
e.target.select();
await copy(e.target.value);
showSuccess(`系统令牌已复制到剪切板`);
};const deleteAccount = async () => {
if (inputs.self_account_deletion_confirmation !== userState.user.username) { if (inputs.self_account_deletion_confirmation !== userState.user.username) {
showError('请输入你的账户名以确认删除!'); showError('请输入你的账户名以确认删除!');
return; return;
} }
const res = await API.delete('/api/user/self'); const res = await API.delete('/api/user/self');
const { success, message } = res.data; const { success, message } = res.data;
if (success) { if (success) {
showSuccess('账户已删除!'); showSuccess('账户已删除!');
await API.get('/api/user/logout'); await API.get('/api/user/logout');
userDispatch({ type: 'logout' }); userDispatch({ type: 'logout' });
localStorage.removeItem('user'); localStorage.removeItem('user');
navigate('/login'); navigate('/login');
} else { } else {
showError(message); 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 openGitHubOAuth = () => { const bindWeChat = async () => {
window.open( if (inputs.wechat_verification_code === '') return;
`https://github.com/login/oauth/authorize?client_id=${status.github_client_id}&scope=user:email` 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 sendVerificationCode = async () => { const openGitHubOAuth = () => {
setDisableButton(true); window.open(
if (inputs.email === '') return; `https://github.com/login/oauth/authorize?client_id=${status.github_client_id}&scope=user:email`
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 () => { const sendVerificationCode = async () => {
if (inputs.email_verification_code === '') return; setDisableButton(true);
setLoading(true); if (inputs.email === '') return;
const res = await API.get( if (turnstileEnabled && turnstileToken === '') {
`/api/oauth/email/bind?email=${inputs.email}&code=${inputs.email_verification_code}` showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
); return;
const { success, message } = res.data; }
if (success) { setLoading(true);
showSuccess('邮箱账户绑定成功!'); const res = await API.get(
setShowEmailBindModal(false); `/api/verification?email=${inputs.email}&turnstile=${turnstileToken}`
} else { );
showError(message); const { success, message } = res.data;
} if (success) {
setLoading(false); showSuccess('验证码发送成功,请检查邮箱!');
}; } else {
showError(message);
}
setLoading(false);
};
// const setStableMod = ; 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 ( return (
<div style={{lineHeight: '40px'}}> <div style={{ lineHeight: '40px' }}>
<Header as='h3'>通用设置</Header> <Header as='h3'>通用设置</Header>
<Message> <Message>
注意,此处生成的令牌用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。 注意,此处生成的令牌用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。
...@@ -209,84 +186,26 @@ const PersonalSetting = () => { ...@@ -209,84 +186,26 @@ const PersonalSetting = () => {
<Button onClick={() => { <Button onClick={() => {
setShowAccountDeleteModal(true); setShowAccountDeleteModal(true);
}}>删除个人账户</Button> }}>删除个人账户</Button>
<Divider/>
<Header as='h3'>GPT-4消费设置</Header>
<Form>
<Form.Field>
<Checkbox label="启用稳定模式(当低价渠道宕机时,自动选择已开启的渠道,以保证稳定性,仅影响GPT-4)"
checked={stableMode.stableMode}
onChange={
(e, data) => {
setStableMode({
...stableMode,
stableMode: data.checked
})
}
}
></Checkbox>
</Form.Field>
<Form.Field
control={Input}
label='最高接受价格(n元/刀)'
placeholder='7'
type={'number'}
value={stableMode.maxPrice}
onChange={
(e, data) => {
setStableMode({
...stableMode,
maxPrice: data.value
})
}
}
>
{/*<label></label>*/}
{/*<input placeholder='7' value= {stableMode.maxPrice}/>*/}
</Form.Field>
<Button type='submit' onClick={
async (e, data) => {
if (stableMode.localMaxPrice === '') return;
// console.log(data)
// post to /api/user/set_stable_mode
const res = await API.post(`/api/user/set_stable_mode`, stableMode)
const {success, message} = res.data;
if (success) {
// userDispatch({type: 'stable_mode', payload: stableMode})
userState.user.stable_mode = stableMode.stableMode
userState.user.max_price = stableMode.maxPrice
localStorage.setItem('user', JSON.stringify(userState.user));
showSuccess('设置成功!');
} else {
showError(message);
}
}
}>保存消费设置</Button>
</Form>
{/*<Checkbox label="启用稳定模式(当低价渠道宕机时,自动选择已开启的渠道,以保证稳定性)" onChange={*/}
{/* (e, data) => {*/}
{/* // if (inputs.email_verification_code === '') return;*/}
{/* console.log(data)*/}
{/* }*/}
{/*}></Checkbox>*/}
{/*<Input label="最高接受价格(n元/刀)" type="integer"></Input>*/}
{systemToken && ( {systemToken && (
<Form.Input <Form.Input
fluid fluid
readOnly readOnly
value={systemToken} value={systemToken}
onClick={handleSystemTokenClick} onClick={handleSystemTokenClick}
style={{ marginTop: '10px' }} style={{ marginTop: '10px' }}
/> />
)} )}
{affLink && ( {affLink && (
<Form.Input <Form.Input
fluid fluid
readOnly readOnly
value={affLink} value={affLink}
onClick={handleAffLinkClick} onClick={handleAffLinkClick}
style={{ marginTop: '10px' }} style={{ marginTop: '10px' }}
/> />
)}<Divider/> )}
<Divider />
<Header as='h3'>账号绑定</Header> <Header as='h3'>账号绑定</Header>
{ {
status.wechat_login && ( status.wechat_login && (
...@@ -307,8 +226,8 @@ const PersonalSetting = () => { ...@@ -307,8 +226,8 @@ const PersonalSetting = () => {
> >
<Modal.Content> <Modal.Content>
<Modal.Description> <Modal.Description>
<Image src={status.wechat_qrcode} fluid/> <Image src={status.wechat_qrcode} fluid />
<div style={{textAlign: 'center'}}> <div style={{ textAlign: 'center' }}>
<p> <p>
微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效) 微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)
</p> </p>
...@@ -345,7 +264,7 @@ const PersonalSetting = () => { ...@@ -345,7 +264,7 @@ const PersonalSetting = () => {
onOpen={() => setShowEmailBindModal(true)} onOpen={() => setShowEmailBindModal(true)}
open={showEmailBindModal} open={showEmailBindModal}
size={'tiny'} size={'tiny'}
style={{maxWidth: '450px'}} style={{ maxWidth: '450px' }}
> >
<Modal.Header>绑定邮箱地址</Modal.Header> <Modal.Header>绑定邮箱地址</Modal.Header>
<Modal.Content> <Modal.Content>
...@@ -381,25 +300,25 @@ const PersonalSetting = () => { ...@@ -381,25 +300,25 @@ const PersonalSetting = () => {
<></> <></>
)} )}
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}> <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
<Button <Button
color='' color=''
fluid fluid
size='large' size='large'
onClick={bindEmail} onClick={bindEmail}
loading={loading} loading={loading}
> >
确认绑定 确认绑定
</Button> </Button>
<div style={{ width: '1rem' }}></div> <div style={{ width: '1rem' }}></div>
<Button <Button
fluid fluid
size='large' size='large'
onClick={() => setShowEmailBindModal(false)} onClick={() => setShowEmailBindModal(false)}
> >
取消 取消
</Button> </Button>
</div> </div>
</Form> </Form>
</Modal.Description> </Modal.Description>
</Modal.Content> </Modal.Content>
</Modal> </Modal>
...@@ -408,11 +327,11 @@ const PersonalSetting = () => { ...@@ -408,11 +327,11 @@ const PersonalSetting = () => {
onOpen={() => setShowAccountDeleteModal(true)} onOpen={() => setShowAccountDeleteModal(true)}
open={showAccountDeleteModal} open={showAccountDeleteModal}
size={'tiny'} size={'tiny'}
style={{maxWidth: '450px'}} style={{ maxWidth: '450px' }}
> >
<Modal.Header>危险操作</Modal.Header> <Modal.Header>危险操作</Modal.Header>
<Modal.Content> <Modal.Content>
<Message>您正在删除自己的帐户,将清空所有数据且不可恢复</Message> <Message>您正在删除自己的帐户,将清空所有数据且不可恢复</Message>
<Modal.Description> <Modal.Description>
<Form size='large'> <Form size='large'>
<Form.Input <Form.Input
...@@ -431,24 +350,26 @@ const PersonalSetting = () => { ...@@ -431,24 +350,26 @@ const PersonalSetting = () => {
/> />
) : ( ) : (
<></> <></>
)}<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}> )}
<Button <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
color='red' <Button
fluid color='red'
size='large' fluid
onClick={deleteAccount} size='large'
loading={loading} onClick={deleteAccount}
> loading={loading}
确认删除 >
</Button><div style={{ width: '1rem' }}></div> 确认删除
<Button </Button>
fluid <div style={{ width: '1rem' }}></div>
size='large' <Button
onClick={() => setShowAccountDeleteModal(false)} fluid
> size='large'
取消 onClick={() => setShowAccountDeleteModal(false)}
</Button> >
</div> 取消
</Button>
</div>
</Form> </Form>
</Modal.Description> </Modal.Description>
</Modal.Content> </Modal.Content>
...@@ -457,4 +378,4 @@ const PersonalSetting = () => { ...@@ -457,4 +378,4 @@ const PersonalSetting = () => {
); );
}; };
export default PersonalSetting; export default PersonalSetting;
\ No newline at end of file
import React, { useEffect, useState } from 'react'; import React, {useEffect, useState} from 'react';
import { Button, Divider, Form, Grid, Header, Modal, Message } from 'semantic-ui-react'; import {Button, Divider, Form, Grid, Header, Modal, Message} from 'semantic-ui-react';
import { API, removeTrailingSlash, showError } from '../helpers'; import {API, removeTrailingSlash, showError} from '../helpers';
const SystemSetting = () => { const SystemSetting = () => {
let [inputs, setInputs] = useState({ let [inputs, setInputs] = useState({
PasswordLoginEnabled: '', PasswordLoginEnabled: '',
PasswordRegisterEnabled: '', PasswordRegisterEnabled: '',
EmailVerificationEnabled: '', EmailVerificationEnabled: '',
GitHubOAuthEnabled: '', GitHubOAuthEnabled: '',
GitHubClientId: '', GitHubClientId: '',
GitHubClientSecret: '', GitHubClientSecret: '',
Notice: '', Notice: '',
SMTPServer: '', SMTPServer: '',
SMTPPort: '', SMTPPort: '',
SMTPAccount: '', SMTPAccount: '',
SMTPFrom: '', SMTPFrom: '',
SMTPToken: '', SMTPToken: '',
ServerAddress: '', ServerAddress: '',
Footer: '', EpayId: '',
WeChatAuthEnabled: '', EpayKey: '',
WeChatServerAddress: '', Price: '',
WeChatServerToken: '', PayAddress: '',
WeChatAccountQRCodeImageURL: '', Footer: '',
TurnstileCheckEnabled: '', WeChatAuthEnabled: '',
TurnstileSiteKey: '', WeChatServerAddress: '',
TurnstileSecretKey: '', WeChatServerToken: '',
RegisterEnabled: '', WeChatAccountQRCodeImageURL: '',
EmailDomainRestrictionEnabled: '', TurnstileCheckEnabled: '',
EmailDomainWhitelist: '' TurnstileSiteKey: '',
}); TurnstileSecretKey: '',
const [originInputs, setOriginInputs] = useState({}); RegisterEnabled: '',
let [loading, setLoading] = useState(false); EmailDomainRestrictionEnabled: '',
const [EmailDomainWhitelist, setEmailDomainWhitelist] = useState([]); EmailDomainWhitelist: ''
const [restrictedDomainInput, setRestrictedDomainInput] = useState(''); });
const [showPasswordWarningModal, setShowPasswordWarningModal] = useState(false); const [originInputs, setOriginInputs] = useState({});
let [loading, setLoading] = useState(false);
const [EmailDomainWhitelist, setEmailDomainWhitelist] = useState([]);
const [restrictedDomainInput, setRestrictedDomainInput] = useState('');
const [showPasswordWarningModal, setShowPasswordWarningModal] = useState(false);
const getOptions = async () => { const getOptions = async () => {
const res = await API.get('/api/option/'); const res = await API.get('/api/option/');
const { success, message, data } = res.data; const {success, message, data} = res.data;
if (success) { if (success) {
let newInputs = {}; let newInputs = {};
data.forEach((item) => { data.forEach((item) => {
newInputs[item.key] = item.value; newInputs[item.key] = item.value;
}); });
setInputs({ setInputs({
...newInputs, ...newInputs,
EmailDomainWhitelist: newInputs.EmailDomainWhitelist.split(',') EmailDomainWhitelist: newInputs.EmailDomainWhitelist.split(',')
}); });
setOriginInputs(newInputs); setOriginInputs(newInputs);
setEmailDomainWhitelist(newInputs.EmailDomainWhitelist.split(',').map((item) => { setEmailDomainWhitelist(newInputs.EmailDomainWhitelist.split(',').map((item) => {
return { key: item, text: item, value: item }; return {key: item, text: item, value: item};
})); }));
} else { } else {
showError(message); showError(message);
} }
}; };
useEffect(() => { useEffect(() => {
getOptions().then(); getOptions().then();
}, []); }, []);
const updateOption = async (key, value) => { const updateOption = async (key, value) => {
setLoading(true); setLoading(true);
switch (key) { switch (key) {
case 'PasswordLoginEnabled': case 'PasswordLoginEnabled':
case 'PasswordRegisterEnabled': case 'PasswordRegisterEnabled':
case 'EmailVerificationEnabled': case 'EmailVerificationEnabled':
case 'GitHubOAuthEnabled': case 'GitHubOAuthEnabled':
case 'WeChatAuthEnabled': case 'WeChatAuthEnabled':
case 'TurnstileCheckEnabled': case 'TurnstileCheckEnabled':
case 'EmailDomainRestrictionEnabled': case 'EmailDomainRestrictionEnabled':
case 'RegisterEnabled': case 'RegisterEnabled':
value = inputs[key] === 'true' ? 'false' : 'true'; value = inputs[key] === 'true' ? 'false' : 'true';
break; break;
default: default:
break; break;
} }
const res = await API.put('/api/option/', { const res = await API.put('/api/option/', {
key, key,
value value
}); });
const { success, message } = res.data; const {success, message} = res.data;
if (success) { if (success) {
if (key === 'EmailDomainWhitelist') { if (key === 'EmailDomainWhitelist') {
value = value.split(','); value = value.split(',');
} }
setInputs((inputs) => ({ setInputs((inputs) => ({
...inputs, [key]: value ...inputs, [key]: value
})); }));
} else { } else {
showError(message); showError(message);
} }
setLoading(false); setLoading(false);
}; };
const handleInputChange = async (e, { name, value }) => { const handleInputChange = async (e, {name, value}) => {
if (name === 'PasswordLoginEnabled' && inputs[name] === 'true') { if (name === 'PasswordLoginEnabled' && inputs[name] === 'true') {
// block disabling password login // block disabling password login
setShowPasswordWarningModal(true); setShowPasswordWarningModal(true);
return; return;
} }
if ( if (
name === 'Notice' || name === 'Notice' ||
name.startsWith('SMTP') || name.startsWith('SMTP') ||
name === 'ServerAddress' || name === 'ServerAddress' ||
name === 'GitHubClientId' || name === 'EpayId' ||
name === 'GitHubClientSecret' || name === 'EpayKey' ||
name === 'WeChatServerAddress' || name === 'Price' ||
name === 'WeChatServerToken' || name === 'PayAddress' ||
name === 'WeChatAccountQRCodeImageURL' || name === 'GitHubClientId' ||
name === 'TurnstileSiteKey' || name === 'GitHubClientSecret' ||
name === 'TurnstileSecretKey' || name === 'WeChatServerAddress' ||
name === 'EmailDomainWhitelist' name === 'WeChatServerToken' ||
) { name === 'WeChatAccountQRCodeImageURL' ||
setInputs((inputs) => ({ ...inputs, [name]: value })); name === 'TurnstileSiteKey' ||
} else { name === 'TurnstileSecretKey' ||
await updateOption(name, value); name === 'EmailDomainWhitelist'
} ) {
}; setInputs((inputs) => ({...inputs, [name]: value}));
} else {
await updateOption(name, value);
}
};
const submitServerAddress = async () => { const submitServerAddress = async () => {
let ServerAddress = removeTrailingSlash(inputs.ServerAddress); let ServerAddress = removeTrailingSlash(inputs.ServerAddress);
await updateOption('ServerAddress', ServerAddress); await updateOption('ServerAddress', ServerAddress);
}; };
const submitSMTP = async () => { const submitPayAddress = async () => {
if (originInputs['SMTPServer'] !== inputs.SMTPServer) { if (inputs.ServerAddress === '') {
await updateOption('SMTPServer', inputs.SMTPServer); showError('请先填写服务器地址');
} return
if (originInputs['SMTPAccount'] !== inputs.SMTPAccount) { }
await updateOption('SMTPAccount', inputs.SMTPAccount); let PayAddress = removeTrailingSlash(inputs.PayAddress);
} await updateOption('PayAddress', PayAddress);
if (originInputs['SMTPFrom'] !== inputs.SMTPFrom) { await updateOption('EpayId', inputs.EpayId);
await updateOption('SMTPFrom', inputs.SMTPFrom); await updateOption('EpayKey', inputs.EpayKey);
} await updateOption('Price', inputs.Price);
if ( };
originInputs['SMTPPort'] !== inputs.SMTPPort &&
inputs.SMTPPort !== ''
) {
await updateOption('SMTPPort', inputs.SMTPPort);
}
if (
originInputs['SMTPToken'] !== inputs.SMTPToken &&
inputs.SMTPToken !== ''
) {
await updateOption('SMTPToken', inputs.SMTPToken);
}
};
const submitSMTP = async () => {
if (originInputs['SMTPServer'] !== inputs.SMTPServer) {
await updateOption('SMTPServer', inputs.SMTPServer);
}
if (originInputs['SMTPAccount'] !== inputs.SMTPAccount) {
await updateOption('SMTPAccount', inputs.SMTPAccount);
}
if (originInputs['SMTPFrom'] !== inputs.SMTPFrom) {
await updateOption('SMTPFrom', inputs.SMTPFrom);
}
if (
originInputs['SMTPPort'] !== inputs.SMTPPort &&
inputs.SMTPPort !== ''
) {
await updateOption('SMTPPort', inputs.SMTPPort);
}
if (
originInputs['SMTPToken'] !== inputs.SMTPToken &&
inputs.SMTPToken !== ''
) {
await updateOption('SMTPToken', inputs.SMTPToken);
}
};
const submitEmailDomainWhitelist = async () => {
if (
originInputs['EmailDomainWhitelist'] !== inputs.EmailDomainWhitelist.join(',') &&
inputs.SMTPToken !== ''
) {
await updateOption('EmailDomainWhitelist', inputs.EmailDomainWhitelist.join(','));
}
};
const submitWeChat = async () => { const submitEmailDomainWhitelist = async () => {
if (originInputs['WeChatServerAddress'] !== inputs.WeChatServerAddress) { if (
await updateOption( originInputs['EmailDomainWhitelist'] !== inputs.EmailDomainWhitelist.join(',') &&
'WeChatServerAddress', inputs.SMTPToken !== ''
removeTrailingSlash(inputs.WeChatServerAddress) ) {
); await updateOption('EmailDomainWhitelist', inputs.EmailDomainWhitelist.join(','));
} }
if ( };
originInputs['WeChatAccountQRCodeImageURL'] !==
inputs.WeChatAccountQRCodeImageURL
) {
await updateOption(
'WeChatAccountQRCodeImageURL',
inputs.WeChatAccountQRCodeImageURL
);
}
if (
originInputs['WeChatServerToken'] !== inputs.WeChatServerToken &&
inputs.WeChatServerToken !== ''
) {
await updateOption('WeChatServerToken', inputs.WeChatServerToken);
}
};
const submitGitHubOAuth = async () => { const submitWeChat = async () => {
if (originInputs['GitHubClientId'] !== inputs.GitHubClientId) { if (originInputs['WeChatServerAddress'] !== inputs.WeChatServerAddress) {
await updateOption('GitHubClientId', inputs.GitHubClientId); await updateOption(
} 'WeChatServerAddress',
if ( removeTrailingSlash(inputs.WeChatServerAddress)
originInputs['GitHubClientSecret'] !== inputs.GitHubClientSecret && );
inputs.GitHubClientSecret !== '' }
) { if (
await updateOption('GitHubClientSecret', inputs.GitHubClientSecret); originInputs['WeChatAccountQRCodeImageURL'] !==
} inputs.WeChatAccountQRCodeImageURL
}; ) {
await updateOption(
'WeChatAccountQRCodeImageURL',
inputs.WeChatAccountQRCodeImageURL
);
}
if (
originInputs['WeChatServerToken'] !== inputs.WeChatServerToken &&
inputs.WeChatServerToken !== ''
) {
await updateOption('WeChatServerToken', inputs.WeChatServerToken);
}
};
const submitTurnstile = async () => { const submitGitHubOAuth = async () => {
if (originInputs['TurnstileSiteKey'] !== inputs.TurnstileSiteKey) { if (originInputs['GitHubClientId'] !== inputs.GitHubClientId) {
await updateOption('TurnstileSiteKey', inputs.TurnstileSiteKey); await updateOption('GitHubClientId', inputs.GitHubClientId);
} }
if ( if (
originInputs['TurnstileSecretKey'] !== inputs.TurnstileSecretKey && originInputs['GitHubClientSecret'] !== inputs.GitHubClientSecret &&
inputs.TurnstileSecretKey !== '' inputs.GitHubClientSecret !== ''
) { ) {
await updateOption('TurnstileSecretKey', inputs.TurnstileSecretKey); await updateOption('GitHubClientSecret', inputs.GitHubClientSecret);
} }
}; };
const submitNewRestrictedDomain = () => { const submitTurnstile = async () => {
const localDomainList = inputs.EmailDomainWhitelist; if (originInputs['TurnstileSiteKey'] !== inputs.TurnstileSiteKey) {
if (restrictedDomainInput !== '' && !localDomainList.includes(restrictedDomainInput)) { await updateOption('TurnstileSiteKey', inputs.TurnstileSiteKey);
setRestrictedDomainInput(''); }
setInputs({ if (
...inputs, originInputs['TurnstileSecretKey'] !== inputs.TurnstileSecretKey &&
EmailDomainWhitelist: [...localDomainList, restrictedDomainInput], inputs.TurnstileSecretKey !== ''
}); ) {
setEmailDomainWhitelist([...EmailDomainWhitelist, { await updateOption('TurnstileSecretKey', inputs.TurnstileSecretKey);
key: restrictedDomainInput, }
text: restrictedDomainInput, };
value: restrictedDomainInput,
}]); const submitNewRestrictedDomain = () => {
const localDomainList = inputs.EmailDomainWhitelist;
if (restrictedDomainInput !== '' && !localDomainList.includes(restrictedDomainInput)) {
setRestrictedDomainInput('');
setInputs({
...inputs,
EmailDomainWhitelist: [...localDomainList, restrictedDomainInput],
});
setEmailDomainWhitelist([...EmailDomainWhitelist, {
key: restrictedDomainInput,
text: restrictedDomainInput,
value: restrictedDomainInput,
}]);
}
} }
}
return ( return (
<Grid columns={1}> <Grid columns={1}>
<Grid.Column> <Grid.Column>
<Form loading={loading}> <Form loading={loading}>
<Header as='h3'>通用设置</Header> <Header as='h3'>通用设置</Header>
<Form.Group widths='equal'> <Form.Group widths='equal'>
<Form.Input <Form.Input
label='服务器地址' label='服务器地址'
placeholder='例如:https://yourdomain.com' placeholder='例如:https://yourdomain.com'
value={inputs.ServerAddress} value={inputs.ServerAddress}
name='ServerAddress' name='ServerAddress'
onChange={handleInputChange} onChange={handleInputChange}
/> />
</Form.Group> </Form.Group>
<Form.Button onClick={submitServerAddress}> <Form.Button onClick={submitServerAddress}>
更新服务器地址 更新服务器地址
</Form.Button> </Form.Button>
<Divider /> <Divider/>
<Header as='h3'>配置登录注册</Header> <Header as='h3'>支付设置(当前仅支持易支付接口,使用上方服务器地址作为回调地址!)</Header>
<Form.Group inline> <Form.Group widths='equal'>
<Form.Checkbox <Form.Input
checked={inputs.PasswordLoginEnabled === 'true'} label='支付地址,不填写则不启用在线支付'
label='允许通过密码进行登录' placeholder='例如:https://yourdomain.com'
name='PasswordLoginEnabled' value={inputs.PayAddress}
onChange={handleInputChange} name='PayAddress'
/> onChange={handleInputChange}
{ />
showPasswordWarningModal && <Form.Input
<Modal label='易支付商户ID'
open={showPasswordWarningModal} placeholder='例如:0001'
onClose={() => setShowPasswordWarningModal(false)} value={inputs.EpayId}
size={'tiny'} name='EpayId'
style={{ maxWidth: '450px' }} onChange={handleInputChange}
> />
<Modal.Header>警告</Modal.Header> <Form.Input
<Modal.Content> label='易支付商户密钥'
<p>取消密码登录将导致所有未绑定其他登录方式的用户(包括管理员)无法通过密码登录,确认取消?</p> placeholder='例如:dejhfueqhujasjmndbjkqaw'
</Modal.Content> value={inputs.EpayKey}
<Modal.Actions> name='EpayKey'
<Button onClick={() => setShowPasswordWarningModal(false)}>取消</Button> onChange={handleInputChange}
<Button />
color='yellow' <Form.Input
onClick={async () => { label='充值价格(x元/美金)'
setShowPasswordWarningModal(false); placeholder='例如:7,就是7元/美金'
await updateOption('PasswordLoginEnabled', 'false'); value={inputs.Price}
}} name='Price'
> type='number'
确定 min={0}
</Button> onChange={handleInputChange}
</Modal.Actions> />
</Modal> </Form.Group>
} <Form.Button onClick={submitPayAddress}>
<Form.Checkbox 更新支付地址
checked={inputs.PasswordRegisterEnabled === 'true'} </Form.Button>
label='允许通过密码进行注册' <Divider/>
name='PasswordRegisterEnabled' <Header as='h3'>配置登录注册</Header>
onChange={handleInputChange} <Form.Group inline>
/> <Form.Checkbox
<Form.Checkbox checked={inputs.PasswordLoginEnabled === 'true'}
checked={inputs.EmailVerificationEnabled === 'true'} label='允许通过密码进行登录'
label='通过密码注册时需要进行邮箱验证' name='PasswordLoginEnabled'
name='EmailVerificationEnabled' onChange={handleInputChange}
onChange={handleInputChange} />
/> {
<Form.Checkbox showPasswordWarningModal &&
checked={inputs.GitHubOAuthEnabled === 'true'} <Modal
label='允许通过 GitHub 账户登录 & 注册' open={showPasswordWarningModal}
name='GitHubOAuthEnabled' onClose={() => setShowPasswordWarningModal(false)}
onChange={handleInputChange} size={'tiny'}
/> style={{maxWidth: '450px'}}
<Form.Checkbox >
checked={inputs.WeChatAuthEnabled === 'true'} <Modal.Header>警告</Modal.Header>
label='允许通过微信登录 & 注册' <Modal.Content>
name='WeChatAuthEnabled' <p>取消密码登录将导致所有未绑定其他登录方式的用户(包括管理员)无法通过密码登录,确认取消?</p>
onChange={handleInputChange} </Modal.Content>
/> <Modal.Actions>
</Form.Group> <Button onClick={() => setShowPasswordWarningModal(false)}>取消</Button>
<Form.Group inline> <Button
<Form.Checkbox color='yellow'
checked={inputs.RegisterEnabled === 'true'} onClick={async () => {
label='允许新用户注册(此项为否时,新用户将无法以任何方式进行注册)' setShowPasswordWarningModal(false);
name='RegisterEnabled' await updateOption('PasswordLoginEnabled', 'false');
onChange={handleInputChange} }}
/> >
<Form.Checkbox 确定
checked={inputs.TurnstileCheckEnabled === 'true'} </Button>
label='启用 Turnstile 用户校验' </Modal.Actions>
name='TurnstileCheckEnabled' </Modal>
onChange={handleInputChange} }
/> <Form.Checkbox
</Form.Group> checked={inputs.PasswordRegisterEnabled === 'true'}
<Divider /> label='允许通过密码进行注册'
<Header as='h3'> name='PasswordRegisterEnabled'
配置邮箱域名白名单 onChange={handleInputChange}
<Header.Subheader>用以防止恶意用户利用临时邮箱批量注册</Header.Subheader> />
</Header> <Form.Checkbox
<Form.Group widths={3}> checked={inputs.EmailVerificationEnabled === 'true'}
<Form.Checkbox label='通过密码注册时需要进行邮箱验证'
label='启用邮箱域名白名单' name='EmailVerificationEnabled'
name='EmailDomainRestrictionEnabled' onChange={handleInputChange}
onChange={handleInputChange} />
checked={inputs.EmailDomainRestrictionEnabled === 'true'} <Form.Checkbox
/> checked={inputs.GitHubOAuthEnabled === 'true'}
</Form.Group> label='允许通过 GitHub 账户登录 & 注册'
<Form.Group widths={2}> name='GitHubOAuthEnabled'
<Form.Dropdown onChange={handleInputChange}
label='允许的邮箱域名' />
placeholder='允许的邮箱域名' <Form.Checkbox
name='EmailDomainWhitelist' checked={inputs.WeChatAuthEnabled === 'true'}
required label='允许通过微信登录 & 注册'
fluid name='WeChatAuthEnabled'
multiple onChange={handleInputChange}
selection />
onChange={handleInputChange} </Form.Group>
value={inputs.EmailDomainWhitelist} <Form.Group inline>
autoComplete='new-password' <Form.Checkbox
options={EmailDomainWhitelist} checked={inputs.RegisterEnabled === 'true'}
/> label='允许新用户注册(此项为否时,新用户将无法以任何方式进行注册)'
<Form.Input name='RegisterEnabled'
label='添加新的允许的邮箱域名' onChange={handleInputChange}
action={ />
<Button type='button' onClick={() => { <Form.Checkbox
submitNewRestrictedDomain(); checked={inputs.TurnstileCheckEnabled === 'true'}
}}>填入</Button> label='启用 Turnstile 用户校验'
} name='TurnstileCheckEnabled'
onKeyDown={(e) => { onChange={handleInputChange}
if (e.key === 'Enter') { />
submitNewRestrictedDomain(); </Form.Group>
} <Divider/>
}} <Header as='h3'>
autoComplete='new-password' 配置邮箱域名白名单
placeholder='输入新的允许的邮箱域名' <Header.Subheader>用以防止恶意用户利用临时邮箱批量注册</Header.Subheader>
value={restrictedDomainInput} </Header>
onChange={(e, { value }) => { <Form.Group widths={3}>
setRestrictedDomainInput(value); <Form.Checkbox
}} label='启用邮箱域名白名单'
/> name='EmailDomainRestrictionEnabled'
</Form.Group> onChange={handleInputChange}
<Form.Button onClick={submitEmailDomainWhitelist}>保存邮箱域名白名单设置</Form.Button> checked={inputs.EmailDomainRestrictionEnabled === 'true'}
<Divider /> />
<Header as='h3'> </Form.Group>
配置 SMTP <Form.Group widths={2}>
<Header.Subheader>用以支持系统的邮件发送</Header.Subheader> <Form.Dropdown
</Header> label='允许的邮箱域名'
<Form.Group widths={3}> placeholder='允许的邮箱域名'
<Form.Input name='EmailDomainWhitelist'
label='SMTP 服务器地址' required
name='SMTPServer' fluid
onChange={handleInputChange} multiple
autoComplete='new-password' selection
value={inputs.SMTPServer} onChange={handleInputChange}
placeholder='例如:smtp.qq.com' value={inputs.EmailDomainWhitelist}
/> autoComplete='new-password'
<Form.Input options={EmailDomainWhitelist}
label='SMTP 端口' />
name='SMTPPort' <Form.Input
onChange={handleInputChange} label='添加新的允许的邮箱域名'
autoComplete='new-password' action={
value={inputs.SMTPPort} <Button type='button' onClick={() => {
placeholder='默认: 587' submitNewRestrictedDomain();
/> }}>填入</Button>
<Form.Input }
label='SMTP 账户' onKeyDown={(e) => {
name='SMTPAccount' if (e.key === 'Enter') {
onChange={handleInputChange} submitNewRestrictedDomain();
autoComplete='new-password' }
value={inputs.SMTPAccount} }}
placeholder='通常是邮箱地址' autoComplete='new-password'
/> placeholder='输入新的允许的邮箱域名'
</Form.Group> value={restrictedDomainInput}
<Form.Group widths={3}> onChange={(e, {value}) => {
<Form.Input setRestrictedDomainInput(value);
label='SMTP 发送者邮箱' }}
name='SMTPFrom' />
onChange={handleInputChange} </Form.Group>
autoComplete='new-password' <Form.Button onClick={submitEmailDomainWhitelist}>保存邮箱域名白名单设置</Form.Button>
value={inputs.SMTPFrom} <Divider/>
placeholder='通常和邮箱地址保持一致' <Header as='h3'>
/> 配置 SMTP
<Form.Input <Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
label='SMTP 访问凭证' </Header>
name='SMTPToken' <Form.Group widths={3}>
onChange={handleInputChange} <Form.Input
type='password' label='SMTP 服务器地址'
autoComplete='new-password' name='SMTPServer'
checked={inputs.RegisterEnabled === 'true'} onChange={handleInputChange}
placeholder='敏感信息不会发送到前端显示' autoComplete='new-password'
/> value={inputs.SMTPServer}
</Form.Group> placeholder='例如:smtp.qq.com'
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button> />
<Divider /> <Form.Input
<Header as='h3'> label='SMTP 端口'
配置 GitHub OAuth App name='SMTPPort'
<Header.Subheader> onChange={handleInputChange}
用以支持通过 GitHub 进行登录注册, autoComplete='new-password'
<a href='https://github.com/settings/developers' target='_blank'> value={inputs.SMTPPort}
点击此处 placeholder='默认: 587'
</a> />
管理你的 GitHub OAuth App <Form.Input
</Header.Subheader> label='SMTP 账户'
</Header> name='SMTPAccount'
<Message> onChange={handleInputChange}
Homepage URL <code>{inputs.ServerAddress}</code> autoComplete='new-password'
Authorization callback URL {' '} value={inputs.SMTPAccount}
<code>{`${inputs.ServerAddress}/oauth/github`}</code> placeholder='通常是邮箱地址'
</Message> />
<Form.Group widths={3}> </Form.Group>
<Form.Input <Form.Group widths={3}>
label='GitHub Client ID' <Form.Input
name='GitHubClientId' label='SMTP 发送者邮箱'
onChange={handleInputChange} name='SMTPFrom'
autoComplete='new-password' onChange={handleInputChange}
value={inputs.GitHubClientId} autoComplete='new-password'
placeholder='输入你注册的 GitHub OAuth APP 的 ID' value={inputs.SMTPFrom}
/> placeholder='通常和邮箱地址保持一致'
<Form.Input />
label='GitHub Client Secret' <Form.Input
name='GitHubClientSecret' label='SMTP 访问凭证'
onChange={handleInputChange} name='SMTPToken'
type='password' onChange={handleInputChange}
autoComplete='new-password' type='password'
value={inputs.GitHubClientSecret} autoComplete='new-password'
placeholder='敏感信息不会发送到前端显示' checked={inputs.RegisterEnabled === 'true'}
/> placeholder='敏感信息不会发送到前端显示'
</Form.Group> />
<Form.Button onClick={submitGitHubOAuth}> </Form.Group>
保存 GitHub OAuth 设置 <Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
</Form.Button> <Divider/>
<Divider /> <Header as='h3'>
<Header as='h3'> 配置 GitHub OAuth App
配置 WeChat Server <Header.Subheader>
<Header.Subheader> 用以支持通过 GitHub 进行登录注册,
用以支持通过微信进行登录注册, <a href='https://github.com/settings/developers' target='_blank'>
<a 点击此处
href='https://github.com/songquanpeng/wechat-server' </a>
target='_blank' 管理你的 GitHub OAuth App
> </Header.Subheader>
点击此处 </Header>
</a> <Message>
了解 WeChat Server Homepage URL <code>{inputs.ServerAddress}</code>
</Header.Subheader> Authorization callback URL {' '}
</Header> <code>{`${inputs.ServerAddress}/oauth/github`}</code>
<Form.Group widths={3}> </Message>
<Form.Input <Form.Group widths={3}>
label='WeChat Server 服务器地址' <Form.Input
name='WeChatServerAddress' label='GitHub Client ID'
placeholder='例如:https://yourdomain.com' name='GitHubClientId'
onChange={handleInputChange} onChange={handleInputChange}
autoComplete='new-password' autoComplete='new-password'
value={inputs.WeChatServerAddress} value={inputs.GitHubClientId}
/> placeholder='输入你注册的 GitHub OAuth APP 的 ID'
<Form.Input />
label='WeChat Server 访问凭证' <Form.Input
name='WeChatServerToken' label='GitHub Client Secret'
type='password' name='GitHubClientSecret'
onChange={handleInputChange} onChange={handleInputChange}
autoComplete='new-password' type='password'
value={inputs.WeChatServerToken} autoComplete='new-password'
placeholder='敏感信息不会发送到前端显示' value={inputs.GitHubClientSecret}
/> placeholder='敏感信息不会发送到前端显示'
<Form.Input />
label='微信公众号二维码图片链接' </Form.Group>
name='WeChatAccountQRCodeImageURL' <Form.Button onClick={submitGitHubOAuth}>
onChange={handleInputChange} 保存 GitHub OAuth 设置
autoComplete='new-password' </Form.Button>
value={inputs.WeChatAccountQRCodeImageURL} <Divider/>
placeholder='输入一个图片链接' <Header as='h3'>
/> 配置 WeChat Server
</Form.Group> <Header.Subheader>
<Form.Button onClick={submitWeChat}> 用以支持通过微信进行登录注册,
保存 WeChat Server 设置 <a
</Form.Button> href='https://github.com/songquanpeng/wechat-server'
<Divider /> target='_blank'
<Header as='h3'> >
配置 Turnstile 点击此处
<Header.Subheader> </a>
用以支持用户校验, 了解 WeChat Server
<a href='https://dash.cloudflare.com/' target='_blank'> </Header.Subheader>
点击此处 </Header>
</a> <Form.Group widths={3}>
管理你的 Turnstile Sites,推荐选择 Invisible Widget Type <Form.Input
</Header.Subheader> label='WeChat Server 服务器地址'
</Header> name='WeChatServerAddress'
<Form.Group widths={3}> placeholder='例如:https://yourdomain.com'
<Form.Input onChange={handleInputChange}
label='Turnstile Site Key' autoComplete='new-password'
name='TurnstileSiteKey' value={inputs.WeChatServerAddress}
onChange={handleInputChange} />
autoComplete='new-password' <Form.Input
value={inputs.TurnstileSiteKey} label='WeChat Server 访问凭证'
placeholder='输入你注册的 Turnstile Site Key' name='WeChatServerToken'
/> type='password'
<Form.Input onChange={handleInputChange}
label='Turnstile Secret Key' autoComplete='new-password'
name='TurnstileSecretKey' value={inputs.WeChatServerToken}
onChange={handleInputChange} placeholder='敏感信息不会发送到前端显示'
type='password' />
autoComplete='new-password' <Form.Input
value={inputs.TurnstileSecretKey} label='微信公众号二维码图片链接'
placeholder='敏感信息不会发送到前端显示' name='WeChatAccountQRCodeImageURL'
/> onChange={handleInputChange}
</Form.Group> autoComplete='new-password'
<Form.Button onClick={submitTurnstile}> value={inputs.WeChatAccountQRCodeImageURL}
保存 Turnstile 设置 placeholder='输入一个图片链接'
</Form.Button> />
</Form> </Form.Group>
</Grid.Column> <Form.Button onClick={submitWeChat}>
</Grid> 保存 WeChat Server 设置
); </Form.Button>
<Divider/>
<Header as='h3'>
配置 Turnstile
<Header.Subheader>
用以支持用户校验,
<a href='https://dash.cloudflare.com/' target='_blank'>
点击此处
</a>
管理你的 Turnstile Sites,推荐选择 Invisible Widget Type
</Header.Subheader>
</Header>
<Form.Group widths={3}>
<Form.Input
label='Turnstile Site Key'
name='TurnstileSiteKey'
onChange={handleInputChange}
autoComplete='new-password'
value={inputs.TurnstileSiteKey}
placeholder='输入你注册的 Turnstile Site Key'
/>
<Form.Input
label='Turnstile Secret Key'
name='TurnstileSecretKey'
onChange={handleInputChange}
type='password'
autoComplete='new-password'
value={inputs.TurnstileSecretKey}
placeholder='敏感信息不会发送到前端显示'
/>
</Form.Group>
<Form.Button onClick={submitTurnstile}>
保存 Turnstile 设置
</Form.Button>
</Form>
</Grid.Column>
</Grid>
);
}; };
export default SystemSetting; export default SystemSetting;
import React, {useContext, useEffect, useState} from 'react'; import React, { useContext, useEffect, useState } from 'react';
import {Card, Grid, Header, Segment} from 'semantic-ui-react'; import { Card, Grid, Header, Segment } from 'semantic-ui-react';
import {API, showError, showNotice, timestamp2string} from '../../helpers'; import { API, showError, showNotice, timestamp2string } from '../../helpers';
import {StatusContext} from '../../context/Status'; import { StatusContext } from '../../context/Status';
import {marked} from 'marked'; import { marked } from 'marked';
const Home = () => { const Home = () => {
const [statusState, statusDispatch] = useContext(StatusContext); const [statusState, statusDispatch] = useContext(StatusContext);
...@@ -11,11 +11,12 @@ const Home = () => { ...@@ -11,11 +11,12 @@ const Home = () => {
const displayNotice = async () => { const displayNotice = async () => {
const res = await API.get('/api/notice'); const res = await API.get('/api/notice');
const {success, message, data} = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
let oldNotice = localStorage.getItem('notice'); let oldNotice = localStorage.getItem('notice');
if (data !== oldNotice && data !== '') { if (data !== oldNotice && data !== '') {
showNotice(data); const htmlNotice = marked(data);
showNotice(htmlNotice, true);
localStorage.setItem('notice', data); localStorage.setItem('notice', data);
} }
} else { } else {
...@@ -26,7 +27,7 @@ const Home = () => { ...@@ -26,7 +27,7 @@ const Home = () => {
const displayHomePageContent = async () => { const displayHomePageContent = async () => {
setHomePageContent(localStorage.getItem('home_page_content') || ''); setHomePageContent(localStorage.getItem('home_page_content') || '');
const res = await API.get('/api/home_page_content'); const res = await API.get('/api/home_page_content');
const {success, message, data} = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
let content = data; let content = data;
if (!data.startsWith('https://')) { if (!data.startsWith('https://')) {
...@@ -53,20 +54,28 @@ const Home = () => { ...@@ -53,20 +54,28 @@ const Home = () => {
return ( return (
<> <>
{ {
// homePageContentLoaded && homePageContent === '' ? homePageContentLoaded && homePageContent === '' ? <>
<>
<Segment> <Segment>
<Header as='h3'>当前状态</Header> <Header as='h3'>系统状况</Header>
<Grid columns={2} stackable> <Grid columns={2} stackable>
<Grid.Column> <Grid.Column>
<Card fluid> <Card fluid>
<Card.Content> <Card.Content>
<Card.Header>GPT-3.5</Card.Header> <Card.Header>系统信息</Card.Header>
<Card.Meta>信息总览</Card.Meta> <Card.Meta>系统信息总览</Card.Meta>
<Card.Description> <Card.Description>
<p>通道:官方通道</p> <p>名称:{statusState?.status?.system_name}</p>
<p>状态:存活</p> <p>版本:{statusState?.status?.version ? statusState?.status?.version : "unknown"}</p>
<p>价格:{statusState?.status?.base_price}R&nbsp;/&nbsp;刀</p> <p>
源码:
<a
href='https://github.com/songquanpeng/one-api'
target='_blank'
>
https://github.com/songquanpeng/one-api
</a>
</p>
<p>启动时间:{getStartTimeString()}</p>
</Card.Description> </Card.Description>
</Card.Content> </Card.Content>
</Card> </Card>
...@@ -74,26 +83,32 @@ const Home = () => { ...@@ -74,26 +83,32 @@ const Home = () => {
<Grid.Column> <Grid.Column>
<Card fluid> <Card fluid>
<Card.Content> <Card.Content>
<Card.Header>GPT-4</Card.Header> <Card.Header>系统配置</Card.Header>
<Card.Meta>信息总览</Card.Meta> <Card.Meta>系统配置总览</Card.Meta>
<Card.Description> <Card.Description>
<p>通道:官方通道|低价通道</p>
<p> <p>
状态: 邮箱验证:
{statusState?.status?.stable_price===-1? {statusState?.status?.email_verification === true
<span style={{color:'red'}}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> ? '已启用'
: : '未启用'}
<span style={{color:'green'}}>&emsp;&emsp;</span> </p>
} <p>
GitHub 身份验证:
{statusState?.status?.normal_price===-1? {statusState?.status?.github_oauth === true
<span style={{color:'red'}}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> ? '已启用'
: : '未启用'}
<span style={{color:'green'}}>&emsp;&emsp;</span>
}
</p> </p>
<p> <p>
价格:{statusState?.status?.stable_price}R&nbsp;/&nbsp;刀|{statusState?.status?.normal_price}R&nbsp;/&nbsp; 微信身份验证:
{statusState?.status?.wechat_login === true
? '已启用'
: '未启用'}
</p>
<p>
Turnstile 用户校验:
{statusState?.status?.turnstile_check === true
? '已启用'
: '未启用'}
</p> </p>
</Card.Description> </Card.Description>
</Card.Content> </Card.Content>
...@@ -101,21 +116,18 @@ const Home = () => { ...@@ -101,21 +116,18 @@ const Home = () => {
</Grid.Column> </Grid.Column>
</Grid> </Grid>
</Segment> </Segment>
{ </> : <>
homePageContent.startsWith('https://') ? <iframe {
src={homePageContent} homePageContent.startsWith('https://') ? <iframe
style={{ width: '100%', height: '100vh', border: 'none' }} src={homePageContent}
/> : <div style={{ fontSize: 'larger' }} dangerouslySetInnerHTML={{ __html: homePageContent }}></div> style={{ width: '100%', height: '100vh', border: 'none' }}
} /> : <div style={{ fontSize: 'larger' }} dangerouslySetInnerHTML={{ __html: homePageContent }}></div>
}
</> </>
// :
// <>
// </>
} }
</> </>
); );
}; };
export default Home; export default Home;
\ No newline at end of file
...@@ -78,9 +78,9 @@ const TopUp = () => { ...@@ -78,9 +78,9 @@ const TopUp = () => {
form.submit() form.submit()
document.body.removeChild(form) document.body.removeChild(form)
} else { } else {
showError(message); showError(data);
// setTopUpCount(parseInt(res.data.count)); // setTopUpCount(parseInt(res.data.count));
setAmount(parseInt(data)); // setAmount(parseInt(data));
} }
} else { } else {
showError(res); showError(res);
...@@ -160,9 +160,9 @@ const TopUp = () => { ...@@ -160,9 +160,9 @@ const TopUp = () => {
setRedemptionCode(e.target.value); setRedemptionCode(e.target.value);
}} }}
/> />
{/*<Button color='green' onClick={openTopUpLink}>*/} <Button color='green' onClick={openTopUpLink}>
{/* 获取兑换码*/} 获取兑换码
{/*</Button>*/} </Button>
<Button color='yellow' onClick={topUp} disabled={isSubmitting}> <Button color='yellow' onClick={topUp} disabled={isSubmitting}>
{isSubmitting ? '兑换中...' : '兑换'} {isSubmitting ? '兑换中...' : '兑换'}
</Button> </Button>
...@@ -179,7 +179,7 @@ const TopUp = () => { ...@@ -179,7 +179,7 @@ const TopUp = () => {
</Grid> </Grid>
</Segment> </Segment>
<Segment> <Segment>
<Header as='h3'>在线充值(最小10刀)</Header> <Header as='h3'>在线充值</Header>
<Grid columns={2} stackable> <Grid columns={2} stackable>
<Grid.Column> <Grid.Column>
<Form> <Form>
...@@ -194,27 +194,27 @@ const TopUp = () => { ...@@ -194,27 +194,27 @@ const TopUp = () => {
await getAmount(e.target.value); await getAmount(e.target.value);
}} }}
/> />
<Form.Input {/*<Form.Input*/}
placeholder='充值码,如果你没有充值码,可不填写' {/* placeholder='充值码,如果你没有充值码,可不填写'*/}
name='redemptionCount' {/* name='redemptionCount'*/}
value={topUpCode} {/* value={topUpCode}*/}
onChange={(e) => { {/* onChange={(e) => {*/}
setTopUpCode(e.target.value); {/* setTopUpCode(e.target.value);*/}
}} {/* }}*/}
/> {/*/>*/}
<Button color='blue' onClick={ <Button color='blue' onClick={
async () => { async () => {
onlineTopUp('zfb') onlineTopUp('zfb')
} }
}> }>
支付宝(最大2000元) 支付宝
</Button> </Button>
<Button color='green' onClick={ <Button color='green' onClick={
async () => { async () => {
onlineTopUp('wx') onlineTopUp('wx')
} }
}> }>
微信(最大2000元) 微信
</Button> </Button>
</Form> </Form>
</Grid.Column> </Grid.Column>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment