feat: add UI for basic relay operations
This commit is contained in:
parent
0db0d9c351
commit
a849dea99f
6 changed files with 152 additions and 62 deletions
53
crud.py
53
crud.py
|
|
@ -1,10 +1,59 @@
|
|||
import json
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
from . import db
|
||||
from .models import NostrEvent, NostrFilter
|
||||
from .models import NostrEvent, NostrFilter, NostrRelay
|
||||
|
||||
########################## RELAYS ####################
|
||||
|
||||
async def create_relay(user_id: str, r: NostrRelay) -> NostrRelay:
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO nostrrelay.relays (user_id, id, name, description, pubkey, contact)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(user_id, r.id, r.name, r.description, r.pubkey, r.contact,),
|
||||
)
|
||||
relay = await get_relay(user_id, r.id)
|
||||
assert relay, "Created relay cannot be retrieved"
|
||||
return relay
|
||||
|
||||
|
||||
async def get_relay(user_id: str, relay_id: str) -> Optional[NostrRelay]:
|
||||
row = await db.fetchone("""SELECT * FROM nostrrelay.relays WHERE user_id = ? AND id = ?""", (user_id, relay_id,))
|
||||
|
||||
return NostrRelay.from_row(row) if row else None
|
||||
|
||||
async def get_relays(user_id: str) -> List[NostrRelay]:
|
||||
rows = await db.fetchall("""SELECT * FROM nostrrelay.relays WHERE user_id = ?""", (user_id,))
|
||||
|
||||
return [NostrRelay.from_row(row) for row in rows]
|
||||
|
||||
|
||||
async def get_public_relay(relay_id: str) -> Optional[dict]:
|
||||
row = await db.fetchone("""SELECT * FROM nostrrelay.relays WHERE id = ?""", (relay_id,))
|
||||
|
||||
if row:
|
||||
relay = NostrRelay.parse_obj({"id": row["id"], **json.loads(row["meta"])})
|
||||
|
||||
return {
|
||||
"id": relay.id,
|
||||
"name": relay.name,
|
||||
"description":relay.description,
|
||||
"pubkey":relay.pubkey,
|
||||
"contact":relay.contact,
|
||||
"supported_nips":relay.supported_nips,
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
async def delete_relay(user_id: str, relay_id: str):
|
||||
await db.execute("""DELETE FROM nostrrelay.relays WHERE user_id = ? AND id = ?""", (user_id, relay_id,))
|
||||
|
||||
|
||||
########################## EVENTS ####################
|
||||
async def create_event(relay_id: str, e: NostrEvent):
|
||||
await db.execute(
|
||||
"""
|
||||
|
|
@ -28,7 +77,6 @@ async def create_event(relay_id: str, e: NostrEvent):
|
|||
extra = json.dumps(rest) if rest else None
|
||||
await create_event_tags(relay_id, e.id, name, value, extra)
|
||||
|
||||
|
||||
async def get_events(relay_id: str, filter: NostrFilter, include_tags = True) -> List[NostrEvent]:
|
||||
values, query = build_select_events_query(relay_id, filter)
|
||||
|
||||
|
|
@ -43,7 +91,6 @@ async def get_events(relay_id: str, filter: NostrFilter, include_tags = True) ->
|
|||
|
||||
return events
|
||||
|
||||
|
||||
async def get_event(relay_id: str, id: str) -> Optional[NostrEvent]:
|
||||
row = await db.fetchone("SELECT * FROM nostrrelay.events WHERE relay_id = ? AND id = ?", (relay_id, id,))
|
||||
if not row:
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@ async def m001_initial(db):
|
|||
"""
|
||||
Initial nostrrelays tables.
|
||||
"""
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
CREATE TABLE nostrrelay.relays (
|
||||
user_id TEXT NOT NULL,
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
pubkey TEXT,
|
||||
contact TEXT,
|
||||
supported_nips TEXT,
|
||||
software TEXT,
|
||||
version TEXT,
|
||||
active BOOLEAN DEFAULT false,
|
||||
meta TEXT NOT NULL DEFAULT '{}'
|
||||
);
|
||||
"""
|
||||
|
|
|
|||
18
models.py
18
models.py
|
|
@ -10,25 +10,19 @@ from secp256k1 import PublicKey
|
|||
|
||||
class NostrRelay(BaseModel):
|
||||
id: str
|
||||
wallet: str
|
||||
name: str
|
||||
currency: str
|
||||
tip_options: Optional[str]
|
||||
tip_wallet: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row) -> "NostrRelay":
|
||||
return cls(**dict(row))
|
||||
|
||||
|
||||
class NostrRelayInfo(BaseModel):
|
||||
name: Optional[str]
|
||||
description: Optional[str]
|
||||
pubkey: Optional[str]
|
||||
contact: Optional[str] = "https://t.me/lnbits"
|
||||
supported_nips: List[str] = ["NIP01", "NIP09", "NIP11", "NIP15", "NIP20"]
|
||||
software: Optional[str] = "LNbist"
|
||||
version: Optional[str]
|
||||
# meta: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row) -> "NostrRelay":
|
||||
return cls(**dict(row))
|
||||
|
||||
|
||||
|
||||
class NostrEventType(str, Enum):
|
||||
|
|
|
|||
|
|
@ -22,29 +22,22 @@ const relays = async () => {
|
|||
wallet: ''
|
||||
}
|
||||
},
|
||||
relayTypes: [
|
||||
{
|
||||
id: 'rating',
|
||||
label: 'Rating (rate one item from a list)'
|
||||
},
|
||||
{
|
||||
id: 'poll',
|
||||
label: 'Poll (choose one item from a list)'
|
||||
},
|
||||
{
|
||||
id: 'likes',
|
||||
label: 'Likes (like or dislike an item)'
|
||||
}
|
||||
],
|
||||
|
||||
relaysTable: {
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
align: 'left',
|
||||
label: 'ID',
|
||||
field: 'id'
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
align: 'left',
|
||||
label: '',
|
||||
field: ''
|
||||
},
|
||||
|
||||
{
|
||||
name: 'name',
|
||||
align: 'left',
|
||||
|
|
@ -58,16 +51,16 @@ const relays = async () => {
|
|||
field: 'description'
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
name: 'pubkey',
|
||||
align: 'left',
|
||||
label: 'Type',
|
||||
field: 'type'
|
||||
label: 'Public Key',
|
||||
field: 'pubkey'
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
name: 'contact',
|
||||
align: 'left',
|
||||
label: 'Amount',
|
||||
field: 'amount'
|
||||
label: 'Contact',
|
||||
field: 'contact'
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
|
|
@ -79,17 +72,14 @@ const relays = async () => {
|
|||
methods: {
|
||||
getDefaultRelayData: function () {
|
||||
return {
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
type: this.relayTypes[0],
|
||||
amount: '100',
|
||||
wallet: ''
|
||||
pubkey: '',
|
||||
contact: ''
|
||||
}
|
||||
},
|
||||
getRelayTypeLabel: function (relayType) {
|
||||
const type = this.relayTypes.find(s => (s.id = relayType))
|
||||
return type ? type.label : '?'
|
||||
},
|
||||
|
||||
openCreateRelayDialog: function () {
|
||||
this.formDialogRelay.data = this.getDefaultRelayData()
|
||||
this.formDialogRelay.show = true
|
||||
|
|
@ -98,7 +88,7 @@ const relays = async () => {
|
|||
try {
|
||||
const {data} = await LNbits.api.request(
|
||||
'GET',
|
||||
'/reviews/api/v1/survey',
|
||||
'/nostrrelay/api/v1/relay',
|
||||
this.g.user.wallets[0].inkey
|
||||
)
|
||||
this.relayLinks = data.map(c =>
|
||||
|
|
@ -116,10 +106,10 @@ const relays = async () => {
|
|||
|
||||
createRelay: async function (data) {
|
||||
try {
|
||||
data.type = data.type.id
|
||||
console.log('### createRelay', data)
|
||||
const resp = await LNbits.api.request(
|
||||
'POST',
|
||||
'/reviews/api/v1/survey',
|
||||
'/nostrrelay/api/v1/relay',
|
||||
this.g.user.wallets[0].adminkey,
|
||||
data
|
||||
)
|
||||
|
|
@ -138,7 +128,7 @@ const relays = async () => {
|
|||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'DELETE',
|
||||
'/reviews/api/v1/survey/' + relayId,
|
||||
'/nostrrelay/api/v1/relay/' + relayId,
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -69,14 +69,15 @@
|
|||
</q-td>
|
||||
|
||||
<q-td auto-width> {{props.row.name}} </q-td>
|
||||
<q-td key="description" :props="props" :class="">
|
||||
<q-td key="id" :props="props"> {{props.row.id}} </q-td>
|
||||
<q-td key="description" :props="props">
|
||||
{{props.row.description}}
|
||||
</q-td>
|
||||
<q-td key="type" :props="props" :class="">
|
||||
<div>{{getRelayTypeLabel(props.row.type)}}</div>
|
||||
<q-td key="contact" :props="props">
|
||||
<div>{{props.row.contact}}</div>
|
||||
</q-td>
|
||||
<q-td key="amount" :props="props" :class="">
|
||||
<div>{{props.row.amount}}</div>
|
||||
<q-td key="pubkey" :props="props">
|
||||
<div>{{props.row.pubkey}}</div>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
<q-tr v-if="props.row.expanded" :props="props">
|
||||
|
|
|
|||
74
views_api.py
74
views_api.py
|
|
@ -1,12 +1,23 @@
|
|||
from http import HTTPStatus
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import Query, WebSocket
|
||||
from fastapi import Depends, Query, WebSocket
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.decorators import (
|
||||
WalletTypeInfo,
|
||||
check_admin,
|
||||
require_admin_key,
|
||||
require_invoice_key,
|
||||
)
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
from . import nostrrelay_ext
|
||||
from .client_manager import NostrClientConnection, NostrClientManager
|
||||
from .models import NostrRelayInfo
|
||||
from .crud import create_relay, delete_relay, get_relay, get_relays
|
||||
from .models import NostrRelay
|
||||
|
||||
client_manager = NostrClientManager()
|
||||
|
||||
|
|
@ -29,14 +40,61 @@ async def api_nostrrelay_info():
|
|||
"Access-Control-Allow-Headers": "*",
|
||||
"Access-Control-Allow-Methods": "GET"
|
||||
}
|
||||
info = NostrRelayInfo()
|
||||
info = NostrRelay()
|
||||
return JSONResponse(content=dict(info), headers=headers)
|
||||
|
||||
|
||||
@nostrrelay_ext.get("/api/v1/enable", status_code=HTTPStatus.OK)
|
||||
async def api_nostrrelay(enable: bool = Query(True)):
|
||||
return await enable_relay(enable)
|
||||
|
||||
@nostrrelay_ext.post("/api/v1/relay")
|
||||
async def api_create_survey(data: NostrRelay, wallet: WalletTypeInfo = Depends(require_admin_key)) -> NostrRelay:
|
||||
|
||||
try:
|
||||
relay = await create_relay(wallet.wallet.user, data)
|
||||
return relay
|
||||
|
||||
except Exception as ex:
|
||||
logger.warning(ex)
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
detail="Cannot create relay",
|
||||
)
|
||||
|
||||
|
||||
async def enable_relay(enable: bool):
|
||||
return enable
|
||||
@nostrrelay_ext.get("/api/v1/relay")
|
||||
async def api_get_relays(wallet: WalletTypeInfo = Depends(require_invoice_key)) -> List[NostrRelay]:
|
||||
try:
|
||||
return await get_relays(wallet.wallet.user)
|
||||
except Exception as ex:
|
||||
logger.warning(ex)
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
detail="Cannot fetch relays",
|
||||
)
|
||||
|
||||
@nostrrelay_ext.get("/api/v1/relay/{relay_id}")
|
||||
async def api_get_relay(relay_id: str, wallet: WalletTypeInfo = Depends(require_invoice_key)) -> Optional[NostrRelay]:
|
||||
try:
|
||||
relay = await get_relay(wallet.wallet.user, relay_id)
|
||||
except Exception as ex:
|
||||
logger.warning(ex)
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
detail="Cannot fetch relay",
|
||||
)
|
||||
if not relay:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail="Cannot find relay",
|
||||
)
|
||||
return relay
|
||||
|
||||
@nostrrelay_ext.delete("/api/v1/relay/{relay_id}")
|
||||
async def api_delete_relay(relay_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)):
|
||||
try:
|
||||
await delete_relay(wallet.wallet.user, relay_id)
|
||||
except Exception as ex:
|
||||
logger.warning(ex)
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
detail="Cannot delete relay",
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue