chore: initial commit - CloudSearch v0.0.2

This commit is contained in:
2026-05-15 05:50:50 +08:00
commit d83225d736
102 changed files with 37926 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
// Native fetch available in Node 20+
import { getSystemConfig } from '../admin/system-config.service';
type NotifyLevel = 'info' | 'warn' | 'error';
interface NotifyChannel {
send(title: string, content: string, level: NotifyLevel): Promise<void>;
}
// ---- Feishu Webhook Channel ----
class FeishuChannel implements NotifyChannel {
private webhookUrl: string;
constructor(webhookUrl: string) {
this.webhookUrl = webhookUrl;
}
async send(title: string, content: string, _level: NotifyLevel): Promise<void> {
try {
const body = JSON.stringify({
msg_type: 'interactive',
card: {
header: {
title: { tag: 'plain_text', content: title },
template: _level === 'error' ? 'red' : _level === 'warn' ? 'orange' : 'blue',
},
elements: [
{ tag: 'div', text: { tag: 'lark_md', content } },
{
tag: 'note',
elements: [
{ tag: 'plain_text', content: `CloudSearch · ${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}` },
],
},
],
},
});
const resp = await fetch(this.webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body,
});
if (!resp.ok) {
console.error(`[Notify] Feishu send failed: ${resp.status}`);
}
} catch (err: any) {
console.error('[Notify] Feishu send error:', err.message);
}
}
}
// ---- Notification Manager ----
let _channel: NotifyChannel | null = null;
function getChannel(): NotifyChannel | null {
const feishuUrl = process.env.FEISHU_WEBHOOK || getSystemConfig('feishu_webhook_url');
if (!feishuUrl) return null;
if (!_channel) {
_channel = new FeishuChannel(feishuUrl);
console.log('[Notify] Feishu webhook configured');
}
return _channel;
}
/**
* Send a notification through configured channels.
* Returns immediately — failures are logged silently.
*/
export function notify(title: string, content: string, level: NotifyLevel = 'info'): void {
const ch = getChannel();
if (!ch) return;
// Fire-and-forget — don't block the caller
ch.send(title, content, level).catch(() => {});
}
/**
* Notify on critical events:
* - Cookie expired / login failed
* - Save/transfer failed repeatedly
* - Storage below threshold
*/
export function notifyError(title: string, detail: string): void {
notify(`⚠️ ${title}`, detail, 'error');
}
export function notifyWarn(title: string, detail: string): void {
notify(`🔔 ${title}`, detail, 'warn');
}
export function notifyInfo(title: string, detail: string): void {
notify(` ${title}`, detail, 'info');
}