Skip to content

Commit 0c0c25c

Browse files
committed
fix: 推送展示html标签
1 parent 1c7cb97 commit 0c0c25c

7 files changed

Lines changed: 96 additions & 44 deletions

File tree

apps/client/tsconfig.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"root":["./src/main/index.ts","./src/preload/index.ts"],"version":"5.9.3"}
1+
{"root":["./src/main/index.ts","./src/preload/index.ts"],"errors":true,"version":"5.9.3"}

apps/server/src/db/connection.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,8 @@ export function initializeDatabase() {
4545
queueLimit: 0,
4646
enableKeepAlive: true,
4747
keepAliveInitialDelay: 0,
48-
// Connection timeout and retry settings
48+
// Connection timeout settings
4949
connectTimeout: 10000, // 10 seconds
50-
acquireTimeout: 10000, // 10 seconds
51-
timeout: 60000, // 60 seconds for query execution
5250
// Automatically reconnect on connection loss
5351
maxIdle: 10, // Maximum idle connections
5452
idleTimeout: 60000, // Close idle connections after 60 seconds

apps/server/src/services/channels/daily-content.generator.ts

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,11 @@ export class DailyContentGenerator implements ContentGenerator {
5454
const createdAt = memo.createdAt
5555
? new Date(memo.createdAt).toLocaleDateString('zh-CN')
5656
: '';
57-
return `
58-
<div style="margin-bottom: 16px; padding: 12px; background: #f5f5f5; border-radius: 8px;">
59-
<p style="color: #999; font-size: 12px; margin: 0 0 8px 0;">${createdAt}</p>
60-
<p style="margin: 0; font-size: 14px; line-height: 1.6;">${this.escapeHtml(content)}</p>
61-
</div>
62-
`;
57+
return `<div style="margin-bottom: 16px; padding: 12px; background: #f5f5f5; border-radius: 8px;"><p style="color: #999; font-size: 16px; margin: 0 0 8px 0;">${createdAt}</p><p style="margin: 0; font-size: 16px; line-height: 1.6;">${this.escapeHtml(content)}</p></div>`;
6358
})
6459
.join('');
6560

66-
const message = `
67-
<div style="font-size: 14px; line-height: 1.6;">
68-
${memoItems}
69-
</div>
70-
`;
71-
72-
return { title, msg: message, isHtml: true };
61+
return { title, msg: memoItems, isHtml: true };
7362
}
7463

7564
/**
@@ -112,22 +101,11 @@ export class DailyContentGenerator implements ContentGenerator {
112101
})
113102
: '';
114103

115-
return `
116-
<div style="margin-bottom: 12px; padding: 8px; background: #f5f5f5; border-radius: 6px;">
117-
<p style="color: #999; font-size: 12px; margin: 0 0 4px 0;">${createdAt}</p>
118-
<p style="margin: 0;">${this.escapeHtml(content)}</p>
119-
</div>
120-
`;
104+
return `<div style="margin-bottom: 12px; padding: 8px; background: #f5f5f5; border-radius: 6px;"><p style="color: #999; font-size: 16px; margin: 0 0 4px 0;">${createdAt}</p><p style="margin: 0;">${this.escapeHtml(content)}</p></div>`;
121105
})
122106
.join('');
123107

124-
const message = `
125-
<div style="font-size: 14px; line-height: 1.6;">
126-
${memoItems}
127-
</div>
128-
`;
129-
130-
return { title, msg: message, isHtml: true };
108+
return { title, msg: memoItems, isHtml: true };
131109
}
132110

133111
/**
@@ -142,6 +120,6 @@ export class DailyContentGenerator implements ContentGenerator {
142120
"'": '&#x27;',
143121
'/': '&#x2F;',
144122
};
145-
return text.replaceAll(/[&<>"'\/]/g, (char) => htmlEscapes[char] || char);
123+
return text.replaceAll(/[&<>"'/]/g, (char) => htmlEscapes[char] || char);
146124
}
147125
}

apps/server/src/services/channels/meow.channel.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { Service } from 'typedi';
2-
31
import { logger } from '../../utils/logger.js';
42

53
import type { PushChannel, PushChannelOptions } from './push-channel.interface.js';
@@ -25,17 +23,36 @@ export class MeowChannel implements PushChannel {
2523
async send(options: PushChannelOptions): Promise<void> {
2624
try {
2725
const messageType = this.config.msgType || 'text';
28-
const response = await fetch(`${this.apiUrl}/${this.config.nickname}`, {
26+
27+
// Build URL with query parameters (msgType and htmlHeight go in URL, not body)
28+
const url = new URL(`${this.apiUrl}/${this.config.nickname}`);
29+
url.searchParams.set('msgType', messageType.toLowerCase());
30+
31+
// Add htmlHeight parameter when msgType is html
32+
if (messageType === 'html' && this.config.htmlHeight) {
33+
url.searchParams.set('htmlHeight', this.config.htmlHeight.toString());
34+
}
35+
36+
// Request body only contains title, msg, and optional url
37+
const requestBody: {
38+
title: string;
39+
msg: string;
40+
url?: string;
41+
} = {
42+
title: options.title,
43+
msg: options.msg,
44+
url: options.url,
45+
};
46+
47+
logger.info(`MeoW request URL: ${url.toString()}`);
48+
logger.info(`MeoW request body:`, JSON.stringify(requestBody, undefined, 2));
49+
50+
const response = await fetch(url.toString(), {
2951
method: 'POST',
3052
headers: {
3153
'Content-Type': 'application/json',
3254
},
33-
body: JSON.stringify({
34-
msgType: messageType.toLocaleLowerCase(),
35-
title: options.title,
36-
msg: options.msg,
37-
url: options.url,
38-
}),
55+
body: JSON.stringify(requestBody),
3956
});
4057

4158
if (!response.ok) {

apps/server/src/services/push-rule.service.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,16 @@ export class PushRuleService {
201201
for (const channelConfig of rule.channels) {
202202
try {
203203
const channel = this.channelFactory.getChannel(channelConfig);
204+
205+
// Format test message based on channel msgType
206+
let testMessage = '这是一条测试消息,如果你能看到这条消息,说明推送配置正确!';
207+
if (channelConfig.msgType === 'html') {
208+
testMessage = '<p style="font-size: 14px; line-height: 1.6;">这是一条测试消息,如果你能看到这条消息,说明推送配置正确!</p>';
209+
}
210+
204211
await channel.send({
205212
title: '测试推送',
206-
msg: '这是一条测试消息,如果你能看到这条消息,说明推送配置正确!',
213+
msg: testMessage,
207214
});
208215
logger.info(`Test push sent for rule ${ruleId} via channel ${channelConfig.type}`);
209216
} catch (error) {

apps/server/src/services/scheduler.service.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,16 @@ export class SchedulerService {
153153
try {
154154
const content = await this.contentGenerator.generate(rule.contentType, rule.uid);
155155

156+
// If channel is text type and content is HTML, convert to plain text
157+
let message = content.msg;
158+
if (channelConfig.msgType === 'text' && content.isHtml) {
159+
message = this.stripHtml(content.msg);
160+
}
161+
156162
const channel = this.channelFactory.getChannel(channelConfig);
157163
await channel.send({
158164
title: content.title,
159-
msg: content.msg,
165+
msg: message,
160166
});
161167
logger.info(`Push sent for rule ${rule.id} via channel ${channelConfig.type}`);
162168
} catch (error) {
@@ -169,6 +175,26 @@ export class SchedulerService {
169175
}
170176
}
171177

178+
/**
179+
* Strip HTML tags and convert to plain text
180+
*/
181+
private stripHtml(html: string): string {
182+
return html
183+
.replaceAll(/<\/div>/gi, '\n')
184+
.replaceAll(/<\/p>/gi, '\n')
185+
.replaceAll(/<br\s*\/?>/gi, '\n')
186+
.replaceAll(/<[^>]+>/g, '')
187+
.replaceAll(/&nbsp;/g, ' ')
188+
.replaceAll(/&amp;/g, '&')
189+
.replaceAll(/&lt;/g, '<')
190+
.replaceAll(/&gt;/g, '>')
191+
.replaceAll(/&quot;/g, '"')
192+
.replaceAll(/&#x27;/g, "'")
193+
.replaceAll(/&#x2F;/g, '/')
194+
.replaceAll(/\n\s*\n/g, '\n\n')
195+
.trim();
196+
}
197+
172198
/**
173199
* 停止所有定时任务
174200
*/

apps/web/src/pages/settings/components/push-rule/push-rules-list.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { useEffect } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { useService, view } from '@rabjs/react';
3-
import { Bell, Plus, Pencil, Trash2 } from 'lucide-react';
3+
import { Bell, Plus, Pencil, Trash2, Send } from 'lucide-react';
44
import type { PushRuleDto, PushChannelConfigDto } from '@aimo/dto';
55
import { PushRuleService } from './push-rule.service';
6+
import { toast } from '../../../../services/toast.service';
67

78
interface PushRulesListProps {
89
onAddRule: () => void;
@@ -11,6 +12,7 @@ interface PushRulesListProps {
1112

1213
export const PushRulesList = view(({ onAddRule, onEditRule }: PushRulesListProps) => {
1314
const pushRuleService = useService(PushRuleService);
15+
const [testingRuleId, setTestingRuleId] = useState<string | null>(null);
1416

1517
useEffect(() => {
1618
pushRuleService.fetchRules();
@@ -25,6 +27,22 @@ export const PushRulesList = view(({ onAddRule, onEditRule }: PushRulesListProps
2527
await pushRuleService.deleteRule(ruleId);
2628
};
2729

30+
const handleTest = async (ruleId: string) => {
31+
setTestingRuleId(ruleId);
32+
try {
33+
const result = await pushRuleService.testPush(ruleId);
34+
if (result.success) {
35+
toast.success(result.message || '测试消息已发送');
36+
} else {
37+
toast.error(`发送失败: ${result.message || '未知错误'}`);
38+
}
39+
} catch (error) {
40+
toast.error(`发送失败: ${error instanceof Error ? error.message : '未知错误'}`);
41+
} finally {
42+
setTestingRuleId(null);
43+
}
44+
};
45+
2846
if (pushRuleService.loading) {
2947
return (
3048
<div className="flex items-center justify-center py-8">
@@ -88,6 +106,14 @@ export const PushRulesList = view(({ onAddRule, onEditRule }: PushRulesListProps
88106
</div>
89107
</div>
90108
<div className="flex items-center gap-2">
109+
<button
110+
onClick={() => handleTest(rule.id)}
111+
disabled={testingRuleId === rule.id}
112+
className="p-2 text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400 hover:bg-gray-100 dark:hover:bg-dark-700 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
113+
title="测试推送"
114+
>
115+
<Send className={`w-4 h-4 ${testingRuleId === rule.id ? 'animate-pulse' : ''}`} />
116+
</button>
91117
<button
92118
onClick={() => onEditRule(rule)}
93119
className="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-dark-700 rounded-lg transition-colors"

0 commit comments

Comments
 (0)