#include "common/Email.h" #include #include namespace visione::email { namespace { std::string configString(const std::string &key, const std::string &fallback) { const auto &mail = drogon::app().getCustomConfig()["mail"]; return mail.get(key, fallback).asString(); } } // namespace std::string EmailService::deliveryName() const { return configString("provider", "resend"); } bool EmailService::sendOtp(const std::string &to, const std::string &code) const { const auto apiBaseUrl = configString("api_base_url", "https://api.resend.com"); const auto apiKey = configString("api_key", ""); const auto from = configString("from", ""); if (apiKey.empty() || from.empty()) { LOG_ERROR << "Email delivery configuration missing: api_key or from"; return false; } auto client = drogon::HttpClient::newHttpClient(apiBaseUrl); Json::Value payload; payload["from"] = "Visione <" + from + ">"; payload["subject"] = "Codice OTP Visione"; payload["text"] = "Il tuo codice OTP per Visione e': " + code + "\nScade tra 10 minuti.\n"; payload["to"] = Json::arrayValue; payload["to"].append(to); auto request = drogon::HttpRequest::newHttpJsonRequest(payload); request->setMethod(drogon::Post); request->setPath("/emails"); request->addHeader("Authorization", "Bearer " + apiKey); request->addHeader("User-Agent", "visione-backend/1.0"); client->sendRequest( request, [recipient = to](drogon::ReqResult result, const drogon::HttpResponsePtr &response) { if (result == drogon::ReqResult::Ok && response != nullptr && response->statusCode() == drogon::k200OK) { LOG_INFO << "OTP email queued via Resend for " << recipient; return; } const auto body = response != nullptr ? response->getBody() : std::string{}; LOG_ERROR << "OTP email delivery failed via Resend for " << recipient << ", result=" << static_cast(result) << ", status=" << (response != nullptr ? std::to_string(response->statusCode()) : "none") << ", body=" << body; }); return true; } } // namespace visione::email