Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

chore: refactor and upgrade first path to nextjs 13 #533

Merged
merged 3 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ check-code:
# 16 is exit code for critical https://classic.yarnpkg.com/lang/en/docs/cli/audit
audit:
bash -c 'yarn audit --level critical; [[ $$? -ge 16 ]] && exit 1 || exit 0'

codegen:
yarn codegen
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ Galoy-Pay uses query, mutation, and subscription operations from the Galoy's gra

In the project directory, create a file name `.env.local` and fill it with

```
NEXT_PUBLIC_GRAPHQL_HOSTNAME='localhost:4002'
NEXT_PUBLIC_GRAPHQL_WEBSOCKET_URL='FIXME'
```

for staging, use

```
NEXT_PUBLIC_GRAPHQL_HOSTNAME='api.staging.galoy.io'
NEXT_PUBLIC_GRAPHQL_HOSTNAME_INTERNAL='api.staging.galoy.io'
NEXT_PUBLIC_GRAPHQL_URI_INTERNAL='api.staging.galoy.io'
NEXT_PUBLIC_GRAPHQL_WEBSOCKET_URL='wss://ws.staging.galoy.io/graphql'
```

Expand All @@ -28,8 +33,6 @@ yarn install
yarn dev
```

This will run the app in the development mode.

Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

The page will automatically reload when you make edits.
Expand Down Expand Up @@ -65,4 +68,17 @@ yarn build

This will build the app for production under a `build` folder. It will bundle React in production mode and optimize the build for the best performance. The build will be minified, and the bundled files will include unique hashes in their names.

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
## Test lnurlp

```
GRAPHQL_HOSTNAME_INTERNAL="api.galoy-staging-galoy.svc.cluster.local"
```

or
```
GRAPHQL_HOSTNAME_INTERNAL="localhost:4002"
```

This environment variable is needed for getting the lnurlp endpoint working.

curl localhost:3000/.well-known/lnurlp/alice
129 changes: 58 additions & 71 deletions pages/api/lnurlp/[username].ts → app/lnurlp/[username]/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { NextResponse } from "next/server"
import { URL } from "url"

import crypto from "crypto"
import originalUrl from "original-url"
import {
ApolloClient,
ApolloLink,
Expand All @@ -8,10 +10,15 @@ import {
HttpLink,
InMemoryCache,
} from "@apollo/client"
import type { NextApiRequest, NextApiResponse } from "next"
import Redis from "ioredis"

import { GRAPHQL_URI_INTERNAL, NOSTR_PUBKEY } from "../../../lib/config"
import {
AccountDefaultWalletDocument,
AccountDefaultWalletQuery,
LnInvoiceCreateOnBehalfOfRecipientDocument,
LnInvoiceCreateOnBehalfOfRecipientMutation,
} from "../../../lib/graphql/generated"

const ipForwardingMiddleware = new ApolloLink((operation, forward) => {
operation.setContext(({ headers = {} }) => ({
Expand All @@ -35,17 +42,15 @@ const client = new ApolloClient({
cache: new InMemoryCache(),
})

const ACCOUNT_DEFAULT_WALLET = gql`
gql`
query accountDefaultWallet($username: Username!, $walletCurrency: WalletCurrency!) {
accountDefaultWallet(username: $username, walletCurrency: $walletCurrency) {
__typename
id
walletCurrency
}
}
`

const LNURL_INVOICE = gql`
mutation lnInvoiceCreateOnBehalfOfRecipient(
$walletId: WalletId!
$amount: SatAmount!
Expand All @@ -69,35 +74,6 @@ const LNURL_INVOICE = gql`
}
`

type CreateInvoiceOutput = {
invoice?: {
paymentRequest: string
paymentHash: string
}
errors?: {
message: string
}[]
}

type CreateInvoiceParams = {
walletId: string
amount: number
descriptionHash: string
}

async function createInvoice(params: CreateInvoiceParams): Promise<CreateInvoiceOutput> {
const {
data: {
mutationData: { errors, invoice },
},
} = await client.mutate({
mutation: LNURL_INVOICE,
variables: params,
})

return { errors, invoice }
}

const nostrEnabled = !!NOSTR_PUBKEY

let redis: Redis | null = null
Expand All @@ -108,15 +84,15 @@ if (nostrEnabled) {
sentinels: [
{
host: `${process.env.REDIS_0_DNS}`,
port: Number(process.env.REDIS_0_SENTINEL_PORT) || 26379,
port: 26379,
},
{
host: `${process.env.REDIS_1_DNS}`,
port: Number(process.env.REDIS_1_SENTINEL_PORT) || 26379,
port: 26379,
},
{
host: `${process.env.REDIS_2_DNS}`,
port: Number(process.env.REDIS_2_SENTINEL_PORT) || 26379,
port: 26379,
},
],
name: process.env.REDIS_MASTER_NAME ?? "mymaster",
Expand All @@ -128,33 +104,55 @@ if (nostrEnabled) {
redis.on("error", (err) => console.log({ err }, "Redis error"))
}

export default async function (req: NextApiRequest, res: NextApiResponse) {
export async function GET(
request: Request,
{ params }: { params: { username: string } },
) {
console.log(NOSTR_PUBKEY)

const { username, amount, nostr } = req.query
const url = originalUrl(req)
const { searchParams, hostname } = new URL(request.url)

const username = params.username
const amount = searchParams.get("amount")
const nostr = searchParams.get("nostr")

const accountUsername = username ? username.toString() : ""

const walletId = await getUserWalletId(accountUsername, req)
let walletId: string | null = null

try {
const { data } = await client.query<AccountDefaultWalletQuery>({
query: AccountDefaultWalletDocument,
variables: { username: accountUsername, walletCurrency: "BTC" },
context: {
"x-real-ip": request.headers.get("x-real-ip"),
"x-forwarded-for": request.headers.get("x-forwarded-for"),
},
})
walletId = data?.accountDefaultWallet?.id
} catch (err: unknown) {
console.log(err)
}

if (!walletId) {
return res.json({
return NextResponse.json({
status: "ERROR",
reason: `Couldn't find user '${username}'.`,
})
}

const metadata = JSON.stringify([
["text/plain", `Payment to ${accountUsername}`],
["text/identifier", `${accountUsername}@${url.hostname}`],
["text/identifier", `${accountUsername}@${hostname}`],
])

// lnurl options call
if (!amount) {
return res.json({
callback: url.full,
return NextResponse.json({
callback: request.url,
minSendable: 1000,
maxSendable: 100000000000,
metadata: metadata,
metadata,
tag: "payRequest",
...(nostrEnabled
? {
Expand All @@ -173,7 +171,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {

const amountSats = Math.round(parseInt(amount, 10) / 1000)
if ((amountSats * 1000).toString() !== amount) {
return res.json({
return NextResponse.json({
status: "ERROR",
reason: "Millisatoshi amount is not supported, please send a value in full sats.",
})
Expand All @@ -187,15 +185,21 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
descriptionHash = crypto.createHash("sha256").update(metadata).digest("hex")
}

const { invoice, errors } = await createInvoice({
walletId,
amount: amountSats,
descriptionHash,
const result = await client.mutate<LnInvoiceCreateOnBehalfOfRecipientMutation>({
mutation: LnInvoiceCreateOnBehalfOfRecipientDocument,
variables: {
walletId,
amount: amountSats,
descriptionHash,
},
})

const errors = result.errors
const invoice = result.data?.mutationData?.invoice

if ((errors && errors.length) || !invoice) {
console.log("error getting invoice", errors)
return res.json({
return NextResponse.json({
status: "ERROR",
reason: `Failed to get invoice: ${errors ? errors[0].message : "unknown error"}`,
})
Expand All @@ -205,32 +209,15 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
redis.set(`nostrInvoice:${invoice.paymentHash}`, nostr, "EX", 1440)
}

return res.json({
return NextResponse.json({
pr: invoice.paymentRequest,
routes: [],
})
} catch (err: unknown) {
console.log("unexpected error getting invoice", err)
res.json({
NextResponse.json({
status: "ERROR",
reason: err instanceof Error ? err.message : "unexpected error",
})
}
}

const getUserWalletId = async (accountUsername: string, req: NextApiRequest) => {
try {
const { data } = await client.query({
query: ACCOUNT_DEFAULT_WALLET,
variables: { username: accountUsername, walletCurrency: "BTC" },
context: {
"x-real-ip": req.headers["x-real-ip"],
"x-forwarded-for": req.headers["x-forwarded-for"],
},
})
return data?.accountDefaultWallet?.id
} catch (err) {
console.log("error getting user wallet id:", err)
return undefined
}
}
13 changes: 11 additions & 2 deletions codegen.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
overwrite: true
schema: "https://raw.githubusercontent.com/GaloyMoney/galoy/main/src/graphql/main/schema.graphql"
schema: "https://raw.githubusercontent.com/GaloyMoney/galoy/main/src/graphql/public/schema.graphql"
documents:
- "components/**/*.ts"
- "components/**/*.tsx"
Expand All @@ -11,7 +11,6 @@ documents:
- "hooks/**/*.tsx"
generates:
lib/graphql/generated.ts:
schema: "lib/graphql/local-schema.gql"
plugins:
- typescript
- typescript-operations
Expand All @@ -26,6 +25,7 @@ generates:
immutableTypes: true
strictScalars: true
nonOptionalTypename: true
federation: true
scalars:
AccountApiKeyLabel: "string"
AuthToken: "string"
Expand Down Expand Up @@ -53,5 +53,14 @@ generates:
DisplayCurrency: "string"
SignedDisplayMajorAmount: "string"
CountryCode: "string"
EmailRegistrationId: "string"
TotpRegistrationId: "string"
EmailAddress: "string"
TotpSecret: "string"
TotpCode: "string"
Feedback: "string"
Minutes: "string"
LeaderboardName: "string"
join__FieldSet: "string"
link__Import: "string"
_FieldSet: "string"
8 changes: 4 additions & 4 deletions components/Layouts/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@ const AppLayout = ({ children, username }: Props) => {
<li>{`Ways to pay ${username ?? "user"} `}</li>
<li onClick={closeSideBar}>
<Link href={cashRegisterLink}>
<a>
<>
<Image src="/register-black&white.svg" width={"15"} height={"15"} />
Cash Register App
</a>
</>
</Link>
</li>
<li onClick={closeSideBar}>
<Link href={payCodeLink}>
<a>
<>
<Image src="/paycode-black&white.svg" width={"15"} height={"15"} />
Printable Paycode
</a>
</>
</Link>
</li>
<div className={styles.lightning_addr}>
Expand Down
17 changes: 0 additions & 17 deletions components/header.tsx

This file was deleted.

Loading