| 20 | const logger = createLogger('post-invoice-controller') |
| 21 | |
| 22 | export class PostInvoiceController implements IController { |
| 23 | public constructor( |
| 24 | private readonly userRepository: IUserRepository, |
| 25 | private readonly paymentsService: IPaymentsService, |
| 26 | private readonly settings: () => Settings, |
| 27 | private readonly rateLimiter: () => IRateLimiter, |
| 28 | ) {} |
| 29 | |
| 30 | public async handleRequest(request: Request, response: Response): Promise<void> { |
| 31 | logger('params: %o', request.params) |
| 32 | logger('body: %o', request.body) |
| 33 | |
| 34 | const currentSettings = this.settings() |
| 35 | |
| 36 | const { |
| 37 | info: { name: relayName, relay_url: relayUrl }, |
| 38 | } = currentSettings |
| 39 | |
| 40 | const limited = await this.isRateLimited(request, currentSettings) |
| 41 | if (limited) { |
| 42 | response.status(429).setHeader('content-type', 'text/plain; charset=utf8').send('Too many requests') |
| 43 | return |
| 44 | } |
| 45 | |
| 46 | if (!request.body || typeof request.body !== 'object') { |
| 47 | response.status(400).setHeader('content-type', 'text/plain; charset=utf8').send('Invalid request') |
| 48 | |
| 49 | return |
| 50 | } |
| 51 | |
| 52 | const tosAccepted = request.body?.tosAccepted === 'yes' |
| 53 | |
| 54 | if (!tosAccepted) { |
| 55 | response.status(400).setHeader('content-type', 'text/plain; charset=utf8').send('ToS agreement: not accepted') |
| 56 | |
| 57 | return |
| 58 | } |
| 59 | |
| 60 | const isAdmissionInvoice = request.body?.feeSchedule === 'admission' |
| 61 | if (!isAdmissionInvoice) { |
| 62 | response.status(400).setHeader('content-type', 'text/plain; charset=utf8').send('Invalid fee') |
| 63 | |
| 64 | return |
| 65 | } |
| 66 | |
| 67 | const pubkeyRaw = path(['body', 'pubkey'], request) |
| 68 | |
| 69 | let pubkey: string |
| 70 | if (typeof pubkeyRaw !== 'string') { |
| 71 | response.status(400).setHeader('content-type', 'text/plain; charset=utf8').send('Invalid pubkey: missing') |
| 72 | |
| 73 | return |
| 74 | } else if (/^[0-9a-f]{64}$/.test(pubkeyRaw)) { |
| 75 | pubkey = pubkeyRaw |
| 76 | } else if (/^npub1/.test(pubkeyRaw)) { |
| 77 | try { |
| 78 | pubkey = fromBech32(pubkeyRaw) |
| 79 | } catch (_error) { |
nothing calls this directly
no outgoing calls
no test coverage detected