Skip to content

Commit

Permalink
chore: expose basic webpage for card
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Burtey committed Sep 21, 2023
1 parent ed7fb77 commit 59bda84
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 93 deletions.
2 changes: 2 additions & 0 deletions apps/boltcard/TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
- Tilt setup
- verify writing card do the callback/creation of Card entity
- open telemetry

- add lnurl / lightning address / invoice
4 changes: 2 additions & 2 deletions apps/boltcard/app/api/card/id/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { NextRequest, NextResponse } from "next/server"

Check failure on line 1 in apps/boltcard/app/api/card/id/[id]/route.ts

View workflow job for this annotation

GitHub Actions / Check Code

Cannot find module 'next/server' or its corresponding type declarations.

import { fetchByCardId } from "@/services/db/card"
import { fetchPublicByCardId } from "@/services/db/card"

Check failure on line 3 in apps/boltcard/app/api/card/id/[id]/route.ts

View workflow job for this annotation

GitHub Actions / Check Code

Cannot find module '@/services/db/card' or its corresponding type declarations.

export async function GET(req: NextRequest, { params }: { params: { id: string } }) {
const id = params.id
const card = await fetchByCardId(id)
const card = await fetchPublicByCardId(id)
if (!card) {
return NextResponse.json(
{ status: "ERROR", reason: "card not found" },
Expand Down
2 changes: 1 addition & 1 deletion apps/boltcard/app/api/card/id/[id]/transactions/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { NextRequest, NextResponse } from "next/server"

import { gql } from "@apollo/client"

import { fetchByCardId } from "@/services/db/card"
import { apollo } from "@/services/core"

Check failure on line 5 in apps/boltcard/app/api/card/id/[id]/transactions/route.ts

View workflow job for this annotation

GitHub Actions / Check Code

Cannot find module '@/services/core' or its corresponding type declarations.
import { TransactionsDocument, TransactionsQuery } from "@/services/core/generated"

Check failure on line 6 in apps/boltcard/app/api/card/id/[id]/transactions/route.ts

View workflow job for this annotation

GitHub Actions / Check Code

Cannot find module '@/services/core/generated' or its corresponding type declarations.
import { fetchByCardId } from "@/services/db/card"

gql`
query transactions($first: Int, $after: String) {
Expand Down
4 changes: 2 additions & 2 deletions apps/boltcard/app/api/card/uid/[uid]/route.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { NextRequest, NextResponse } from "next/server"

import { fetchByUid } from "@/services/db/card"
import { fetchPublicByCardUid } from "@/services/db/card"

export async function GET(req: NextRequest, { params }: { params: { uid: string } }) {
const uid = params.uid
const card = await fetchByUid(uid)
const card = await fetchPublicByCardUid(uid)
if (!card) {
return NextResponse.json(
{ status: "ERROR", reason: "card not found" },
Expand Down
4 changes: 2 additions & 2 deletions apps/boltcard/app/api/createboltcard/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { randomBytes } from "crypto"
import { NextRequest, NextResponse } from "next/server"

import { createCardInit } from "@/services/db/card-init"
import { serverUrl } from "@/services/config"
import { serverApi } from "@/services/config"

const randomHex = (): string => randomBytes(16).toString("hex")

Expand Down Expand Up @@ -45,7 +45,7 @@ export async function GET(req: NextRequest) {
)
}

const url = `${serverUrl}/new?a=${oneTimeCode}`
const url = `${serverApi}/new?a=${oneTimeCode}`
return NextResponse.json({
status: "OK",
url,
Expand Down
176 changes: 97 additions & 79 deletions apps/boltcard/app/api/ln/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { NextRequest, NextResponse } from "next/server"

import { ApolloQueryResult, gql } from "@apollo/client"

import { aesDecryptKey, serverUrl } from "@/services/config"
import { aesDecryptKey, serverApi } from "@/services/config"
import { aesDecrypt, checkSignature } from "@/services/crypto/aes"
import { decodePToUidCtr } from "@/services/crypto/decoder"
import { createCard, fetchByUid } from "@/services/db/card"
Expand Down Expand Up @@ -98,6 +98,96 @@ const maybeSetupCard = async ({
return null
}

const setupCard = async ({
cardInit,
uid,
ctr,
}: {
cardInit: CardInitInput
uid: string
ctr: number
}): Promise<NextResponse | undefined> => {
const { k0AuthKey, k2CmacKey, k3, k4, token } = cardInit

const client = apollo(token).getClient()

let data: ApolloQueryResult<GetUsdWalletIdQuery>
try {
data = await client.query<GetUsdWalletIdQuery>({
query: GetUsdWalletIdDocument,
})
} catch (error) {
console.error(error)
return NextResponse.json(
{ status: "ERROR", reason: "issue fetching walletId" },
{ status: 400 },
)
}

const accountId = data.data?.me?.defaultAccount?.id
if (!accountId) {
return NextResponse.json(
{ status: "ERROR", reason: "no accountId found" },
{ status: 400 },
)
}

const wallets = data.data?.me?.defaultAccount.wallets

if (!wallets) {
return NextResponse.json(
{ status: "ERROR", reason: "no wallets found" },
{ status: 400 },
)
}

const usdWallet = wallets.find((wallet) => wallet.walletCurrency === "USD")
const walletId = usdWallet?.id

console.log({ usdWallet, wallets })

if (!walletId) {
return NextResponse.json(
{ status: "ERROR", reason: "no usd wallet found" },
{ status: 400 },
)
}

const dataOnchain = await client.mutate<OnChainAddressCurrentMutation>({
mutation: OnChainAddressCurrentDocument,
variables: { input: { walletId } },
})
const onchainAddress = dataOnchain.data?.onChainAddressCurrent?.address
if (!onchainAddress) {
console.log(dataOnchain.data?.onChainAddressCurrent, "dataOnchain")
return NextResponse.json(
{ status: "ERROR", reason: `onchain address not found` },
{ status: 400 },
)
}

const id = generateReadableCode(12)
console.log({ id, onchainAddress }, "new card id")

await markCardInitAsUsed(k2CmacKey)

const card = await createCard({
id,
uid,
k0AuthKey,
k2CmacKey,
k3,
k4,
ctr,
token,
accountId,
onchainAddress,
walletId,
})

return card
}

gql`
query getUsdWalletId {
me {
Expand Down Expand Up @@ -159,86 +249,14 @@ export async function GET(req: NextRequest) {
let card = await fetchByUid(uid)

if (!card) {
const result = await maybeSetupCard({ uidRaw, ctrRawInverseBytes, ba_c })

if (result) {
const { k0AuthKey, k2CmacKey, k3, k4, token } = result

const client = apollo(token).getClient()

let data: ApolloQueryResult<GetUsdWalletIdQuery>
try {
data = await client.query<GetUsdWalletIdQuery>({
query: GetUsdWalletIdDocument,
})
} catch (error) {
console.error(error)
return NextResponse.json(
{ status: "ERROR", reason: "issue fetching walletId" },
{ status: 400 },
)
}

const accountId = data.data?.me?.defaultAccount?.id
if (!accountId) {
return NextResponse.json(
{ status: "ERROR", reason: "no accountId found" },
{ status: 400 },
)
}
const cardInit = await maybeSetupCard({ uidRaw, ctrRawInverseBytes, ba_c })

Check warning on line 252 in apps/boltcard/app/api/ln/route.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"ba" should be "by" or "be".

const wallets = data.data?.me?.defaultAccount.wallets
if (cardInit) {
card = await setupCard({ cardInit, uid, ctr })

if (!wallets) {
return NextResponse.json(
{ status: "ERROR", reason: "no wallets found" },
{ status: 400 },
)
if (card instanceof NextResponse) {
return card
}

const usdWallet = wallets.find((wallet) => wallet.walletCurrency === "USD")
const walletId = usdWallet?.id

console.log({ usdWallet, wallets })

if (!walletId) {
return NextResponse.json(
{ status: "ERROR", reason: "no usd wallet found" },
{ status: 400 },
)
}

const dataOnchain = await client.mutate<OnChainAddressCurrentMutation>({
mutation: OnChainAddressCurrentDocument,
variables: { input: { walletId } },
})
const onchainAddress = dataOnchain.data?.onChainAddressCurrent?.address
if (!onchainAddress) {
console.log(dataOnchain.data?.onChainAddressCurrent, "dataOnchain")
return NextResponse.json(
{ status: "ERROR", reason: `onchain address not found` },
{ status: 400 },
)
}

const id = generateReadableCode(12)
console.log({ id, onchainAddress }, "new card id")

await markCardInitAsUsed(k2CmacKey)

card = await createCard({
id,
uid,
k0AuthKey,
k2CmacKey,
k3,
k4,
ctr,
token,
accountId,
onchainAddress,
walletId,
})
} else {
return NextResponse.json(
{ status: "ERROR", reason: "card not found" },
Expand Down Expand Up @@ -270,7 +288,7 @@ export async function GET(req: NextRequest) {

return NextResponse.json({
tag: "withdrawRequest",
callback: serverUrl + "/callback",
callback: serverApi + "/callback",
k1,
defaultDescription: "payment for a blink card",
minWithdrawable: 1000,
Expand Down
4 changes: 2 additions & 2 deletions apps/boltcard/app/api/new/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server"

import { aesDecryptKey, serverUrl } from "@/services/config"
import { aesDecryptKey, serverApi } from "@/services/config"
import { fetchByOneTimeCode } from "@/services/db/card-init"

interface NewCardResponse {
Expand Down Expand Up @@ -43,7 +43,7 @@ export async function GET(req: NextRequest) {
)
}

const lnurlwBase = `${serverUrl}/ln`
const lnurlwBase = `${serverApi}/ln`
.replace("http://", "lnurlw://")
.replace("https://", "lnurlw://")

Expand Down
68 changes: 65 additions & 3 deletions apps/boltcard/app/card/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,71 @@
export default function Home({ params }: { params: { id: string } }) {
import { serverApi } from "@/services/config"

export default async function Home({ params }: { params: { id: string } }) {
const { id } = params

const cardApi = `${serverApi}/card/id/${id}`
const cardResult = await fetch(cardApi, { cache: "no-store" })
const cardInfo = await cardResult.json()

const transactionsApi = `${serverApi}/card/id/${id}/transactions`
const transactionsResult = await fetch(transactionsApi)
const transactionInfo = await transactionsResult.json()

return (
<main className="flex min-h-screen items-center justify-center">
<h1>cardId: {id}</h1>
<main className="flex flex-col min-h-screen items-center justify-center">
<h1>boltcard</h1>

<section className="my-4">
<h2>Card Information:</h2>
<ul>
<li>
<strong>ID:</strong> {cardInfo.id}
</li>
<li>
<strong>UID:</strong> {cardInfo.uid}
</li>
<li>
<strong>Onchain Address:</strong> {cardInfo.onchainAddress}
</li>
<li>
<strong>Enabled:</strong> {cardInfo.enabled ? "Yes" : "No"}
</li>
</ul>
</section>

<section className="my-4">
<h2>Transactions:</h2>
<ul>
{transactionInfo.map((tx, index) => (
<li key={tx.id} className="mb-2">
<strong>Transaction {index + 1}:</strong>
<ul>
<li>
<strong>ID:</strong> {tx.id}
</li>
<li>
<strong>Status:</strong> {tx.status}
</li>
<li>
<strong>Direction:</strong> {tx.direction}
</li>
<li>
<strong>Memo:</strong> {tx.memo}
</li>
<li>
<strong>Amount:</strong> {tx.settlementDisplayAmount}{" "}
{tx.settlementDisplayCurrency}
</li>
<li>
<strong>Fee:</strong> {tx.settlementDisplayFee}{" "}
{tx.settlementDisplayCurrency}
</li>
{/* Add more fields as required */}
</ul>
</li>
))}
</ul>
</section>
</main>
)
}
Binary file modified apps/boltcard/bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion apps/boltcard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"eslint-config-next": "13.4.19",
"graphql": "^16.8.0",
"knex": "^2.5.1",
"next": "13.4.19",
"next": "latest",
"node-aes-cmac": "^0.1.1",
"pg": "^8.11.3",
"postcss": "8.4.29",
Expand Down
2 changes: 1 addition & 1 deletion apps/boltcard/services/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const serverUrl = process.env.SERVER_URL ?? "http://localhost:3000/api"
export const serverApi = process.env.SERVER_URL ?? "http://localhost:3000/api"

export const coreUrl = process.env.CORE_URL ?? "http://localhost:4002/graphql"

Expand Down
16 changes: 16 additions & 0 deletions apps/boltcard/services/db/card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ export async function fetchByCardId(cardId: string) {
return result
}

export async function fetchPublicByCardUid(uid: string) {
const result = await knex("Card")
.where("uid", uid)
.select("id", "uid", "onchainAddress", "enabled")
.first()
return result
}

export async function fetchPublicByCardId(cardId: string) {
const result = await knex("Card")
.where("id", cardId)
.select("id", "uid", "onchainAddress", "enabled")
.first()
return result
}

interface CardInput {
id: string
uid: string
Expand Down

0 comments on commit 59bda84

Please sign in to comment.