Implement API to Send OTP for Email-Based Login
This commit is contained in:
parent
863a154ec6
commit
5eb100c1f6
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Added_Subject_In_MailingList_And_Removed_From_MailDetails : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Subject",
|
||||
table: "MailDetails");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Subject",
|
||||
table: "MailingList",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Subject",
|
||||
table: "MailingList");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Subject",
|
||||
table: "MailDetails",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
@ -969,10 +969,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
@ -1028,6 +1024,10 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
|
7
Marco.Pms.Model/Dtos/Authentication/GenerateOTPDto.cs
Normal file
7
Marco.Pms.Model/Dtos/Authentication/GenerateOTPDto.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Marco.Pms.Model.Dtos.Authentication
|
||||
{
|
||||
public class GenerateOTPDto
|
||||
{
|
||||
public string? Email { get; set; }
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
{
|
||||
public Guid ProjectId { get; set; }
|
||||
public string Recipient { get; set; } = string.Empty; // Eamil Address of recipient
|
||||
public string Subject { get; set; } = string.Empty;
|
||||
public string Schedule { get; set; } = string.Empty; // json object which includes when to send mail and at what interval
|
||||
public Guid MailListId { get; set; }
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
{
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Body { get; set; } = string.Empty;
|
||||
public string Subject { get; set; } = string.Empty;
|
||||
public string Keywords { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ namespace Marco.Pms.Model.Mail
|
||||
public Guid Id { get; set; }
|
||||
public Guid ProjectId { get; set; }
|
||||
public string Recipient { get; set; } = string.Empty; // Eamil Address of recipient
|
||||
public string Subject { get; set; } = string.Empty; // Eamil Address of recipient
|
||||
public string Schedule { get; set; } = string.Empty; // json object which includes when to send mail and at what interval
|
||||
public Guid MailListId { get; set; }
|
||||
[ValidateNever]
|
||||
|
@ -5,6 +5,7 @@
|
||||
public Guid Id { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Body { get; set; } = string.Empty;
|
||||
public string Subject { get; set; } = string.Empty; // Eamil Subject of recipient
|
||||
public string Keywords { get; set; } = string.Empty; // Comma seprated list of variables in mail body
|
||||
public Guid TenantId { get; set; }
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
// Generate Refresh Token and store in DB
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
// Generate MPIN Token (custom short-term token)
|
||||
// Fetch MPIN Token
|
||||
var mpinToken = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(user.Id) && p.TenantId == emp.TenantId);
|
||||
|
||||
// Combine all tokens in response
|
||||
@ -143,7 +143,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
token,
|
||||
refreshToken,
|
||||
mpinToken
|
||||
mpinToken?.MPINToken
|
||||
};
|
||||
|
||||
// Return success response
|
||||
@ -285,6 +285,70 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(result.Succeeded, "Password reset successfully.", 200));
|
||||
}
|
||||
|
||||
[HttpPost("send-otp")]
|
||||
public async Task<IActionResult> SendOtpEmail([FromBody] GenerateOTPDto generateOTP)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate input email
|
||||
if (string.IsNullOrWhiteSpace(generateOTP.Email))
|
||||
{
|
||||
_logger.LogWarning("Send OTP failed - Email is missing");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Email is required", "Invalid email", 400));
|
||||
}
|
||||
|
||||
// Fetch user by email
|
||||
var requestedUser = await _userManager.FindByEmailAsync(generateOTP.Email);
|
||||
string title = "Send OTP";
|
||||
|
||||
if (requestedUser != null && requestedUser.IsActive)
|
||||
{
|
||||
// Fetch employee details
|
||||
var requestedEmployee = await _context.Employees
|
||||
.FirstOrDefaultAsync(e => e.ApplicationUserId == requestedUser.Id);
|
||||
|
||||
// Generate a random 4-digit OTP
|
||||
string otp = new Random().Next(1000, 9999).ToString();
|
||||
|
||||
// Store OTP in database
|
||||
var otpDetails = new OTPDetails
|
||||
{
|
||||
UserId = Guid.Parse(requestedUser.Id),
|
||||
OTP = otp,
|
||||
ExpriesInSec = 600, // 10 minutes
|
||||
TimeStamp = DateTime.UtcNow,
|
||||
TenantId = requestedUser.TenantId
|
||||
};
|
||||
|
||||
_context.OTPDetails.Add(otpDetails);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Prepare email
|
||||
List<string> toEmails = [generateOTP.Email];
|
||||
string name = $"{requestedEmployee?.FirstName} {requestedEmployee?.LastName}".Trim();
|
||||
var mailTemplate = await _context.MailingList
|
||||
.FirstOrDefaultAsync(t => t.Title.ToLower() == title.ToLower());
|
||||
|
||||
string subject = mailTemplate?.Subject ?? string.Empty;
|
||||
string emailBody = mailTemplate?.Body ?? string.Empty;
|
||||
|
||||
// Send OTP via email
|
||||
await _emailSender.SendOTP(toEmails, emailBody, name, otp, subject);
|
||||
|
||||
_logger.LogInfo("OTP sent successfully to {Email}", generateOTP.Email);
|
||||
return Ok(ApiResponse<object>.SuccessResponse("Success", "OTP generated successfully", 200));
|
||||
}
|
||||
|
||||
_logger.LogWarning("Send OTP failed - Invalid or inactive user: {Email}", generateOTP.Email);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Provided invalid information", "User not found or inactive", 400));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("An unexpected error occurred while sending OTP to {Email} : {Error}", generateOTP.Email ?? "", ex.Message);
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("An unexpected error occurred.", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("sendmail")]
|
||||
public async Task<IActionResult> SendEmail([FromBody] EmailDot emailDot)
|
||||
{
|
||||
@ -392,7 +456,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
.FirstOrDefaultAsync(e => e.Id == generateMPINDto.EmployeeId && e.TenantId == tenantId);
|
||||
|
||||
// Validate employee and MPIN input
|
||||
if (requestEmployee == null || string.IsNullOrWhiteSpace(generateMPINDto.MPIN))
|
||||
if (requestEmployee == null || string.IsNullOrWhiteSpace(generateMPINDto.MPIN) || generateMPINDto.MPIN.Length != 6 || !generateMPINDto.MPIN.All(char.IsDigit))
|
||||
{
|
||||
_logger.LogError("Employee {EmployeeId} provided invalid information to generate MPIN", loggedInEmployee.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Provided invalid information", "Provided invalid information", 400));
|
||||
|
@ -45,7 +45,6 @@ namespace Marco.Pms.Services.Controllers
|
||||
Recipient = mailDetailsDto.Recipient,
|
||||
Schedule = mailDetailsDto.Schedule,
|
||||
MailListId = mailDetailsDto.MailListId,
|
||||
Subject = mailDetailsDto.Subject,
|
||||
TenantId = tenantId
|
||||
};
|
||||
_context.MailDetails.Add(mailDetails);
|
||||
@ -57,10 +56,22 @@ namespace Marco.Pms.Services.Controllers
|
||||
public async Task<IActionResult> AddMailTemplate([FromBody] MailTemeplateDto mailTemeplateDto)
|
||||
{
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
if (string.IsNullOrWhiteSpace(mailTemeplateDto.Body) && string.IsNullOrWhiteSpace(mailTemeplateDto.Title))
|
||||
{
|
||||
_logger.LogWarning("User tries to set email template but send invalid data");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Provided Invalid data", "Provided Invalid data", 400));
|
||||
}
|
||||
var existngTemalate = await _context.MailingList.FirstOrDefaultAsync(t => t.Title.ToLower() == mailTemeplateDto.Title.ToLower());
|
||||
if (existngTemalate != null)
|
||||
{
|
||||
_logger.LogWarning("User tries to set email template, but title already existed in database");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Email title is already existed", "Email title is already existed", 400));
|
||||
}
|
||||
MailingList mailingList = new MailingList
|
||||
{
|
||||
Title = mailTemeplateDto.Title,
|
||||
Body = mailTemeplateDto.Body,
|
||||
Subject = mailTemeplateDto.Subject,
|
||||
Keywords = mailTemeplateDto.Keywords,
|
||||
TenantId = tenantId
|
||||
};
|
||||
@ -92,7 +103,8 @@ namespace Marco.Pms.Services.Controllers
|
||||
ProjectId = g.Key.ProjectId,
|
||||
MailListId = g.Key.MailListId,
|
||||
Recipients = g.Select(m => m.Recipient).Distinct().ToList(),
|
||||
MailBody = g.FirstOrDefault()?.MailBody?.Body ?? ""
|
||||
MailBody = g.FirstOrDefault()?.MailBody?.Body ?? "",
|
||||
Subject = g.FirstOrDefault()?.MailBody?.Subject ?? string.Empty,
|
||||
})
|
||||
.ToList();
|
||||
|
||||
@ -104,7 +116,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
await semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
var response = await GetProjectStatistics(mailDetail.ProjectId, mailDetail.Recipients, mailDetail.MailBody, tenantId);
|
||||
var response = await GetProjectStatistics(mailDetail.ProjectId, mailDetail.Recipients, mailDetail.MailBody, mailDetail.Subject, tenantId);
|
||||
if (response.StatusCode == 200)
|
||||
Interlocked.Increment(ref successCount);
|
||||
else if (response.StatusCode == 404)
|
||||
@ -137,7 +149,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
/// <param name="projectId">The ID of the project.</param>
|
||||
/// <param name="recipientEmail">The email address of the recipient.</param>
|
||||
/// <returns>An ApiResponse indicating the success or failure of retrieving statistics and sending the email.</returns>
|
||||
private async Task<ApiResponse<object>> GetProjectStatistics(Guid projectId, List<string> recipientEmails, string body, Guid tenantId)
|
||||
private async Task<ApiResponse<object>> GetProjectStatistics(Guid projectId, List<string> recipientEmails, string body, string subject, Guid tenantId)
|
||||
{
|
||||
DateTime reportDate = DateTime.UtcNow.AddDays(-1).Date;
|
||||
|
||||
@ -303,7 +315,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
statisticReport.PerformedAttendance = performedAttendance;
|
||||
|
||||
// Send Email
|
||||
var emailBody = await _emailSender.SendProjectStatisticsEmail(recipientEmails, body, statisticReport);
|
||||
var emailBody = await _emailSender.SendProjectStatisticsEmail(recipientEmails, body, subject, statisticReport);
|
||||
var employee = await _context.Employees.FirstOrDefaultAsync(e => e.Email != null && recipientEmails.Contains(e.Email)) ?? new Employee();
|
||||
|
||||
List<MailLog> mailLogs = new List<MailLog>();
|
||||
|
538
Marco.Pms.Services/EmailTemplates/send-otp.html
Normal file
538
Marco.Pms.Services/EmailTemplates/send-otp.html
Normal file
@ -0,0 +1,538 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
<head>
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!--[if !mso]><!-->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]-->
|
||||
<title></title>
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
@media only screen and (min-width: 620px) {
|
||||
.u-row {
|
||||
width: 600px !important;
|
||||
}
|
||||
|
||||
.u-row .u-col {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
|
||||
.u-row .u-col-50 {
|
||||
width: 300px !important;
|
||||
}
|
||||
|
||||
|
||||
.u-row .u-col-100 {
|
||||
width: 600px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 620px) {
|
||||
.u-row-container {
|
||||
max-width: 100% !important;
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
.u-row {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.u-row .u-col {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
min-width: 320px !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.u-row .u-col > div {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
||||
.u-row .u-col img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
table, td, tr {
|
||||
border-collapse: collapse;
|
||||
vertical-align: top
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.ie-container table, .mso-container table {
|
||||
table-layout: fixed
|
||||
}
|
||||
|
||||
* {
|
||||
line-height: inherit
|
||||
}
|
||||
|
||||
a[x-apple-data-detectors=true] {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important
|
||||
}
|
||||
|
||||
|
||||
table, td {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#u_body a {
|
||||
color: #e93f32;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!--[if !mso]><!-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet" type="text/css"><!--<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body class="clean-body u_body" style="margin: 0;padding: 0;-webkit-text-size-adjust: 100%;background-color: #f9f9f9;color: #000000">
|
||||
<!--[if IE]><div class="ie-container"><![endif]-->
|
||||
<!--[if mso]><div class="mso-container"><![endif]-->
|
||||
<table id="u_body" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;min-width: 320px;Margin: 0 auto;background-color: #f9f9f9;width:100%" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top">
|
||||
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color: #f9f9f9;"><![endif]-->
|
||||
<!--<div>Top Spacing</div>-->
|
||||
|
||||
<div class="u-row-container" style="padding: 0px;background-color: #f9f9f9">
|
||||
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f9f9f9;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: #f9f9f9;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:600px;"><tr style="background-color: #f9f9f9;"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="600" style="width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:15px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<table height="0px" align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;border-top: 1px solid #f9f9f9;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top">
|
||||
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top;font-size: 0px;line-height: 0px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
|
||||
Sita
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!--<div>Logo Block</div>-->
|
||||
|
||||
<div class="u-row-container" style="padding: 0px;background-color: transparent">
|
||||
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #ffffff;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:600px;"><tr style="background-color: #ffffff;"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="600" style="width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:25px 10px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<td style="padding-right: 0px;padding-left: 0px;" align="center">
|
||||
|
||||
<img border="0" src="https://stageapi.marcoaiot.com/logos/marco-aiot-tech-logo.jpg" alt="Image" title="Image" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: inline-block !important;border: none;height: auto;float: none;width: 29%;max-width: 168.2px;" width="168" />
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--<div>Title Block</div>-->
|
||||
|
||||
|
||||
<div class="u-row-container" style="padding: 0px;background-color: transparent">
|
||||
<div class="u-row" style="margin: 0 auto; min-width: 320px; max-width: 600px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: #f46b61;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:600px;"><tr style="background-color: #e93f32;"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="600" style="width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:35px 10px 10px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<td style="padding-right: 0px;padding-left: 0px;" align="center">
|
||||
|
||||
<img border="0" src="https://cdn.templates.unlayer.com/assets/1593141680866-reset.png" alt="Image" title="Image" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: inline-block !important;border: none;height: auto;float: none;width: 10%;max-width: 58px;" width="58" />
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:0px 10px 30px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<div style="font-size: 14px; line-height: 140%; text-align: left; word-wrap: break-word;">
|
||||
<p style="font-size: 14px; line-height: 140%; text-align: center;"><span style="font-size: 28px; line-height: 39.2px; color: #ffffff; font-family: Lato, sans-serif;">Your OTP Code for Verification</span></p>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--<div>Mail Body</div>-->
|
||||
|
||||
|
||||
<div class="u-row-container" style="padding: 0px;background-color: transparent">
|
||||
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #ffffff;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:600px;"><tr style="background-color: #ffffff;"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="600" style="width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:40px 40px 30px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<p>Dear {{NAME}},</p>
|
||||
<br/>
|
||||
<p>Your One-Time Password (OTP) for verification is: <strong style="color:#2e6c80;">{{OTP}}</strong></p>
|
||||
<br/>
|
||||
<p>This OTP is valid for the next <strong>10 minutes</strong>. Please do not share this code with anyone.</p>
|
||||
<br/>
|
||||
<p>If you did not request this code, please ignore this email or contact our support team immediately.</p>
|
||||
|
||||
<br>
|
||||
<p>
|
||||
Thank you,<br>
|
||||
Marco AIoT Team
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--<div>Contact</div>-->
|
||||
|
||||
|
||||
<div class="u-row-container" style="padding: 0px;background-color: transparent">
|
||||
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #e93f32;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:600px;"><tr style="background-color: #e93f32;"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="300" style="width: 300px;padding: 20px 20px 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-50" style="max-width: 320px;min-width: 300px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 20px 0px 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:10px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<div style="font-size: 14px; line-height: 140%; text-align: left; word-wrap: break-word;">
|
||||
<!--<p style="font-size: 14px; line-height: 140%;"><span style="font-size: 16px; line-height: 22.4px; color: #ecf0f1;">Contact</span></p>-->
|
||||
<!--<p style="font-size: 14px; line-height: 140%;"><span style="font-size: 14px; line-height: 19.6px; color: #ecf0f1;">2nd Floor, Fullora Building, Tejas CHS, Dahanukar Colony, Kothrud, Pune (INDIA) - 411038</span></p>-->
|
||||
<p style="font-size: 14px; line-height: 140%;"><span style="font-size: 14px; line-height: 19.6px; color: #ecf0f1;">Contact Us: <a href="mailto:info@marcoaiot.com" style="color:#ffff" target="_blank">info@marcoaiot.com</a></span></p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="300" style="width: 300px;padding: 0px 0px 0px 20px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-50" style="max-width: 320px;min-width: 300px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px 0px 0px 20px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:25px 10px 10px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<div align="right" style="direction: ltr;">
|
||||
<div style="display: table; max-width:187px;">
|
||||
<!--[if (mso)|(IE)]><table width="187" cellpadding="0" cellspacing="0" border="0"><tr><td style="border-collapse:collapse;" align="left"><table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse; mso-table-lspace: 0pt;mso-table-rspace: 0pt; width:187px;"><tr><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td width="32" style="width:32px; padding-right: 15px;" valign="top"><![endif]-->
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 15px">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top">
|
||||
<td valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
|
||||
<a href=" " title="Facebook" target="_blank">
|
||||
<img src="https://cdn.tools.unlayer.com/social/icons/circle-white/facebook.png" alt="Facebook" title="Facebook" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td width="32" style="width:32px; padding-right: 15px;" valign="top"><![endif]-->
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 15px">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top">
|
||||
<td valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
|
||||
<a href="https://x.com/marcoaiot" title="X" target="_blank">
|
||||
<img src="https://cdn.tools.unlayer.com/social/icons/circle-white/x.png" alt="Twitter" title="Twitter" width="32" style="color:#000;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td width="32" style="width:32px; padding-right: 15px;" valign="top"><![endif]-->
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 15px">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top">
|
||||
<td valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
|
||||
<a href=" " title="Instagram" target="_blank">
|
||||
<img src="https://cdn.tools.unlayer.com/social/icons/circle-white/instagram.png" alt="Instagram" title="Instagram" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td width="32" style="width:32px; padding-right: 0px;" valign="top"><![endif]-->
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="width: 32px !important;height: 32px !important;display: inline-block;border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;margin-right: 0px">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top">
|
||||
<td valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
|
||||
<a href=" " title="LinkedIn" target="_blank">
|
||||
<img src="https://cdn.tools.unlayer.com/social/icons/circle-white/linkedin.png" alt="LinkedIn" title="LinkedIn" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:5px 10px 10px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<div style="font-size: 14px; line-height: 140%; text-align: left; word-wrap: break-word;">
|
||||
<p style="line-height: 140%; font-size: 14px;"><span style="font-size: 14px; line-height: 19.6px;"><span style="color: #ecf0f1; font-size: 14px; line-height: 19.6px;"><span style="line-height: 19.6px; font-size: 14px;">Marco AIoT Technologies Pvt. Ltd. © All Rights Reserved</span></span></span></p>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>-->
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!--<div>Bottom Spacing - light red</div>-->
|
||||
|
||||
<div class="u-row-container" style="padding: 0px;background-color: #f9f9f9">
|
||||
<div class="u-row" style="margin: 0 auto; min-width: 320px; max-width: 600px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: #f46b61;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: #f9f9f9;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:600px;"><tr style="background-color: #e93f32;"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="600" style="width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:15px;font-family:'Lato',sans-serif;" align="center">
|
||||
<p style="line-height: 140%; font-size: 14px;"><span style="font-size: 14px; line-height: 19.6px;"><span style="color: #ecf0f1; font-size: 14px; line-height: 19.6px;"><span style="line-height: 19.6px; font-size: 14px;">Marco AIoT Technologies Pvt. Ltd. © All Rights Reserved</span></span></span></p>
|
||||
|
||||
<!--<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;border-top: 1px solid #e93f32;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
|
||||
<tbody>
|
||||
<tr style="vertical-align: top">
|
||||
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>-->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="u-row-container" style="padding: 0px;background-color: transparent">
|
||||
<div class="u-row" style="margin: 0 auto;min-width: 320px;max-width: 600px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #f9f9f9;">
|
||||
<div style="border-collapse: collapse;display: table;width: 100%;height: 100%;background-color: transparent;">
|
||||
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding: 0px;background-color: transparent;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:600px;"><tr style="background-color: #f9f9f9;"><![endif]-->
|
||||
<!--[if (mso)|(IE)]><td align="center" width="600" style="width: 600px;padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;" valign="top"><![endif]-->
|
||||
<div class="u-col u-col-100" style="max-width: 320px;min-width: 600px;display: table-cell;vertical-align: top;">
|
||||
<div style="height: 100%;width: 100% !important;">
|
||||
<!--[if (!mso)&(!IE)]><!--><div style="box-sizing: border-box; height: 100%; padding: 0px;border-top: 0px solid transparent;border-left: 0px solid transparent;border-right: 0px solid transparent;border-bottom: 0px solid transparent;">
|
||||
<!--<![endif]-->
|
||||
|
||||
<table style="font-family:'Lato',sans-serif;" role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="overflow-wrap:break-word;word-break:break-word;padding:20px 40px 30px 20px;font-family:'Lato',sans-serif;" align="left">
|
||||
|
||||
<div style="font-size: 14px; line-height: 140%; text-align: left; word-wrap: break-word;">
|
||||
<small style="color: #a5a3a3;"> You're receiving this email because you have a MarcoPMS account. This email is not a marketing or promotional email. That is why this email does not contain an unsubscribe link. You will receive this email even if you have unsubscribed from MarcoPMS's marketing emails</small>
|
||||
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--[if (!mso)&(!IE)]><!-->
|
||||
</div><!--<![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td><![endif]-->
|
||||
<!--[if (mso)|(IE)]></tr></table></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if mso]></div><![endif]-->
|
||||
<!--[if IE]></div><![endif]-->
|
||||
</body>
|
||||
|
||||
</html>
|
@ -38,6 +38,17 @@ namespace MarcoBMS.Services.Service
|
||||
|
||||
return content;
|
||||
}
|
||||
//public async Task<string> GetEmailTemplate1()
|
||||
//{
|
||||
// string path = Path.Combine(_env.ContentRootPath, "EmailTemplates", $"send-otp.html");
|
||||
|
||||
// if (!File.Exists(path))
|
||||
// throw new FileNotFoundException("Template file not found");
|
||||
|
||||
// string content = await File.ReadAllTextAsync(path);
|
||||
|
||||
// return content;
|
||||
//}
|
||||
|
||||
public async Task SendResetPasswordEmailOnRegister(string toEmail, string toName, string resetLink)
|
||||
{
|
||||
@ -74,7 +85,6 @@ namespace MarcoBMS.Services.Service
|
||||
await SendEmailAsync(toEmails, "Reset Your Password", emailBody);
|
||||
|
||||
}
|
||||
|
||||
public async Task SendResetPasswordSuccessEmail(string toEmail, string toName)
|
||||
{
|
||||
var replacements = new Dictionary<string, string>
|
||||
@ -111,8 +121,7 @@ namespace MarcoBMS.Services.Service
|
||||
await SendEmailAsync(toEmails, "User Requested a Demo", emailBody);
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> SendProjectStatisticsEmail(List<string> toEmails, string emailBody, ProjectStatisticReport report)
|
||||
public async Task<string> SendProjectStatisticsEmail(List<string> toEmails, string emailBody, string subject, ProjectStatisticReport report)
|
||||
{
|
||||
var date = report.Date.ToString("dd-MMM-yyyy", CultureInfo.InvariantCulture);
|
||||
var replacements = new Dictionary<string, string>
|
||||
@ -141,11 +150,68 @@ namespace MarcoBMS.Services.Service
|
||||
emailBody = emailBody.Replace("{{TEAM_ON_SITE}}", BuildTeamOnSiteHtml(report.TeamOnSite));
|
||||
emailBody = emailBody.Replace("{{PERFORMED_TASK}}", BuildPerformedTaskHtml(report.PerformedTasks, report.Date));
|
||||
emailBody = emailBody.Replace("{{PERFORMED_ATTENDANCE}}", BuildPerformedAttendanceHtml(report.PerformedAttendance));
|
||||
|
||||
string subject = $"DPR - {date} - {report.ProjectName}";
|
||||
var subjectReplacements = new Dictionary<string, string>
|
||||
{
|
||||
{"DATE", date },
|
||||
{"PROJECT_NAME", report.ProjectName}
|
||||
};
|
||||
foreach (var item in subjectReplacements)
|
||||
{
|
||||
subject = subject.Replace($"{{{{{item.Key}}}}}", item.Value);
|
||||
}
|
||||
string env = _configuration["environment:Title"] ?? string.Empty;
|
||||
subject = CheckSubject(subject);
|
||||
await SendEmailAsync(toEmails, subject, emailBody);
|
||||
return emailBody;
|
||||
}
|
||||
public async Task SendOTP(List<string> toEmails, string emailBody, string name, string otp, string subject)
|
||||
{
|
||||
var replacements = new Dictionary<string, string>
|
||||
{
|
||||
{ "NAME", name },
|
||||
{ "OTP", otp }
|
||||
};
|
||||
|
||||
foreach (var item in replacements)
|
||||
{
|
||||
emailBody = emailBody.Replace($"{{{{{item.Key}}}}}", item.Value);
|
||||
}
|
||||
|
||||
subject = CheckSubject(subject);
|
||||
await SendEmailAsync(toEmails, subject, emailBody);
|
||||
}
|
||||
public async Task SendEmailAsync(List<string> toEmails, string subject, string body)
|
||||
{
|
||||
var email = new MimeMessage();
|
||||
email.From.Add(new MailboxAddress(_smtpSettings.SenderName, _smtpSettings.SenderEmail));
|
||||
foreach (var toEmail in toEmails)
|
||||
{
|
||||
email.To.Add(MailboxAddress.Parse(toEmail));
|
||||
}
|
||||
email.Subject = subject;
|
||||
|
||||
var bodyBuilder = new BodyBuilder { HtmlBody = body };
|
||||
email.Body = bodyBuilder.ToMessageBody();
|
||||
|
||||
using var smtp = new SmtpClient();
|
||||
await smtp.ConnectAsync(_smtpSettings.SmtpServer, _smtpSettings.Port, MailKit.Security.SecureSocketOptions.StartTls);
|
||||
await smtp.AuthenticateAsync(_smtpSettings.SenderEmail, _smtpSettings.Password);
|
||||
await smtp.SendAsync(email);
|
||||
await smtp.DisconnectAsync(true);
|
||||
}
|
||||
|
||||
private string CheckSubject(string subject)
|
||||
{
|
||||
string env = _configuration["Environment:Title"] ?? string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(env))
|
||||
{
|
||||
return subject = $"{subject}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return subject = $"({env}) {subject}";
|
||||
}
|
||||
}
|
||||
private string BuildTeamOnSiteHtml(List<TeamOnSite> team)
|
||||
{
|
||||
if (team == null || !team.Any()) return "";
|
||||
@ -234,26 +300,6 @@ namespace MarcoBMS.Services.Service
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public async Task SendEmailAsync(List<string> toEmails, string subject, string body)
|
||||
{
|
||||
var email = new MimeMessage();
|
||||
email.From.Add(new MailboxAddress(_smtpSettings.SenderName, _smtpSettings.SenderEmail));
|
||||
foreach (var toEmail in toEmails)
|
||||
{
|
||||
email.To.Add(MailboxAddress.Parse(toEmail));
|
||||
}
|
||||
email.Subject = subject;
|
||||
|
||||
var bodyBuilder = new BodyBuilder { HtmlBody = body };
|
||||
email.Body = bodyBuilder.ToMessageBody();
|
||||
|
||||
using var smtp = new SmtpClient();
|
||||
await smtp.ConnectAsync(_smtpSettings.SmtpServer, _smtpSettings.Port, MailKit.Security.SecureSocketOptions.StartTls);
|
||||
await smtp.AuthenticateAsync(_smtpSettings.SenderEmail, _smtpSettings.Password);
|
||||
await smtp.SendAsync(email);
|
||||
await smtp.DisconnectAsync(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,11 +5,13 @@ namespace MarcoBMS.Services.Service
|
||||
{
|
||||
public interface IEmailSender
|
||||
{
|
||||
//Task<string> GetEmailTemplate1();
|
||||
Task SendResetPasswordEmail(string toEmail, string userName, string resetLink);
|
||||
Task SendResetPasswordEmailOnRegister(string toEmail, string toName, string resetLink);
|
||||
Task SendResetPasswordSuccessEmail(string toEmail, string userName);
|
||||
Task SendRequestDemoEmail(List<string> toEmails, InquiryEmailObject demoEmailObject);
|
||||
Task SendEmailAsync(List<string> toEmails, string subject, string body);
|
||||
Task<string> SendProjectStatisticsEmail(List<string> toEmails, string emailBody, ProjectStatisticReport report);
|
||||
Task SendOTP(List<string> toEmails, string emailBody, string name, string otp, string subject);
|
||||
Task<string> SendProjectStatisticsEmail(List<string> toEmails, string emailBody, string subject, ProjectStatisticReport report);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,10 @@
|
||||
"AllowedMethods": "*",
|
||||
"AllowedHeaders": "*"
|
||||
},
|
||||
|
||||
"Environment": {
|
||||
"Name": "Development",
|
||||
"Title": "Dev"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS10"
|
||||
},
|
||||
|
@ -4,6 +4,10 @@
|
||||
"AllowedMethods": "*",
|
||||
"AllowedHeaders": "*"
|
||||
},
|
||||
"Environment": {
|
||||
"Name": "Production",
|
||||
"Title": ""
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user