aicodeswitch 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,206 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { api } from '../api/client';
3
+ import type { AppConfig } from '../../types';
4
+
5
+ function SettingsPage() {
6
+ const [config, setConfig] = useState<AppConfig | null>(null);
7
+ const [password, setPassword] = useState('');
8
+ const [importData, setImportData] = useState('');
9
+ const [exportedData, setExportedData] = useState('');
10
+
11
+ useEffect(() => {
12
+ loadConfig();
13
+ }, []);
14
+
15
+ const loadConfig = async () => {
16
+ const data = await api.getConfig();
17
+ setConfig(data);
18
+ };
19
+
20
+ const handleSaveConfig = async (e: React.FormEvent<HTMLFormElement>) => {
21
+ e.preventDefault();
22
+ const formData = new FormData(e.currentTarget);
23
+ const newConfig: AppConfig = {
24
+ enableLogging: formData.get('enableLogging') === 'true',
25
+ logRetentionDays: parseInt(formData.get('logRetentionDays') as string),
26
+ maxLogSize: parseInt(formData.get('maxLogSize') as string),
27
+ apiKey: formData.get('apiKey') as string,
28
+ enableFailover: formData.get('enableFailover') === 'true',
29
+ };
30
+
31
+ const success = await api.updateConfig(newConfig);
32
+ if (success) {
33
+ alert('配置保存成功');
34
+ loadConfig();
35
+ } else {
36
+ alert('配置保存失败');
37
+ }
38
+ };
39
+
40
+ const handleExport = async () => {
41
+ if (!password) {
42
+ alert('请输入密码');
43
+ return;
44
+ }
45
+
46
+ try {
47
+ const data = await api.exportData(password);
48
+ setExportedData(data);
49
+ alert('导出成功,请复制下方数据');
50
+ } catch (error: any) {
51
+ alert('导出失败: ' + error.message);
52
+ }
53
+ };
54
+
55
+ const handleImport = async () => {
56
+ if (!password || !importData) {
57
+ alert('请输入密码和导入数据');
58
+ return;
59
+ }
60
+
61
+ try {
62
+ const success = await api.importData(importData, password);
63
+ if (success) {
64
+ alert('导入成功');
65
+ setImportData('');
66
+ setPassword('');
67
+ } else {
68
+ alert('导入失败,请检查密码是否正确');
69
+ }
70
+ } catch (error: any) {
71
+ alert('导入失败: ' + error.message);
72
+ }
73
+ };
74
+
75
+ if (!config) {
76
+ return <div>加载中...</div>;
77
+ }
78
+
79
+ return (
80
+ <div>
81
+ <div className="page-header">
82
+ <h1>设置</h1>
83
+ <p>管理应用配置和数据导入导出</p>
84
+ </div>
85
+
86
+ <div className="card">
87
+ <h3>应用配置</h3>
88
+ <form onSubmit={handleSaveConfig}>
89
+
90
+ <div className="form-group">
91
+ <label>API Key</label>
92
+ <input
93
+ type="password"
94
+ name="apiKey"
95
+ defaultValue={config.apiKey}
96
+ />
97
+ </div>
98
+ <div className="form-group">
99
+ <label>启用日志</label>
100
+ <select name="enableLogging" defaultValue={config.enableLogging ? 'true' : 'false'}>
101
+ <option value="true">是</option>
102
+ <option value="false">否</option>
103
+ </select>
104
+ </div>
105
+ <div className="form-group">
106
+ <label>启用智能故障切换</label>
107
+ <select name="enableFailover" defaultValue={config.enableFailover !== false ? 'true' : 'false'}>
108
+ <option value="true">是</option>
109
+ <option value="false">否</option>
110
+ </select>
111
+ <small style={{ display: 'block', marginTop: '4px', color: '#666', fontSize: '12px' }}>
112
+ 启用后,当某个服务报错时会自动切换到备用服务,并将报错服务标记为不可用10分钟
113
+ </small>
114
+ </div>
115
+ <div className="form-group">
116
+ <label>日志保留天数</label>
117
+ <input
118
+ type="number"
119
+ name="logRetentionDays"
120
+ defaultValue={config.logRetentionDays}
121
+ required
122
+ />
123
+ </div>
124
+ <div className="form-group">
125
+ <label>最大日志数量</label>
126
+ <input
127
+ type="number"
128
+ name="maxLogSize"
129
+ defaultValue={config.maxLogSize}
130
+ required
131
+ />
132
+ </div>
133
+ <button type="submit" className="btn btn-primary">保存配置</button>
134
+ </form>
135
+ </div>
136
+
137
+ <div className="card" style={{ marginTop: '20px' }}>
138
+ <h3>数据导出</h3>
139
+ <p style={{ color: '#7f8c8d', fontSize: '14px' }}>
140
+ 导出所有配置数据,包括供应商、API服务、路由等。数据将使用密码加密。
141
+ </p>
142
+ <div className="form-group">
143
+ <label>加密密码</label>
144
+ <input
145
+ type="password"
146
+ value={password}
147
+ onChange={(e) => setPassword(e.target.value)}
148
+ placeholder="用于加密导出数据"
149
+ />
150
+ </div>
151
+ <button className="btn btn-primary" onClick={handleExport}>导出数据</button>
152
+
153
+ {exportedData && (
154
+ <div className="form-group" style={{ marginTop: '20px' }}>
155
+ <label>导出的数据(请复制保存)</label>
156
+ <textarea
157
+ rows={6}
158
+ value={exportedData}
159
+ readOnly
160
+ style={{ fontFamily: 'monospace', fontSize: '12px' }}
161
+ />
162
+ <button
163
+ className="btn btn-secondary"
164
+ style={{ marginTop: '10px' }}
165
+ onClick={() => {
166
+ navigator.clipboard.writeText(exportedData);
167
+ alert('已复制到剪贴板');
168
+ }}
169
+ >
170
+ 复制到剪贴板
171
+ </button>
172
+ </div>
173
+ )}
174
+ </div>
175
+
176
+ <div className="card" style={{ marginTop: '20px' }}>
177
+ <h3>数据导入</h3>
178
+ <p style={{ color: '#e74c3c', fontSize: '14px', fontWeight: 500 }}>
179
+ 警告:导入数据将覆盖所有现有配置!请确保已备份重要数据。
180
+ </p>
181
+ <div className="form-group">
182
+ <label>解密密码</label>
183
+ <input
184
+ type="password"
185
+ value={password}
186
+ onChange={(e) => setPassword(e.target.value)}
187
+ placeholder="用于解密导入数据"
188
+ />
189
+ </div>
190
+ <div className="form-group">
191
+ <label>导入数据</label>
192
+ <textarea
193
+ rows={6}
194
+ value={importData}
195
+ onChange={(e) => setImportData(e.target.value)}
196
+ placeholder="粘贴导出的加密数据"
197
+ style={{ fontFamily: 'monospace', fontSize: '12px' }}
198
+ />
199
+ </div>
200
+ <button className="btn btn-danger" onClick={handleImport}>导入数据</button>
201
+ </div>
202
+ </div>
203
+ );
204
+ }
205
+
206
+ export default SettingsPage;