From 1eb69361b7ecbf70b3759fce5022a3e9327a8942 Mon Sep 17 00:00:00 2001 From: Nicolas Burtey Date: Sun, 17 Sep 2023 18:09:44 +0100 Subject: [PATCH] refactor: use nextjs framework --- .../.next/server/app-paths-manifest.json | 1 + .../.next/server/middleware-manifest.json | 6 + .../.next/server/pages-manifest.json | 1 + .../.next/server/server-reference-manifest.js | 1 + .../server/server-reference-manifest.json | 4 + apps/boltcard-galoy/.next/types/package.json | 1 + apps/boltcard/.eslintrc.json | 3 + apps/boltcard/.gitignore | 35 +++++ apps/boltcard/README.md | 34 +++++ apps/boltcard/TODO.txt | 3 +- .../api/callback/route.ts} | 75 +++++----- apps/boltcard/app/api/createboltcard/route.ts | 63 ++++++++ apps/boltcard/app/api/health/route.ts | 5 + .../{lnurlw.ts => app/api/ln/route.ts} | 63 ++++---- apps/boltcard/app/api/new/route.ts | 65 +++++++++ apps/boltcard/app/api/wipe/route.ts | 54 +++++++ apps/boltcard/{ => app}/config.ts | 4 +- apps/boltcard/{ => app/crypto}/aes.ts | 0 apps/boltcard/{ => app}/decoder.spec.ts | 2 +- apps/boltcard/{ => app}/decoder.ts | 0 apps/boltcard/app/favicon.ico | Bin 0 -> 4286 bytes apps/boltcard/app/globals.css | 27 ++++ apps/boltcard/{ => app}/knex.ts | 6 +- apps/boltcard/app/layout.tsx | 22 +++ apps/boltcard/app/page.tsx | 113 +++++++++++++++ apps/boltcard/bats/e2e-test.bats | 15 +- apps/boltcard/bun.lockb | Bin 44784 -> 160131 bytes apps/boltcard/index.ts | 36 ----- apps/boltcard/knexfile.ts | 10 ++ apps/boltcard/new.ts | 137 ------------------ apps/boltcard/next.config.js | 9 ++ apps/boltcard/package.json | 29 +++- apps/boltcard/postcss.config.js | 6 + apps/boltcard/public/next.svg | 1 + apps/boltcard/public/vercel.svg | 1 + apps/boltcard/router.ts | 3 - apps/boltcard/tailwind.config.ts | 20 +++ apps/boltcard/tsconfig.json | 27 ++++ apps/boltcard/wipe.ts | 59 -------- 39 files changed, 620 insertions(+), 321 deletions(-) create mode 100644 apps/boltcard-galoy/.next/server/app-paths-manifest.json create mode 100644 apps/boltcard-galoy/.next/server/middleware-manifest.json create mode 100644 apps/boltcard-galoy/.next/server/pages-manifest.json create mode 100644 apps/boltcard-galoy/.next/server/server-reference-manifest.js create mode 100644 apps/boltcard-galoy/.next/server/server-reference-manifest.json create mode 100644 apps/boltcard-galoy/.next/types/package.json create mode 100644 apps/boltcard/.eslintrc.json create mode 100644 apps/boltcard/.gitignore create mode 100644 apps/boltcard/README.md rename apps/boltcard/{callback.ts => app/api/callback/route.ts} (67%) create mode 100644 apps/boltcard/app/api/createboltcard/route.ts create mode 100644 apps/boltcard/app/api/health/route.ts rename apps/boltcard/{lnurlw.ts => app/api/ln/route.ts} (68%) create mode 100644 apps/boltcard/app/api/new/route.ts create mode 100644 apps/boltcard/app/api/wipe/route.ts rename apps/boltcard/{ => app}/config.ts (71%) rename apps/boltcard/{ => app/crypto}/aes.ts (100%) rename apps/boltcard/{ => app}/decoder.spec.ts (96%) rename apps/boltcard/{ => app}/decoder.ts (100%) create mode 100644 apps/boltcard/app/favicon.ico create mode 100644 apps/boltcard/app/globals.css rename apps/boltcard/{ => app}/knex.ts (97%) create mode 100644 apps/boltcard/app/layout.tsx create mode 100644 apps/boltcard/app/page.tsx delete mode 100644 apps/boltcard/index.ts create mode 100644 apps/boltcard/knexfile.ts delete mode 100644 apps/boltcard/new.ts create mode 100644 apps/boltcard/next.config.js create mode 100644 apps/boltcard/postcss.config.js create mode 100644 apps/boltcard/public/next.svg create mode 100644 apps/boltcard/public/vercel.svg delete mode 100644 apps/boltcard/router.ts create mode 100644 apps/boltcard/tailwind.config.ts create mode 100644 apps/boltcard/tsconfig.json delete mode 100644 apps/boltcard/wipe.ts diff --git a/apps/boltcard-galoy/.next/server/app-paths-manifest.json b/apps/boltcard-galoy/.next/server/app-paths-manifest.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/apps/boltcard-galoy/.next/server/app-paths-manifest.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/apps/boltcard-galoy/.next/server/middleware-manifest.json b/apps/boltcard-galoy/.next/server/middleware-manifest.json new file mode 100644 index 00000000000..57712aad396 --- /dev/null +++ b/apps/boltcard-galoy/.next/server/middleware-manifest.json @@ -0,0 +1,6 @@ +{ + "sortedMiddleware": [], + "middleware": {}, + "functions": {}, + "version": 2 +} \ No newline at end of file diff --git a/apps/boltcard-galoy/.next/server/pages-manifest.json b/apps/boltcard-galoy/.next/server/pages-manifest.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/apps/boltcard-galoy/.next/server/pages-manifest.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/apps/boltcard-galoy/.next/server/server-reference-manifest.js b/apps/boltcard-galoy/.next/server/server-reference-manifest.js new file mode 100644 index 00000000000..4dbb0b3fed1 --- /dev/null +++ b/apps/boltcard-galoy/.next/server/server-reference-manifest.js @@ -0,0 +1 @@ +self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {}\n}" \ No newline at end of file diff --git a/apps/boltcard-galoy/.next/server/server-reference-manifest.json b/apps/boltcard-galoy/.next/server/server-reference-manifest.json new file mode 100644 index 00000000000..27a92af52d0 --- /dev/null +++ b/apps/boltcard-galoy/.next/server/server-reference-manifest.json @@ -0,0 +1,4 @@ +{ + "node": {}, + "edge": {} +} \ No newline at end of file diff --git a/apps/boltcard-galoy/.next/types/package.json b/apps/boltcard-galoy/.next/types/package.json new file mode 100644 index 00000000000..1632c2c4df6 --- /dev/null +++ b/apps/boltcard-galoy/.next/types/package.json @@ -0,0 +1 @@ +{"type": "module"} \ No newline at end of file diff --git a/apps/boltcard/.eslintrc.json b/apps/boltcard/.eslintrc.json new file mode 100644 index 00000000000..bffb357a712 --- /dev/null +++ b/apps/boltcard/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/apps/boltcard/.gitignore b/apps/boltcard/.gitignore new file mode 100644 index 00000000000..8f322f0d8f4 --- /dev/null +++ b/apps/boltcard/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/apps/boltcard/README.md b/apps/boltcard/README.md new file mode 100644 index 00000000000..f4da3c4c1cf --- /dev/null +++ b/apps/boltcard/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/boltcard/TODO.txt b/apps/boltcard/TODO.txt index 94e04f7671c..566422e4c01 100644 --- a/apps/boltcard/TODO.txt +++ b/apps/boltcard/TODO.txt @@ -1,4 +1,3 @@ - rate limits - proper link of .gql (instead of copy) -- Tilt setup -- next-apisation? \ No newline at end of file +- Tilt setup \ No newline at end of file diff --git a/apps/boltcard/callback.ts b/apps/boltcard/app/api/callback/route.ts similarity index 67% rename from apps/boltcard/callback.ts rename to apps/boltcard/app/api/callback/route.ts index a4f31932394..d3eecac2530 100644 --- a/apps/boltcard/callback.ts +++ b/apps/boltcard/app/api/callback/route.ts @@ -1,10 +1,9 @@ -import express from "express" - import { gql, GraphQLClient } from "graphql-request" -import { boltcardRouter } from "./router" -import { fetchByCardId, fetchByK1 } from "./knex" -import { apiUrl } from "./config" +import { NextRequest, NextResponse } from "next/server" + +import { fetchByCardId, fetchByK1 } from "../../knex" +import { coreUrl } from "../../config" type GetUsdWalletIdQuery = { readonly __typename: "Query" @@ -71,28 +70,26 @@ const lnInvoicePaymentSendMutation = gql` } ` -boltcardRouter.get("/callback", async (req: express.Request, res: express.Response) => { - const k1 = req?.query?.k1 - const pr = req?.query?.pr - - console.log({ k1, pr }) +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url) + const k1 = searchParams.get("k1") + const pr = searchParams.get("pr") if (!k1 || !pr) { - res.status(400).send({ status: "ERROR", reason: "missing k1 or pr" }) - return - } - - if (typeof k1 !== "string" || typeof pr !== "string") { - res.status(400).send({ status: "ERROR", reason: "invalid k1 or pr" }) - return + return NextResponse.json( + { status: "ERROR", reason: "missing k1 or pr" }, + { status: 400 }, + ) } const payment = await fetchByK1(k1) + console.log({ payment, k1 }) + const { cardId } = payment const card = await fetchByCardId(cardId) - const graphQLClient = new GraphQLClient(apiUrl, { + const graphQLClient = new GraphQLClient(coreUrl, { headers: { authorization: `Bearer ${card.token}`, }, @@ -103,15 +100,19 @@ boltcardRouter.get("/callback", async (req: express.Request, res: express.Respon data = await graphQLClient.request(getUsdWalletIdQuery) } catch (error) { console.error(error) - res.status(400).send({ status: "ERROR", reason: "issue fetching walletId" }) - return + return NextResponse.json( + { status: "ERROR", reason: "issue fetching walletId" }, + { status: 400 }, + ) } const wallets = data.me?.defaultAccount.wallets if (!wallets) { - res.status(400).send({ status: "ERROR", reason: "no wallets found" }) - return + return NextResponse.json( + { status: "ERROR", reason: "no wallets found" }, + { status: 400 }, + ) } const usdWallet = wallets.find((wallet) => wallet.walletCurrency === "USD") @@ -120,8 +121,10 @@ boltcardRouter.get("/callback", async (req: express.Request, res: express.Respon console.log({ usdWallet, wallets }) if (!usdWalletId) { - res.status(400).send({ status: "ERROR", reason: "no usd wallet found" }) - return + return NextResponse.json( + { status: "ERROR", reason: "no usd wallet found" }, + { status: 400 }, + ) } let result: LnInvoicePaymentSendMutation @@ -132,19 +135,21 @@ boltcardRouter.get("/callback", async (req: express.Request, res: express.Respon }) } catch (error) { console.error(error) - res.status(400).send({ status: "ERROR", reason: "payment failed" }) - return + return NextResponse.json( + { status: "ERROR", reason: "payment failed" }, + { status: 400 }, + ) } if (result.lnInvoicePaymentSend.status === "SUCCESS") { - res.json({ status: "OK" }) + return NextResponse.json({ status: "OK" }) } else { - res.status(400).send({ - status: "ERROR", - reason: `payment failed: ${result.lnInvoicePaymentSend.errors[0].message}`, - }) + return NextResponse.json( + { + status: "ERROR", + reason: `payment failed: ${result.lnInvoicePaymentSend.errors[0].message}`, + }, + { status: 400 }, + ) } -}) - -const callback = "dummy" -export { callback } +} diff --git a/apps/boltcard/app/api/createboltcard/route.ts b/apps/boltcard/app/api/createboltcard/route.ts new file mode 100644 index 00000000000..53992e3b2a3 --- /dev/null +++ b/apps/boltcard/app/api/createboltcard/route.ts @@ -0,0 +1,63 @@ +import { randomBytes } from "crypto" + +import { NextRequest, NextResponse } from "next/server" + +import { serverUrl } from "../../config" +import { createCardInit } from "../../knex" + +function randomHex(): string { + try { + const bytes: Buffer = randomBytes(16) + return bytes.toString("hex") + } catch (error) { + if (error instanceof Error) { + console.warn(error.message) + throw error + } + } +} + +export async function GET(req: NextRequest) { + // should be pass with POST? not sure if this would be compatible + // with the wallet that can create cards + + const { searchParams } = new URL(req.url) + const token = searchParams.get("token") + + if (!token) { + return NextResponse.json( + { status: "ERROR", reason: "token missing" }, + { status: 400 }, + ) + } + + // TODO: token validation + + const oneTimeCode = randomHex() + const k0AuthKey = randomHex() + const k2CmacKey = randomHex() + const k3 = randomHex() + const k4 = randomHex() + + const result = await createCardInit({ + oneTimeCode, + k0AuthKey, + k2CmacKey, + k3, + k4, + token, + }) + + if (result instanceof Error) { + return NextResponse.json( + { status: "ERROR", reason: "impossible to create card" }, + { status: 400 }, + ) + } + + const url = `${serverUrl}/new?a=${oneTimeCode}` + return NextResponse.json({ + status: "OK", + url, + }) +} diff --git a/apps/boltcard/app/api/health/route.ts b/apps/boltcard/app/api/health/route.ts new file mode 100644 index 00000000000..df7e186fc0b --- /dev/null +++ b/apps/boltcard/app/api/health/route.ts @@ -0,0 +1,5 @@ +import { NextRequest, NextResponse } from "next/server" + +export async function GET(req: NextRequest) { + return NextResponse.json({ status: "OK" }) +} diff --git a/apps/boltcard/lnurlw.ts b/apps/boltcard/app/api/ln/route.ts similarity index 68% rename from apps/boltcard/lnurlw.ts rename to apps/boltcard/app/api/ln/route.ts index f06e093f27a..5ab6e6fa230 100644 --- a/apps/boltcard/lnurlw.ts +++ b/apps/boltcard/app/api/ln/route.ts @@ -1,10 +1,11 @@ import { randomBytes } from "crypto" -import express from "express" +import { NextRequest, NextResponse } from "next/server" + +import { aesDecrypt, checkSignature } from "../../crypto/aes" + +import { aesDecryptKey, serverUrl } from "../../config" -import { aesDecrypt, checkSignature } from "./aes" -import { aesDecryptKey, serverUrl } from "./config" -import { decryptedPToUidCtr } from "./decoder" import { CardInitInput, createCard, @@ -12,8 +13,9 @@ import { fetchByUid, insertk1, markCardInitAsUsed, -} from "./knex" -import { boltcardRouter } from "./router" +} from "../../knex" + +import { decryptedPToUidCtr } from "../../decoder" function generateSecureRandomString(length: number): string { return randomBytes(Math.ceil(length / 2)) @@ -53,23 +55,23 @@ const maybeSetupCard = async ({ return null } -boltcardRouter.get("/ln", async (req: express.Request, res: express.Response) => { - const raw_p = req?.query?.p - const raw_c = req?.query?.c +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url) + const raw_p = searchParams.get("p") + const raw_c = searchParams.get("c") if (!raw_p || !raw_c) { - res.status(400).send({ status: "ERROR", reason: "missing p or c" }) - return + return NextResponse.json( + { status: "ERROR", reason: "missing p or c" }, + { status: 400 }, + ) } if (raw_p?.length !== 32 || raw_c?.length !== 16) { - res.status(400).send({ status: "ERROR", reason: "invalid p or c" }) - return - } - - if (typeof raw_p !== "string" || typeof raw_c !== "string") { - res.status(400).send({ status: "ERROR", reason: "invalid p or c" }) - return + return NextResponse.json( + { status: "ERROR", reason: "invalid p or c" }, + { status: 400 }, + ) } const ba_p = Buffer.from(raw_p, "hex") @@ -79,8 +81,10 @@ boltcardRouter.get("/ln", async (req: express.Request, res: express.Response) => const decryptedP = aesDecrypt(aesDecryptKey, ba_p) if (decryptedP instanceof Error) { - res.status(400).send({ status: "ERROR", reason: "impossible to decrypt P" }) - return + return NextResponse.json( + { status: "ERROR", reason: "impossible to decrypt P" }, + { status: 400 }, + ) } // TODO error management @@ -104,13 +108,17 @@ boltcardRouter.get("/ln", async (req: express.Request, res: express.Response) => card = await createCard({ uid, k0AuthKey, k2CmacKey, k3, k4, ctr, token }) } else { - res.status(400).send({ status: "ERROR", reason: "card not found" }) - return + return NextResponse.json( + { status: "ERROR", reason: "card not found" }, + { status: 400 }, + ) } } else { if (!card.enabled) { - res.status(400).send({ status: "ERROR", reason: "card disabled" }) - return + return NextResponse.json( + { status: "ERROR", reason: "card disabled" }, + { status: 400 }, + ) } } @@ -121,7 +129,7 @@ boltcardRouter.get("/ln", async (req: express.Request, res: express.Response) => await insertk1({ k1, cardId: card.id }) - res.json({ + return NextResponse.json({ tag: "withdrawRequest", callback: serverUrl + "/callback", k1, @@ -129,7 +137,4 @@ boltcardRouter.get("/ln", async (req: express.Request, res: express.Response) => minWithdrawable: 1000, maxWithdrawable: 100000000000, }) -}) - -const lnurlw = "dummy" -export { lnurlw } +} diff --git a/apps/boltcard/app/api/new/route.ts b/apps/boltcard/app/api/new/route.ts new file mode 100644 index 00000000000..cddfe5ca7a2 --- /dev/null +++ b/apps/boltcard/app/api/new/route.ts @@ -0,0 +1,65 @@ +import { NextRequest, NextResponse } from "next/server" + +import { aesDecryptKey, serverUrl } from "../../config" +import { fetchByOneTimeCode } from "../../knex" + +interface NewCardResponse { + protocol_name: string + protocol_version: number + card_name: string + lnurlw_base: string + k0: string + k1: string + k2: string + k3: string + k4: string + uid_privacy: string +} + +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url) + const oneTimeCode = searchParams.get("a") + + if (!oneTimeCode) { + return NextResponse.json( + { status: "ERROR", reason: "value a is missing" }, + { status: 400 }, + ) + } + + const cardInit = await fetchByOneTimeCode(oneTimeCode) + + if (!cardInit) { + return NextResponse.json( + { status: "ERROR", reason: "cardInit not found" }, + { status: 400 }, + ) + } + + if (cardInit.status !== "init") { + return NextResponse.json( + { status: "ERROR", reason: "code has already been used" }, + { status: 400 }, + ) + } + + const lnurlwBase = `${serverUrl}/ln` + .replace("http://", "lnurlw://") + .replace("https://", "lnurlw://") + const k1DecryptKey = aesDecryptKey.toString("hex") + + const response: NewCardResponse = { + protocol_name: "create_bolt_card_response", + protocol_version: 2, + card_name: "", + lnurlw_base: lnurlwBase, + k0: cardInit.k0AuthKey, + k1: k1DecryptKey, + k2: cardInit.k2CmacKey, + k3: cardInit.k3, + k4: cardInit.k4, + uid_privacy: "Y", + } + + return NextResponse.json(response) +} diff --git a/apps/boltcard/app/api/wipe/route.ts b/apps/boltcard/app/api/wipe/route.ts new file mode 100644 index 00000000000..f809e00c844 --- /dev/null +++ b/apps/boltcard/app/api/wipe/route.ts @@ -0,0 +1,54 @@ +import { NextRequest, NextResponse } from "next/server" + +import { AES_DECRYPT_KEY } from "../../config" + +import { fetchByCardId, fetchByOneTimeCode } from "../../knex" + +export async function GET(req: NextRequest) { + // should be pass with POST? not sure if this would be compatible + // with the wallet that can create cards + + const { searchParams } = new URL(req.url) + const cardId = searchParams.get("cardId") + const oneTimeCode = searchParams.get("a") + + if (!cardId && !oneTimeCode) { + return NextResponse.json( + { status: "ERROR", reason: "cardId or a missing" }, + { status: 400 }, + ) + } + // TODO authorization + + // TODO may be both on CardInit and Card table + let card + if (cardId) { + card = await fetchByCardId(cardId) + } else if (oneTimeCode) { + card = await fetchByOneTimeCode(oneTimeCode) + } else { + return NextResponse.json( + { status: "ERROR", reason: "cardId or a missing" }, + { status: 400 }, + ) + } + + if (!card) { + return NextResponse.json( + { status: "ERROR", reason: "card not found" }, + { status: 400 }, + ) + } + + return NextResponse.json({ + status: "OK", + action: "wipe", + k0: card.k0AuthKey, + k1: AES_DECRYPT_KEY, + k2: card.k2CmacKey, + k3: card.k3, + k4: card.k4, + uid: card.uid, + version: 1, + }) +} diff --git a/apps/boltcard/config.ts b/apps/boltcard/app/config.ts similarity index 71% rename from apps/boltcard/config.ts rename to apps/boltcard/app/config.ts index a29aa8ce171..d2bb3f76fb2 100644 --- a/apps/boltcard/config.ts +++ b/apps/boltcard/app/config.ts @@ -1,6 +1,6 @@ -export const serverUrl = process.env.SERVER_URL ?? "http://localhost:3000" +export const serverUrl = process.env.SERVER_URL ?? "http://localhost:3000/api" -export const apiUrl = process.env.API_URL ?? "http://localhost:4002/graphql" +export const coreUrl = process.env.CORE_URL ?? "http://localhost:4002/graphql" export const AES_DECRYPT_KEY = process.env.AES_DECRYPT_KEY ?? "0c3b25d92b38ae443229dd59ad34b85d" diff --git a/apps/boltcard/aes.ts b/apps/boltcard/app/crypto/aes.ts similarity index 100% rename from apps/boltcard/aes.ts rename to apps/boltcard/app/crypto/aes.ts diff --git a/apps/boltcard/decoder.spec.ts b/apps/boltcard/app/decoder.spec.ts similarity index 96% rename from apps/boltcard/decoder.spec.ts rename to apps/boltcard/app/decoder.spec.ts index d0029f1c51c..9ef8ae3fd74 100644 --- a/apps/boltcard/decoder.spec.ts +++ b/apps/boltcard/app/decoder.spec.ts @@ -1,4 +1,4 @@ -import { aesDecrypt, checkSignature } from "./aes" +import { aesDecrypt, checkSignature } from "../boltcard-galoy/app/crypto/aes" import { decryptedPToUidCtr } from "./decoder" const aesjs = require("aes-js") diff --git a/apps/boltcard/decoder.ts b/apps/boltcard/app/decoder.ts similarity index 100% rename from apps/boltcard/decoder.ts rename to apps/boltcard/app/decoder.ts diff --git a/apps/boltcard/app/favicon.ico b/apps/boltcard/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..388c6266c1c01ece0d8dca4b14f1e1380bb8ec2b GIT binary patch literal 4286 zcmb_gYfKzf6rKVpc3IeIDRf`6d)cMd#>A{qe@Lus`l}C2R*mJ^W^8P+(1g`cLfcr@ zV1uB^uEvVAn%Szg#0N`b)c$A!(U>$6Gqw_23CZ+j7q+E?P|zPH$9o259f#eW#d^ru zd*{x%=R1$@&P-sK74(k~27j4;mSIX6hIxQ0U1I!Haa|F5uee}A7Z#TF%UZ)f@Yde( zsQvxxB30S>$z9-1&4In|x@>>vs_2M}3&k&f$P+=PNdJ#mV}B6G=W|i}$(!K(Yz|5~ z&H}ru2h3ZV!SrN3m^LjaTh|SxZC?X-Vg|Ur8QIZwIl>0o$vs5Dt~)*!aSqIb_1#Ng z@;AT=lLe^1jEXjeo14JiISS66Yv6cwG(z_*s~$?u1M`x#cLCQ3Gu{0{e-k*nu7aae zl1P_KsGXs(2Tu7M6ub1z^N?HO%9ty4W_UkujSFlqe+TZKX{n@6@MQ`>$@`0WY4!d- zL%-Qzt2R0JS-{rvEx5a6iG)(ufy89i7QG>rzHvUQKC8a79We%H>jiLk-jV2@C45-- z-sOlbdegAxKA%si&Ct*g2!gPzI_0r0aK7{t)t`}*NwJ=$XYA8CFjY2W^e2iUpwf+i z__8c#v`0I=ntioEj@$v#{!bsA{j-s^N6s#>)$hf8Q3xRriA0onOG^ucLLo(i!62|K zYv{+hUj%Y!N}&E3?7OpnPBw3CHr$8)Q80H&l9ajr2Wx`y>0{Skn;)tLZl6*|)^U1< zx1abeE$94xKSZO^lz51hDi&PR;_2sk77oe|+y)2#J@5EN8l)Q#&FzwC6Hi4(x+_!TbEAOn6Co z54*y@3y#AV4e_aPs15y(wbUi7q*X6LI?E>I80$t2XvHw^Tw8uD%`krUz zz*9}JpAo}J;Kd33E@R12S+ef_GAnGv#9kZm9PLn%b6nLqDBE#KB7udzFG42={5Eul zrlhrjV_C){Dj}vlmO2Tgx)B0`yIff!{HITpvcx&S{?|<`c38-#?1SQtfHWe;w zy#==C2EpXrM(a^z7z@_Akm96&L0}30hOaK4? literal 0 HcmV?d00001 diff --git a/apps/boltcard/app/globals.css b/apps/boltcard/app/globals.css new file mode 100644 index 00000000000..fd81e885836 --- /dev/null +++ b/apps/boltcard/app/globals.css @@ -0,0 +1,27 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} diff --git a/apps/boltcard/knex.ts b/apps/boltcard/app/knex.ts similarity index 97% rename from apps/boltcard/knex.ts rename to apps/boltcard/app/knex.ts index debaca9c1ae..903e975cbdb 100644 --- a/apps/boltcard/knex.ts +++ b/apps/boltcard/app/knex.ts @@ -1,9 +1,11 @@ -const options = { +import createKnex, { Knex } from "knex" + +export const knexConfig: Knex.Config = { client: "pg", connection: "postgres://dbuser:secret@localhost:5436/default?sslmode=disable", } -const knex = require("knex")(options) +const knex = createKnex(knexConfig) export async function createTable() { const hasPaymentTable = await knex.schema.hasTable("Payment") diff --git a/apps/boltcard/app/layout.tsx b/apps/boltcard/app/layout.tsx new file mode 100644 index 00000000000..ae845621236 --- /dev/null +++ b/apps/boltcard/app/layout.tsx @@ -0,0 +1,22 @@ +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/apps/boltcard/app/page.tsx b/apps/boltcard/app/page.tsx new file mode 100644 index 00000000000..7a8286b5768 --- /dev/null +++ b/apps/boltcard/app/page.tsx @@ -0,0 +1,113 @@ +import Image from 'next/image' + +export default function Home() { + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+ +
+ +
+ Next.js Logo +
+ + +
+ ) +} diff --git a/apps/boltcard/bats/e2e-test.bats b/apps/boltcard/bats/e2e-test.bats index 7fd4f86226d..1a1683b94a6 100644 --- a/apps/boltcard/bats/e2e-test.bats +++ b/apps/boltcard/bats/e2e-test.bats @@ -9,35 +9,34 @@ load "../../../test/bats/helpers/ln" echo "TOKEN_ALICE=$(read_value "alice")" export TOKEN_ALICE=$(read_value "alice") - RESPONSE=$(curl -s "http://localhost:3000/createboltcard?token=${TOKEN_ALICE}") + RESPONSE=$(curl -s "http://localhost:3000/api/createboltcard?token=${TOKEN_ALICE}") CALLBACK_URL=$(echo $RESPONSE | jq -r '.url') # Making the follow-up curl request RESPONSE=$(curl -s "${CALLBACK_URL}") echo "$RESPONSE" - [[ $(echo $RESPONSE | jq -r '.PROTOCOL_NAME') == "create_bolt_card_response" ]] || exit 1 + [[ $(echo $RESPONSE | jq -r '.protocol_name') == "create_bolt_card_response" ]] || exit 1 } @test "auth: create payment and follow up" { P_VALUE="4E2E289D945A66BB13377A728884E867" C_VALUE="E19CCB1FED8892CE" - RESPONSE=$(curl -s "http://localhost:3000/ln?p=${P_VALUE}&c=${C_VALUE}") + RESPONSE=$(curl -s "http://localhost:3000/api/ln?p=${P_VALUE}&c=${C_VALUE}") echo "$RESPONSE" CALLBACK_URL=$(echo $RESPONSE | jq -r '.callback') K1_VALUE=$(echo $RESPONSE | jq -r '.k1') - echo "CALLBACK_URL: $CALLBACK_URL" echo "K1_VALUE: $K1_VALUE" - cache_value "k1" "$K1_VALUE" - cache_value "CALLBACK_URL" "$CALLBACK_URL" + + exit 1 } @test "callback" { K1_VALUE=$(read_value "k1") - CALLBACK_URL=$(read_value "CALLBACK_URL") + CALLBACK_URL=http://localhost:3000/api/callback invoice_response="$(lnd_outside_2_cli addinvoice --amt 1000)" payment_request=$(echo $invoice_response | jq -r '.payment_request') @@ -50,5 +49,5 @@ load "../../../test/bats/helpers/ln" @test "wipecard" { "skip" id="" - curl -s http://localhost:3000/wipeboltcard + curl -s http://localhost:3000/api/wipeboltcard } \ No newline at end of file diff --git a/apps/boltcard/bun.lockb b/apps/boltcard/bun.lockb index fd05e46c4d6d2ac8405b4cf399e2f717d63528ec..630c02b6731e0cda78b6ccef51b431b971f4c3dc 100755 GIT binary patch literal 160131 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 delta 15417 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 diff --git a/apps/boltcard/index.ts b/apps/boltcard/index.ts deleted file mode 100644 index c5db186a365..00000000000 --- a/apps/boltcard/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -// server.js - -import express from "express" -import bodyParser from "body-parser" - -import { boltcardRouter } from "./router" - -// loading router -import { lnurlw } from "./lnurlw" -import { callback } from "./callback" -import { createboltcard } from "./new" -import { wipe } from "./wipe" - -import { createTable } from "./knex" - -lnurlw -callback -createboltcard -wipe - -await createTable() - -const app = express() -const PORT = 3000 - -// Middleware to parse POST requests -app.use(bodyParser.json()) -app.use(bodyParser.urlencoded({ extended: true })) - -// Use the router -app.use(boltcardRouter) - -// Start the server -app.listen(PORT, () => { - console.log(`Server started on http://localhost:${PORT}`) -}) diff --git a/apps/boltcard/knexfile.ts b/apps/boltcard/knexfile.ts new file mode 100644 index 00000000000..81227f08ee7 --- /dev/null +++ b/apps/boltcard/knexfile.ts @@ -0,0 +1,10 @@ +import type { Knex } from "knex" + +// Update with your config settings. + +const config: { [key: string]: Knex.Config } = { + client: "pg", + connection: "postgres://dbuser:secret@localhost:5436/default?sslmode=disable", +} + +module.exports = config diff --git a/apps/boltcard/new.ts b/apps/boltcard/new.ts deleted file mode 100644 index fc941a6ec0e..00000000000 --- a/apps/boltcard/new.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { randomBytes } from "crypto" - -import express from "express" - -import { boltcardRouter } from "./router" -import { aesDecryptKey, serverUrl } from "./config" -import { createCardInit, fetchByOneTimeCode } from "./knex" - -function randomHex(): string { - try { - const bytes: Buffer = randomBytes(16) - return bytes.toString("hex") - } catch (error) { - if (error instanceof Error) { - console.warn(error.message) - throw error - } - } -} - -// curl -s http://localhost:3000/createboltcard - -boltcardRouter.get( - "/createboltcard", - async (req: express.Request, res: express.Response) => { - // should be pass with POST? not sure if this would be compatible - // with the wallet that can create cards - const token = req.query.token - - if (!token) { - res.status(400).send({ status: "ERROR", reason: "token missing" }) - return - } - - if (typeof token !== "string") { - res.status(400).send({ status: "ERROR", reason: "token is not a string" }) - return - } - - // TODO: token validation? - - const oneTimeCode = randomHex() - const k0AuthKey = randomHex() - const k2CmacKey = randomHex() - const k3 = randomHex() - const k4 = randomHex() - - const result = await createCardInit({ - oneTimeCode, - k0AuthKey, - k2CmacKey, - k3, - k4, - token, - }) - - if (result instanceof Error) { - res.status(400).send({ status: "ERROR", reason: "impossible to create card" }) - return - } - - const url = `${serverUrl}/new?a=${oneTimeCode}` - res.json({ - status: "OK", - url, - }) - }, -) - -interface NewCardResponse { - PROTOCOL_NAME: string - PROTOCOL_VERSION: number - CARD_NAME: string - lnurlw_base: string - k0: string - k1: string - k2: string - k3: string - k4: string - uid_privacy: string -} - -boltcardRouter.get("/new", async (req: express.Request, res: express.Response) => { - const url = req.url - console.debug("new_card url:", url) - - const oneTimeCode = req.query.a - - if (!oneTimeCode) { - console.debug("a not found") - res.status(400).send({ status: "ERROR", reason: "a missing" }) - return - } - - if (typeof oneTimeCode !== "string") { - console.debug("a is not a string") - res.status(400).send({ status: "ERROR", reason: "Bad Request" }) - return - } - - const cardInit = await fetchByOneTimeCode(oneTimeCode) - - if (!cardInit) { - console.debug("cardInit not found") - res.status(400).send({ status: "ERROR", reason: "cardInit not found" }) - return - } - - if (cardInit.status !== "init") { - console.debug("cardInit already fetched") - res.status(400).send({ status: "ERROR", reason: "code has been fetched" }) - return - } - - const lnurlwBase = `${serverUrl}/ln` - .replace("http://", "lnurlw://") - .replace("https://", "lnurlw://") - const k1DecryptKey = aesDecryptKey.toString("hex") - - const response: NewCardResponse = { - PROTOCOL_NAME: "create_bolt_card_response", - PROTOCOL_VERSION: 2, - CARD_NAME: "dummy", - lnurlw_base: lnurlwBase, - k0: cardInit.k0AuthKey, - k1: k1DecryptKey, - k2: cardInit.k2CmacKey, - k3: cardInit.k3, - k4: cardInit.k4, - uid_privacy: "Y", - } - - res.status(200).json(response) -}) - -const createboltcard = "" -export { createboltcard } diff --git a/apps/boltcard/next.config.js b/apps/boltcard/next.config.js new file mode 100644 index 00000000000..bff9f6995d1 --- /dev/null +++ b/apps/boltcard/next.config.js @@ -0,0 +1,9 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + serverActions: true, + serverComponentsExternalPackages: ['knex', 'pg'], + }, +} + +module.exports = nextConfig diff --git a/apps/boltcard/package.json b/apps/boltcard/package.json index 9243867d93c..eaf41631348 100644 --- a/apps/boltcard/package.json +++ b/apps/boltcard/package.json @@ -1,18 +1,33 @@ { - "name": "boltcard", - "version": "0.0.1", - "main": "index.ts", - "license": "MIT", + "name": "boltcard-galoy", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, "dependencies": { - "aes-cmac": "^2.0.0", + "@types/node": "20.6.2", + "@types/react": "18.2.21", + "@types/react-dom": "18.2.7", "aes-js": "^3.1.2", + "autoprefixer": "10.4.15", "body-parser": "^1.20.2", - "express": "^4.18.2", + "eslint": "8.49.0", + "eslint-config-next": "13.4.19", "graphql": "^16.8.0", "graphql-request": "^6.1.0", "knex": "^2.5.1", + "next": "13.4.19", "node-aes-cmac": "^0.1.1", - "pg": "^8.11.3" + "pg": "^8.11.3", + "postcss": "8.4.29", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwindcss": "3.3.3", + "typescript": "5.2.2" }, "devDependencies": { "@types/pg": "^8.10.2" diff --git a/apps/boltcard/postcss.config.js b/apps/boltcard/postcss.config.js new file mode 100644 index 00000000000..33ad091d26d --- /dev/null +++ b/apps/boltcard/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/apps/boltcard/public/next.svg b/apps/boltcard/public/next.svg new file mode 100644 index 00000000000..5174b28c565 --- /dev/null +++ b/apps/boltcard/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/boltcard/public/vercel.svg b/apps/boltcard/public/vercel.svg new file mode 100644 index 00000000000..d2f84222734 --- /dev/null +++ b/apps/boltcard/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/boltcard/router.ts b/apps/boltcard/router.ts deleted file mode 100644 index 5b173dcc642..00000000000 --- a/apps/boltcard/router.ts +++ /dev/null @@ -1,3 +0,0 @@ -import express from "express" - -export const boltcardRouter = express.Router() diff --git a/apps/boltcard/tailwind.config.ts b/apps/boltcard/tailwind.config.ts new file mode 100644 index 00000000000..c7ead804652 --- /dev/null +++ b/apps/boltcard/tailwind.config.ts @@ -0,0 +1,20 @@ +import type { Config } from 'tailwindcss' + +const config: Config = { + content: [ + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': + 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + }, + }, + }, + plugins: [], +} +export default config diff --git a/apps/boltcard/tsconfig.json b/apps/boltcard/tsconfig.json new file mode 100644 index 00000000000..ff59394dd24 --- /dev/null +++ b/apps/boltcard/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/api/callback"], + "exclude": ["node_modules"] +} diff --git a/apps/boltcard/wipe.ts b/apps/boltcard/wipe.ts deleted file mode 100644 index d1043dc8377..00000000000 --- a/apps/boltcard/wipe.ts +++ /dev/null @@ -1,59 +0,0 @@ -import express from "express" - -import { boltcardRouter } from "./router" -import { fetchByCardId, fetchByOneTimeCode } from "./knex" -import { AES_DECRYPT_KEY } from "./config" - -boltcardRouter.get( - "/wipeboltcard", - async (req: express.Request, res: express.Response) => { - // should be pass with POST? not sure if this would be compatible - // with the wallet that can create cards - const cardId = req.query.cardId - const oneTimeCode = req.query.a - - if (!cardId && !oneTimeCode) { - res.status(400).send({ status: "ERROR", reason: "cardId missing" }) - return - } - // TODO authorization - - // TODO may be both on CardInit and Card table - let card - if (cardId) { - if (typeof cardId !== "string") { - res.status(400).send({ status: "ERROR", reason: "cardId is not a string" }) - return - } - - card = await fetchByCardId(cardId) - } else { - if (typeof oneTimeCode !== "string") { - res.status(400).send({ status: "ERROR", reason: "oneTimeCode is not a string" }) - return - } - - card = await fetchByOneTimeCode(oneTimeCode) - } - - if (!card) { - res.status(400).send({ status: "ERROR", reason: "card not found" }) - return - } - - res.json({ - status: "OK", - action: "wipe", - k0: card.k0AuthKey, - k1: AES_DECRYPT_KEY, - k2: card.k2CmacKey, - k3: card.k3, - k4: card.k4, - uid: card.uid, - version: 1, - }) - }, -) - -const wipe = "" -export { wipe }