diff --git a/backend/database.js b/backend/database.js
index 7c429b9..679be1b 100644
--- a/backend/database.js
+++ b/backend/database.js
@@ -446,7 +446,7 @@ const ShareDB = {
});
const result = db.prepare(`
- SELECT s.*, u.username, u.ftp_host, u.ftp_port, u.ftp_user, u.ftp_password, u.http_download_base_url
+ SELECT s.*, u.username, u.ftp_host, u.ftp_port, u.ftp_user, u.ftp_password, u.http_download_base_url, u.theme_preference
FROM shares s
JOIN users u ON s.user_id = u.id
WHERE s.share_code = ?
@@ -616,6 +616,26 @@ function initDefaultSettings() {
if (!SettingsDB.get('max_upload_size')) {
SettingsDB.set('max_upload_size', '10737418240'); // 10GB in bytes
}
+ // 默认全局主题为暗色
+ if (!SettingsDB.get('global_theme')) {
+ SettingsDB.set('global_theme', 'dark');
+ }
+}
+
+// 数据库迁移 - 主题偏好字段
+function migrateThemePreference() {
+ try {
+ const columns = db.prepare("PRAGMA table_info(users)").all();
+ const hasThemePreference = columns.some(col => col.name === 'theme_preference');
+
+ if (!hasThemePreference) {
+ console.log('[数据库迁移] 添加主题偏好字段...');
+ db.exec(`ALTER TABLE users ADD COLUMN theme_preference TEXT DEFAULT NULL`);
+ console.log('[数据库迁移] ✓ 主题偏好字段已添加');
+ }
+ } catch (error) {
+ console.error('[数据库迁移] 主题偏好迁移失败:', error);
+ }
}
// 数据库版本迁移 - v2.0 本地存储功能
@@ -798,6 +818,7 @@ initDatabase();
createDefaultAdmin();
initDefaultSettings();
migrateToV2(); // 执行数据库迁移
+migrateThemePreference(); // 主题偏好迁移
module.exports = {
db,
diff --git a/backend/server.js b/backend/server.js
index 5935d5a..d4041d7 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -1628,6 +1628,56 @@ app.get('/api/user/profile', authMiddleware, (req, res) => {
});
});
+// 获取用户主题偏好(包含全局默认主题)
+app.get('/api/user/theme', authMiddleware, (req, res) => {
+ try {
+ const globalTheme = SettingsDB.get('global_theme') || 'dark';
+ const userTheme = req.user.theme_preference; // null表示跟随全局
+
+ res.json({
+ success: true,
+ theme: {
+ global: globalTheme,
+ user: userTheme,
+ effective: userTheme || globalTheme // 用户设置优先,否则使用全局
+ }
+ });
+ } catch (error) {
+ res.status(500).json({ success: false, message: '获取主题失败' });
+ }
+});
+
+// 设置用户主题偏好
+app.post('/api/user/theme', authMiddleware, (req, res) => {
+ try {
+ const { theme } = req.body;
+ const validThemes = ['dark', 'light', null]; // null表示跟随全局
+
+ if (!validThemes.includes(theme)) {
+ return res.status(400).json({
+ success: false,
+ message: '无效的主题设置,可选: dark, light, null(跟随全局)'
+ });
+ }
+
+ UserDB.update(req.user.id, { theme_preference: theme });
+
+ const globalTheme = SettingsDB.get('global_theme') || 'dark';
+ res.json({
+ success: true,
+ message: '主题偏好已更新',
+ theme: {
+ global: globalTheme,
+ user: theme,
+ effective: theme || globalTheme
+ }
+ });
+ } catch (error) {
+ console.error('更新主题失败:', error);
+ res.status(500).json({ success: false, message: '更新主题失败' });
+ }
+});
+
// 更新FTP配置
app.post('/api/user/update-ftp',
authMiddleware,
@@ -2888,6 +2938,44 @@ app.delete('/api/share/:id', authMiddleware, (req, res) => {
// ===== 分享链接访问(公开) =====
+// 获取公共主题设置(用于分享页面,无需认证)
+app.get('/api/public/theme', (req, res) => {
+ try {
+ const globalTheme = SettingsDB.get('global_theme') || 'dark';
+ res.json({
+ success: true,
+ theme: globalTheme
+ });
+ } catch (error) {
+ res.json({ success: true, theme: 'dark' }); // 出错默认暗色
+ }
+});
+
+// 获取分享页面主题(基于分享者偏好或全局设置)
+app.get('/api/share/:code/theme', (req, res) => {
+ try {
+ const { code } = req.params;
+ const share = ShareDB.findByCode(code);
+ const globalTheme = SettingsDB.get('global_theme') || 'dark';
+
+ if (!share) {
+ return res.json({
+ success: true,
+ theme: globalTheme
+ });
+ }
+
+ // 优先使用分享者的主题偏好,否则使用全局主题
+ const effectiveTheme = share.theme_preference || globalTheme;
+ res.json({
+ success: true,
+ theme: effectiveTheme
+ });
+ } catch (error) {
+ res.json({ success: true, theme: 'dark' });
+ }
+});
+
// 访问分享链接 - 验证密码(支持本地存储和SFTP)
app.post('/api/share/:code/verify', shareRateLimitMiddleware, async (req, res) => {
const { code } = req.params;
@@ -3418,11 +3506,13 @@ app.get('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => {
const smtpUser = SettingsDB.get('smtp_user');
const smtpFrom = SettingsDB.get('smtp_from') || smtpUser;
const smtpHasPassword = !!SettingsDB.get('smtp_password');
+ const globalTheme = SettingsDB.get('global_theme') || 'dark';
res.json({
success: true,
settings: {
max_upload_size: maxUploadSize,
+ global_theme: globalTheme,
smtp: {
host: smtpHost || '',
port: smtpPort ? parseInt(smtpPort, 10) : 465,
@@ -3445,7 +3535,7 @@ app.get('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => {
// 更新系统设置
app.post('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => {
try {
- const { max_upload_size, smtp } = req.body;
+ const { max_upload_size, smtp, global_theme } = req.body;
if (max_upload_size !== undefined) {
const size = parseInt(max_upload_size);
@@ -3458,6 +3548,18 @@ app.post('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => {
SettingsDB.set('max_upload_size', size.toString());
}
+ // 更新全局主题
+ if (global_theme !== undefined) {
+ const validThemes = ['dark', 'light'];
+ if (!validThemes.includes(global_theme)) {
+ return res.status(400).json({
+ success: false,
+ message: '无效的主题设置'
+ });
+ }
+ SettingsDB.set('global_theme', global_theme);
+ }
+
if (smtp) {
if (!smtp.host || !smtp.port || !smtp.user) {
return res.status(400).json({ success: false, message: 'SMTP配置不完整' });
diff --git a/frontend/app.html b/frontend/app.html
index 96cdd87..be6469d 100644
--- a/frontend/app.html
+++ b/frontend/app.html
@@ -107,7 +107,7 @@