(method: string, path: string, durationMs: number, status: number)
| 46 | } |
| 47 | |
| 48 | function notifySlack(method: string, path: string, durationMs: number, status: number): void { |
| 49 | if (!OPS_ALERT_CHANNEL_ID || !isSlackConfigured()) return; |
| 50 | |
| 51 | const safePath = path.slice(0, 200).replace(/[`*_~<>]/g, ""); |
| 52 | const seconds = (durationMs / 1000).toFixed(1); |
| 53 | |
| 54 | sendChannelMessage(OPS_ALERT_CHANNEL_ID, { |
| 55 | text: `Slow API: ${method} ${safePath} took ${seconds}s (status ${status})`, |
| 56 | blocks: [ |
| 57 | { |
| 58 | type: "section", |
| 59 | text: { |
| 60 | type: "mrkdwn", |
| 61 | text: [ |
| 62 | `:snail: *Slow API response* on \`${process.env.FLY_APP_NAME || "aao-server"}\``, |
| 63 | `*Endpoint:* \`${method} ${safePath}\``, |
| 64 | `*Duration:* ${seconds}s`, |
| 65 | `*Status:* ${status}`, |
| 66 | "_This usually means an external service (Stripe, WorkOS) is slow or hanging._", |
| 67 | ].join("\n"), |
| 68 | }, |
| 69 | }, |
| 70 | ], |
| 71 | }).catch(() => {}); |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Express middleware — mount before routes. |
no test coverage detected