Initial commit — federated self-custodial Spark/Lightning tip bot
- grammY bot: /start, /unlock, /tip, /contact, /claim, /settings, /wallet - AES-256-GCM mnemonic encryption with scrypt key derivation - In-memory unlock sessions with background sweep - Atomic claim handling (TOCTOU-safe) - PIN rate limiting (5 attempts → 15 min lockout) - Fastify API server + Telegram Mini App (setup, unlock, send, receive, history) - One-time seed reveal via Mini App or auto-deleted DM message - Federated registry client - Docker Compose deployment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import { validateInitData, AuthError } from "../auth";
|
||||
import { findUser } from "../../db/users";
|
||||
import { getDb } from "../../db/schema";
|
||||
|
||||
interface TxRow {
|
||||
id: string;
|
||||
amount_sats: number;
|
||||
status: string;
|
||||
recipient_address: string | null;
|
||||
recipient_user_id: number | null;
|
||||
initiated_via: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export async function historyRoutes(app: FastifyInstance): Promise<void> {
|
||||
app.get<{ Querystring: { limit?: string; offset?: string } }>(
|
||||
"/api/history",
|
||||
async (req, reply) => {
|
||||
const initData = req.headers["x-init-data"] as string | undefined;
|
||||
if (!initData) return reply.status(401).send({ error: "Missing X-Init-Data header" });
|
||||
|
||||
let userId: number;
|
||||
try {
|
||||
const validated = validateInitData(initData);
|
||||
userId = validated.user.id;
|
||||
} catch (err) {
|
||||
return reply.status(401).send({ error: err instanceof AuthError ? err.message : "Unauthorized" });
|
||||
}
|
||||
|
||||
if (!findUser(userId)) return reply.status(404).send({ error: "Wallet not found" });
|
||||
|
||||
const limit = Math.min(parseInt(req.query.limit ?? "20", 10), 100);
|
||||
const offset = parseInt(req.query.offset ?? "0", 10);
|
||||
|
||||
const rows = getDb()
|
||||
.prepare(
|
||||
`SELECT id, amount_sats, status, recipient_address, recipient_user_id,
|
||||
initiated_via, created_at
|
||||
FROM pending_transactions
|
||||
WHERE initiator_user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ? OFFSET ?`
|
||||
)
|
||||
.all(userId, limit, offset) as TxRow[];
|
||||
|
||||
return { transactions: rows, limit, offset };
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user