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

Commit

Permalink
chore: refactor and upgrade first path to nextjs 13
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Burtey committed Aug 21, 2023
1 parent a52330c commit 4d90b8e
Show file tree
Hide file tree
Showing 14 changed files with 570 additions and 203 deletions.
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
223 changes: 223 additions & 0 deletions app/lnurlp/[username]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import { NextResponse } from "next/server"
import { URL } from "url"

import crypto from "crypto"
import {
ApolloClient,
ApolloLink,
concat,
gql,
HttpLink,
InMemoryCache,
} from "@apollo/client"
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 = {} }) => ({
headers: {
...headers,
"x-real-ip": operation.getContext()["x-real-ip"],
"x-forwarded-for": operation.getContext()["x-forwarded-for"],
},
}))

return forward(operation)
})

const client = new ApolloClient({
link: concat(
ipForwardingMiddleware,
new HttpLink({
uri: GRAPHQL_URI_INTERNAL,
}),
),
cache: new InMemoryCache(),
})

gql`
query accountDefaultWallet($username: Username!, $walletCurrency: WalletCurrency!) {
accountDefaultWallet(username: $username, walletCurrency: $walletCurrency) {
__typename
id
walletCurrency
}
}
mutation lnInvoiceCreateOnBehalfOfRecipient(
$walletId: WalletId!
$amount: SatAmount!
$descriptionHash: Hex32Bytes!
) {
mutationData: lnInvoiceCreateOnBehalfOfRecipient(
input: {
recipientWalletId: $walletId
amount: $amount
descriptionHash: $descriptionHash
}
) {
errors {
message
}
invoice {
paymentRequest
paymentHash
}
}
}
`

const nostrEnabled = !!NOSTR_PUBKEY

let redis: Redis | null = null

if (nostrEnabled) {
const connectionObj = {
sentinelPassword: process.env.REDIS_PASSWORD,
sentinels: [
{
host: `${process.env.REDIS_0_DNS}`,
port: 26379,
},
{
host: `${process.env.REDIS_1_DNS}`,
port: 26379,
},
{
host: `${process.env.REDIS_2_DNS}`,
port: 26379,
},
],
name: process.env.REDIS_MASTER_NAME ?? "mymaster",
password: process.env.REDIS_PASSWORD,
}

redis = new Redis(connectionObj)

redis.on("error", (err) => console.log({ err }, "Redis error"))
}

export async function GET(
request: Request,
{ params }: { params: { username: string } },
) {
console.log(NOSTR_PUBKEY)

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() : ""

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 NextResponse.json({
status: "ERROR",
reason: `Couldn't find user '${username}'.`,
})
}

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

// lnurl options call
if (!amount) {
return NextResponse.json({
callback: request.url,
minSendable: 1000,
maxSendable: 100000000000,
metadata,
tag: "payRequest",
...(nostrEnabled
? {
allowsNostr: true,
nostrPubkey: NOSTR_PUBKEY,
}
: {}),
})
}

// lnurl generate invoice
try {
if (Array.isArray(amount) || Array.isArray(nostr)) {
throw new Error("Invalid request")
}

const amountSats = Math.round(parseInt(amount, 10) / 1000)
if ((amountSats * 1000).toString() !== amount) {
return NextResponse.json({
status: "ERROR",
reason: "Millisatoshi amount is not supported, please send a value in full sats.",
})
}

let descriptionHash: string

if (nostrEnabled && nostr) {
descriptionHash = crypto.createHash("sha256").update(nostr).digest("hex")
} else {
descriptionHash = crypto.createHash("sha256").update(metadata).digest("hex")
}

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 NextResponse.json({
status: "ERROR",
reason: `Failed to get invoice: ${errors ? errors[0].message : "unknown error"}`,
})
}

if (nostrEnabled && nostr && redis) {
redis.set(`nostrInvoice:${invoice.paymentHash}`, nostr, "EX", 1440)
}

return NextResponse.json({
pr: invoice.paymentRequest,
routes: [],
})
} catch (err: unknown) {
console.log("unexpected error getting invoice", err)
NextResponse.json({
status: "ERROR",
reason: err instanceof Error ? err.message : "unexpected error",
})
}
}
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
2 changes: 1 addition & 1 deletion lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ if (!GRAPHQL_HOSTNAME) {
}
}

const GRAPHQL_URI = `https://${GRAPHQL_HOSTNAME}/graphql`
const GRAPHQL_URI = `http://${GRAPHQL_HOSTNAME}/graphql`
const GRAPHQL_SUBSCRIPTION_URI = GRAPHQL_WEBSOCKET_URL

const NOSTR_PUBKEY = process.env.NOSTR_PUBKEY as string
Expand Down
Loading

0 comments on commit 4d90b8e

Please sign in to comment.