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

hicommonwealth / commonwealth / 15169302594

21 May 2025 06:01PM UTC coverage: 41.15% (-0.2%) from 41.385%
15169302594

push

github

web-flow
Merge pull request #12188 from hicommonwealth/rotorsoft/12175-decouple-router-from-models

Decouples router from models

1704 of 4566 branches covered (37.32%)

Branch coverage included in aggregate %.

5 of 65 new or added lines in 9 files covered. (7.69%)

1 existing line in 1 file now uncovered.

3157 of 7247 relevant lines covered (43.56%)

37.32 hits per line

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

16.67
/libs/model/src/aggregates/user/UpdateEmail.command.ts
1
import { type Command } from '@hicommonwealth/core';
2
import * as schemas from '@hicommonwealth/schemas';
3
import { DynamicTemplate, PRODUCTION_DOMAIN } from '@hicommonwealth/shared';
4
import sgMail from '@sendgrid/mail';
5
import crypto from 'crypto';
6
import { config } from '../../config';
7
import { models } from '../../database';
8
import { authVerified, mustExist } from '../../middleware';
9

10
const TOKEN_EXPIRES_IN = 30;
34✔
11
config.SENDGRID.API_KEY && sgMail.setApiKey(config.SENDGRID.API_KEY);
34!
12

13
const notifyUpdateEmail = async ({
34✔
14
  email,
15
  update_token,
16
}: {
17
  email: string;
18
  update_token: string;
19
}) => {
NEW
20
  const loginLink = `${config.SERVER_URL}/api/finishUpdateEmail?token=${
×
21
    update_token
22
  }&email=${encodeURIComponent(email)}`;
23

NEW
24
  const msg = {
×
25
    to: email,
26
    from: `Commonwealth <no-reply@${PRODUCTION_DOMAIN}>`,
27
    subject: 'Verify your Commonwealth email',
28
    templateId: DynamicTemplate.UpdateEmail,
29
    dynamic_template_data: { loginLink },
30
  };
NEW
31
  await sgMail.send(msg);
×
32
};
33

34
export function UpdateEmail(): Command<typeof schemas.UpdateEmail> {
NEW
35
  return {
×
36
    ...schemas.UpdateEmail,
37
    auth: [authVerified()],
38
    secure: true,
39
    body: async ({ actor, payload }) => {
NEW
40
      const { email } = payload;
×
41

NEW
42
      const user = await models.User.scope('withPrivateData').findOne({
×
43
        where: { id: actor.user!.id },
44
      });
NEW
45
      mustExist('User', user);
×
46

NEW
47
      const token = crypto.randomBytes(24).toString('hex');
×
NEW
48
      const expires = new Date(+new Date() + TOKEN_EXPIRES_IN * 60 * 1000);
×
49

NEW
50
      return await models.sequelize.transaction(async (transaction) => {
×
NEW
51
        const eut = await models.EmailUpdateToken.create(
×
52
          { email, expires, token },
53
          { transaction },
54
        );
55

NEW
56
        if (config.SENDGRID.API_KEY)
×
NEW
57
          await notifyUpdateEmail({ email, update_token: eut.token });
×
58

NEW
59
        user.email = email;
×
NEW
60
        user.emailVerified = false;
×
NEW
61
        await user.save({ transaction });
×
62

NEW
63
        return { ...user.toJSON(), email };
×
64
      });
65
    },
66
  };
67
}
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