Explorar o código

feat: able to customize system name & logo now

JustSong %!s(int64=2) %!d(string=hai) anos
pai
achega
926951ee03

+ 1 - 0
common/constants.go

@@ -11,6 +11,7 @@ var Version = "v0.0.0"            // this hard coding will be replaced automatic
 var SystemName = "One API"
 var ServerAddress = "http://localhost:3000"
 var Footer = ""
+var Logo = ""
 var TopUpLink = ""
 
 var UsingSQLite = false

+ 1 - 0
controller/misc.go

@@ -20,6 +20,7 @@ func GetStatus(c *gin.Context) {
 			"github_oauth":       common.GitHubOAuthEnabled,
 			"github_client_id":   common.GitHubClientId,
 			"system_name":        common.SystemName,
+			"logo":               common.Logo,
 			"footer_html":        common.Footer,
 			"wechat_qrcode":      common.WeChatAccountQRCodeImageURL,
 			"wechat_login":       common.WeChatAuthEnabled,

+ 6 - 0
model/option.go

@@ -41,6 +41,8 @@ func InitOptionMap() {
 	common.OptionMap["About"] = ""
 	common.OptionMap["HomePageContent"] = ""
 	common.OptionMap["Footer"] = common.Footer
+	common.OptionMap["SystemName"] = common.SystemName
+	common.OptionMap["Logo"] = common.Logo
 	common.OptionMap["ServerAddress"] = ""
 	common.OptionMap["GitHubClientId"] = ""
 	common.OptionMap["GitHubClientSecret"] = ""
@@ -134,6 +136,10 @@ func updateOptionMap(key string, value string) (err error) {
 		common.GitHubClientSecret = value
 	case "Footer":
 		common.Footer = value
+	case "SystemName":
+		common.SystemName = value
+	case "Logo":
+		common.Logo = value
 	case "WeChatServerAddress":
 		common.WeChatServerAddress = value
 	case "WeChatServerToken":

+ 2 - 0
web/src/App.js

@@ -42,6 +42,8 @@ function App() {
     if (success) {
       localStorage.setItem('status', JSON.stringify(data));
       statusDispatch({ type: 'set', payload: data });
+      localStorage.setItem('system_name', data.system_name);
+      localStorage.setItem('logo', data.logo);
       localStorage.setItem('footer_html', data.footer_html);
       if (
         data.version !== process.env.REACT_APP_VERSION &&

+ 17 - 20
web/src/components/Footer.js

@@ -1,40 +1,37 @@
-import React, { useEffect, useState } from 'react';
+import React from 'react';
 
 import { Container, Segment } from 'semantic-ui-react';
+import { getFooterHTML, getSystemName } from '../helpers';
 
 const Footer = () => {
-  const [Footer, setFooter] = useState('');
-  useEffect(() => {
-    let savedFooter = localStorage.getItem('footer_html');
-    if (!savedFooter) savedFooter = '';
-    setFooter(savedFooter);
-  });
+  const systemName = getSystemName();
+  const footer = getFooterHTML();
 
   return (
     <Segment vertical>
-      <Container textAlign="center">
-        {Footer === '' ? (
-          <div className="custom-footer">
+      <Container textAlign='center'>
+        {footer ? (
+          <div
+            className='custom-footer'
+            dangerouslySetInnerHTML={{ __html: footer }}
+          ></div>
+        ) : (
+          <div className='custom-footer'>
             <a
-              href="https://github.com/songquanpeng/one-api"
-              target="_blank"
+              href='https://github.com/songquanpeng/one-api'
+              target='_blank'
             >
-              One API {process.env.REACT_APP_VERSION}{' '}
+              {systemName} {process.env.REACT_APP_VERSION}{' '}
             </a>
             由{' '}
-            <a href="https://github.com/songquanpeng" target="_blank">
+            <a href='https://github.com/songquanpeng' target='_blank'>
               JustSong
             </a>{' '}
             构建,源代码遵循{' '}
-            <a href="https://opensource.org/licenses/mit-license.php">
+            <a href='https://opensource.org/licenses/mit-license.php'>
               MIT 协议
             </a>
           </div>
-        ) : (
-          <div
-            className="custom-footer"
-            dangerouslySetInnerHTML={{ __html: Footer }}
-          ></div>
         )}
       </Container>
     </Segment>

+ 7 - 5
web/src/components/Header.js

@@ -3,7 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
 import { UserContext } from '../context/User';
 
 import { Button, Container, Dropdown, Icon, Menu, Segment } from 'semantic-ui-react';
-import { API, isAdmin, isMobile, showSuccess } from '../helpers';
+import { API, getLogo, getSystemName, isAdmin, isMobile, showSuccess } from '../helpers';
 import '../index.css';
 
 // Header Buttons
@@ -53,6 +53,8 @@ const Header = () => {
   let navigate = useNavigate();
 
   const [showSidebar, setShowSidebar] = useState(false);
+  const systemName = getSystemName();
+  const logo = getLogo();
 
   async function logout() {
     setShowSidebar(false);
@@ -111,12 +113,12 @@ const Header = () => {
           <Container>
             <Menu.Item as={Link} to='/'>
               <img
-                src='/logo.png'
+                src={logo}
                 alt='logo'
                 style={{ marginRight: '0.75em' }}
               />
               <div style={{ fontSize: '20px' }}>
-                <b>One API</b>
+                <b>{systemName}</b>
               </div>
             </Menu.Item>
             <Menu.Menu position='right'>
@@ -168,9 +170,9 @@ const Header = () => {
       <Menu borderless style={{ borderTop: 'none' }}>
         <Container>
           <Menu.Item as={Link} to='/' className={'hide-on-mobile'}>
-            <img src='/logo.png' alt='logo' style={{ marginRight: '0.75em' }} />
+            <img src={logo} alt='logo' style={{ marginRight: '0.75em' }} />
             <div style={{ fontSize: '20px' }}>
-              <b>One API</b>
+              <b>{systemName}</b>
             </div>
           </Menu.Item>
           {renderButtons(false)}

+ 3 - 2
web/src/components/LoginForm.js

@@ -12,7 +12,7 @@ import {
 } from 'semantic-ui-react';
 import { Link, useNavigate, useSearchParams } from 'react-router-dom';
 import { UserContext } from '../context/User';
-import { API, showError, showSuccess } from '../helpers';
+import { API, getLogo, showError, showSuccess } from '../helpers';
 
 const LoginForm = () => {
   const [inputs, setInputs] = useState({
@@ -27,6 +27,7 @@ const LoginForm = () => {
   let navigate = useNavigate();
 
   const [status, setStatus] = useState({});
+  const logo = getLogo();
 
   useEffect(() => {
     if (searchParams.get("expired")) {
@@ -95,7 +96,7 @@ const LoginForm = () => {
     <Grid textAlign="center" style={{ marginTop: '48px' }}>
       <Grid.Column style={{ maxWidth: 450 }}>
         <Header as="h2" color="" textAlign="center">
-          <Image src="/logo.png" /> 用户登录
+          <Image src={logo} /> 用户登录
         </Header>
         <Form size="large">
           <Segment>

+ 31 - 0
web/src/components/OtherSetting.js

@@ -8,6 +8,8 @@ const OtherSetting = () => {
     Footer: '',
     Notice: '',
     About: '',
+    SystemName: '',
+    Logo: '',
     HomePageContent: '',
   });
   let originInputs = {};
@@ -66,6 +68,14 @@ const OtherSetting = () => {
     await updateOption('Footer', inputs.Footer);
   };
 
+  const submitSystemName = async () => {
+    await updateOption('SystemName', inputs.SystemName);
+  };
+
+  const submitLogo = async () => {
+    await updateOption('Logo', inputs.Logo);
+  };
+
   const submitAbout = async () => {
     await updateOption('About', inputs.About);
   };
@@ -114,6 +124,27 @@ const OtherSetting = () => {
           <Form.Button onClick={submitNotice}>保存公告</Form.Button>
           <Divider />
           <Header as='h3'>个性化设置</Header>
+          <Form.Group widths='equal'>
+            <Form.Input
+              label='系统名称'
+              placeholder='在此输入系统名称'
+              value={inputs.SystemName}
+              name='SystemName'
+              onChange={handleInputChange}
+            />
+          </Form.Group>
+          <Form.Button onClick={submitSystemName}>设置系统名称</Form.Button>
+          <Form.Group widths='equal'>
+            <Form.Input
+              label='Logo 图片地址'
+              placeholder='在此输入 Logo 图片地址'
+              value={inputs.Logo}
+              name='Logo'
+              type='url'
+              onChange={handleInputChange}
+            />
+          </Form.Group>
+          <Form.Button onClick={submitLogo}>设置 Logo</Form.Button>
           <Form.Group widths='equal'>
             <Form.TextArea
               label='首页内容'

+ 3 - 2
web/src/components/RegisterForm.js

@@ -9,7 +9,7 @@ import {
   Segment,
 } from 'semantic-ui-react';
 import { Link, useNavigate } from 'react-router-dom';
-import { API, showError, showInfo, showSuccess } from '../helpers';
+import { API, getLogo, showError, showInfo, showSuccess } from '../helpers';
 import Turnstile from 'react-turnstile';
 
 const RegisterForm = () => {
@@ -26,6 +26,7 @@ const RegisterForm = () => {
   const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
   const [turnstileToken, setTurnstileToken] = useState('');
   const [loading, setLoading] = useState(false);
+  const logo = getLogo();
 
   useEffect(() => {
     let status = localStorage.getItem('status');
@@ -100,7 +101,7 @@ const RegisterForm = () => {
     <Grid textAlign='center' style={{ marginTop: '48px' }}>
       <Grid.Column style={{ maxWidth: 450 }}>
         <Header as='h2' color='' textAlign='center'>
-          <Image src='/logo.png' /> 新用户注册
+          <Image src={logo} /> 新用户注册
         </Header>
         <Form size='large'>
           <Segment>

+ 16 - 0
web/src/helpers/utils.js

@@ -15,6 +15,22 @@ export function isRoot() {
   return user.role >= 100;
 }
 
+export function getSystemName() {
+  let system_name = localStorage.getItem('system_name');
+  if (!system_name) return 'One API';
+  return system_name;
+}
+
+export function getLogo() {
+  let logo = localStorage.getItem('logo');
+  if (!logo) return '/logo.png';
+  return logo
+}
+
+export function getFooterHTML() {
+  return localStorage.getItem('footer_html');
+}
+
 export async function copy(text) {
   let okay = true;
   try {