• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / blurts-server / e167d181-a7f4-4d07-93f9-fe22e604cc50

pending completion
e167d181-a7f4-4d07-93f9-fe22e604cc50

Pull #2765

circleci

GitHub
Merge branch 'MNTOR-983-settings-v2-frontend' into MNTOR-983-settings-v2-frontend-additions
Pull Request #2765: Settings page layout (MNTOR-983)

282 of 1212 branches covered (23.27%)

Branch coverage included in aggregate %.

133 of 133 new or added lines in 8 files covered. (100.0%)

959 of 3252 relevant lines covered (29.49%)

4.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/controllers/settings.js
1
import {
2
  getUserEmails,
3
  resetUnverifiedEmailAddress,
4
  addSubscriberUnverifiedEmailHash,
5
  removeOneSecondaryEmail,
6
  getEmailById,
7
  verifyEmailHash
8
} from '../db/tables/email_addresses.js'
9

10
import { setAllEmailsToPrimary } from '../db/tables/subscribers.js'
11

12
import { fluentError, getMessage } from '../utils/fluent.js'
13
import EmailUtils from '../utils/email.js'
14
import { mainLayout } from '../views/main.js'
15
import { settings } from '../views/partials/settings.js'
16
import AppConstants from '../app-constants.js'
17
import { getBreachesForEmail } from '../utils/hibp.js'
18
import { getTemplate } from '../views/email-2022.js'
19
import { generateToken } from '../utils/csrf.js'
20

21
async function settingsPage (req, res) {
22
  const emails = await getUserEmails(req.session.user.id)
×
23
  // Add primary subscriber email to the list
24
  emails.push({
×
25
    email: req.session.user.primary_email,
26
    sha1: req.session.user.primary_sha1,
27
    primary: true,
28
    verified: true
29
  })
30

31
  const breachCounts = new Map()
×
32
  emails.forEach((email) => breachCounts.set(email.email, 0))
×
33

34
  const allBreaches = req.app.locals.breaches
×
35
  for (const email of emails) {
×
36
    const breaches = await getBreachesForEmail(
×
37
      email.sha1,
38
      allBreaches,
39
      true,
40
      false
41
    )
42
    breachCounts.set(email.email, breaches.length)
×
43
  }
44

45
  const data = {
×
46
    partial: settings,
47
    emails,
48
    breachCounts,
49
    limit: AppConstants.MAX_NUM_ADDRESSES,
50
    csrfToken: generateToken(res)
51
  }
52

53
  res.send(mainLayout(data))
×
54
}
55

56
async function addEmail (req, res) {
57
  const sessionUser = await req.user
×
58
  const email = req.body.email
×
59
  // FIXME what should we use in v2 for email address validation?
60
  if (!email) {
×
61
    throw fluentError('user-add-invalid-email')
×
62
  }
63

64
  if (sessionUser.email_addresses.length >= AppConstants.MAX_NUM_ADDRESSES) {
×
65
    throw fluentError('user-add-too-many-emails')
×
66
  }
67
  _checkForDuplicateEmail(sessionUser, email)
×
68

69
  const unverifiedSubscriber = await addSubscriberUnverifiedEmailHash(
×
70
    req.session.user,
71
    email
72
  )
73

74
  await _sendVerificationEmail(unverifiedSubscriber.id)
×
75

76
  // TODO: what should we return to the client?
77
  return res.json('Resent the email')
×
78
}
79

80
function _checkForDuplicateEmail (sessionUser, email) {
81
  email = email.toLowerCase()
×
82
  if (email === sessionUser.primary_email.toLowerCase()) {
×
83
    throw fluentError('user-add-duplicate-email')
×
84
  }
85
  for (const secondaryEmail of sessionUser.email_addresses) {
×
86
    if (email === secondaryEmail.email.toLowerCase()) {
×
87
      throw fluentError('user-add-duplicate-email')
×
88
    }
89
  }
90
}
91

92
async function removeEmail (req, res) {
93
  const emailId = req.body.emailId
×
94
  const sessionUser = req.user
×
95
  const existingEmail = await getEmailById(emailId)
×
96

97
  if (existingEmail.subscriber_id !== sessionUser.id) {
×
98
    throw fluentError('error-not-subscribed')
×
99
  }
100

101
  removeOneSecondaryEmail(emailId)
×
102
  res.redirect('/user/settings')
×
103
}
104

105
async function resendEmail (req, res) {
106
  const emailId = req.body.emailId
×
107
  const sessionUser = req.user
×
108
  const existingEmail = await getUserEmails(sessionUser.id)
×
109

110
  const filteredEmail = existingEmail.filter(
×
111
    (a) => a.email === emailId && a.subscriber_id === sessionUser.id
×
112
  )
113

114
  if (!filteredEmail) {
×
115
    throw fluentError('user-verify-token-error')
×
116
  }
117

118
  await _sendVerificationEmail(emailId)
×
119

120
  // TODO: what should we return to the client?
121
  return res.json('Resent the email')
×
122
}
123

124
async function _sendVerificationEmail (emailId) {
125
  const unverifiedEmailAddressRecord = await resetUnverifiedEmailAddress(
×
126
    emailId
127
  )
128
  const recipientEmail = unverifiedEmailAddressRecord.email
×
129
  await EmailUtils.sendEmail(
×
130
    recipientEmail,
131
    getMessage('email-subject-verify'),
132
    getTemplate,
133
    {
134
      recipientEmail,
135
      ctaHref: EmailUtils.getVerificationUrl(unverifiedEmailAddressRecord),
136
      utmCampaign: 'email_verify',
137
      unsubscribeUrl: EmailUtils.getUnsubscribeUrl(
138
        unverifiedEmailAddressRecord,
139
        'account-verification-email'
140
      ),
141
      whichPartial: 'email_partials/email_verify',
142
      heading: getMessage('email-verify-heading'),
143
      subheading: getMessage('email-verify-subhead')
144
    }
145
  )
146
}
147

148
async function verifyEmail (req, res) {
149
  const token = req.query.token
×
150
  await verifyEmailHash(token)
×
151

152
  return res.redirect('/user/settings')
×
153
}
154

155
async function updateCommunicationOptions (req, res) {
156
  try {
×
157
    const sessionUser = req.user
×
158
    // 0 = Send breach alerts to the email address found in brew breach.
159
    // 1 = Send all breach alerts to user's primary email address.
160
    const allEmailsToPrimary = Number(req.body.communicationOption) === 1
×
161
    const updatedSubscriber = await setAllEmailsToPrimary(
×
162
      sessionUser,
163
      allEmailsToPrimary
164
    )
165
    req.session.user = updatedSubscriber
×
166

167
    return res.json('Comm options updated')
×
168
  } catch (ex) {
169
    return res.json('Error updating comm options')
×
170
  }
171
}
172

173
export {
174
  settingsPage,
175
  resendEmail,
176
  addEmail,
177
  removeEmail,
178
  verifyEmail,
179
  updateCommunicationOptions
180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc