easy-pingv0.7.0

Get in touch

Questions, bug reports, or anything about easy-ping. Either of these reaches me.

Emailteklumo.jembere@gmail.comTelegram@teklumt

For anything others would benefit from, a GitHub issue is better than a DM, because it's searchable.

GitHub

Plugins / Preferences

Edit this page

Preferences

A per-type, per-channel opt-out center with signed unsubscribe links, as a plugin.

Enabling it

import { preferences } from "easy-ping/plugins/preferences";

plugins: [preferences()],

Options

preferences({
  defaultEnabled: true,        // what an absent row means
  alwaysSend: ["securityAlert"],  // types that ignore preferences entirely
  unsubscribeTtlSeconds: 60 * 60 * 24 * 30,  // how long an unsubscribe link stays valid
})

Flip defaultEnabled to false for an opt-in-only notification type — most products want most types on by default, but a marketing digest usually shouldn't be.

Routes

RouteMethodScopeDoes
/preferencesGETuserEvery type/channel pair and its current state
/preferencesPOSTuserUpdate one pair. type must be a configured notification, channel and frequency valid, enabled a boolean — anything else is a 400
/unsubscribePOSTsignedOne-click opt-out from an email footer link — no session required

Unsubscribe links

Because /unsubscribe is session-less (the person clicking it isn't logged in, and shouldn't need to be), it's authenticated by a signed token embedded in the link itself rather than a cookie:

import { buildUnsubscribeToken } from "easy-ping/plugins/preferences";

const token = buildUnsubscribeToken(secret, userId, "commentReply", "email");
const link = `https://acme.dev/api/notifications/unsubscribe?token=${token}`;

The token expires after unsubscribeTtlSeconds (capped at 90 days) and is scoped to exactly one user/type/channel — it can't be replayed to unsubscribe someone from a different notification type. It also carries its issue time: if the user changed that preference after the email went out, the old link is refused with a 400 rather than silently undoing a newer decision made while logged in. Clicking the same link twice is still a 200, which one-click mail clients rely on. This is what the updatedAt column on notification_preference is for; see Upgrading if you are coming from 0.3.0.

usePreferences

import { usePreferences } from "easy-ping/plugins/preferences/react";

const { preferences, isEnabled, setEnabled, setFrequency, isLoading } = usePreferences();

Headless, like useNotifications — state and persistence only. isEnabled({ type, channel }) resolves against defaultEnabled for any pair with no stored row, so the UI never has to special- case "the user hasn't visited this settings page yet."