Commit 9d384f97 by Apple\Apple

refactor(console_setting): migrate console settings to model_setting auto-injection

Backend
- Introduce `setting/console_setting` package that defines `ConsoleSetting` struct with JSON tags and validation rules.
- Register the new module with `config.GlobalConfig` to enable automatic injection/export of configuration values.
- Remove legacy `setting/console.go` and the manual `OptionMap` hooks; clean up `model/option.go`.
- Add `controller/console_migrate.go` providing `/api/option/migrate_console_setting` endpoint for one-off data migration.
- Update controllers (`misc`, `option`, `uptime_kuma`) and router to consume namespaced keys `console_setting.*`.

Frontend
- Refactor dashboard pages (`SettingsAPIInfo`, `SettingsAnnouncements`, `SettingsFAQ`, `SettingsUptimeKuma`) and detail page to read/write the new keys.
- Simplify `DashboardSetting.js` state to only include namespaced options.

BREAKING CHANGE: All console-related option keys are now stored under `console_setting.*`. Run the migration endpoint once after deployment to preserve existing data.
parent edd44b7e
......@@ -11,6 +11,7 @@ import (
"one-api/setting"
"one-api/setting/operation_setting"
"one-api/setting/system_setting"
"one-api/setting/console_setting"
"strings"
"github.com/gin-gonic/gin"
......@@ -79,9 +80,9 @@ func GetStatus(c *gin.Context) {
"oidc_client_id": system_setting.GetOIDCSettings().ClientId,
"oidc_authorization_endpoint": system_setting.GetOIDCSettings().AuthorizationEndpoint,
"setup": constant.Setup,
"api_info": setting.GetApiInfo(),
"announcements": setting.GetAnnouncements(),
"faq": setting.GetFAQ(),
"api_info": console_setting.GetApiInfo(),
"announcements": console_setting.GetAnnouncements(),
"faq": console_setting.GetFAQ(),
},
})
return
......
......@@ -6,6 +6,7 @@ import (
"one-api/common"
"one-api/model"
"one-api/setting"
"one-api/setting/console_setting"
"one-api/setting/system_setting"
"strings"
......@@ -119,8 +120,8 @@ func UpdateOption(c *gin.Context) {
})
return
}
case "ApiInfo":
err = setting.ValidateApiInfo(option.Value)
case "console_setting.api_info":
err = console_setting.ValidateApiInfo(option.Value)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
......@@ -128,8 +129,8 @@ func UpdateOption(c *gin.Context) {
})
return
}
case "Announcements":
err = setting.ValidateConsoleSettings(option.Value, "Announcements")
case "console_setting.announcements":
err = console_setting.ValidateConsoleSettings(option.Value, "Announcements")
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
......@@ -137,8 +138,8 @@ func UpdateOption(c *gin.Context) {
})
return
}
case "FAQ":
err = setting.ValidateConsoleSettings(option.Value, "FAQ")
case "console_setting.faq":
err = console_setting.ValidateConsoleSettings(option.Value, "FAQ")
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
......
......@@ -6,7 +6,7 @@ import (
"errors"
"fmt"
"net/http"
"one-api/common"
"one-api/setting/console_setting"
"strings"
"time"
......@@ -77,10 +77,9 @@ func getAndDecode(ctx context.Context, client *http.Client, url string, dest int
}
func GetUptimeKumaStatus(c *gin.Context) {
common.OptionMapRWMutex.RLock()
uptimeKumaUrl := common.OptionMap["UptimeKumaUrl"]
slug := common.OptionMap["UptimeKumaSlug"]
common.OptionMapRWMutex.RUnlock()
cs := console_setting.GetConsoleSetting()
uptimeKumaUrl := cs.UptimeKumaUrl
slug := cs.UptimeKumaSlug
if uptimeKumaUrl == "" || slug == "" {
c.JSON(http.StatusOK, gin.H{
......
......@@ -123,9 +123,6 @@ func InitOptionMap() {
common.OptionMap["SensitiveWords"] = setting.SensitiveWordsToString()
common.OptionMap["StreamCacheQueueLength"] = strconv.Itoa(setting.StreamCacheQueueLength)
common.OptionMap["AutomaticDisableKeywords"] = operation_setting.AutomaticDisableKeywordsToString()
common.OptionMap["ApiInfo"] = ""
common.OptionMap["UptimeKumaUrl"] = ""
common.OptionMap["UptimeKumaSlug"] = ""
// 自动添加所有注册的模型配置
modelConfigs := config.GlobalConfig.ExportAllConfigs()
......
package console_setting
import "one-api/setting/config"
type ConsoleSetting struct {
ApiInfo string `json:"api_info"` // 控制台 API 信息 (JSON 数组字符串)
UptimeKumaUrl string `json:"uptime_kuma_url"` // Uptime Kuma 服务地址(如 https://status.example.com )
UptimeKumaSlug string `json:"uptime_kuma_slug"` // Uptime Kuma Status Page Slug
Announcements string `json:"announcements"` // 系统公告 (JSON 数组字符串)
FAQ string `json:"faq"` // 常见问题 (JSON 数组字符串)
}
// 默认配置
var defaultConsoleSetting = ConsoleSetting{
ApiInfo: "",
UptimeKumaUrl: "",
UptimeKumaSlug: "",
Announcements: "",
FAQ: "",
}
// 全局实例
var consoleSetting = defaultConsoleSetting
func init() {
// 注册到全局配置管理器,键名为 console_setting
config.GlobalConfig.Register("console_setting", &consoleSetting)
}
// GetConsoleSetting 获取 ConsoleSetting 配置实例
func GetConsoleSetting() *ConsoleSetting {
return &consoleSetting
}
\ No newline at end of file
......@@ -8,11 +8,11 @@ import SettingsUptimeKuma from '../../pages/Setting/Dashboard/SettingsUptimeKuma
const DashboardSetting = () => {
let [inputs, setInputs] = useState({
ApiInfo: '',
Announcements: '',
FAQ: '',
UptimeKumaUrl: '',
UptimeKumaSlug: '',
'console_setting.api_info': '',
'console_setting.announcements': '',
'console_setting.faq': '',
'console_setting.uptime_kuma_url': '',
'console_setting.uptime_kuma_slug': '',
});
let [loading, setLoading] = useState(false);
......
......@@ -1231,10 +1231,10 @@ const Detail = (props) => {
{faqData.map((item, index) => (
<Collapse.Panel
key={index}
header={item.title}
header={item.question}
itemKey={index.toString()}
>
<p>{item.content}</p>
<p>{item.answer}</p>
</Collapse.Panel>
))}
</Collapse>
......
......@@ -85,7 +85,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
try {
setLoading(true);
const apiInfoJson = JSON.stringify(apiInfoList);
await updateOption('ApiInfo', apiInfoJson);
await updateOption('console_setting.api_info', apiInfoJson);
setHasChanges(false);
} catch (error) {
console.error('API信息更新失败', error);
......@@ -185,10 +185,11 @@ const SettingsAPIInfo = ({ options, refresh }) => {
};
useEffect(() => {
if (options.ApiInfo !== undefined) {
parseApiInfo(options.ApiInfo);
const apiInfoStr = options['console_setting.api_info'] ?? options.ApiInfo;
if (apiInfoStr !== undefined) {
parseApiInfo(apiInfoStr);
}
}, [options.ApiInfo]);
}, [options['console_setting.api_info'], options.ApiInfo]);
const columns = [
{
......
......@@ -176,7 +176,7 @@ const SettingsAnnouncements = ({ options, refresh }) => {
try {
setLoading(true);
const announcementsJson = JSON.stringify(announcementsList);
await updateOption('Announcements', announcementsJson);
await updateOption('console_setting.announcements', announcementsJson);
setHasChanges(false);
} catch (error) {
console.error('系统公告更新失败', error);
......@@ -288,10 +288,11 @@ const SettingsAnnouncements = ({ options, refresh }) => {
};
useEffect(() => {
if (options.Announcements !== undefined) {
parseAnnouncements(options.Announcements);
const annStr = options['console_setting.announcements'] ?? options.Announcements;
if (annStr !== undefined) {
parseAnnouncements(annStr);
}
}, [options.Announcements]);
}, [options['console_setting.announcements'], options.Announcements]);
const handleBatchDelete = () => {
if (selectedRowKeys.length === 0) {
......
......@@ -37,8 +37,8 @@ const SettingsFAQ = ({ options, refresh }) => {
const [loading, setLoading] = useState(false);
const [hasChanges, setHasChanges] = useState(false);
const [faqForm, setFaqForm] = useState({
title: '',
content: ''
question: '',
answer: ''
});
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
......@@ -47,8 +47,8 @@ const SettingsFAQ = ({ options, refresh }) => {
const columns = [
{
title: t('问题标题'),
dataIndex: 'title',
key: 'title',
dataIndex: 'question',
key: 'question',
render: (text) => (
<div style={{
maxWidth: '300px',
......@@ -61,8 +61,8 @@ const SettingsFAQ = ({ options, refresh }) => {
},
{
title: t('回答内容'),
dataIndex: 'content',
key: 'content',
dataIndex: 'answer',
key: 'answer',
render: (text) => (
<div style={{
maxWidth: '400px',
......@@ -124,7 +124,7 @@ const SettingsFAQ = ({ options, refresh }) => {
try {
setLoading(true);
const faqJson = JSON.stringify(faqList);
await updateOption('FAQ', faqJson);
await updateOption('console_setting.faq', faqJson);
setHasChanges(false);
} catch (error) {
console.error('常见问答更新失败', error);
......@@ -137,8 +137,8 @@ const SettingsFAQ = ({ options, refresh }) => {
const handleAddFaq = () => {
setEditingFaq(null);
setFaqForm({
title: '',
content: ''
question: '',
answer: ''
});
setShowFaqModal(true);
};
......@@ -146,8 +146,8 @@ const SettingsFAQ = ({ options, refresh }) => {
const handleEditFaq = (faq) => {
setEditingFaq(faq);
setFaqForm({
title: faq.title,
content: faq.content
question: faq.question,
answer: faq.answer
});
setShowFaqModal(true);
};
......@@ -169,7 +169,7 @@ const SettingsFAQ = ({ options, refresh }) => {
};
const handleSaveFaq = async () => {
if (!faqForm.title || !faqForm.content) {
if (!faqForm.question || !faqForm.answer) {
showError('请填写完整的问答信息');
return;
}
......@@ -226,10 +226,10 @@ const SettingsFAQ = ({ options, refresh }) => {
};
useEffect(() => {
if (options.FAQ !== undefined) {
parseFAQ(options.FAQ);
if (options['console_setting.faq'] !== undefined) {
parseFAQ(options['console_setting.faq']);
}
}, [options.FAQ]);
}, [options['console_setting.faq']]);
const handleBatchDelete = () => {
if (selectedRowKeys.length === 0) {
......@@ -372,21 +372,21 @@ const SettingsFAQ = ({ options, refresh }) => {
>
<Form layout='vertical' initValues={faqForm} key={editingFaq ? editingFaq.id : 'new'}>
<Form.Input
field='title'
field='question'
label={t('问题标题')}
placeholder={t('请输入问题标题')}
maxLength={200}
rules={[{ required: true, message: t('请输入问题标题') }]}
onChange={(value) => setFaqForm({ ...faqForm, title: value })}
onChange={(value) => setFaqForm({ ...faqForm, question: value })}
/>
<Form.TextArea
field='content'
field='answer'
label={t('回答内容')}
placeholder={t('请输入回答内容')}
maxCount={1000}
rows={6}
rules={[{ required: true, message: t('请输入回答内容') }]}
onChange={(value) => setFaqForm({ ...faqForm, content: value })}
onChange={(value) => setFaqForm({ ...faqForm, answer: value })}
/>
</Form>
</Modal>
......
......@@ -22,9 +22,9 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
const formApiRef = useRef(null);
const initValues = useMemo(() => ({
uptimeKumaUrl: options?.UptimeKumaUrl || '',
uptimeKumaSlug: options?.UptimeKumaSlug || ''
}), [options?.UptimeKumaUrl, options?.UptimeKumaSlug]);
uptimeKumaUrl: options?.['console_setting.uptime_kuma_url'] || '',
uptimeKumaSlug: options?.['console_setting.uptime_kuma_slug'] || ''
}), [options?.['console_setting.uptime_kuma_url'], options?.['console_setting.uptime_kuma_slug']]);
useEffect(() => {
if (formApiRef.current) {
......@@ -46,18 +46,18 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
const trimmedUrl = (uptimeKumaUrl || '').trim();
const trimmedSlug = (uptimeKumaSlug || '').trim();
if (trimmedUrl === options?.UptimeKumaUrl && trimmedSlug === options?.UptimeKumaSlug) {
if (trimmedUrl === options?.['console_setting.uptime_kuma_url'] && trimmedSlug === options?.['console_setting.uptime_kuma_slug']) {
showSuccess(t('无需保存,配置未变动'));
return;
}
const [urlRes, slugRes] = await Promise.all([
trimmedUrl === options?.UptimeKumaUrl ? Promise.resolve({ data: { success: true } }) : API.put('/api/option/', {
key: 'UptimeKumaUrl',
trimmedUrl === options?.['console_setting.uptime_kuma_url'] ? Promise.resolve({ data: { success: true } }) : API.put('/api/option/', {
key: 'console_setting.uptime_kuma_url',
value: trimmedUrl
}),
trimmedSlug === options?.UptimeKumaSlug ? Promise.resolve({ data: { success: true } }) : API.put('/api/option/', {
key: 'UptimeKumaSlug',
trimmedSlug === options?.['console_setting.uptime_kuma_slug'] ? Promise.resolve({ data: { success: true } }) : API.put('/api/option/', {
key: 'console_setting.uptime_kuma_slug',
value: trimmedSlug
})
]);
......
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