feat(desktop): add sort/filter, update center, and local sync workspace

This commit is contained in:
2026-02-18 20:07:21 +08:00
parent d4818a78d3
commit 32a66e6c77
8 changed files with 1184 additions and 39 deletions

View File

@@ -97,6 +97,9 @@ const DOWNLOAD_RESERVATION_TTL_MS = Number(process.env.DOWNLOAD_RESERVATION_TTL_
const DOWNLOAD_LOG_RECONCILE_INTERVAL_MS = Number(process.env.DOWNLOAD_LOG_RECONCILE_INTERVAL_MS || (5 * 60 * 1000)); // 5分钟
const DOWNLOAD_LOG_MAX_FILES_PER_SWEEP = Number(process.env.DOWNLOAD_LOG_MAX_FILES_PER_SWEEP || 40);
const DOWNLOAD_LOG_LIST_MAX_KEYS = Number(process.env.DOWNLOAD_LOG_LIST_MAX_KEYS || 200);
const DEFAULT_DESKTOP_VERSION = process.env.DESKTOP_LATEST_VERSION || '0.1.0';
const DEFAULT_DESKTOP_INSTALLER_URL = process.env.DESKTOP_INSTALLER_URL || '';
const DEFAULT_DESKTOP_RELEASE_NOTES = process.env.DESKTOP_RELEASE_NOTES || '';
const RESUMABLE_UPLOAD_SESSION_TTL_MS = Number(process.env.RESUMABLE_UPLOAD_SESSION_TTL_MS || (24 * 60 * 60 * 1000)); // 24小时
const RESUMABLE_UPLOAD_CHUNK_SIZE_BYTES = Number(process.env.RESUMABLE_UPLOAD_CHUNK_SIZE_BYTES || (4 * 1024 * 1024)); // 4MB
const RESUMABLE_UPLOAD_MAX_CHUNK_SIZE_BYTES = Number(process.env.RESUMABLE_UPLOAD_MAX_CHUNK_SIZE_BYTES || (32 * 1024 * 1024)); // 32MB
@@ -127,6 +130,56 @@ const SHOULD_USE_SECURE_COOKIES =
COOKIE_SECURE_MODE === 'true' ||
(process.env.NODE_ENV === 'production' && COOKIE_SECURE_MODE !== 'false');
function normalizeVersion(rawVersion, fallback = '0.0.0') {
const value = String(rawVersion || '').trim();
if (!value) return fallback;
return value;
}
function compareLooseVersion(left, right) {
const normalize = (value) => normalizeVersion(value, '0.0.0')
.replace(/^v/i, '')
.split('.')
.map((part) => parseInt(part, 10))
.map((num) => (Number.isFinite(num) ? num : 0));
const a = normalize(left);
const b = normalize(right);
const size = Math.max(a.length, b.length);
for (let i = 0; i < size; i += 1) {
const av = a[i] || 0;
const bv = b[i] || 0;
if (av > bv) return 1;
if (av < bv) return -1;
}
return 0;
}
function getDesktopUpdateConfig() {
const latestVersion = normalizeVersion(
SettingsDB.get('desktop_latest_version') || DEFAULT_DESKTOP_VERSION,
DEFAULT_DESKTOP_VERSION
);
const installerUrl = String(
SettingsDB.get('desktop_installer_url_win_x64') ||
SettingsDB.get('desktop_installer_url') ||
DEFAULT_DESKTOP_INSTALLER_URL ||
''
).trim();
const releaseNotes = String(
SettingsDB.get('desktop_release_notes') ||
DEFAULT_DESKTOP_RELEASE_NOTES ||
''
).trim();
const mandatory = SettingsDB.get('desktop_force_update') === 'true';
return {
latestVersion,
installerUrl,
releaseNotes,
mandatory
};
}
function getResolvedStorageRoot() {
const configuredRoot = process.env.STORAGE_ROOT;
if (!configuredRoot) {
@@ -3358,6 +3411,39 @@ app.get('/api/config', (req, res) => {
});
});
// 桌面客户端更新信息(无需登录)
app.get('/api/client/desktop-update', (req, res) => {
try {
const currentVersion = normalizeVersion(req.query.currentVersion || '0.0.0', '0.0.0');
const platform = String(req.query.platform || 'windows-x64').trim();
const channel = String(req.query.channel || 'stable').trim();
const config = getDesktopUpdateConfig();
const hasDownload = Boolean(config.installerUrl);
const updateAvailable = hasDownload && compareLooseVersion(currentVersion, config.latestVersion) < 0;
res.json({
success: true,
currentVersion,
latestVersion: config.latestVersion,
updateAvailable,
downloadUrl: config.installerUrl,
releaseNotes: config.releaseNotes,
mandatory: config.mandatory && updateAvailable,
platform,
channel,
message: hasDownload
? (updateAvailable ? '检测到新版本' : '当前已是最新版本')
: '服务器暂未配置桌面端升级包地址'
});
} catch (error) {
console.error('[客户端更新] 获取升级信息失败:', error);
res.status(500).json({
success: false,
message: '获取客户端更新信息失败'
});
}
});
// 生成验证码API
app.get('/api/captcha', captchaRateLimitMiddleware, (req, res) => {
try {
@@ -8479,6 +8565,7 @@ app.get('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => {
const smtpHasPassword = !!SettingsDB.get('smtp_password');
const globalTheme = SettingsDB.get('global_theme') || 'dark';
const downloadSecurity = getDownloadSecuritySettings();
const desktopUpdate = getDesktopUpdateConfig();
res.json({
success: true,
@@ -8486,6 +8573,12 @@ app.get('/api/admin/settings', authMiddleware, adminMiddleware, (req, res) => {
max_upload_size: maxUploadSize,
global_theme: globalTheme,
download_security: downloadSecurity,
desktop_update: {
latest_version: desktopUpdate.latestVersion,
installer_url: desktopUpdate.installerUrl,
release_notes: desktopUpdate.releaseNotes,
force_update: desktopUpdate.mandatory
},
smtp: {
host: smtpHost || '',
port: smtpPort ? parseInt(smtpPort, 10) : 465,
@@ -8516,7 +8609,8 @@ app.post('/api/admin/settings',
max_upload_size,
smtp,
global_theme,
download_security
download_security,
desktop_update
} = req.body;
if (max_upload_size !== undefined) {
@@ -8572,6 +8666,29 @@ app.post('/api/admin/settings',
}
}
if (desktop_update !== undefined) {
if (!desktop_update || typeof desktop_update !== 'object') {
return res.status(400).json({
success: false,
message: '桌面端更新配置格式错误'
});
}
if (desktop_update.latest_version !== undefined) {
SettingsDB.set('desktop_latest_version', normalizeVersion(desktop_update.latest_version, DEFAULT_DESKTOP_VERSION));
}
if (desktop_update.installer_url !== undefined) {
SettingsDB.set('desktop_installer_url', String(desktop_update.installer_url || '').trim());
SettingsDB.set('desktop_installer_url_win_x64', String(desktop_update.installer_url || '').trim());
}
if (desktop_update.release_notes !== undefined) {
SettingsDB.set('desktop_release_notes', String(desktop_update.release_notes || '').trim());
}
if (desktop_update.force_update !== undefined) {
SettingsDB.set('desktop_force_update', desktop_update.force_update ? 'true' : 'false');
}
}
res.json({
success: true,
message: '系统设置已更新'