Add a created at field (#77)
Some checks failed
/ release (push) Has been cancelled
/ pullrequest (push) Has been cancelled

This commit is contained in:
Tiago Vasconcelos 2025-03-03 17:01:32 +00:00 committed by GitHub
commit 2118c8c745
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 91 additions and 22 deletions

View file

@ -1,3 +1,4 @@
from datetime import datetime, timezone
from typing import List, Optional, Union from typing import List, Optional, Union
from lnbits.db import Database from lnbits.db import Database
@ -39,10 +40,10 @@ async def get_pay_link_by_username(username: str) -> Optional[PayLink]:
async def create_pay_link(data: CreatePayLinkData) -> PayLink: async def create_pay_link(data: CreatePayLinkData) -> PayLink:
link_id = urlsafe_short_hash()[:6] link_id = urlsafe_short_hash()[:6]
assert data.wallet, "Wallet is required" assert data.wallet, "Wallet is required"
now = datetime.now(timezone.utc)
link = PayLink( link = PayLink(
id=link_id, id=link_id,
@ -63,6 +64,8 @@ async def create_pay_link(data: CreatePayLinkData) -> PayLink:
currency=data.currency, currency=data.currency,
comment_chars=data.comment_chars, comment_chars=data.comment_chars,
fiat_base_multiplier=data.fiat_base_multiplier, fiat_base_multiplier=data.fiat_base_multiplier,
created_at=now,
updated_at=now,
) )
await db.insert("lnurlp.pay_links", link) await db.insert("lnurlp.pay_links", link)
@ -96,6 +99,7 @@ async def get_pay_links(wallet_ids: Union[str, List[str]]) -> List[PayLink]:
async def update_pay_link(link: PayLink) -> PayLink: async def update_pay_link(link: PayLink) -> PayLink:
link.updated_at = datetime.now(timezone.utc)
await db.update("lnurlp.pay_links", link) await db.update("lnurlp.pay_links", link)
return link return link

View file

@ -1,3 +1,8 @@
from time import time
from lnbits.db import Connection
async def m001_initial(db): async def m001_initial(db):
""" """
Initial pay table. Initial pay table.
@ -181,3 +186,29 @@ async def m010_add_pay_link_domain(db):
Add domain to pay links Add domain to pay links
""" """
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN domain TEXT;") await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN domain TEXT;")
async def m011_add_created_at(db: Connection):
"""
Add created_at to pay links
"""
await db.execute(
f"""ALTER TABLE lnurlp.pay_links ADD COLUMN
created_at TIMESTAMP DEFAULT {db.timestamp_column_default}"""
)
await db.execute(
f"""ALTER TABLE lnurlp.pay_links ADD COLUMN
updated_at TIMESTAMP DEFAULT {db.timestamp_column_default}"""
)
now = int(time())
await db.execute(
f"""
UPDATE lnurlp.pay_links
SET created_at = {db.timestamp_placeholder('now')},
updated_at = {db.timestamp_placeholder('now')}
WHERE created_at IS NULL AND updated_at IS NULL
""",
{"now": now},
)

View file

@ -1,4 +1,5 @@
import json import json
from datetime import datetime
from typing import Optional from typing import Optional
from fastapi import Query, Request from fastapi import Query, Request
@ -59,6 +60,8 @@ class PayLink(BaseModel):
comment_chars: int comment_chars: int
max: float max: float
fiat_base_multiplier: int fiat_base_multiplier: int
created_at: datetime
updated_at: datetime
def lnurl(self, req: Request) -> str: def lnurl(self, req: Request) -> str:
url = req.url_for("lnurlp.api_lnurl_response", link_id=self.id) url = req.url_for("lnurlp.api_lnurl_response", link_id=self.id)

View file

@ -9,9 +9,9 @@ const locationPath = [
const mapPayLink = obj => { const mapPayLink = obj => {
obj._data = _.clone(obj) obj._data = _.clone(obj)
obj.date = LNbits.utils.formatDate(obj.time) obj.created_at = LNbits.utils.formatDateString(obj.created_at)
obj.updated_at = LNbits.utils.formatDateString(obj.updated_at)
obj.amount = new Intl.NumberFormat(LOCALE).format(obj.amount)
obj.print_url = [locationPath, 'print/', obj.id].join('') obj.print_url = [locationPath, 'print/', obj.id].join('')
obj.pay_url = [locationPath, 'link/', obj.id].join('') obj.pay_url = [locationPath, 'link/', obj.id].join('')
return obj return obj
@ -39,6 +39,48 @@ window.app = Vue.createApp({
fiatRates: {}, fiatRates: {},
payLinks: [], payLinks: [],
payLinksTable: { payLinksTable: {
columns: [
{
name: 'created_at',
label: 'Created',
align: 'left',
field: 'created_at',
sortable: true
},
{
name: 'description',
label: 'Description',
align: 'left',
field: 'description'
},
{
name: 'amount',
label: 'Amount',
align: 'left',
format: (_, row) => {
const min = row.min
const max = row.max
if (min === max) return `${min}`
return `${min} - ${max}`
}
},
{
name: 'currency',
label: 'Currency',
align: 'left',
field: 'currency',
format: val => val ?? 'sat'
},
{
name: 'username',
label: 'Username',
align: 'left',
field: 'username',
sortable: true,
format: val => val ?? 'None',
classes: val => (val ? 'text-normal' : 'text-grey')
}
],
pagination: { pagination: {
rowsPerPage: 10 rowsPerPage: 10
} }

View file

@ -26,16 +26,16 @@
dense dense
flat flat
:rows="payLinks" :rows="payLinks"
:columns="payLinksTable.columns"
row-key="id" row-key="id"
v-model:pagination="payLinksTable.pagination" v-model:pagination="payLinksTable.pagination"
> >
<template v-slot:header="props"> <template v-slot:header="props">
<q-tr class="text-left" :props="props"> <q-tr class="text-left" :props="props">
<q-th auto-width></q-th> <q-th auto-width></q-th>
<q-th auto-width>Description</q-th> <q-th v-for="col in props.cols" :key="col.name" :props="props">
<q-th auto-width>Amount</q-th> <span v-text="col.label"></span>
<q-th auto-width>Currency</q-th> </q-th>
<q-th auto-width>Username</q-th>
<q-th auto-width></q-th> <q-th auto-width></q-th>
<q-th auto-width></q-th> <q-th auto-width></q-th>
</q-tr> </q-tr>
@ -66,22 +66,11 @@
><q-tooltip>View Link</q-tooltip></q-btn ><q-tooltip>View Link</q-tooltip></q-btn
> >
</q-td> </q-td>
<q-td auto-width v-text="props.row.description"></q-td>
<q-td auto-width>
<span
v-if="props.row.min == props.row.max"
v-text="props.row.min"
></span>
<span
v-else
v-text="props.row.min + ' - ' + props.row.max"
></span>
</q-td>
<q-td v-text="props.row.currency || 'sat'"></q-td>
<q-td <q-td
auto-width v-for="col in props.cols"
:class="(props.row.username) ? 'text-normal' : 'text-grey'" :key="col.name"
v-text="props.row.username || 'None'" :props="props"
v-text="col.value"
></q-td> ></q-td>
<q-td> <q-td>
<q-icon v-if="props.row.webhook_url" size="14px" name="http"> <q-icon v-if="props.row.webhook_url" size="14px" name="http">