diff --git a/.env.sample b/.env.sample index 54a4f5e..5c5bbee 100644 --- a/.env.sample +++ b/.env.sample @@ -32,3 +32,7 @@ MDK_MNEMONIC="your_mnemonic_here" # LND_PORT="" # ex. 8082 # LND_MACAROON="" # base64 invoices macaroon # LND_TLS_CERT="" # base64 + +# Activity Logging (optional) +# Webhook URL for logging pay code registrations (e.g., Discord webhook) +# ACTIVITY_WEBHOOK_URL="" diff --git a/src/lib/notifications.ts b/src/lib/notifications.ts new file mode 100644 index 0000000..8a5d397 --- /dev/null +++ b/src/lib/notifications.ts @@ -0,0 +1,45 @@ +/** + * Activity logging utilities for monitoring pay code registrations. + * Supports optional webhook notifications. + */ + +type PayCodeType = "free" | "paid"; + +interface ActivityLogOptions { + type: PayCodeType; + domain: string; +} + +/** + * Logs pay code registration activity to configured webhook (if set). + * Privacy-preserving: only logs type and domain, not usernames. + */ +export async function logPayCodeActivity( + options: ActivityLogOptions +): Promise { + const webhookUrl = process.env.ACTIVITY_WEBHOOK_URL; + + if (!webhookUrl) { + // Webhook not configured, skip silently + return; + } + + const emoji = options.type === "free" ? "🆓" : "💰"; + const label = options.type === "free" ? "Free" : "Paid"; + const message = `${emoji} ${label} PayCode registered on ${options.domain}`; + + try { + await fetch(webhookUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + content: message, + }), + }); + } catch (error) { + // Log error but don't throw - notifications are non-critical + console.error("Failed to send activity notification:", error); + } +} diff --git a/src/server/api/routers/payCode.ts b/src/server/api/routers/payCode.ts index 03a5e33..2859a56 100644 --- a/src/server/api/routers/payCode.ts +++ b/src/server/api/routers/payCode.ts @@ -14,6 +14,7 @@ import { createBip21FromParams, createPayCodeParams, } from "@/lib/util"; +import { logPayCodeActivity } from "@/lib/notifications"; import axios from "axios"; import { adjectives, @@ -196,6 +197,9 @@ export const payCodeRouter = createTRPCRouter({ }); // TODO: catch and throw again? + // Log activity (non-blocking, fire-and-forget) + logPayCodeActivity({ type: "free", domain: input.domain }); + return payCode; }), @@ -481,6 +485,9 @@ export const payCodeRouter = createTRPCRouter({ }); }); + // Log activity (non-blocking, fire-and-forget) + logPayCodeActivity({ type: "paid", domain: payCode.domain }); + return payCode; }),