nostrclient/views_api.py
Vlad Stan 16ae9d15a1
Stabilize (#24)
* refactor: clean-up

* refactor: extra logs plus try-catch

* refactor: do not use bare `except`

* refactor: clean-up redundant fields

* chore: pass code checks

* chore: code format

* refactor: code clean-up

* fix: refactoring stuff

* refactor: remove un-used file

* chore: code clean-up

* chore: code clean-up

* chore: code-format fix

* refactor: remove nostr.client wrapper

* refactor: code clean-up

* chore: code format

* refactor: remove `RelayList` class

* refactor: extract smaller methods with try-catch

* fix: better exception handling

* fix: remove redundant filters

* fix: simplify event

* chore: code format

* fix: code check

* fix: code check

* fix: simplify `REQ`

* fix: more clean-ups

* refactor: use simpler method

* refactor: re-order and rename

* fix: stop logic

* fix: subscription close before disconnect

* chore: play commit
2023-11-01 17:46:42 +02:00

151 lines
4.5 KiB
Python

import asyncio
from http import HTTPStatus
from typing import List
from fastapi import Depends, WebSocket
from loguru import logger
from starlette.exceptions import HTTPException
from lnbits.decorators import check_admin
from lnbits.helpers import urlsafe_short_hash
from . import nostr_client, nostrclient_ext, scheduled_tasks
from .crud import add_relay, delete_relay, get_relays
from .helpers import normalize_public_key
from .models import Relay, TestMessage, TestMessageResponse
from .nostr.key import EncryptedDirectMessage, PrivateKey
from .router import NostrRouter
# we keep this in
all_routers: list[NostrRouter] = []
@nostrclient_ext.get("/api/v1/relays")
async def api_get_relays() -> List[Relay]:
relays = []
for url, r in nostr_client.relay_manager.relays.items():
relay_id = urlsafe_short_hash()
relays.append(
Relay(
id=relay_id,
url=url,
connected=r.connected,
status={
"num_sent_events": r.num_sent_events,
"num_received_events": r.num_received_events,
"error_counter": r.error_counter,
"error_list": r.error_list,
"notice_list": r.notice_list,
},
ping=r.ping,
active=True,
)
)
return relays
@nostrclient_ext.post(
"/api/v1/relay", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
)
async def api_add_relay(relay: Relay) -> List[Relay]:
if not relay.url:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Relay url not provided."
)
if relay.url in nostr_client.relay_manager.relays:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"Relay: {relay.url} already exists.",
)
relay.id = urlsafe_short_hash()
await add_relay(relay)
nostr_client.relay_manager.add_relay(relay.url)
return await get_relays()
@nostrclient_ext.delete(
"/api/v1/relay", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
)
async def api_delete_relay(relay: Relay) -> None:
if not relay.url:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST, detail="Relay url not provided."
)
# we can remove relays during runtime
nostr_client.relay_manager.remove_relay(relay.url)
await delete_relay(relay)
@nostrclient_ext.put(
"/api/v1/relay/test", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
)
async def api_test_endpoint(data: TestMessage) -> TestMessageResponse:
try:
to_public_key = normalize_public_key(data.reciever_public_key)
pk = bytes.fromhex(data.sender_private_key) if data.sender_private_key else None
private_key = PrivateKey(pk) if pk else PrivateKey()
dm = EncryptedDirectMessage(
recipient_pubkey=to_public_key, cleartext_content=data.message
)
private_key.sign_event(dm)
return TestMessageResponse(
private_key=private_key.hex(),
public_key=to_public_key,
event_json=dm.to_message(),
)
except (ValueError, AssertionError) as ex:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=str(ex),
)
except Exception as ex:
logger.warning(ex)
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Cannot generate test event",
)
@nostrclient_ext.delete(
"/api/v1", status_code=HTTPStatus.OK, dependencies=[Depends(check_admin)]
)
async def api_stop():
for router in all_routers:
try:
await router.stop()
all_routers.remove(router)
except Exception as e:
logger.error(e)
nostr_client.close()
for scheduled_task in scheduled_tasks:
try:
scheduled_task.cancel()
except Exception as ex:
logger.warning(ex)
return {"success": True}
@nostrclient_ext.websocket("/api/v1/relay")
async def ws_relay(websocket: WebSocket) -> None:
"""Relay multiplexer: one client (per endpoint) <-> multiple relays"""
await websocket.accept()
router = NostrRouter(websocket)
router.start()
all_routers.append(router)
# we kill this websocket and the subscriptions
# if the user disconnects and thus `connected==False`
while router.connected:
await asyncio.sleep(10)
await router.stop()
all_routers.remove(router)