Compare commits

..

14 Commits

Author SHA1 Message Date
Ian Li
227e11c5ac chore: Remove unused pipelines and triggers. 2024-01-07 21:07:31 +08:00
Ian Li
b0bf224bb1 feat: support LOGIN as SMTP authentication method. 2024-01-07 21:07:31 +08:00
Ian Li
5342af9222 feat: allow querying multiple channels. 2024-01-07 20:56:54 +08:00
JustSong
d062bc60e4 chore: update ui copy 2024-01-07 19:18:52 +08:00
JustSong
39c1882970 chore: add back THEMES 2024-01-07 19:01:31 +08:00
JustSong
9c42c7dfd9 docs: update theme readme 2024-01-07 18:59:26 +08:00
JustSong
903aaeded0 chore: revert change 2024-01-07 18:47:39 +08:00
JustSong
bdd4be562d chore: add theme validation 2024-01-07 18:44:26 +08:00
Buer
37afb313b5 fix: fix some issues with berry (#913)
* fix: login address error

* fix: Normal users display profile menu

* fix: remove redundant code
2024-01-07 18:39:15 +08:00
JustSong
c9ebcab8b8 fix: fix theme logging 2024-01-07 18:02:59 +08:00
JustSong
86261cc656 feat: able to change theme 2024-01-07 17:53:05 +08:00
JustSong
8491785c9d chore: remove useless logging 2024-01-07 17:13:24 +08:00
JustSong
e848a3f7fa fix: fix error loading stats (#912) 2024-01-07 17:10:59 +08:00
JustSong
318adf5985 chore: remove useless lines 2024-01-07 16:28:44 +08:00
23 changed files with 169 additions and 398 deletions

View File

@@ -1,49 +0,0 @@
name: Publish Docker image (amd64, English)
on:
push:
tags:
- '*'
workflow_dispatch:
inputs:
name:
description: 'reason'
required: false
jobs:
push_to_registries:
name: Push Docker image to multiple registries
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Save version info
run: |
git describe --tags > VERSION
- name: Translate
run: |
python ./i18n/translate.py --repository_path . --json_file_path ./i18n/en.json
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
justsong/one-api-en
- name: Build and push Docker images
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -1,34 +1,28 @@
name: Publish Docker image (amd64)
on:
push:
tags:
- '*'
workflow_dispatch:
inputs:
name:
description: 'reason'
required: false
jobs:
push_to_registries:
name: Push Docker image to multiple registries
build-and-push-image:
name: Push Docker image to GitHub registry
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Save version info
run: |
git describe --tags > VERSION
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
git describe --tags > VERSION
- name: Log in to the Container registry
uses: docker/login-action@v2
@@ -41,9 +35,7 @@ jobs:
id: meta
uses: docker/metadata-action@v4
with:
images: |
justsong/one-api
ghcr.io/${{ github.repository }}
images: ghcr.io/${{ github.repository }}
- name: Build and push Docker images
uses: docker/build-push-action@v3

View File

@@ -1,62 +0,0 @@
name: Publish Docker image (arm64)
on:
push:
tags:
- '*'
- '!*-alpha*'
workflow_dispatch:
inputs:
name:
description: 'reason'
required: false
jobs:
push_to_registries:
name: Push Docker image to multiple registries
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Save version info
run: |
git describe --tags > VERSION
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
justsong/one-api
ghcr.io/${{ github.repository }}
- name: Build and push Docker images
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -1,59 +0,0 @@
name: Linux Release
permissions:
contents: write
on:
push:
tags:
- '*'
- '!*-alpha*'
workflow_dispatch:
inputs:
name:
description: 'reason'
required: false
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Build Frontend (theme default)
env:
CI: ""
run: |
cd web
git describe --tags > VERSION
REACT_APP_VERSION=$(git describe --tags) chmod u+x ./build.sh && ./build.sh
cd ..
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '>=1.18.0'
- name: Build Backend (amd64)
run: |
go mod download
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o one-api
- name: Build Backend (arm64)
run: |
sudo apt-get update
sudo apt-get install gcc-aarch64-linux-gnu
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o one-api-arm64
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
one-api
one-api-arm64
draft: true
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,50 +0,0 @@
name: macOS Release
permissions:
contents: write
on:
push:
tags:
- '*'
- '!*-alpha*'
workflow_dispatch:
inputs:
name:
description: 'reason'
required: false
jobs:
release:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Build Frontend (theme default)
env:
CI: ""
run: |
cd web
git describe --tags > VERSION
REACT_APP_VERSION=$(git describe --tags) chmod u+x ./build.sh && ./build.sh
cd ..
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '>=1.18.0'
- name: Build Backend
run: |
go mod download
go build -ldflags "-X 'one-api/common.Version=$(git describe --tags)'" -o one-api-macos
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: one-api-macos
draft: true
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,53 +0,0 @@
name: Windows Release
permissions:
contents: write
on:
push:
tags:
- '*'
- '!*-alpha*'
workflow_dispatch:
inputs:
name:
description: 'reason'
required: false
jobs:
release:
runs-on: windows-latest
defaults:
run:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Build Frontend (theme default)
env:
CI: ""
run: |
cd web/default
npm install
REACT_APP_VERSION=$(git describe --tags) npm run build
cd ../..
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '>=1.18.0'
- name: Build Backend
run: |
go mod download
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)'" -o one-api.exe
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: one-api.exe
draft: true
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -12,11 +12,6 @@ WORKDIR /web/berry
RUN npm install
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build
WORKDIR /web
RUN ls
RUN ls ./build
RUN ls ./build/default
FROM golang AS builder2
ENV GO111MODULE=on \

View File

@@ -62,6 +62,7 @@ var SMTPPort = 587
var SMTPAccount = ""
var SMTPFrom = ""
var SMTPToken = ""
var SMTPAuthLoginEnabled = false
var GitHubClientId = ""
var GitHubClientSecret = ""
@@ -101,6 +102,10 @@ var RelayTimeout = GetOrDefault("RELAY_TIMEOUT", 0) // unit is second
var GeminiSafetySetting = GetOrDefaultString("GEMINI_SAFETY_SETTING", "BLOCK_NONE")
var Theme = GetOrDefaultString("THEME", "default")
var ValidThemes = map[string]bool{
"default": true,
"berry": true,
}
const (
RequestIdKey = "X-Oneapi-Request-Id"

View File

@@ -4,12 +4,39 @@ import (
"crypto/rand"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"net/smtp"
"strings"
"time"
)
type loginAuth struct {
username, password string
}
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}
func (a *loginAuth) Start(_ *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", []byte(a.username), nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.New("unknown command from server during login auth")
}
}
return nil, nil
}
func SendEmail(subject string, receiver string, content string) error {
if SMTPFrom == "" { // for compatibility
SMTPFrom = SMTPAccount
@@ -37,7 +64,13 @@ func SendEmail(subject string, receiver string, content string) error {
"Date: %s\r\n"+
"Content-Type: text/html; charset=UTF-8\r\n\r\n%s\r\n",
receiver, SystemName, SMTPFrom, encodedSubject, messageId, time.Now().Format(time.RFC1123Z), content))
auth := smtp.PlainAuth("", SMTPAccount, SMTPToken, SMTPServer)
var auth smtp.Auth
if SMTPAuthLoginEnabled {
auth = LoginAuth(SMTPAccount, SMTPToken)
} else {
auth = smtp.PlainAuth("", SMTPAccount, SMTPToken, SMTPServer)
}
addr := fmt.Sprintf("%s:%d", SMTPServer, SMTPPort)
to := strings.Split(receiver, ";")

View File

@@ -6,8 +6,23 @@ import (
"one-api/common"
"one-api/model"
"strconv"
"strings"
)
func parseIntArray(input string) []int {
values := strings.Split(input, ",")
result := make([]int, 0)
for _, value := range values {
num, err := strconv.Atoi(strings.TrimSpace(value))
if err == nil {
result = append(result, num)
}
}
return result
}
func GetAllLogs(c *gin.Context) {
p, _ := strconv.Atoi(c.Query("p"))
if p < 0 {
@@ -19,8 +34,8 @@ func GetAllLogs(c *gin.Context) {
username := c.Query("username")
tokenName := c.Query("token_name")
modelName := c.Query("model_name")
channel, _ := strconv.Atoi(c.Query("channel"))
logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*common.ItemsPerPage, common.ItemsPerPage, channel)
channels := parseIntArray(c.Query("channel"))
logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*common.ItemsPerPage, common.ItemsPerPage, channels)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
@@ -107,8 +122,8 @@ func GetLogsStat(c *gin.Context) {
tokenName := c.Query("token_name")
username := c.Query("username")
modelName := c.Query("model_name")
channel, _ := strconv.Atoi(c.Query("channel"))
quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName, channel)
channels := parseIntArray(c.Query("channel"))
quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName, channels)
//tokenNum := model.SumUsedToken(logType, startTimestamp, endTimestamp, modelName, username, "")
c.JSON(http.StatusOK, gin.H{
"success": true,
@@ -128,8 +143,8 @@ func GetLogsSelfStat(c *gin.Context) {
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
tokenName := c.Query("token_name")
modelName := c.Query("model_name")
channel, _ := strconv.Atoi(c.Query("channel"))
quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName, channel)
channels := parseIntArray(c.Query("channel"))
quotaNum := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName, channels)
//tokenNum := model.SumUsedToken(logType, startTimestamp, endTimestamp, modelName, username, tokenName)
c.JSON(http.StatusOK, gin.H{
"success": true,

View File

@@ -42,6 +42,14 @@ func UpdateOption(c *gin.Context) {
return
}
switch option.Key {
case "Theme":
if !common.ValidThemes[option.Value] {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "无效的主题",
})
return
}
case "GitHubOAuthEnabled":
if option.Value == "true" && common.GitHubClientId == "" {
c.JSON(http.StatusOK, gin.H{

View File

@@ -330,6 +330,7 @@
"通常和邮箱地址保持一致": "Usually consistent with the email address",
"SMTP 访问凭证": "SMTP Access Credential",
"敏感信息不会发送到前端显示": "Sensitive information will not be displayed in the frontend",
"使用 SMTP LOGIN 认证方式": "Use LOGIN as SMTP authentication method",
"保存 SMTP 设置": "Save SMTP Settings",
"配置 GitHub OAuth App": "Configure GitHub OAuth App",
"用以支持通过 GitHub 进行登录注册": "To support login & registration via GitHub",

View File

@@ -20,7 +20,7 @@ var buildFS embed.FS
func main() {
common.SetupLogger()
common.SysLog(fmt.Sprintf("One API %s started with theme %s", common.Version, common.Theme))
common.SysLog(fmt.Sprintf("One API %s started", common.Version))
if os.Getenv("GIN_MODE") != "debug" {
gin.SetMode(gin.ReleaseMode)
}
@@ -47,6 +47,7 @@ func main() {
// Initialize options
model.InitOptionMap()
common.SysLog(fmt.Sprintf("using theme %s", common.Theme))
if common.RedisEnabled {
// for compatibility with old versions
common.MemoryCacheEnabled = true

View File

@@ -72,7 +72,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
}
}
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int) (logs []*Log, err error) {
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channels []int) (logs []*Log, err error) {
var tx *gorm.DB
if logType == LogTypeUnknown {
tx = DB
@@ -94,9 +94,12 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName
if endTimestamp != 0 {
tx = tx.Where("created_at <= ?", endTimestamp)
}
if channel != 0 {
tx = tx.Where("channel_id = ?", channel)
if len(channels) > 1 {
tx = tx.Where("channel_id IN ?", channels)
} else if len(channels) == 1 && channels[0] != 0 {
tx = tx.Where("channel_id = ?", channels[0])
}
err = tx.Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error
return logs, err
}
@@ -134,7 +137,7 @@ func SearchUserLogs(userId int, keyword string) (logs []*Log, err error) {
return logs, err
}
func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, channel int) (quota int) {
func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, channels []int) (quota int) {
tx := DB.Table("logs").Select("ifnull(sum(quota),0)")
if username != "" {
tx = tx.Where("username = ?", username)
@@ -151,8 +154,10 @@ func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelNa
if modelName != "" {
tx = tx.Where("model_name = ?", modelName)
}
if channel != 0 {
tx = tx.Where("channel_id = ?", channel)
if len(channels) > 1 {
tx = tx.Where("channel_id IN ?", channels)
} else if len(channels) == 1 && channels[0] != 0 {
tx = tx.Where("channel_id = ?", channels[0])
}
tx.Where("type = ?", LogTypeConsume).Scan(&quota)
return quota
@@ -212,13 +217,11 @@ func SearchLogsByDayAndModel(userId, start, end int) (LogStatistics []*LogStatis
sum(completion_tokens) as completion_tokens
FROM logs
WHERE type=2
AND userId= ?
AND user_id= ?
AND created_at BETWEEN ? AND ?
GROUP BY day, model_name
ORDER BY day, model_name
`, userId, start, end).Scan(&LogStatistics).Error
fmt.Println(userId, start, end)
return LogStatistics, err
}

View File

@@ -47,6 +47,7 @@ func InitOptionMap() {
common.OptionMap["SMTPPort"] = strconv.Itoa(common.SMTPPort)
common.OptionMap["SMTPAccount"] = ""
common.OptionMap["SMTPToken"] = ""
common.OptionMap["SMTPAuthLoginEnabled"] = strconv.FormatBool(common.SMTPAuthLoginEnabled)
common.OptionMap["Notice"] = ""
common.OptionMap["About"] = ""
common.OptionMap["HomePageContent"] = ""
@@ -72,6 +73,7 @@ func InitOptionMap() {
common.OptionMap["ChatLink"] = common.ChatLink
common.OptionMap["QuotaPerUnit"] = strconv.FormatFloat(common.QuotaPerUnit, 'f', -1, 64)
common.OptionMap["RetryTimes"] = strconv.Itoa(common.RetryTimes)
common.OptionMap["Theme"] = common.Theme
common.OptionMapRWMutex.Unlock()
loadOptionsFromDatabase()
}
@@ -158,6 +160,8 @@ func updateOptionMap(key string, value string) (err error) {
common.DisplayInCurrencyEnabled = boolValue
case "DisplayTokenStatEnabled":
common.DisplayTokenStatEnabled = boolValue
case "SMTPAuthLoginEnabled":
common.SMTPAuthLoginEnabled = boolValue
}
}
switch key {
@@ -220,6 +224,8 @@ func updateOptionMap(key string, value string) (err error) {
common.ChannelDisableThreshold, _ = strconv.ParseFloat(value, 64)
case "QuotaPerUnit":
common.QuotaPerUnit, _ = strconv.ParseFloat(value, 64)
case "Theme":
common.Theme = value
}
return err
}

View File

@@ -8,7 +8,9 @@
1.`web` 文件夹下新建一个文件夹,文件夹名为主题名。
2. 把你的主题文件放到这个文件夹下。
3. 修改 `package.json` 文件,把 `build` 命令改为:`"build": "react-scripts build && mv -f build ../build/default"`,其中 `default` 为你的主题名。
3. 修改你的 `package.json` 文件,把 `build` 命令改为:`"build": "react-scripts build && mv -f build ../build/default"`,其中 `default` 为你的主题名。
4. 修改 `common/constants.go` 中的 `ValidThemes`,把你的主题名称注册进去。
5. 修改 `web/THEMES` 文件,这里也需要同步修改。
## 主题列表

View File

@@ -87,7 +87,7 @@ const panel = {
url: '/panel/profile',
icon: icons.IconUserScan,
breadcrumbs: false,
isAdmin: true
isAdmin: false
},
{
id: 'setting',

View File

@@ -14,7 +14,7 @@ API.interceptors.response.use(
if (error.response?.status === 401) {
localStorage.removeItem('user');
store.dispatch({ type: LOGIN, payload: null });
window.location.href = config.basename + '/login';
window.location.href = config.basename + 'login';
}
if (error.response?.data?.message) {

View File

@@ -92,7 +92,7 @@ const LoginForm = ({ ...others }) => {
<Box sx={{ mr: { xs: 1, sm: 2, width: 20 }, display: 'flex', alignItems: 'center' }}>
<img src={Github} alt="github" width={25} height={25} style={{ marginRight: matchDownSM ? 8 : 16 }} />
</Box>
使用 Github 登录
使用 GitHub 登录
</Button>
</AnimateButton>
</Grid>
@@ -115,7 +115,7 @@ const LoginForm = ({ ...others }) => {
<Box sx={{ mr: { xs: 1, sm: 2, width: 20 }, display: 'flex', alignItems: 'center' }}>
<img src={Wechat} alt="Wechat" width={25} height={25} style={{ marginRight: matchDownSM ? 8 : 16 }} />
</Box>
使用 Wechat 登录
使用微信登录
</Button>
</AnimateButton>
<WechatModal open={openWechat} handleClose={handleWechatClose} wechatLogin={wechatLogin} qrCode={siteInfo.wechat_qrcode} />

View File

@@ -38,8 +38,6 @@ const validationSchema = Yup.object().shape({
then: Yup.string().required("密钥 不能为空"),
}),
other: Yup.string(),
proxy: Yup.string(),
test_model: Yup.string(),
models: Yup.array().min(1, "模型 不能为空"),
groups: Yup.array().min(1, "用户组 不能为空"),
base_url: Yup.string().when("type", {
@@ -623,71 +621,6 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => {
</FormHelperText>
)}
</FormControl>
<FormControl
fullWidth
error={Boolean(touched.proxy && errors.proxy)}
sx={{ ...theme.typography.otherInput }}
>
<InputLabel htmlFor="channel-proxy-label">
{inputLabel.proxy}
</InputLabel>
<OutlinedInput
id="channel-proxy-label"
label={inputLabel.proxy}
type="text"
value={values.proxy}
name="proxy"
onBlur={handleBlur}
onChange={handleChange}
inputProps={{}}
aria-describedby="helper-text-channel-proxy-label"
/>
{touched.proxy && errors.proxy ? (
<FormHelperText error id="helper-tex-channel-proxy-label">
{errors.proxy}
</FormHelperText>
) : (
<FormHelperText id="helper-tex-channel-proxy-label">
{" "}
{inputPrompt.proxy}{" "}
</FormHelperText>
)}
</FormControl>
{inputPrompt.test_model && (
<FormControl
fullWidth
error={Boolean(touched.test_model && errors.test_model)}
sx={{ ...theme.typography.otherInput }}
>
<InputLabel htmlFor="channel-test_model-label">
{inputLabel.test_model}
</InputLabel>
<OutlinedInput
id="channel-test_model-label"
label={inputLabel.test_model}
type="text"
value={values.test_model}
name="test_model"
onBlur={handleBlur}
onChange={handleChange}
inputProps={{}}
aria-describedby="helper-text-channel-test_model-label"
/>
{touched.test_model && errors.test_model ? (
<FormHelperText
error
id="helper-tex-channel-test_model-label"
>
{errors.test_model}
</FormHelperText>
) : (
<FormHelperText id="helper-tex-channel-test_model-label">
{" "}
{inputPrompt.test_model}{" "}
</FormHelperText>
)}
</FormControl>
)}
<DialogActions>
<Button onClick={onCancel}>取消</Button>
<Button

View File

@@ -1,18 +1,18 @@
import { useState, useEffect } from 'react';
import SubCard from 'ui-component/cards/SubCard';
import {
Stack,
FormControl,
InputLabel,
OutlinedInput,
Button,
Alert,
TextField,
Dialog,
DialogTitle,
DialogActions,
DialogContent,
Divider
Stack,
FormControl,
InputLabel,
OutlinedInput,
Button,
Alert,
TextField,
Dialog,
DialogTitle,
DialogActions,
DialogContent,
Divider, Link
} from '@mui/material';
import Grid from '@mui/material/Unstable_Grid2';
import { showError, showSuccess } from 'utils/common'; //,
@@ -26,7 +26,8 @@ const OtherSetting = () => {
About: '',
SystemName: '',
Logo: '',
HomePageContent: ''
HomePageContent: '',
Theme: '',
});
let [loading, setLoading] = useState(false);
const [showUpdateModal, setShowUpdateModal] = useState(false);
@@ -88,6 +89,10 @@ const OtherSetting = () => {
await updateOption('SystemName', inputs.SystemName);
};
const submitTheme = async () => {
await updateOption('Theme', inputs.Theme);
};
const submitLogo = async () => {
await updateOption('Logo', inputs.Logo);
};
@@ -171,6 +176,25 @@ const OtherSetting = () => {
设置系统名称
</Button>
</Grid>
<Grid xs={12}>
<FormControl fullWidth>
<InputLabel htmlFor="Theme">主题名称</InputLabel>
<OutlinedInput
id="Theme"
name="Theme"
value={inputs.Theme || ''}
onChange={handleInputChange}
label="主题名称"
placeholder="请输入主题名称"
disabled={loading}
/>
</FormControl>
</Grid>
<Grid xs={12}>
<Button variant="contained" onClick={submitTheme}>
设置主题重启生效
</Button>
</Grid>
<Grid xs={12}>
<FormControl fullWidth>
<InputLabel htmlFor="Logo">Logo 图片地址</InputLabel>

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import { Button, Divider, Form, Grid, Header, Message, Modal } from 'semantic-ui-react';
import { API, showError, showSuccess } from '../helpers';
import { marked } from 'marked';
import { Link } from 'react-router-dom';
const OtherSetting = () => {
let [inputs, setInputs] = useState({
@@ -10,7 +11,8 @@ const OtherSetting = () => {
About: '',
SystemName: '',
Logo: '',
HomePageContent: ''
HomePageContent: '',
Theme: ''
});
let [loading, setLoading] = useState(false);
const [showUpdateModal, setShowUpdateModal] = useState(false);
@@ -70,6 +72,10 @@ const OtherSetting = () => {
await updateOption('SystemName', inputs.SystemName);
};
const submitTheme = async () => {
await updateOption('Theme', inputs.Theme);
};
const submitLogo = async () => {
await updateOption('Logo', inputs.Logo);
};
@@ -132,6 +138,17 @@ const OtherSetting = () => {
/>
</Form.Group>
<Form.Button onClick={submitSystemName}>设置系统名称</Form.Button>
<Form.Group widths='equal'>
<Form.Input
label={<label>主题名称<Link
to='https://github.com/songquanpeng/one-api/blob/main/web/README.md'>当前可用主题</Link></label>}
placeholder='请输入主题名称'
value={inputs.Theme}
name='Theme'
onChange={handleInputChange}
/>
</Form.Group>
<Form.Button onClick={submitTheme}>设置主题重启生效</Form.Button>
<Form.Group widths='equal'>
<Form.Input
label='Logo 图片地址'
@@ -165,7 +182,8 @@ const OtherSetting = () => {
/>
</Form.Group>
<Form.Button onClick={submitAbout}>保存关于</Form.Button>
<Message>移除 One API 的版权标识必须首先获得授权项目维护需要花费大量精力如果本项目对你有意义请主动支持本项目</Message>
<Message>移除 One API
的版权标识必须首先获得授权项目维护需要花费大量精力如果本项目对你有意义请主动支持本项目</Message>
<Form.Group widths='equal'>
<Form.Input
label='页脚'

View File

@@ -16,6 +16,7 @@ const SystemSetting = () => {
SMTPAccount: '',
SMTPFrom: '',
SMTPToken: '',
SMTPAuthLoginEnabled: '',
ServerAddress: '',
Footer: '',
WeChatAuthEnabled: '',
@@ -72,6 +73,7 @@ const SystemSetting = () => {
case 'TurnstileCheckEnabled':
case 'EmailDomainRestrictionEnabled':
case 'RegisterEnabled':
case 'SMTPAuthLoginEnabled':
value = inputs[key] === 'true' ? 'false' : 'true';
break;
default:
@@ -103,7 +105,7 @@ const SystemSetting = () => {
}
if (
name === 'Notice' ||
name.startsWith('SMTP') ||
(name.startsWith('SMTP') && !name.endsWith('Enabled')) ||
name === 'ServerAddress' ||
name === 'GitHubClientId' ||
name === 'GitHubClientSecret' ||
@@ -411,6 +413,12 @@ const SystemSetting = () => {
checked={inputs.RegisterEnabled === 'true'}
placeholder='敏感信息不会发送到前端显示'
/>
<Form.Checkbox
checked={inputs.SMTPAuthLoginEnabled === 'true'}
label='使用 SMTP LOGIN 认证方式'
name='SMTPAuthLoginEnabled'
onChange={handleInputChange}
/>
</Form.Group>
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
<Divider />