e857bf4ec6
- 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>
51 lines
1.6 KiB
TypeScript
51 lines
1.6 KiB
TypeScript
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 };
|
|
}
|
|
);
|
|
}
|