diff options
| author | Adam Soltys <[email protected]> | 2026-03-04 12:43:11 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-04 12:43:11 -0800 |
| commit | a6bc64017162bcaf1b7c2f3525f89c0e8dbe8d68 (patch) | |
| tree | 13e8bc40b4dca5426565a851c7fa372271aa5f88 | |
| parent | 6781fd2935ebc3abdeed182b4cee62c25c5482e7 (diff) | |
| download | coinos-server-a6bc64017162bcaf1b7c2f3525f89c0e8dbe8d68.tar.gz coinos-server-a6bc64017162bcaf1b7c2f3525f89c0e8dbe8d68.zip | |
fund authorizations
| -rw-r--r-- | index.ts | 2 | ||||
| -rw-r--r-- | lib/payments.ts | 2 | ||||
| -rw-r--r-- | routes/payments.ts | 59 |
3 files changed, 56 insertions, 7 deletions
@@ -108,6 +108,8 @@ app.get("/fund/:name/managers", payments.managers); app.post("/fund/managers", auth, payments.addManager); app.post("/fund/:name/managers/delete", auth, payments.deleteManager); app.post("/authorize", auth, payments.authorize); +app.get("/fund/:id/authorizations", payments.listAuthorizations); +app.post("/fund/:id/authorization/:authId/delete", auth, payments.deleteAuthorization); app.post("/take", auth, payments.take); app.post("/print", auth, payments.print); app.post("/send/:lnaddress/:amount", auth, payments.lnaddress); diff --git a/lib/payments.ts b/lib/payments.ts index bdaf2b1d..d0c43235 100644 --- a/lib/payments.ts +++ b/lib/payments.ts @@ -159,7 +159,7 @@ export const debit = async ({ const userLimit = await g("limit"); const frozen = (await g("hardfreeze")) || ((await g("freeze")) && type !== PaymentType.internal); - if (frozen || (amount > userLimit && !whitelisted) || amount > serverLimit) { + if (frozen || (userLimit != null && amount > userLimit && !whitelisted) || (serverLimit != null && amount > serverLimit)) { warn("Blocking", user.username, amount, hash, user.id, type, frozen, userLimit, serverLimit); fail("Problem sending payment"); } diff --git a/routes/payments.ts b/routes/payments.ts index dd679816..ab876823 100644 --- a/routes/payments.ts +++ b/routes/payments.ts @@ -360,8 +360,10 @@ export default { payments = payments.filter((p) => p); - const authorization = await g(`authorization:${id}`); - return c.json({ amount, authorization: authorization?.amount, payments }); + const authIds = await db.lRange(`fund:${id}:authorizations`, 0, -1) || []; + const allAuths = await Promise.all(authIds.map((authId) => g(`authorization:${authId}`))); + const authorizations = allAuths.filter((a) => a && !a.claimed); + return c.json({ amount, authorizations, payments }); }, async authorize(c) { @@ -373,21 +375,51 @@ export default { const managers = [...await db.sMembers(`fund:${id}:managers`)]; if (managers.length && !managers.includes(uid)) fail("Unauthorized"); + const authId = v4(); const authorization = { + authId, + fundId: id, uid, currency, fiat, amount, + created: Date.now(), }; - await s(`authorization:${id}`, authorization); + await s(`authorization:${authId}`, authorization); + await db.lPush(`fund:${id}:authorizations`, authId); + return c.json({ authId }); + }, + + async listAuthorizations(c) { + const id = c.req.param("id"); + const authIds = await db.lRange(`fund:${id}:authorizations`, 0, -1) || []; + const allAuths = await Promise.all(authIds.map((authId) => g(`authorization:${authId}`))); + const authorizations = allAuths.filter((a) => a && !a.claimed); + return c.json(authorizations); + }, + + async deleteAuthorization(c) { + const user = c.get("user"); + const id = c.req.param("id"); + const authId = c.req.param("authId"); + + const managers = [...await db.sMembers(`fund:${id}:managers`)]; + if (managers.length && !managers.includes(user.id)) fail("Unauthorized"); + + const authorization = await g(`authorization:${authId}`); + if (!authorization || authorization.fundId !== id) return bail(c, "Authorization not found"); + if (authorization.claimed) return bail(c, "Authorization already claimed"); + + await db.lRem(`fund:${id}:authorizations`, 0, authId); + await db.del(`authorization:${authId}`); return c.json({}); }, async take(c) { const body = await c.req.json(); const user = c.get("user"); - let { id, amount, invoice: iid } = body; + let { id, amount, invoice: iid, authId } = body; try { amount = Number.parseInt(amount); if (amount <= 0) fail("Invalid amount"); @@ -402,14 +434,29 @@ export default { iid = inv.id; } - const authorization = await g(`authorization:${id}`); + let authorization; + if (authId) { + authorization = await g(`authorization:${authId}`); + if (authorization && authorization.fundId !== id) authorization = null; + } else { + const authIds = await db.lRange(`fund:${id}:authorizations`, 0, -1) || []; + for (const aid of authIds) { + const auth = await g(`authorization:${aid}`); + if (auth && !auth.claimed) { + authorization = auth; + authId = aid; + break; + } + } + } + if (authorization && !authorization.claimed) { const { currency, fiat } = authorization; amount = Math.min(amount, sats(fiat / rates[currency])); const sender = await getUser(authorization.uid); authorization.claimed = true; - await s(`authorization:${id}`, authorization); + await s(`authorization:${authId}`, authorization); const { hash } = await generate({ invoice: { amount, type: "lightning" }, |
