Skip to content

Commit

Permalink
Merge pull request #937 from opencrvs/ocrvs-6410-mass-email-users
Browse files Browse the repository at this point in the history
tahmidrahman-dsi authored and Zangetsu101 committed May 13, 2024
1 parent bc2c05d commit ed06831
Showing 11 changed files with 127 additions and 12 deletions.
1 change: 1 addition & 0 deletions infrastructure/deployment/deploy.sh
Original file line number Diff line number Diff line change
@@ -307,6 +307,7 @@ export METRICS_MONGODB_PASSWORD=`generate_password`
export PERFORMANCE_MONGODB_PASSWORD=`generate_password`
export OPENHIM_MONGODB_PASSWORD=`generate_password`
export WEBHOOKS_MONGODB_PASSWORD=`generate_password`
export NOTIFICATION_MONGODB_PASSWORD=`generate_password`

#
# Elasticsearch credentials
6 changes: 5 additions & 1 deletion infrastructure/docker-compose.deploy.yml
Original file line number Diff line number Diff line change
@@ -241,6 +241,7 @@ services:
- METRICS_MONGODB_PASSWORD=${METRICS_MONGODB_PASSWORD}
- OPENHIM_MONGODB_PASSWORD=${OPENHIM_MONGODB_PASSWORD}
- WEBHOOKS_MONGODB_PASSWORD=${WEBHOOKS_MONGODB_PASSWORD}
- NOTIFICATION_MONGODB_PASSWORD=${NOTIFICATION_MONGODB_PASSWORD}
networks:
- overlay_net
logging:
@@ -553,7 +554,7 @@ services:
deploy:
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`)'
- 'traefik.http.routers.countryconfig.rule=Host(`countryconfig.{{hostname}}`) && !Path(`/email`) && !Path(`/notification`)'
- 'traefik.http.services.countryconfig.loadbalancer.server.port=3040'
- 'traefik.http.routers.countryconfig.tls=true'
- 'traefik.http.routers.countryconfig.tls.certresolver=certResolver'
@@ -571,6 +572,8 @@ services:
- 'traefik.http.middlewares.block-email.ipwhitelist.sourcerange=255.255.255.255'
- 'traefik.http.routers.block-email.rule=Host(`countryconfig.{{hostname}}`) && Path(`/email`)'
- 'traefik.http.routers.block-email.middlewares=block-email'
- 'traefik.http.routers.block-notification.rule=Host(`countryconfig.{{hostname}}`) && Path(`/notification`)'
- 'traefik.http.routers.block-notification.middlewares=block-email'
replicas: 1
environment:
- MONGO_URL=mongodb://mongo1/user-mgnt?replicaSet=rs0
@@ -686,6 +689,7 @@ services:
environment:
- APN_SERVICE_URL=http://apm-server:8200
- CERT_PUBLIC_KEY_PATH=/run/secrets/jwt-public-key.{{ts}}
- MONGO_URL=mongodb://notification:${NOTIFICATION_MONGODB_PASSWORD}@mongo1/notification?replicaSet=rs0
deploy:
replicas: 1
labels:
1 change: 1 addition & 0 deletions infrastructure/docker-compose.production-deploy.yml
Original file line number Diff line number Diff line change
@@ -65,6 +65,7 @@ services:
- NODE_ENV=production
- LANGUAGES=en,fr
- SENTRY_DSN=${SENTRY_DSN:-""}
- MONGO_URL=mongodb://notification:${NOTIFICATION_MONGODB_PASSWORD}@mongo1,mongo2/notification?replicaSet=rs0
deploy:
replicas: 2

1 change: 1 addition & 0 deletions infrastructure/docker-compose.staging-deploy.yml
Original file line number Diff line number Diff line change
@@ -65,6 +65,7 @@ services:
- NODE_ENV=production
- LANGUAGES=en,fr
- SENTRY_DSN=${SENTRY_DSN:-""}
- MONGO_URL=mongodb://notification:${NOTIFICATION_MONGODB_PASSWORD}@mongo1/notification?replicaSet=rs0
deploy:
replicas: 1

22 changes: 22 additions & 0 deletions infrastructure/mongodb/on-deploy.sh
Original file line number Diff line number Diff line change
@@ -231,3 +231,25 @@ else
})
EOF
fi

NOTIFICATION_USER=$(echo $(checkIfUserExists "notification"))
if [[ $NOTIFICATION_USER != "FOUND" ]]; then
echo "notification user not found"
mongo $(mongo_credentials) --host $HOST <<EOF
use notification
db.createUser({
user: 'notification',
pwd: '$NOTIFICATION_MONGODB_PASSWORD',
roles: [{ role: 'readWrite', db: 'notification' }]
})
EOF
else
echo "notification user exists"
mongo $(mongo_credentials) --host $HOST <<EOF
use notification
db.updateUser('notification', {
pwd: '$NOTIFICATION_MONGODB_PASSWORD',
roles: [{ role: 'readWrite', db: 'notification' }]
})
EOF
fi
16 changes: 12 additions & 4 deletions src/api/notification/email-service.ts
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ export const sendEmail = async (params: {
html: string
from: string
to: string
bcc?: string[]
}) => {
const replaceVariables = (text: string) =>
Handlebars.compile(text)({
@@ -65,13 +66,20 @@ export const sendEmail = async (params: {
pass: SMTP_PASSWORD
}
})
const mailOptions = params.bcc
? { ...formattedParams, bcc: params.bcc }
: formattedParams

try {
await emailTransport.sendMail(formattedParams)
await emailTransport.sendMail(mailOptions)
} catch (error) {
logger.error(
`Unable to send email to ${formattedParams.to} for error : ${error}`
)
if (params.bcc) {
logger.error(`Unable to send mass email for error : ${error}`)
} else {
logger.error(
`Unable to send email to ${formattedParams.to} for error : ${error}`
)
}

if (error.response) {
logger.error(error.response.body)
12 changes: 12 additions & 0 deletions src/api/notification/email-templates/index.ts
Original file line number Diff line number Diff line change
@@ -119,6 +119,11 @@ type RejectionDeclarationVariables = DeclarationCommonVariables & {
name: string
}

type AllUserNotificationVariables = {
subject: string
body: string
}

const templates = {
'onboarding-invite': {
type: 'onboarding-invite',
@@ -223,6 +228,13 @@ const templates = {
type: 'deathRejectionNotification',
subject: 'Death declaration required update',
template: readDeathTemplate<RejectionDeclarationVariables>('rejection')
},
allUserNotification: {
type: 'allUserNotification',
subject: '', // Subject defined from National Sys Admin Dashboard
template: readOtherTemplate<AllUserNotificationVariables>(
'all-user-notification'
)
}
} as const

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>

<head>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;700&display=swap" rel="stylesheet" />
<style type="text/css">
body {
font-family: 'Noto Sans', sans-serif;
color: #222;
padding: 16px 24px;
}

h1 {
font-size: 21px;
margin-top: 32px;
margin-bottom: 36px;
font-weight: 700;
}

p {
font-weight: 400;
font-size: 16px;
line-height: 1.8;
margin-bottom: 24px;
}

i {
color: #666;
font-weight: 400;
font-size: 16px;
}
</style>
</head>

<body>
<img src="{{countryLogo}}" alt="country_logo" style="max-height: 88px;
max-width: 100%;">
<h1>{{subject}}</h1>

<p>
{{body}}
</p>
</br>
<p>
Best regards,
<br />
Farajaland CRVS Team
</p>
</br>
<i>This is an automated message. Please do not reply to this email.</i>
</body>

</html>
17 changes: 10 additions & 7 deletions src/api/notification/handler.ts
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ type EmailNotificationPayload = {
}
recipient: {
email: string
bcc?: string[]
}
type: 'user' | 'informant'
locale: string
@@ -64,13 +65,14 @@ type NotificationPayload = SMSNotificationPayload | EmailNotificationPayload

export const notificationSchema = Joi.object({
templateName: Joi.object({
email: Joi.string().required(),
sms: Joi.string().required()
}),
email: Joi.string(),
sms: Joi.string()
}).xor('email', 'sms'),
recipient: Joi.object({
email: Joi.string().allow(null, '').optional(),
sms: Joi.string().allow(null, '').optional()
}),
email: Joi.string(),
sms: Joi.string(),
bcc: Joi.array().items(Joi.string().required()).optional()
}).xor('email', 'sms'),
type: Joi.string().valid('user', 'informant').required()
}).unknown(true)

@@ -116,7 +118,8 @@ export async function notificationHandler(
subject: emailSubject,
html: emailBody,
from: SENDER_EMAIL_ADDRESS,
to: recipient.email
to: recipient.email,
bcc: recipient.bcc
})
} else {
const { templateName, variables, recipient, locale } = payload
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -451,6 +451,7 @@ export async function createServer() {
handler: notificationHandler,
options: {
tags: ['api'],
auth: false,
validate: {
payload: notificationSchema
},
9 changes: 9 additions & 0 deletions src/translations/client.csv
Original file line number Diff line number Diff line change
@@ -253,6 +253,10 @@ config.deathDefaultTempDesc,Label for default death certificate template,Default
config.deathTemplate,Label for death certificate template,Death certificate,Acte de mariage
config.deathUpdatedTempDesc,,Updated {deathLongDate},Mise à jour de {deathLongDate}
config.downloadTemplate,Download action in certificate config action menu,Download,Télécharger
config.emailAllUsers.modal.supportingCopy,Label for send email all users confirmation supporting copy,User will receive emails over the next 24 hours,L'utilisateur recevra des courriels au cours des prochaines 24 heures
config.emailAllUsers.modal.title,Label for send email all users confirmation title,Send email to all users?,Envoyer un e-mail à tous les utilisateurs ?
config.emailAllUsers.subtitle,Subtitle for email all users,This email will be sent to all users you are active. Emails will be sent over the next 24 hours. Only one email can be sent per day,Cet e-mail sera envoyé à tous les utilisateurs que vous activez. Les courriels seront envoyés au cours des prochaines 24 heures. Un seul courriel peut être envoyé par jour
config.emailAllUsers.title,Title for email all users,Email all users,Envoyer un e-mail à tous les utilisateurs
config.eventUpdatedTempDesc,Label for updated birth certificate template,"Updated {lastModified, date, ::dd MMMM yyyy}","Mis à jour {lastModified, date, ::dd MMMM yyyy}"
config.form.settings.time,,Time input,Saisie de l'heure
config.form.tools.input.customSelectWithDynamicOptions,,Custom select with dynamic options,Sélection personnalisée avec options dynamiques
@@ -344,6 +348,8 @@ constants.downloading,Label for declaration download status Downloading,Download
constants.draft,A label for draft,Draft,Brouillon
constants.duplicateOf,table header for `duplicate of` in record audit,Duplicate of,Duplicata de
constants.emailAddress,Email label,Email Address,Adresse e-mail
constants.emailBody,Label for email body input,Message,Message
constants.emailSubject,Label for email subject input,Subject,Sujet
constants.entrepeneur,The description for ENTREPENEUR type,Entrepeneur,Entrepeneur
constants.estimatedNumberOfEvents,A label for Estimated number of events,"Estimated{lineBreak}no. of {eventType, select, birth {birth} death {death} other {birth}}s","Estimation{lineBreak}no. de {eventType, select, naissance {birth} décès {death} autre {birth}}s"
constants.estimatedNumberOfRegistartion,A label for estimated no. of registrations,Estimated no. of registrations,Nombre estimé déclaration
@@ -1451,6 +1457,8 @@ misc.nidCallback.failedToAuthenticateNid,Label for nid authention failed phase,F
misc.notif.declarationsSynced,The message that appears in notification when background sync takes place,"As you have connectivity, we can synchronize your declarations.","Comme vous disposez d'une connectivité, nous pouvons synchroniser vos déclarations."
misc.notif.draftsSaved,The message that appears in notification when save drafts button is clicked,Your draft has been saved,Votre brouillon a été enregistré
misc.notif.duplicateRecord,Label for when a duplicate record is detected when registering a record.,{trackingId} is a potential duplicate. Record is ready for review.,{trackingId} est un doublon potentiel. L'enregistrement est prêt à être examiné.
misc.notif.emailAllUsersError,Label for Email all users error toast,Only one email can be sent per day,Un seul e-mail peut être envoyé par jour
misc.notif.emailAllUsersSuccess,Label for Email all users success toast,Email sent to all users,Email envoyé à tous les utilisateurs
misc.notif.offlineError,The message that appears in notification when a new user creation fails in offline mode,Offline. Try again when reconnected,Hors ligne. Réessayez une fois reconnecté
misc.notif.onlineUserStatus,Label for online user status toast notification,You are back online,Vous êtes de nouveau en ligne
misc.notif.outboxText,Declaration outbox text,Outbox ({num}),Boîte d'envoi({num})
@@ -1477,6 +1485,7 @@ navigation.completenessRates,Completeness rates in navigation,Completeness rates
navigation.config,Config label in navigation,Configuration,Paramétrages
navigation.dashboard,Dashboard Section,Dashboard,Tableau de bord
navigation.declarationForms,Declaration forms label in navigation,Declaration forms,Formulaires de déclaration
navigation.emailAllUsers,Email all users label in navigation,Email all users,Envoyer un e-mail à tous les utilisateurs
navigation.informantNotification,Informant notifications label in navigation,Informant notifications,Notifications des informateurs
navigation.integration,Integration forms label in navigation,Integrations,Intégrations
navigation.leaderboards,Leaderboards Dashboard Section,Leaderboards,Classements

0 comments on commit ed06831

Please sign in to comment.