feat(desktop): add sort/filter, update center, and local sync workspace
This commit is contained in:
@@ -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: '系统设置已更新'
|
||||
|
||||
Reference in New Issue
Block a user