v0.3.5: restore full push notification system (14 channels)
Restored from v0.2.4: - notifiers/: 14 push channels (bark/serverchan/telegram/lark/webhook/wechat/discord/smtp/...) - push-user.service.ts: multi-user push config linked to cloud_configs.promotion_account - notification.service.ts: full dispatcher with per-config + global fallback New integrations: - cloud.service.ts: notifyConfigEvent on save_success/cookie_expire/save_fail - admin.routes.ts: 7 new API endpoints for push users, notify providers, channel test - database.ts: migration for cloud_configs.notify_config column How it works: - Configure push channels in /admin/system-configs (global_notify_config JSON) - Or per-cloud: link push_users.account = cloud_configs.promotion_account - Notify events: save_success (green), cookie_expire (red), save_fail >=3 consecutive (yellow)
This commit is contained in:
@@ -5,6 +5,7 @@ import { QuarkDriver } from './drivers/quark.driver';
|
||||
import { BaiduDriver } from './drivers/baidu.driver';
|
||||
import { CloudConfig, getAndValidateCredential, getActiveCloudConfigs } from './credential.service';
|
||||
import { lookupIpLocation } from './ip-lookup';
|
||||
import { notifyConfigEvent } from './notification.service';
|
||||
|
||||
/** In-flight save dedup: prevents concurrent saves of the same URL (race condition fix) */
|
||||
const inFlightSaves = new Map<string, Promise<SaveResult>>();
|
||||
@@ -174,12 +175,43 @@ async function doSaveFromShare(shareUrl: string, cloudType: string, sourceTitle?
|
||||
db.prepare(
|
||||
`UPDATE cloud_configs SET last_used_at = datetime('now','localtime'), total_saves = total_saves + 1, consecutive_failures = 0 WHERE id = ?`
|
||||
).run(config.id);
|
||||
const nickname = config.nickname || cloudType;
|
||||
notifyConfigEvent(config.id, 'save_success', `✅ 转存成功`, `**${cloudType}** · ${nickname}
|
||||
文件: ${driverResult.folderName || sourceTitle || shareUrl}
|
||||
耗时: ${((Date.now() - startTime) / 1000).toFixed(1)}s`, 'info', {
|
||||
file_name: driverResult.folderName || sourceTitle || shareUrl || '',
|
||||
file_size: '',
|
||||
cloud_type: cloudType,
|
||||
nickname: nickname || '',
|
||||
duration: ((Date.now() - startTime) / 1000).toFixed(1),
|
||||
share_url: shareUrl,
|
||||
});
|
||||
} else if ((driverResult as any).cookieExpired) {
|
||||
// Cookie expired — don't count as failure, user needs to re-login
|
||||
notifyConfigEvent(config.id, 'cookie_expire', `⚠️ Cookie过期`, `**${cloudType}** · ${config.nickname || '未知'}
|
||||
链接: ${shareUrl}
|
||||
请重新登录`, 'error', {
|
||||
cloud_type: cloudType,
|
||||
nickname: config.nickname || '',
|
||||
share_url: shareUrl,
|
||||
});
|
||||
} else {
|
||||
db.prepare(
|
||||
`UPDATE cloud_configs SET consecutive_failures = consecutive_failures + 1 WHERE id = ?`
|
||||
).run(config.id);
|
||||
const failCount = (db.prepare(`SELECT consecutive_failures FROM cloud_configs WHERE id = ?`).get(config.id) as any)?.consecutive_failures || 0;
|
||||
if (failCount >= 3) {
|
||||
notifyConfigEvent(config.id, 'save_fail', `❌ 转存连续失败 ${failCount} 次`, `**${cloudType}** · ${config.nickname || '未知'}
|
||||
链接: ${shareUrl}
|
||||
错误: ${driverResult.message}`, 'warn', {
|
||||
file_name: sourceTitle || shareUrl || '',
|
||||
fail_count: String(failCount),
|
||||
cloud_type: cloudType,
|
||||
nickname: config.nickname || '',
|
||||
error: driverResult.message || '',
|
||||
share_url: shareUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
db.prepare(
|
||||
|
||||
Reference in New Issue
Block a user