✨ 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.
This commit is contained in:
@@ -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
|
||||
})
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user