Show user invoices
This commit is contained in:
parent
9026f56359
commit
6d7cd2573d
|
@ -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
|
||||
|
|
|
@ -47,6 +47,7 @@ type (
|
|||
ConfirmedAt null.Time
|
||||
HeldSince null.Time
|
||||
Description string
|
||||
Status string
|
||||
}
|
||||
Order struct {
|
||||
Id UUID
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue