mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-12-10 02:05:56 +08:00
Compare commits
8 Commits
v0.2.4-alp
...
v0.2.5-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2edd52e851 | ||
|
|
e123c66bc7 | ||
|
|
9edc82bde0 | ||
|
|
d84c2f5c70 | ||
|
|
46e77389a4 | ||
|
|
f5f4e6fbc6 | ||
|
|
dc4a6cb711 | ||
|
|
5798fdac50 |
@@ -56,7 +56,7 @@ _✨ All in one 的 OpenAI 接口,整合各种 API 访问方式,开箱即用
|
|||||||
5. 支持设置令牌的过期时间和使用次数。
|
5. 支持设置令牌的过期时间和使用次数。
|
||||||
6. 支持批量生成和导出兑换码,可使用兑换码为令牌进行充值。
|
6. 支持批量生成和导出兑换码,可使用兑换码为令牌进行充值。
|
||||||
7. 支持为新用户设置初始配额。
|
7. 支持为新用户设置初始配额。
|
||||||
8. 支持发布公告,在线修改关于页面,设置充值链接,自定义页脚。
|
8. 支持自定义首页,发布公告,自定义关于页面,设置充值链接,自定义页脚。
|
||||||
9. 支持通过系统访问令牌访问管理 API。
|
9. 支持通过系统访问令牌访问管理 API。
|
||||||
10. 多种用户登录注册方式:
|
10. 多种用户登录注册方式:
|
||||||
+ 邮箱登录注册以及通过邮箱进行密码重置。
|
+ 邮箱登录注册以及通过邮箱进行密码重置。
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ var RegisterEnabled = true
|
|||||||
var SMTPServer = ""
|
var SMTPServer = ""
|
||||||
var SMTPPort = 587
|
var SMTPPort = 587
|
||||||
var SMTPAccount = ""
|
var SMTPAccount = ""
|
||||||
|
var SMTPFrom = ""
|
||||||
var SMTPToken = ""
|
var SMTPToken = ""
|
||||||
|
|
||||||
var GitHubClientId = ""
|
var GitHubClientId = ""
|
||||||
|
|||||||
@@ -1,20 +1,67 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SendEmail(subject string, receiver string, content string) error {
|
func SendEmail(subject string, receiver string, content string) error {
|
||||||
|
if SMTPFrom == "" { // for compatibility
|
||||||
|
SMTPFrom = SMTPAccount
|
||||||
|
}
|
||||||
|
encodedSubject := fmt.Sprintf("=?UTF-8?B?%s?=", base64.StdEncoding.EncodeToString([]byte(subject)))
|
||||||
mail := []byte(fmt.Sprintf("To: %s\r\n"+
|
mail := []byte(fmt.Sprintf("To: %s\r\n"+
|
||||||
"From: %s<%s>\r\n"+
|
"From: %s<%s>\r\n"+
|
||||||
"Subject: %s\r\n"+
|
"Subject: %s\r\n"+
|
||||||
"Content-Type: text/html; charset=UTF-8\r\n\r\n%s\r\n",
|
"Content-Type: text/html; charset=UTF-8\r\n\r\n%s\r\n",
|
||||||
receiver, SystemName, SMTPAccount, subject, content))
|
receiver, SystemName, SMTPFrom, encodedSubject, content))
|
||||||
auth := smtp.PlainAuth("", SMTPAccount, SMTPToken, SMTPServer)
|
auth := smtp.PlainAuth("", SMTPAccount, SMTPToken, SMTPServer)
|
||||||
addr := fmt.Sprintf("%s:%d", SMTPServer, SMTPPort)
|
addr := fmt.Sprintf("%s:%d", SMTPServer, SMTPPort)
|
||||||
to := strings.Split(receiver, ";")
|
to := strings.Split(receiver, ";")
|
||||||
err := smtp.SendMail(addr, auth, SMTPAccount, to, mail)
|
var err error
|
||||||
|
if SMTPPort == 465 {
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
ServerName: SMTPServer,
|
||||||
|
}
|
||||||
|
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", SMTPServer, SMTPPort), tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client, err := smtp.NewClient(conn, SMTPServer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
if err = client.Auth(auth); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = client.Mail(SMTPFrom); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
receiverEmails := strings.Split(receiver, ";")
|
||||||
|
for _, receiver := range receiverEmails {
|
||||||
|
if err = client.Rcpt(receiver); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w, err := client.Data()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(mail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = w.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = smtp.SendMail(addr, auth, SMTPAccount, to, mail)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"one-api/common"
|
"one-api/common"
|
||||||
"one-api/model"
|
"one-api/model"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetAllChannels(c *gin.Context) {
|
func GetAllChannels(c *gin.Context) {
|
||||||
@@ -84,7 +85,17 @@ func AddChannel(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
channel.CreatedTime = common.GetTimestamp()
|
channel.CreatedTime = common.GetTimestamp()
|
||||||
channel.AccessedTime = common.GetTimestamp()
|
channel.AccessedTime = common.GetTimestamp()
|
||||||
err = channel.Insert()
|
keys := strings.Split(channel.Key, "\n")
|
||||||
|
channels := make([]model.Channel, 0)
|
||||||
|
for _, key := range keys {
|
||||||
|
if key == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
localChannel := channel
|
||||||
|
localChannel.Key = key
|
||||||
|
channels = append(channels, localChannel)
|
||||||
|
}
|
||||||
|
err = model.BatchInsertChannels(channels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
|
|||||||
@@ -54,6 +54,17 @@ func GetAbout(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetHomePageContent(c *gin.Context) {
|
||||||
|
common.OptionMapRWMutex.RLock()
|
||||||
|
defer common.OptionMapRWMutex.RUnlock()
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": "",
|
||||||
|
"data": common.OptionMap["HomePageContent"],
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func SendEmailVerification(c *gin.Context) {
|
func SendEmailVerification(c *gin.Context) {
|
||||||
email := c.Query("email")
|
email := c.Query("email")
|
||||||
if err := common.Validate.Var(email, "required,email"); err != nil {
|
if err := common.Validate.Var(email, "required,email"); err != nil {
|
||||||
|
|||||||
@@ -94,10 +94,12 @@ func relayHelper(c *gin.Context) error {
|
|||||||
if channelType == common.ChannelTypeAzure {
|
if channelType == common.ChannelTypeAzure {
|
||||||
// https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?pivots=rest-api&tabs=command-line#rest-api
|
// https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?pivots=rest-api&tabs=command-line#rest-api
|
||||||
query := c.Request.URL.Query()
|
query := c.Request.URL.Query()
|
||||||
if query.Get("api-version") == "" {
|
apiVersion := query.Get("api-version")
|
||||||
apiVersion := c.GetString("api_version")
|
if apiVersion == "" {
|
||||||
requestURL = fmt.Sprintf("%s?api-version=%s", requestURL, apiVersion)
|
apiVersion = c.GetString("api_version")
|
||||||
}
|
}
|
||||||
|
requestURL := strings.Split(requestURL, "?")[0]
|
||||||
|
requestURL = fmt.Sprintf("%s?api-version=%s", requestURL, apiVersion)
|
||||||
baseURL = c.GetString("base_url")
|
baseURL = c.GetString("base_url")
|
||||||
task := strings.TrimPrefix(requestURL, "/v1/")
|
task := strings.TrimPrefix(requestURL, "/v1/")
|
||||||
model_ := textRequest.Model
|
model_ := textRequest.Model
|
||||||
@@ -186,7 +188,7 @@ func relayHelper(c *gin.Context) error {
|
|||||||
data := scanner.Text()
|
data := scanner.Text()
|
||||||
dataChan <- data
|
dataChan <- data
|
||||||
data = data[6:]
|
data = data[6:]
|
||||||
if data != "[DONE]" {
|
if !strings.HasPrefix(data, "[DONE]") {
|
||||||
var streamResponse StreamResponse
|
var streamResponse StreamResponse
|
||||||
err = json.Unmarshal([]byte(data), &streamResponse)
|
err = json.Unmarshal([]byte(data), &streamResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -207,6 +209,9 @@ func relayHelper(c *gin.Context) error {
|
|||||||
c.Stream(func(w io.Writer) bool {
|
c.Stream(func(w io.Writer) bool {
|
||||||
select {
|
select {
|
||||||
case data := <-dataChan:
|
case data := <-dataChan:
|
||||||
|
if strings.HasPrefix(data, "data: [DONE]") {
|
||||||
|
data = data[:len(data)-1]
|
||||||
|
}
|
||||||
c.Render(-1, common.CustomEvent{Data: data})
|
c.Render(-1, common.CustomEvent{Data: data})
|
||||||
return true
|
return true
|
||||||
case <-stopChan:
|
case <-stopChan:
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ func GetRandomChannel() (*Channel, error) {
|
|||||||
return &channel, err
|
return &channel, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BatchInsertChannels(channels []Channel) error {
|
||||||
|
var err error
|
||||||
|
err = DB.Create(&channels).Error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (channel *Channel) Insert() error {
|
func (channel *Channel) Insert() error {
|
||||||
var err error
|
var err error
|
||||||
err = DB.Create(channel).Error
|
err = DB.Create(channel).Error
|
||||||
|
|||||||
@@ -33,11 +33,13 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled)
|
common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled)
|
||||||
common.OptionMap["RegisterEnabled"] = strconv.FormatBool(common.RegisterEnabled)
|
common.OptionMap["RegisterEnabled"] = strconv.FormatBool(common.RegisterEnabled)
|
||||||
common.OptionMap["SMTPServer"] = ""
|
common.OptionMap["SMTPServer"] = ""
|
||||||
|
common.OptionMap["SMTPFrom"] = ""
|
||||||
common.OptionMap["SMTPPort"] = strconv.Itoa(common.SMTPPort)
|
common.OptionMap["SMTPPort"] = strconv.Itoa(common.SMTPPort)
|
||||||
common.OptionMap["SMTPAccount"] = ""
|
common.OptionMap["SMTPAccount"] = ""
|
||||||
common.OptionMap["SMTPToken"] = ""
|
common.OptionMap["SMTPToken"] = ""
|
||||||
common.OptionMap["Notice"] = ""
|
common.OptionMap["Notice"] = ""
|
||||||
common.OptionMap["About"] = ""
|
common.OptionMap["About"] = ""
|
||||||
|
common.OptionMap["HomePageContent"] = ""
|
||||||
common.OptionMap["Footer"] = common.Footer
|
common.OptionMap["Footer"] = common.Footer
|
||||||
common.OptionMap["ServerAddress"] = ""
|
common.OptionMap["ServerAddress"] = ""
|
||||||
common.OptionMap["GitHubClientId"] = ""
|
common.OptionMap["GitHubClientId"] = ""
|
||||||
@@ -120,6 +122,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.SMTPPort = intValue
|
common.SMTPPort = intValue
|
||||||
case "SMTPAccount":
|
case "SMTPAccount":
|
||||||
common.SMTPAccount = value
|
common.SMTPAccount = value
|
||||||
|
case "SMTPFrom":
|
||||||
|
common.SMTPFrom = value
|
||||||
case "SMTPToken":
|
case "SMTPToken":
|
||||||
common.SMTPToken = value
|
common.SMTPToken = value
|
||||||
case "ServerAddress":
|
case "ServerAddress":
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
apiRouter.GET("/status", controller.GetStatus)
|
apiRouter.GET("/status", controller.GetStatus)
|
||||||
apiRouter.GET("/notice", controller.GetNotice)
|
apiRouter.GET("/notice", controller.GetNotice)
|
||||||
apiRouter.GET("/about", controller.GetAbout)
|
apiRouter.GET("/about", controller.GetAbout)
|
||||||
|
apiRouter.GET("/home_page_content", controller.GetHomePageContent)
|
||||||
apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification)
|
apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification)
|
||||||
apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail)
|
apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail)
|
||||||
apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword)
|
apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const OtherSetting = () => {
|
|||||||
Footer: '',
|
Footer: '',
|
||||||
Notice: '',
|
Notice: '',
|
||||||
About: '',
|
About: '',
|
||||||
|
HomePageContent: '',
|
||||||
});
|
});
|
||||||
let originInputs = {};
|
let originInputs = {};
|
||||||
let [loading, setLoading] = useState(false);
|
let [loading, setLoading] = useState(false);
|
||||||
@@ -69,6 +70,10 @@ const OtherSetting = () => {
|
|||||||
await updateOption('About', inputs.About);
|
await updateOption('About', inputs.About);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const submitOption = async (key) => {
|
||||||
|
await updateOption(key, inputs[key]);
|
||||||
|
};
|
||||||
|
|
||||||
const openGitHubRelease = () => {
|
const openGitHubRelease = () => {
|
||||||
window.location =
|
window.location =
|
||||||
'https://github.com/songquanpeng/one-api/releases/latest';
|
'https://github.com/songquanpeng/one-api/releases/latest';
|
||||||
@@ -109,6 +114,17 @@ const OtherSetting = () => {
|
|||||||
<Form.Button onClick={submitNotice}>保存公告</Form.Button>
|
<Form.Button onClick={submitNotice}>保存公告</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>个性化设置</Header>
|
<Header as='h3'>个性化设置</Header>
|
||||||
|
<Form.Group widths='equal'>
|
||||||
|
<Form.TextArea
|
||||||
|
label='首页内容'
|
||||||
|
placeholder='在此输入首页内容,支持 Markdown & HTML 代码,设置后首页的状态信息将不再显示'
|
||||||
|
value={inputs.HomePageContent}
|
||||||
|
name='HomePageContent'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
style={{ minHeight: 300, fontFamily: 'JetBrains Mono, Consolas' }}
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Button onClick={()=>submitOption('HomePageContent')}>保存首页内容</Form.Button>
|
||||||
<Form.Group widths='equal'>
|
<Form.Group widths='equal'>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label='关于'
|
label='关于'
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const SystemSetting = () => {
|
|||||||
SMTPServer: '',
|
SMTPServer: '',
|
||||||
SMTPPort: '',
|
SMTPPort: '',
|
||||||
SMTPAccount: '',
|
SMTPAccount: '',
|
||||||
|
SMTPFrom: '',
|
||||||
SMTPToken: '',
|
SMTPToken: '',
|
||||||
ServerAddress: '',
|
ServerAddress: '',
|
||||||
Footer: '',
|
Footer: '',
|
||||||
@@ -129,6 +130,9 @@ const SystemSetting = () => {
|
|||||||
if (originInputs['SMTPAccount'] !== inputs.SMTPAccount) {
|
if (originInputs['SMTPAccount'] !== inputs.SMTPAccount) {
|
||||||
await updateOption('SMTPAccount', inputs.SMTPAccount);
|
await updateOption('SMTPAccount', inputs.SMTPAccount);
|
||||||
}
|
}
|
||||||
|
if (originInputs['SMTPFrom'] !== inputs.SMTPFrom) {
|
||||||
|
await updateOption('SMTPFrom', inputs.SMTPFrom);
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
originInputs['SMTPPort'] !== inputs.SMTPPort &&
|
originInputs['SMTPPort'] !== inputs.SMTPPort &&
|
||||||
inputs.SMTPPort !== ''
|
inputs.SMTPPort !== ''
|
||||||
@@ -298,7 +302,7 @@ const SystemSetting = () => {
|
|||||||
配置 SMTP
|
配置 SMTP
|
||||||
<Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
|
<Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
|
||||||
</Header>
|
</Header>
|
||||||
<Form.Group widths={4}>
|
<Form.Group widths={3}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='SMTP 服务器地址'
|
label='SMTP 服务器地址'
|
||||||
name='SMTPServer'
|
name='SMTPServer'
|
||||||
@@ -323,6 +327,16 @@ const SystemSetting = () => {
|
|||||||
value={inputs.SMTPAccount}
|
value={inputs.SMTPAccount}
|
||||||
placeholder='通常是邮箱地址'
|
placeholder='通常是邮箱地址'
|
||||||
/>
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group widths={3}>
|
||||||
|
<Form.Input
|
||||||
|
label='SMTP 发送者邮箱'
|
||||||
|
name='SMTPFrom'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.SMTPFrom}
|
||||||
|
placeholder='通常和邮箱地址保持一致'
|
||||||
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='SMTP 访问凭证'
|
label='SMTP 访问凭证'
|
||||||
name='SMTPToken'
|
name='SMTPToken'
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ const EditChannel = () => {
|
|||||||
base_url: '',
|
base_url: '',
|
||||||
other: ''
|
other: ''
|
||||||
};
|
};
|
||||||
|
const [batch, setBatch] = useState(false);
|
||||||
const [inputs, setInputs] = useState(originInputs);
|
const [inputs, setInputs] = useState(originInputs);
|
||||||
const handleInputChange = (e, { name, value }) => {
|
const handleInputChange = (e, { name, value }) => {
|
||||||
|
console.log(name, value);
|
||||||
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,17 +132,38 @@ const EditChannel = () => {
|
|||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
{
|
||||||
<Form.Input
|
batch ? <Form.Field>
|
||||||
label='密钥'
|
<Form.TextArea
|
||||||
name='key'
|
label='密钥'
|
||||||
placeholder={'请输入密钥'}
|
name='key'
|
||||||
onChange={handleInputChange}
|
placeholder={'请输入密钥,一行一个'}
|
||||||
value={inputs.key}
|
onChange={handleInputChange}
|
||||||
// type='password'
|
value={inputs.key}
|
||||||
autoComplete='new-password'
|
style={{ minHeight: 150, fontFamily: 'JetBrains Mono, Consolas' }}
|
||||||
/>
|
autoComplete='new-password'
|
||||||
</Form.Field>
|
/>
|
||||||
|
</Form.Field> : <Form.Field>
|
||||||
|
<Form.Input
|
||||||
|
label='密钥'
|
||||||
|
name='key'
|
||||||
|
placeholder={'请输入密钥'}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
value={inputs.key}
|
||||||
|
autoComplete='new-password'
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!isEdit && (
|
||||||
|
<Form.Checkbox
|
||||||
|
checked={batch}
|
||||||
|
label='批量创建'
|
||||||
|
name='batch'
|
||||||
|
onChange={() => setBatch(!batch)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
<Button onClick={submit}>提交</Button>
|
<Button onClick={submit}>提交</Button>
|
||||||
</Form>
|
</Form>
|
||||||
</Segment>
|
</Segment>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import React, { useContext, useEffect } 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';
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const [statusState, statusDispatch] = useContext(StatusContext);
|
const [statusState, statusDispatch] = useContext(StatusContext);
|
||||||
|
const [homePageContent, setHomePageContent] = useState('');
|
||||||
|
|
||||||
const displayNotice = async () => {
|
const displayNotice = async () => {
|
||||||
const res = await API.get('/api/notice');
|
const res = await API.get('/api/notice');
|
||||||
@@ -20,6 +22,19 @@ const Home = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const displayHomePageContent = async () => {
|
||||||
|
const res = await API.get('/api/home_page_content');
|
||||||
|
const { success, message, data } = res.data;
|
||||||
|
if (success) {
|
||||||
|
let HTMLContent = marked.parse(data);
|
||||||
|
localStorage.setItem('home_page_content', HTMLContent);
|
||||||
|
setHomePageContent(HTMLContent);
|
||||||
|
} else {
|
||||||
|
showError(message);
|
||||||
|
setHomePageContent('加载首页内容失败...');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getStartTimeString = () => {
|
const getStartTimeString = () => {
|
||||||
const timestamp = statusState?.status?.start_time;
|
const timestamp = statusState?.status?.start_time;
|
||||||
return timestamp2string(timestamp);
|
return timestamp2string(timestamp);
|
||||||
@@ -27,69 +42,76 @@ const Home = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
displayNotice().then();
|
displayNotice().then();
|
||||||
|
displayHomePageContent().then();
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Segment>
|
<Segment>
|
||||||
<Header as='h3'>系统状况</Header>
|
{
|
||||||
<Grid columns={2} stackable>
|
homePageContent === '' ? <>
|
||||||
<Grid.Column>
|
<Header as='h3'>系统状况</Header>
|
||||||
<Card fluid>
|
<Grid columns={2} stackable>
|
||||||
<Card.Content>
|
<Grid.Column>
|
||||||
<Card.Header>系统信息</Card.Header>
|
<Card fluid>
|
||||||
<Card.Meta>系统信息总览</Card.Meta>
|
<Card.Content>
|
||||||
<Card.Description>
|
<Card.Header>系统信息</Card.Header>
|
||||||
<p>名称:{statusState?.status?.system_name}</p>
|
<Card.Meta>系统信息总览</Card.Meta>
|
||||||
<p>版本:{statusState?.status?.version}</p>
|
<Card.Description>
|
||||||
<p>
|
<p>名称:{statusState?.status?.system_name}</p>
|
||||||
源码:
|
<p>版本:{statusState?.status?.version}</p>
|
||||||
<a
|
<p>
|
||||||
href='https://github.com/songquanpeng/one-api'
|
源码:
|
||||||
target='_blank'
|
<a
|
||||||
>
|
href='https://github.com/songquanpeng/one-api'
|
||||||
https://github.com/songquanpeng/one-api
|
target='_blank'
|
||||||
</a>
|
>
|
||||||
</p>
|
https://github.com/songquanpeng/one-api
|
||||||
<p>启动时间:{getStartTimeString()}</p>
|
</a>
|
||||||
</Card.Description>
|
</p>
|
||||||
</Card.Content>
|
<p>启动时间:{getStartTimeString()}</p>
|
||||||
</Card>
|
</Card.Description>
|
||||||
</Grid.Column>
|
</Card.Content>
|
||||||
<Grid.Column>
|
</Card>
|
||||||
<Card fluid>
|
</Grid.Column>
|
||||||
<Card.Content>
|
<Grid.Column>
|
||||||
<Card.Header>系统配置</Card.Header>
|
<Card fluid>
|
||||||
<Card.Meta>系统配置总览</Card.Meta>
|
<Card.Content>
|
||||||
<Card.Description>
|
<Card.Header>系统配置</Card.Header>
|
||||||
<p>
|
<Card.Meta>系统配置总览</Card.Meta>
|
||||||
邮箱验证:
|
<Card.Description>
|
||||||
{statusState?.status?.email_verification === true
|
<p>
|
||||||
? '已启用'
|
邮箱验证:
|
||||||
: '未启用'}
|
{statusState?.status?.email_verification === true
|
||||||
</p>
|
? '已启用'
|
||||||
<p>
|
: '未启用'}
|
||||||
GitHub 身份验证:
|
</p>
|
||||||
{statusState?.status?.github_oauth === true
|
<p>
|
||||||
? '已启用'
|
GitHub 身份验证:
|
||||||
: '未启用'}
|
{statusState?.status?.github_oauth === true
|
||||||
</p>
|
? '已启用'
|
||||||
<p>
|
: '未启用'}
|
||||||
微信身份验证:
|
</p>
|
||||||
{statusState?.status?.wechat_login === true
|
<p>
|
||||||
? '已启用'
|
微信身份验证:
|
||||||
: '未启用'}
|
{statusState?.status?.wechat_login === true
|
||||||
</p>
|
? '已启用'
|
||||||
<p>
|
: '未启用'}
|
||||||
Turnstile 用户校验:
|
</p>
|
||||||
{statusState?.status?.turnstile_check === true
|
<p>
|
||||||
? '已启用'
|
Turnstile 用户校验:
|
||||||
: '未启用'}
|
{statusState?.status?.turnstile_check === true
|
||||||
</p>
|
? '已启用'
|
||||||
</Card.Description>
|
: '未启用'}
|
||||||
</Card.Content>
|
</p>
|
||||||
</Card>
|
</Card.Description>
|
||||||
</Grid.Column>
|
</Card.Content>
|
||||||
</Grid>
|
</Card>
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid>
|
||||||
|
</> : <>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: homePageContent}}></div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
</Segment>
|
</Segment>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user