Show user invoices

This commit is contained in:
ekzyis 2023-11-26 22:07:56 +01:00
parent 9026f56359
commit 6d7cd2573d
10 changed files with 151 additions and 15 deletions

View File

@ -70,6 +70,33 @@ func (db *DB) FetchInvoices(where *FetchInvoicesWhere, invoices *[]Invoice) erro
return nil
}
func (db *DB) FetchUserInvoices(pubkey string, invoices *[]Invoice) error {
var (
rows *sql.Rows
invoice Invoice
err error
)
var (
query = "" +
"SELECT id, pubkey, msats, preimage, hash, bolt11, created_at, expires_at, confirmed_at, held_since, COALESCE(description, ''), " +
"CASE WHEN confirmed_at IS NOT NULL THEN 'PAID' WHEN expires_at < CURRENT_TIMESTAMP THEN 'EXPIRED' ELSE 'WAITING' END AS status " +
"FROM invoices " +
"WHERE pubkey = $1 " +
"ORDER BY created_at DESC"
)
if rows, err = db.Query(query, pubkey); err != nil {
return err
}
defer rows.Close()
for rows.Next() {
rows.Scan(
&invoice.Id, &invoice.Pubkey, &invoice.Msats, &invoice.Preimage, &invoice.Hash,
&invoice.PaymentRequest, &invoice.CreatedAt, &invoice.ExpiresAt, &invoice.ConfirmedAt, &invoice.HeldSince, &invoice.Description, &invoice.Status)
*invoices = append(*invoices, invoice)
}
return nil
}
func (db *DB) ConfirmInvoice(hash string, confirmedAt time.Time, msatsReceived int) error {
if _, err := db.Exec("UPDATE invoices SET confirmed_at = $2, msats_received = $3 WHERE hash = $1", hash, confirmedAt, msatsReceived); err != nil {
return err

View File

@ -47,6 +47,7 @@ type (
ConfirmedAt null.Time
HeldSince null.Time
Description string
Status string
}
Order struct {
Id UUID

View File

@ -93,3 +93,18 @@ func HandleInvoice(sc context.ServerContext) echo.HandlerFunc {
return sc.Render(c, http.StatusOK, "invoice.html", data)
}
}
func HandleInvoices(sc context.ServerContext) echo.HandlerFunc {
return func(c echo.Context) error {
var (
u db.User
invoices []db.Invoice
err error
)
u = c.Get("session").(db.User)
if err = sc.Db.FetchUserInvoices(u.Pubkey, &invoices); err != nil {
return err
}
return c.JSON(http.StatusOK, invoices)
}
}

View File

@ -57,6 +57,9 @@ func addBackendRoutes(e *echo.Echo, sc ServerContext) {
GET(e, sc, "/api/invoice/:id",
handler.HandleInvoiceStatus,
middleware.SessionGuard)
GET(e, sc, "/api/invoices",
handler.HandleInvoices,
middleware.SessionGuard)
}
func GET(e *echo.Echo, sc ServerContext, path string, scF HandlerFunc, scM ...MiddlewareFunc) *echo.Route {

View File

@ -12,6 +12,7 @@
"core-js": "^3.8.3",
"pinia": "^2.1.7",
"register-service-worker": "^1.7.2",
"s-ago": "^2.2.0",
"vite": "^4.5.0",
"vue": "^3.2.13",
"vue-router": "4"

View File

@ -0,0 +1,18 @@
<template>
<div v-if="session.pubkey" class="flex flex-row items-center">
<div>authenticated as {{ session.pubkey.slice(0, 8) }}</div>
<button class="ms-2 my-3" @click="logout">logout</button>
</div>
</template>
<script setup>
import { useSession } from '@/stores/session'
import { useRouter } from 'vue-router'
const session = useSession()
const router = useRouter()
const logout = async () => {
await session.logout()
router.push('/')
}
</script>

View File

@ -0,0 +1,56 @@
<template>
<div class="text w-auto">
<table>
<thead>
<th>sats</th>
<th>created at</th>
<th class="hidden-sm">expires at</th>
<th>status</th>
<th>details</th>
</thead>
<tbody>
<tr v-for="i in invoices " :key="i.id">
<td>{{ i.Msats / 1000 }}</td>
<td :title="i.CreatedAt">{{ ago(new Date(i.CreatedAt)) }}</td>
<td :title="i.ExpiresAt" class="hidden-sm">{{ ago(new Date(i.ExpiresAt)) }}</td>
<td>{{ i.Status }}</td>
<td>
<router-link :to="/invoice/ + i.Id">open</router-link>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup>
import { ref } from 'vue'
import ago from 's-ago'
const invoices = ref(null)
const url = '/api/invoices'
await fetch(url)
.then(r => r.json())
.then(body => {
invoices.value = body
})
.catch(console.error)
</script>
<style scoped>
table {
width: fit-content;
align-items: center;
}
th {
padding: 0 1rem;
}
@media only screen and (max-width: 600px) {
.hidden-sm {
display: none
}
}
</style>

View File

@ -10,6 +10,8 @@ import LoginView from '@/views/LoginView'
import UserView from '@/views/UserView'
import MarketView from '@/views/MarketView'
import InvoiceView from '@/views/InvoiceView'
import UserHome from '@/components/UserHome'
import UserInvoices from '@/components/UserInvoices'
const routes = [
{
@ -19,7 +21,12 @@ const routes = [
path: '/login', component: LoginView
},
{
path: '/user', component: UserView
path: '/user',
component: UserView,
children: [
{ path: '', name: 'user', component: UserHome },
{ path: 'invoices', name: 'invoices', component: UserInvoices }
]
},
{
path: '/market/:id', component: MarketView

View File

@ -9,21 +9,24 @@
\__,_|___/\___|_| </pre>
</div>
<!-- eslint-enable -->
<div v-if="session.pubkey">
<div>authenticated as {{ session.pubkey.slice(0, 8) }}</div>
<button class="my-3" @click="logout">logout</button>
</div>
<header class="flex flex-row text-center justify-center pt-1">
<nav>
<router-link to="/user">settings</router-link>
<router-link to="/user/invoices">invoices</router-link>
</nav>
</header>
<Suspense>
<router-view />
</Suspense>
</template>
<script setup>
import { useSession } from '@/stores/session'
import { useRouter } from 'vue-router'
const session = useSession()
const router = useRouter()
const logout = async () => {
await session.logout()
router.push('/')
<style scoped>
nav {
display: flex;
justify-content: center;
}
</script>
nav>a {
margin: 0 3px;
}
</style>

View File

@ -4012,6 +4012,11 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
s-ago@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/s-ago/-/s-ago-2.2.0.tgz#4143a9d0176b3100dcf649c78e8a1ec8a59b1312"
integrity sha512-t6Q/aFCCJSBf5UUkR/WH0mDHX8EGm2IBQ7nQLobVLsdxOlkryYMbOlwu2D4Cf7jPUp0v1LhfPgvIZNoi9k8lUA==
safe-array-concat@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c"