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

karanshukla / navyfragen-app / 28455186937

30 Jun 2026 03:15PM UTC coverage: 96.163% (-1.4%) from 97.563%
28455186937

push

github

web-flow
Merge pull request #191 from karanshukla/dev-push-notifications

Dev push notifications

2103 of 2293 branches covered (91.71%)

Branch coverage included in aggregate %.

880 of 988 new or added lines in 20 files covered. (89.07%)

4 existing lines in 4 files now uncovered.

7923 of 8133 relevant lines covered (97.42%)

6.63 hits per line

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

97.6
/server/src/controllers/notification-controller.ts
1
import express from "express";
1!
2
import { body } from "express-validator";
1✔
3
import { Logger } from "pino";
1✔
4

1✔
5
import { NotificationService } from "../services/notification-service";
1✔
6

1✔
7
export class NotificationController {
1✔
8
  constructor(
1✔
9
    private notificationService: NotificationService,
9✔
10
    private logger: Logger
9✔
11
  ) {}
9✔
12

9✔
13
  /**
9✔
14
   * Return the server's VAPID public key so the client can subscribe.
9✔
15
   * Responds 501 when web push is not configured, so the client can hide the
9✔
16
   * opt-in UI until the server has VAPID keys.
9✔
17
   *
9✔
18
   * GET /notifications/vapid-public-key
9✔
19
   */
9✔
20
  getVapidPublicKey = async (
9✔
21
    req: express.Request,
2✔
22
    res: express.Response
2✔
23
  ): Promise<express.Response> => {
2✔
24
    const vapidPublicKey = this.notificationService.getVapidPublicKey();
2✔
25
    if (!vapidPublicKey) {
2✔
26
      return res.status(501).json({ error: "Web push not configured" });
1✔
27
    }
1✔
28
    return res.json({ vapidPublicKey });
1✔
29
  };
2✔
30

9✔
31
  /**
9✔
32
   * Validation rules for POST /notifications/subscribe
9✔
33
   */
9✔
34
  validateSubscribe = [
9✔
35
    body("endpoint").isURL().withMessage("endpoint must be a valid URL"),
9✔
36
    body("keys.p256dh").isString().notEmpty().withMessage("keys.p256dh is required"),
9✔
37
    body("keys.auth").isString().notEmpty().withMessage("keys.auth is required"),
9✔
38
  ];
9✔
39

9✔
40
  /**
9✔
41
   * Save a push subscription for the authenticated user.
9✔
42
   *
9✔
43
   * POST /notifications/subscribe
9✔
44
   * Body: { endpoint: string; keys: { p256dh: string; auth: string } }
9✔
45
   */
9✔
46
  subscribe = async (req: express.Request, res: express.Response): Promise<express.Response> => {
9✔
47
    const did = req.session?.did;
4✔
48
    if (!did) {
4✔
49
      return res.status(403).json({ error: "Not authenticated" });
1✔
50
    }
1✔
51

4✔
52
    // Don't accept subscriptions when the server can't actually deliver pushes
4✔
53
    if (!this.notificationService.getVapidPublicKey()) {
4✔
54
      return res.status(501).json({ error: "Web push not configured" });
1✔
55
    }
1✔
56

4✔
57
    const { endpoint, keys } = req.body as {
4✔
58
      endpoint: string;
2✔
59
      keys: { p256dh: string; auth: string };
2✔
60
    };
2✔
61

2✔
62
    try {
2✔
63
      await this.notificationService.saveSubscription(did, endpoint, keys.p256dh, keys.auth);
2✔
64
      this.logger.info({ did }, "Push subscription registered");
4✔
65
      return res.status(201).json({ ok: true });
1✔
66
    } catch (err) {
1✔
67
      this.logger.error({ err, did }, "Failed to save push subscription");
1✔
68
      return res.status(500).json({ error: "Failed to save subscription" });
1✔
69
    }
1✔
70
  };
4✔
71

9✔
72
  /**
9✔
73
   * Validation rules for DELETE /notifications/subscribe
9✔
74
   */
9✔
75
  validateUnsubscribe = [body("endpoint").isURL().withMessage("endpoint must be a valid URL")];
9✔
76

9✔
77
  /**
9✔
78
   * Remove a push subscription for the authenticated user.
9✔
79
   *
9✔
80
   * DELETE /notifications/subscribe
9✔
81
   * Body: { endpoint: string }
9✔
82
   */
9✔
83
  unsubscribe = async (req: express.Request, res: express.Response): Promise<express.Response> => {
1✔
84
    const did = req.session?.did;
3✔
85
    if (!did) {
3✔
86
      return res.status(403).json({ error: "Not authenticated" });
1✔
87
    }
1✔
88

3✔
89
    const { endpoint } = req.body as { endpoint: string };
3✔
90

2✔
91
    try {
2✔
92
      await this.notificationService.deleteSubscription(did, endpoint);
2✔
93
      this.logger.info({ did }, "Push subscription removed");
3✔
94
      return res.json({ ok: true });
1✔
95
    } catch (err) {
1✔
96
      this.logger.error({ err, did }, "Failed to delete push subscription");
1✔
97
      return res.status(500).json({ error: "Failed to delete subscription" });
1✔
98
    }
1✔
99
  };
3✔
UNCOV
100
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc