2026-08-18 16:21:22 +00:00
|
|
|
import { instrument } from 'succinct-async'
|
|
|
|
|
import type { Subscription } from './alert.js'
|
|
|
|
|
import { getCronExpression } from './alert.js'
|
|
|
|
|
import * as mailer from './mailer.js'
|
|
|
|
|
import * as worker from './worker/index.js'
|
|
|
|
|
import * as db from './database.js'
|
|
|
|
|
|
|
|
|
|
export class ActionError extends Error {
|
|
|
|
|
toString() {
|
|
|
|
|
return this.message
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type RegisterSubscriptionParams = {
|
|
|
|
|
pubkey: string
|
|
|
|
|
email: string
|
|
|
|
|
frequency: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const registerSubscription = instrument(
|
|
|
|
|
'actions.registerSubscription',
|
|
|
|
|
async ({ pubkey, email, frequency }: RegisterSubscriptionParams) => {
|
|
|
|
|
const sub = await db.insertSubscription(pubkey, email, frequency)
|
|
|
|
|
const callback = `${process.env.BASE_URL}/notify/${sub.id}`
|
|
|
|
|
|
2026-09-03 15:47:21 +00:00
|
|
|
// Only send a confirmation when the subscription is new, unconfirmed, or
|
|
|
|
|
// its email address changed. An already-confirmed, unchanged subscription
|
|
|
|
|
// (or one where only the frequency changed) skips it.
|
|
|
|
|
if (!sub.confirmed_at) {
|
|
|
|
|
await mailer.sendConfirm(sub)
|
|
|
|
|
}
|
2026-08-18 16:21:22 +00:00
|
|
|
|
|
|
|
|
return { key: sub.key, callback }
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
export type ConfirmSubscriptionParams = {
|
|
|
|
|
token: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const confirmSubscriptionAction = instrument(
|
|
|
|
|
'actions.confirmSubscription',
|
|
|
|
|
async ({ token }: ConfirmSubscriptionParams) => {
|
|
|
|
|
const sub = await db.confirmSubscription(token)
|
|
|
|
|
|
|
|
|
|
if (!sub) {
|
|
|
|
|
throw new ActionError('That confirmation code is invalid or has expired.')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
worker.registerSubscription(sub)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
export type UnsubscribeParams = {
|
|
|
|
|
token: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const unsubscribeAction = instrument(
|
|
|
|
|
'actions.unsubscribe',
|
|
|
|
|
async ({ token }: UnsubscribeParams) => {
|
|
|
|
|
const sub = await db.unsubscribeSubscription(token)
|
|
|
|
|
|
|
|
|
|
if (sub) {
|
|
|
|
|
worker.unregisterSubscription(sub)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)
|