mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
- Implemented MonitorUtil for managing monitor secrets and populating them in monitor steps and tests. - Created StackTraceParser to parse and structure stack traces from various programming languages. - Developed SyslogParser to handle and parse syslog messages in both RFC 5424 and RFC 3164 formats.
124 lines
4.1 KiB
TypeScript
124 lines
4.1 KiB
TypeScript
import Express, {
|
|
ExpressRequest,
|
|
ExpressResponse,
|
|
ExpressRouter,
|
|
} from "Common/Server/Utils/Express";
|
|
import Response from "Common/Server/Utils/Response";
|
|
import BadDataException from "Common/Types/Exception/BadDataException";
|
|
import logger from "Common/Server/Utils/Logger";
|
|
import InboundEmailProviderFactory from "Common/Server/Services/InboundEmail/InboundEmailProviderFactory";
|
|
import InboundEmailProvider, {
|
|
ParsedInboundEmail,
|
|
} from "Common/Server/Services/InboundEmail/InboundEmailProvider";
|
|
import { JSONObject } from "Common/Types/JSON";
|
|
import TelemetryQueueService from "../../Services/Queue/TelemetryQueueService";
|
|
import MultipartFormDataMiddleware from "Common/Server/Middleware/MultipartFormData";
|
|
|
|
const router: ExpressRouter = Express.getRouter();
|
|
|
|
/*
|
|
* Webhook endpoint for SendGrid inbound emails
|
|
* SendGrid sends data as multipart/form-data
|
|
* The webhook secret must be passed as the last path segment: /incoming-email/sendgrid/:secret
|
|
*/
|
|
router.post(
|
|
"/incoming-email/sendgrid/:secret",
|
|
MultipartFormDataMiddleware,
|
|
async (req: ExpressRequest, res: ExpressResponse) => {
|
|
try {
|
|
logger.debug("Received incoming email webhook");
|
|
|
|
// Log raw body for debugging
|
|
logger.debug(
|
|
`Request body keys: ${Object.keys(req.body || {}).join(", ")}`,
|
|
);
|
|
|
|
// Check if inbound email is configured
|
|
if (!InboundEmailProviderFactory.isConfigured()) {
|
|
logger.error("Inbound email is not configured");
|
|
throw new BadDataException(
|
|
"Inbound email is not configured. Please set the INBOUND_EMAIL_DOMAIN environment variable.",
|
|
);
|
|
}
|
|
|
|
const provider: InboundEmailProvider =
|
|
InboundEmailProviderFactory.getProvider();
|
|
|
|
// Get the secret from the URL path if provided
|
|
const pathSecret: string = req.params["secret"] || "";
|
|
|
|
// Validate the webhook request (secret from path takes precedence)
|
|
const isValid: boolean = await provider.validateWebhook({
|
|
headers: req.headers as Record<string, string>,
|
|
body: req.body as JSONObject,
|
|
pathSecret: pathSecret,
|
|
});
|
|
|
|
if (!isValid) {
|
|
logger.error("Invalid webhook signature");
|
|
throw new BadDataException("Invalid webhook signature");
|
|
}
|
|
|
|
// Parse the inbound email
|
|
const parsedEmail: ParsedInboundEmail = await provider.parseInboundEmail(
|
|
req.body as JSONObject,
|
|
);
|
|
|
|
logger.debug(`Parsed email from: ${parsedEmail.from}`);
|
|
logger.debug(`Parsed email to: ${parsedEmail.to}`);
|
|
logger.debug(`Parsed email subject: ${parsedEmail.subject}`);
|
|
|
|
// Extract secret key from the "to" address
|
|
const secretKey: string | null = provider.extractSecretKeyFromEmail(
|
|
parsedEmail.to,
|
|
);
|
|
|
|
if (!secretKey) {
|
|
logger.error(
|
|
`Could not extract secret key from email: ${parsedEmail.to}`,
|
|
);
|
|
throw new BadDataException(
|
|
"Invalid monitor email address. Could not extract secret key.",
|
|
);
|
|
}
|
|
|
|
logger.debug(`Extracted secret key: ${secretKey}`);
|
|
|
|
// Queue the email for async processing using the unified Telemetry queue
|
|
await TelemetryQueueService.addIncomingEmailJob({
|
|
secretKey: secretKey,
|
|
emailFrom: parsedEmail.from,
|
|
emailTo: parsedEmail.to,
|
|
emailSubject: parsedEmail.subject,
|
|
emailBody: parsedEmail.body,
|
|
emailBodyHtml: parsedEmail.bodyHtml,
|
|
emailHeaders: parsedEmail.headers,
|
|
attachments: parsedEmail.attachments,
|
|
});
|
|
|
|
logger.debug("Email queued for processing");
|
|
|
|
// Return 202 Accepted immediately
|
|
return Response.sendJsonObjectResponse(req, res, {
|
|
status: "accepted",
|
|
message: "Email queued for processing",
|
|
});
|
|
} catch (error) {
|
|
logger.error("Error processing incoming email webhook:");
|
|
logger.error(error);
|
|
|
|
if (error instanceof BadDataException) {
|
|
return Response.sendErrorResponse(req, res, error);
|
|
}
|
|
|
|
return Response.sendErrorResponse(
|
|
req,
|
|
res,
|
|
new BadDataException("Failed to process incoming email"),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
export default router;
|