Merge branch 'main' into cron-minutely
This commit is contained in:
commit
2644310bfa
1 changed files with 37 additions and 13 deletions
|
|
@ -1,12 +1,13 @@
|
|||
import { instrument } from 'succinct-async'
|
||||
import express, { Request, Response, NextFunction } from 'express'
|
||||
import rateLimit from 'express-rate-limit'
|
||||
import { appSigner } from './env.js'
|
||||
import { appSigner, BRAND_NAME, BRAND_ACCENT, BRAND_LOGO, EVENT_VIEWER_URL } from './env.js'
|
||||
import { render } from './templates.js'
|
||||
import { confirmSubscriptionAction, unsubscribeAction, registerSubscription, ActionError } from './actions.js'
|
||||
import { getSubscriptionById, insertEvent, getSubscriptionByKey, getSubscriptionByPubkey } from './database.js'
|
||||
import { load } from '@welshman/net'
|
||||
import { getIdFilters } from '@welshman/util'
|
||||
import { verifyEvent } from 'nostr-tools/pure'
|
||||
|
||||
// Endpoints
|
||||
|
||||
|
|
@ -147,7 +148,7 @@ addRoute('delete', '/subscription/:key', async (req: Request, res: Response) =>
|
|||
|
||||
// NIP-9a relay push callback
|
||||
addRoute('post', '/notify/:id', async (req: Request, res: Response) => {
|
||||
const { id, relay } = req.body
|
||||
const { id, relay, event } = req.body
|
||||
|
||||
if (!id || !relay) {
|
||||
return res.status(400).json({ error: 'id and relay are required' })
|
||||
|
|
@ -164,19 +165,30 @@ addRoute('post', '/notify/:id', async (req: Request, res: Response) => {
|
|||
return res.status(404).json({ error: 'Subscription not active' })
|
||||
}
|
||||
|
||||
// Fetch the full event from the relay
|
||||
try {
|
||||
const [event] = await load({
|
||||
relays: [relay],
|
||||
filters: getIdFilters([id]),
|
||||
})
|
||||
let storedEvent = event
|
||||
|
||||
if (!event) {
|
||||
// Event not found at relay — don't 404, just skip
|
||||
return res.json({ ok: true, skipped: true })
|
||||
if (storedEvent) {
|
||||
// If the subscription requested include_event, verify and use it directly
|
||||
if (storedEvent.id !== id || !validEvent(storedEvent)) {
|
||||
return res.status(400).json({ error: 'Invalid event' })
|
||||
}
|
||||
} else {
|
||||
// Otherwise fetch the full event from the relay
|
||||
const [fetched] = await load({
|
||||
relays: [relay],
|
||||
filters: getIdFilters([id]),
|
||||
})
|
||||
|
||||
storedEvent = fetched
|
||||
|
||||
if (!storedEvent) {
|
||||
// Event not found at relay — don't 404, just skip
|
||||
return res.json({ ok: true, skipped: true })
|
||||
}
|
||||
}
|
||||
|
||||
const stored = await insertEvent(id, sub.id, event, relay)
|
||||
const stored = await insertEvent(id, sub.id, storedEvent, relay)
|
||||
|
||||
return res.json({ ok: true, stored })
|
||||
} catch (error) {
|
||||
|
|
@ -198,7 +210,12 @@ addRoute('get', '/confirm', async (req: Request, res: Response) => {
|
|||
try {
|
||||
await confirmSubscriptionAction({ token: req.query.token })
|
||||
|
||||
res.send(await render('pages/confirm-success.html'))
|
||||
res.send(await render('pages/confirm-success.html', {
|
||||
brandName: BRAND_NAME,
|
||||
brandAccent: BRAND_ACCENT,
|
||||
brandLogo: BRAND_LOGO,
|
||||
settingsUrl: `${EVENT_VIEWER_URL.replace(/\/$/, '')}/settings/alerts`,
|
||||
}))
|
||||
} catch (error) {
|
||||
const isActionError = error instanceof ActionError
|
||||
const message = isActionError ? String(error) : 'Oops, something went wrong on our end!'
|
||||
|
|
@ -233,4 +250,11 @@ server.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Validate an event's signature and that its id hash matches (defense against
|
||||
// a malicious relay forwarding tampered content via include_event).
|
||||
const validEvent = (event: any) => {
|
||||
if (!event || typeof event !== 'object') return false
|
||||
return verifyEvent(event)
|
||||
}
|
||||
Loading…
Reference in a new issue