mirror of
				https://github.com/linux-do/new-api.git
				synced 2025-11-04 13:23:42 +08:00 
			
		
		
		
	merge upstream
Signed-off-by: wozulong <>
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@@ -7,7 +7,7 @@ all: build-frontend start-backend
 | 
			
		||||
 | 
			
		||||
build-frontend:
 | 
			
		||||
	@echo "Building frontend..."
 | 
			
		||||
	@cd $(FRONTEND_DIR) && yarn install --network-timeout 1000000 && DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) yarn build
 | 
			
		||||
	@cd $(FRONTEND_DIR) && yarn install --network-timeout 1000000 && DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat VERSION) yarn build
 | 
			
		||||
 | 
			
		||||
start-backend:
 | 
			
		||||
	@echo "Starting backend dev server..."
 | 
			
		||||
 
 | 
			
		||||
@@ -18,16 +18,28 @@ type Adaptor struct {
 | 
			
		||||
func (a *Adaptor) Init(info *relaycommon.RelayInfo, request dto.GeneralOpenAIRequest) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 定义一个映射,存储模型名称和对应的版本
 | 
			
		||||
var modelVersionMap = map[string]string{
 | 
			
		||||
    "gemini-1.5-pro-latest": "v1beta",
 | 
			
		||||
    "gemini-ultra":   "v1beta",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
 | 
			
		||||
	version := "v1"
 | 
			
		||||
	if info.ApiVersion != "" {
 | 
			
		||||
		version = info.ApiVersion
 | 
			
		||||
	}
 | 
			
		||||
	action := "generateContent"
 | 
			
		||||
	if info.IsStream {
 | 
			
		||||
		action = "streamGenerateContent"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s/%s/models/%s:%s", info.BaseUrl, version, info.UpstreamModelName, action), nil
 | 
			
		||||
    // 从映射中获取模型名称对应的版本,如果找不到就使用 info.ApiVersion 或默认的版本 "v1"
 | 
			
		||||
    version, beta := modelVersionMap[info.UpstreamModelName]
 | 
			
		||||
    if !beta {
 | 
			
		||||
        if info.ApiVersion != "" {
 | 
			
		||||
            version = info.ApiVersion
 | 
			
		||||
        } else {
 | 
			
		||||
            version = "v1"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    action := "generateContent"
 | 
			
		||||
    if info.IsStream {
 | 
			
		||||
        action = "streamGenerateContent"
 | 
			
		||||
    }
 | 
			
		||||
    return fmt.Sprintf("%s/%s/models/%s:%s", info.BaseUrl, version, info.UpstreamModelName, action), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,8 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ModelList = []string{
 | 
			
		||||
	"gemini-pro", "gemini-1.0-pro-001", "gemini-1.5-pro",
 | 
			
		||||
	"gemini-pro-vision", "gemini-1.0-pro-vision-001",
 | 
			
		||||
	"gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-ultra",
 | 
			
		||||
	"gemini-1.0-pro-vision-latest", "gemini-1.0-pro-vision-001",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var ChannelName = "google gemini"
 | 
			
		||||
 
 | 
			
		||||
@@ -179,7 +179,13 @@ func xunfeiHandler(c *gin.Context, textRequest dto.GeneralOpenAIRequest, appId s
 | 
			
		||||
		case stop = <-stopChan:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(xunfeiResponse.Payload.Choices.Text) == 0 {
 | 
			
		||||
		xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
 | 
			
		||||
			{
 | 
			
		||||
				Content: "",
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	xunfeiResponse.Payload.Choices.Text[0].Content = content
 | 
			
		||||
 | 
			
		||||
	response := responseXunfei2OpenAI(&xunfeiResponse)
 | 
			
		||||
 
 | 
			
		||||
@@ -254,6 +254,19 @@ const ChannelsTable = () => {
 | 
			
		||||
          >
 | 
			
		||||
            编辑
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Popconfirm
 | 
			
		||||
            title='确定是否要复制此渠道?'
 | 
			
		||||
            content='复制渠道的所有信息'
 | 
			
		||||
            okType={'danger'}
 | 
			
		||||
            position={'left'}
 | 
			
		||||
            onConfirm={async () => {
 | 
			
		||||
              copySelectedChannel(record.id);
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <Button theme='light' type='primary' style={{ marginRight: 1 }}>
 | 
			
		||||
              复制
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Popconfirm>
 | 
			
		||||
        </div>
 | 
			
		||||
      ),
 | 
			
		||||
    },
 | 
			
		||||
@@ -340,6 +353,33 @@ const ChannelsTable = () => {
 | 
			
		||||
    setLoading(false);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const copySelectedChannel = async (id) => {
 | 
			
		||||
    const channelToCopy = channels.find(
 | 
			
		||||
      (channel) => String(channel.id) === String(id),
 | 
			
		||||
    );
 | 
			
		||||
    console.log(channelToCopy);
 | 
			
		||||
    channelToCopy.name += '_复制';
 | 
			
		||||
    channelToCopy.created_time = null;
 | 
			
		||||
    channelToCopy.balance = 0;
 | 
			
		||||
    channelToCopy.used_quota = 0;
 | 
			
		||||
    if (!channelToCopy) {
 | 
			
		||||
      showError('渠道未找到,请刷新页面后重试。');
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
      const newChannel = { ...channelToCopy, id: undefined };
 | 
			
		||||
      const response = await API.post('/api/channel/', newChannel);
 | 
			
		||||
      if (response.data.success) {
 | 
			
		||||
        showSuccess('渠道复制成功');
 | 
			
		||||
        await refresh();
 | 
			
		||||
      } else {
 | 
			
		||||
        showError(response.data.message);
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      showError('渠道复制失败: ' + error.message);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const refresh = async () => {
 | 
			
		||||
    await loadChannels(activePage - 1, pageSize, idSort);
 | 
			
		||||
  };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import React, { useContext, useEffect, useState } from 'react';
 | 
			
		||||
import { Link, useNavigate } from 'react-router-dom';
 | 
			
		||||
import { UserContext } from '../context/User';
 | 
			
		||||
import { useSetTheme, useTheme } from '../context/Theme';
 | 
			
		||||
 | 
			
		||||
import { API, getLogo, getSystemName, showSuccess } from '../helpers';
 | 
			
		||||
import '../index.css';
 | 
			
		||||
@@ -34,10 +35,8 @@ const HeaderBar = () => {
 | 
			
		||||
  let navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
  const [showSidebar, setShowSidebar] = useState(false);
 | 
			
		||||
  const [dark, setDark] = useState(false);
 | 
			
		||||
  const systemName = getSystemName();
 | 
			
		||||
  const logo = getLogo();
 | 
			
		||||
  var themeMode = localStorage.getItem('theme-mode');
 | 
			
		||||
  const currentDate = new Date();
 | 
			
		||||
  // enable fireworks on new year(1.1 and 2.9-2.24)
 | 
			
		||||
  const isNewYear =
 | 
			
		||||
@@ -66,26 +65,19 @@ const HeaderBar = () => {
 | 
			
		||||
    }, 3000);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const theme = useTheme();
 | 
			
		||||
  const setTheme = useSetTheme();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (themeMode === 'dark') {
 | 
			
		||||
      switchMode(true);
 | 
			
		||||
    if (theme === 'dark') {
 | 
			
		||||
      document.body.setAttribute('theme-mode', 'dark');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isNewYear) {
 | 
			
		||||
      console.log('Happy New Year!');
 | 
			
		||||
    }
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  const switchMode = (model) => {
 | 
			
		||||
    const body = document.body;
 | 
			
		||||
    if (!model) {
 | 
			
		||||
      body.removeAttribute('theme-mode');
 | 
			
		||||
      localStorage.setItem('theme-mode', 'light');
 | 
			
		||||
    } else {
 | 
			
		||||
      body.setAttribute('theme-mode', 'dark');
 | 
			
		||||
      localStorage.setItem('theme-mode', 'dark');
 | 
			
		||||
    }
 | 
			
		||||
    setDark(model);
 | 
			
		||||
  };
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Layout>
 | 
			
		||||
@@ -132,9 +124,11 @@ const HeaderBar = () => {
 | 
			
		||||
                <Switch
 | 
			
		||||
                  checkedText='🌞'
 | 
			
		||||
                  size={'large'}
 | 
			
		||||
                  checked={dark}
 | 
			
		||||
                  checked={theme === 'dark'}
 | 
			
		||||
                  uncheckedText='🌙'
 | 
			
		||||
                  onChange={switchMode}
 | 
			
		||||
                  onChange={(checked) => {
 | 
			
		||||
                    setTheme(checked);
 | 
			
		||||
                  }}
 | 
			
		||||
                />
 | 
			
		||||
                {userState.user ? (
 | 
			
		||||
                  <>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ import {
 | 
			
		||||
  verifyJSON,
 | 
			
		||||
} from '../helpers';
 | 
			
		||||
 | 
			
		||||
import { useTheme } from '../context/Theme';
 | 
			
		||||
 | 
			
		||||
const OperationSetting = () => {
 | 
			
		||||
  let now = new Date();
 | 
			
		||||
  let [inputs, setInputs] = useState({
 | 
			
		||||
@@ -77,6 +79,9 @@ const OperationSetting = () => {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const theme = useTheme();
 | 
			
		||||
  const isDark = theme === 'dark';
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    getOptions().then();
 | 
			
		||||
  }, []);
 | 
			
		||||
@@ -219,8 +224,10 @@ const OperationSetting = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Grid columns={1}>
 | 
			
		||||
      <Grid.Column>
 | 
			
		||||
        <Form loading={loading}>
 | 
			
		||||
          <Header as='h3'>通用设置</Header>
 | 
			
		||||
        <Form loading={loading} inverted={isDark}>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            通用设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group widths={4}>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              label='充值链接'
 | 
			
		||||
@@ -299,7 +306,9 @@ const OperationSetting = () => {
 | 
			
		||||
            保存通用设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>绘图设置</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            绘图设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group inline>
 | 
			
		||||
            <Form.Checkbox
 | 
			
		||||
              checked={inputs.DrawingEnabled === 'true'}
 | 
			
		||||
@@ -321,7 +330,9 @@ const OperationSetting = () => {
 | 
			
		||||
            />
 | 
			
		||||
          </Form.Group>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>屏蔽词过滤设置</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            屏蔽词过滤设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group inline>
 | 
			
		||||
            <Form.Checkbox
 | 
			
		||||
              checked={inputs.CheckSensitiveEnabled === 'true'}
 | 
			
		||||
@@ -381,7 +392,9 @@ const OperationSetting = () => {
 | 
			
		||||
            保存屏蔽词设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>日志设置</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            日志设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group inline>
 | 
			
		||||
            <Form.Checkbox
 | 
			
		||||
              checked={inputs.LogConsumeEnabled === 'true'}
 | 
			
		||||
@@ -409,7 +422,9 @@ const OperationSetting = () => {
 | 
			
		||||
            清理历史日志
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>数据看板</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            数据看板
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Checkbox
 | 
			
		||||
            checked={inputs.DataExportEnabled === 'true'}
 | 
			
		||||
            label='启用数据看板(实验性)'
 | 
			
		||||
@@ -439,7 +454,9 @@ const OperationSetting = () => {
 | 
			
		||||
            />
 | 
			
		||||
          </Form.Group>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>监控设置</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            监控设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group widths={3}>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              label='最长响应时间'
 | 
			
		||||
@@ -484,7 +501,9 @@ const OperationSetting = () => {
 | 
			
		||||
            保存监控设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>额度设置</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            额度设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group widths={4}>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              label='新用户初始额度'
 | 
			
		||||
@@ -535,7 +554,9 @@ const OperationSetting = () => {
 | 
			
		||||
            保存额度设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>倍率设置</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            倍率设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group widths='equal'>
 | 
			
		||||
            <Form.TextArea
 | 
			
		||||
              label='模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ import {
 | 
			
		||||
} from 'semantic-ui-react';
 | 
			
		||||
import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers';
 | 
			
		||||
 | 
			
		||||
import { useTheme } from '../context/Theme';
 | 
			
		||||
 | 
			
		||||
const SystemSetting = () => {
 | 
			
		||||
  let [inputs, setInputs] = useState({
 | 
			
		||||
    PasswordLoginEnabled: '',
 | 
			
		||||
@@ -62,6 +64,9 @@ const SystemSetting = () => {
 | 
			
		||||
  const [showPasswordWarningModal, setShowPasswordWarningModal] =
 | 
			
		||||
    useState(false);
 | 
			
		||||
 | 
			
		||||
  const theme = useTheme();
 | 
			
		||||
  const isDark = theme === 'dark';
 | 
			
		||||
 | 
			
		||||
  const getOptions = async () => {
 | 
			
		||||
    const res = await API.get('/api/option/');
 | 
			
		||||
    const { success, message, data } = res.data;
 | 
			
		||||
@@ -346,8 +351,10 @@ const SystemSetting = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Grid columns={1}>
 | 
			
		||||
      <Grid.Column>
 | 
			
		||||
        <Form loading={loading}>
 | 
			
		||||
          <Header as='h3'>通用设置</Header>
 | 
			
		||||
        <Form loading={loading} inverted={isDark}>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            通用设置
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group widths='equal'>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              label='服务器地址'
 | 
			
		||||
@@ -361,7 +368,7 @@ const SystemSetting = () => {
 | 
			
		||||
            更新服务器地址
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            支付设置(当前仅支持Stripe Checkout)
 | 
			
		||||
            <Header.Subheader>
 | 
			
		||||
              密钥、Webhook 等设置请
 | 
			
		||||
@@ -455,7 +462,9 @@ const SystemSetting = () => {
 | 
			
		||||
            />
 | 
			
		||||
          </Form.Group>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>配置登录注册</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            配置登录注册
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group inline>
 | 
			
		||||
            <Form.Checkbox
 | 
			
		||||
              checked={inputs.PasswordLoginEnabled === 'true'}
 | 
			
		||||
@@ -550,7 +559,7 @@ const SystemSetting = () => {
 | 
			
		||||
            />
 | 
			
		||||
          </Form.Group>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            配置邮箱域名白名单
 | 
			
		||||
            <Header.Subheader>
 | 
			
		||||
              用以防止恶意用户利用临时邮箱批量注册
 | 
			
		||||
@@ -615,7 +624,7 @@ const SystemSetting = () => {
 | 
			
		||||
            保存邮箱域名白名单设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            配置 SMTP
 | 
			
		||||
            <Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
 | 
			
		||||
          </Header>
 | 
			
		||||
@@ -674,7 +683,7 @@ const SystemSetting = () => {
 | 
			
		||||
          </Form.Group>
 | 
			
		||||
          <Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            配置 GitHub OAuth App
 | 
			
		||||
            <Header.Subheader>
 | 
			
		||||
              用以支持通过 GitHub 进行登录注册,
 | 
			
		||||
@@ -768,7 +777,7 @@ const SystemSetting = () => {
 | 
			
		||||
            保存 LINUX DO OAuth 设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            配置 WeChat Server
 | 
			
		||||
            <Header.Subheader>
 | 
			
		||||
              用以支持通过微信进行登录注册,
 | 
			
		||||
@@ -813,7 +822,9 @@ const SystemSetting = () => {
 | 
			
		||||
            保存 WeChat Server 设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>配置 Telegram 登录</Header>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            配置 Telegram 登录
 | 
			
		||||
          </Header>
 | 
			
		||||
          <Form.Group inline>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              label='Telegram Bot Token'
 | 
			
		||||
@@ -834,7 +845,7 @@ const SystemSetting = () => {
 | 
			
		||||
            保存 Telegram 登录设置
 | 
			
		||||
          </Form.Button>
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <Header as='h3'>
 | 
			
		||||
          <Header as='h3' inverted={isDark}>
 | 
			
		||||
            配置 Turnstile
 | 
			
		||||
            <Header.Subheader>
 | 
			
		||||
              用以支持用户校验,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								web/src/context/Theme/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/context/Theme/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
import { createContext, useCallback, useContext, useState } from 'react';
 | 
			
		||||
 | 
			
		||||
const ThemeContext = createContext(null);
 | 
			
		||||
export const useTheme = () => useContext(ThemeContext);
 | 
			
		||||
 | 
			
		||||
const SetThemeContext = createContext(null);
 | 
			
		||||
export const useSetTheme = () => useContext(SetThemeContext);
 | 
			
		||||
 | 
			
		||||
export const ThemeProvider = ({ children }) => {
 | 
			
		||||
  const [theme, _setTheme] = useState(() => {
 | 
			
		||||
    try {
 | 
			
		||||
      return localStorage.getItem('theme-mode') || null;
 | 
			
		||||
    } catch {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const setTheme = useCallback((input) => {
 | 
			
		||||
    _setTheme(input ? 'dark' : 'light');
 | 
			
		||||
 | 
			
		||||
    const body = document.body;
 | 
			
		||||
    if (!input) {
 | 
			
		||||
      body.removeAttribute('theme-mode');
 | 
			
		||||
      localStorage.setItem('theme-mode', 'light');
 | 
			
		||||
    } else {
 | 
			
		||||
      body.setAttribute('theme-mode', 'dark');
 | 
			
		||||
      localStorage.setItem('theme-mode', 'dark');
 | 
			
		||||
    }
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SetThemeContext.Provider value={setTheme}>
 | 
			
		||||
      <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
 | 
			
		||||
    </SetThemeContext.Provider>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -12,6 +12,7 @@ import 'react-toastify/dist/ReactToastify.css';
 | 
			
		||||
import { StatusProvider } from './context/Status';
 | 
			
		||||
import { Layout } from '@douyinfe/semi-ui';
 | 
			
		||||
import SiderBar from './components/SiderBar';
 | 
			
		||||
import { ThemeProvider } from './context/Theme';
 | 
			
		||||
 | 
			
		||||
// initialization
 | 
			
		||||
 | 
			
		||||
@@ -22,27 +23,29 @@ root.render(
 | 
			
		||||
    <StatusProvider>
 | 
			
		||||
      <UserProvider>
 | 
			
		||||
        <BrowserRouter>
 | 
			
		||||
          <Layout>
 | 
			
		||||
            <Sider>
 | 
			
		||||
              <SiderBar />
 | 
			
		||||
            </Sider>
 | 
			
		||||
          <ThemeProvider>
 | 
			
		||||
            <Layout>
 | 
			
		||||
              <Header>
 | 
			
		||||
                <HeaderBar />
 | 
			
		||||
              </Header>
 | 
			
		||||
              <Content
 | 
			
		||||
                style={{
 | 
			
		||||
                  padding: '24px',
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                <App />
 | 
			
		||||
              </Content>
 | 
			
		||||
              <Layout.Footer>
 | 
			
		||||
                <Footer></Footer>
 | 
			
		||||
              </Layout.Footer>
 | 
			
		||||
              <Sider>
 | 
			
		||||
                <SiderBar />
 | 
			
		||||
              </Sider>
 | 
			
		||||
              <Layout>
 | 
			
		||||
                <Header>
 | 
			
		||||
                  <HeaderBar />
 | 
			
		||||
                </Header>
 | 
			
		||||
                <Content
 | 
			
		||||
                  style={{
 | 
			
		||||
                    padding: '24px',
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <App />
 | 
			
		||||
                </Content>
 | 
			
		||||
                <Layout.Footer>
 | 
			
		||||
                  <Footer></Footer>
 | 
			
		||||
                </Layout.Footer>
 | 
			
		||||
              </Layout>
 | 
			
		||||
              <ToastContainer />
 | 
			
		||||
            </Layout>
 | 
			
		||||
            <ToastContainer />
 | 
			
		||||
          </Layout>
 | 
			
		||||
          </ThemeProvider>
 | 
			
		||||
        </BrowserRouter>
 | 
			
		||||
      </UserProvider>
 | 
			
		||||
    </StatusProvider>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user