| 1 | import type { DestinationStream } from 'pino' |
| 2 | import crypto from 'node:crypto' |
| 3 | import { Logger } from '@nestjs/common' |
| 4 | |
| 5 | export interface FeishuOptions { |
| 6 | url: string |
| 7 | secret: string |
| 8 | } |
| 9 | export class FeishuLogger implements DestinationStream { |
| 10 | private readonly logger = new Logger(FeishuLogger.name) |
| 11 | |
| 12 | constructor(private readonly options: FeishuOptions) { |
| 13 | } |
| 14 | |
| 15 | async write(msg: string): Promise<void> { |
| 16 | const timestamp = Math.floor(Date.now() / 1000) |
| 17 | const sign = crypto |
| 18 | .createHmac('sha256', `${timestamp}\n${this.options.secret}`) |
| 19 | .digest() |
| 20 | .toString('base64') |
| 21 | |
| 22 | let content: unknown |
| 23 | try { |
| 24 | content = JSON.parse(msg) |
| 25 | } |
| 26 | catch { |
| 27 | content = { raw: msg } |
| 28 | } |
| 29 | |
| 30 | await fetch(this.options.url, { |
| 31 | method: 'POST', |
| 32 | body: JSON.stringify({ |
| 33 | timestamp, |
| 34 | sign, |
| 35 | msg_type: 'text', |
| 36 | content: { |
| 37 | text: JSON.stringify(content, null, 2), |
| 38 | }, |
| 39 | }), |
| 40 | }) |
| 41 | .then(r => r.json() as Promise<{ code: number }>) |
| 42 | .then((r) => { |
| 43 | if (r.code !== 0) { |
| 44 | this.logger.error(r, 'Feishu send failed') |
| 45 | } |
| 46 | }) |
| 47 | .catch(e => this.logger.error(e, 'Feishu request failed')) |
| 48 | } |
| 49 | } |
| 50 |