| 163 | } |
| 164 | |
| 165 | type Webpusher struct { |
| 166 | store database.Store |
| 167 | log *slog.Logger |
| 168 | // VAPID allows us to identify the sender of the message. |
| 169 | // This must be a https:// URL or an email address. |
| 170 | // Some push services (such as Apple's) require this to be set. |
| 171 | vapidSub string |
| 172 | |
| 173 | // public and private keys for VAPID. These are used to sign and encrypt |
| 174 | // the message payload. |
| 175 | VAPIDPublicKey string |
| 176 | VAPIDPrivateKey string |
| 177 | |
| 178 | // httpClient is an SSRF-safe HTTP client that rejects connections to |
| 179 | // private, loopback, and link-local IP addresses at dial time. This |
| 180 | // closes the DNS rebinding TOCTOU gap where a hostname passes URL |
| 181 | // validation but resolves to a private IP when the connection is made. |
| 182 | httpClient *http.Client |
| 183 | |
| 184 | clock quartz.Clock |
| 185 | |
| 186 | cacheMu sync.RWMutex |
| 187 | subscriptionCache map[uuid.UUID]cachedSubscriptions |
| 188 | subscriptionGenerations map[uuid.UUID]uint64 |
| 189 | subscriptionCacheTTL time.Duration |
| 190 | subscriptionFetches singleflight.Group[string, []database.WebpushSubscription] |
| 191 | } |
| 192 | |
| 193 | func (n *Webpusher) Dispatch(ctx context.Context, userID uuid.UUID, msg codersdk.WebpushMessage) error { |
| 194 | subscriptions, err := n.subscriptionsForUser(ctx, userID) |
nothing calls this directly
no outgoing calls
no test coverage detected