chore: code format
This commit is contained in:
parent
0ffb158769
commit
619e0f05e2
20 changed files with 121 additions and 81 deletions
|
|
@ -2,6 +2,4 @@
|
||||||
|
|
||||||
`nostrclient` is an always-on extension that can open multiple connections to nostr relays and act as a multiplexer for other clients: You open a single websocket to `nostrclient` which then sends the data to multiple relays. The responses from these relays are then sent back to the client.
|
`nostrclient` is an always-on extension that can open multiple connections to nostr relays and act as a multiplexer for other clients: You open a single websocket to `nostrclient` which then sends the data to multiple relays. The responses from these relays are then sent back to the client.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
from starlette.staticfiles import StaticFiles
|
||||||
|
|
||||||
from lnbits.db import Database
|
from lnbits.db import Database
|
||||||
from lnbits.helpers import template_renderer
|
from lnbits.helpers import template_renderer
|
||||||
from lnbits.tasks import catch_everything_and_restart
|
from lnbits.tasks import catch_everything_and_restart
|
||||||
from starlette.staticfiles import StaticFiles
|
|
||||||
|
|
||||||
db = Database("ext_nostrclient")
|
db = Database("ext_nostrclient")
|
||||||
|
|
||||||
|
|
|
||||||
1
crud.py
1
crud.py
|
|
@ -1,6 +1,7 @@
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
import shortuuid
|
import shortuuid
|
||||||
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ from typing import Dict, List, Optional
|
||||||
|
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from fastapi.param_functions import Query
|
from fastapi.param_functions import Query
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
|
|
||||||
class Relay(BaseModel):
|
class Relay(BaseModel):
|
||||||
id: Optional[str] = None
|
id: Optional[str] = None
|
||||||
|
|
|
||||||
|
|
@ -23,21 +23,25 @@
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class Encoding(Enum):
|
class Encoding(Enum):
|
||||||
"""Enumeration type to list the various supported encodings."""
|
"""Enumeration type to list the various supported encodings."""
|
||||||
|
|
||||||
BECH32 = 1
|
BECH32 = 1
|
||||||
BECH32M = 2
|
BECH32M = 2
|
||||||
|
|
||||||
|
|
||||||
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||||
BECH32M_CONST = 0x2bc830a3
|
BECH32M_CONST = 0x2BC830A3
|
||||||
|
|
||||||
|
|
||||||
def bech32_polymod(values):
|
def bech32_polymod(values):
|
||||||
"""Internal function that computes the Bech32 checksum."""
|
"""Internal function that computes the Bech32 checksum."""
|
||||||
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
|
generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3]
|
||||||
chk = 1
|
chk = 1
|
||||||
for value in values:
|
for value in values:
|
||||||
top = chk >> 25
|
top = chk >> 25
|
||||||
chk = (chk & 0x1ffffff) << 5 ^ value
|
chk = (chk & 0x1FFFFFF) << 5 ^ value
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
chk ^= generator[i] if ((top >> i) & 1) else 0
|
chk ^= generator[i] if ((top >> i) & 1) else 0
|
||||||
return chk
|
return chk
|
||||||
|
|
@ -57,6 +61,7 @@ def bech32_verify_checksum(hrp, data):
|
||||||
return Encoding.BECH32M
|
return Encoding.BECH32M
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def bech32_create_checksum(hrp, data, spec):
|
def bech32_create_checksum(hrp, data, spec):
|
||||||
"""Compute the checksum values given HRP and data."""
|
"""Compute the checksum values given HRP and data."""
|
||||||
values = bech32_hrp_expand(hrp) + data
|
values = bech32_hrp_expand(hrp) + data
|
||||||
|
|
@ -68,26 +73,29 @@ def bech32_create_checksum(hrp, data, spec):
|
||||||
def bech32_encode(hrp, data, spec):
|
def bech32_encode(hrp, data, spec):
|
||||||
"""Compute a Bech32 string given HRP and data values."""
|
"""Compute a Bech32 string given HRP and data values."""
|
||||||
combined = data + bech32_create_checksum(hrp, data, spec)
|
combined = data + bech32_create_checksum(hrp, data, spec)
|
||||||
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
return hrp + "1" + "".join([CHARSET[d] for d in combined])
|
||||||
|
|
||||||
|
|
||||||
def bech32_decode(bech):
|
def bech32_decode(bech):
|
||||||
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
|
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
|
||||||
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
|
if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or (
|
||||||
(bech.lower() != bech and bech.upper() != bech)):
|
bech.lower() != bech and bech.upper() != bech
|
||||||
|
):
|
||||||
return (None, None, None)
|
return (None, None, None)
|
||||||
bech = bech.lower()
|
bech = bech.lower()
|
||||||
pos = bech.rfind('1')
|
pos = bech.rfind("1")
|
||||||
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
|
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
|
||||||
return (None, None, None)
|
return (None, None, None)
|
||||||
if not all(x in CHARSET for x in bech[pos+1:]):
|
if not all(x in CHARSET for x in bech[pos + 1 :]):
|
||||||
return (None, None, None)
|
return (None, None, None)
|
||||||
hrp = bech[:pos]
|
hrp = bech[:pos]
|
||||||
data = [CHARSET.find(x) for x in bech[pos+1:]]
|
data = [CHARSET.find(x) for x in bech[pos + 1 :]]
|
||||||
spec = bech32_verify_checksum(hrp, data)
|
spec = bech32_verify_checksum(hrp, data)
|
||||||
if spec is None:
|
if spec is None:
|
||||||
return (None, None, None)
|
return (None, None, None)
|
||||||
return (hrp, data[:-6], spec)
|
return (hrp, data[:-6], spec)
|
||||||
|
|
||||||
|
|
||||||
def convertbits(data, frombits, tobits, pad=True):
|
def convertbits(data, frombits, tobits, pad=True):
|
||||||
"""General power-of-2 base conversion."""
|
"""General power-of-2 base conversion."""
|
||||||
acc = 0
|
acc = 0
|
||||||
|
|
@ -123,7 +131,12 @@ def decode(hrp, addr):
|
||||||
return (None, None)
|
return (None, None)
|
||||||
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
if data[0] == 0 and spec != Encoding.BECH32 or data[0] != 0 and spec != Encoding.BECH32M:
|
if (
|
||||||
|
data[0] == 0
|
||||||
|
and spec != Encoding.BECH32
|
||||||
|
or data[0] != 0
|
||||||
|
and spec != Encoding.BECH32M
|
||||||
|
):
|
||||||
return (None, None)
|
return (None, None)
|
||||||
return (data[0], decoded)
|
return (data[0], decoded)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from Cryptodome import Random
|
from Cryptodome import Random
|
||||||
from Cryptodome.Cipher import AES
|
from Cryptodome.Cipher import AES
|
||||||
|
|
||||||
|
|
@ -11,10 +10,10 @@ key = bytes.fromhex("3aa925cb69eb613e2928f8a18279c78b1dca04541dfd064df2eda66b598
|
||||||
|
|
||||||
BLOCK_SIZE = 16
|
BLOCK_SIZE = 16
|
||||||
|
|
||||||
class AESCipher(object):
|
|
||||||
"""This class is compatible with crypto.createCipheriv('aes-256-cbc')
|
|
||||||
|
|
||||||
"""
|
class AESCipher(object):
|
||||||
|
"""This class is compatible with crypto.createCipheriv('aes-256-cbc')"""
|
||||||
|
|
||||||
def __init__(self, key=None):
|
def __init__(self, key=None):
|
||||||
self.key = key
|
self.key = key
|
||||||
|
|
||||||
|
|
@ -33,9 +32,10 @@ class AESCipher(object):
|
||||||
def decrypt(self, iv, enc_text):
|
def decrypt(self, iv, enc_text):
|
||||||
cipher = AES.new(self.key, AES.MODE_CBC, iv=iv)
|
cipher = AES.new(self.key, AES.MODE_CBC, iv=iv)
|
||||||
return self.unpad(cipher.decrypt(enc_text).decode("UTF-8"))
|
return self.unpad(cipher.decrypt(enc_text).decode("UTF-8"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
aes = AESCipher(key=key)
|
aes = AESCipher(key=key)
|
||||||
iv, enc_text = aes.encrypt(plain_text)
|
iv, enc_text = aes.encrypt(plain_text)
|
||||||
dec_text = aes.decrypt(iv, enc_text)
|
dec_text = aes.decrypt(iv, enc_text)
|
||||||
print(dec_text)
|
print(dec_text)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,15 @@
|
||||||
from typing import *
|
import base64
|
||||||
import ssl
|
|
||||||
import time
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import base64
|
import ssl
|
||||||
|
import time
|
||||||
from ..event import Event
|
from typing import *
|
||||||
from ..relay_manager import RelayManager
|
|
||||||
from ..message_type import ClientMessageType
|
|
||||||
from ..key import PrivateKey, PublicKey
|
|
||||||
|
|
||||||
|
from ..event import EncryptedDirectMessage, Event, EventKind
|
||||||
from ..filter import Filter, Filters
|
from ..filter import Filter, Filters
|
||||||
from ..event import Event, EventKind, EncryptedDirectMessage
|
from ..key import PrivateKey, PublicKey
|
||||||
from ..relay_manager import RelayManager
|
|
||||||
from ..message_type import ClientMessageType
|
from ..message_type import ClientMessageType
|
||||||
|
from ..relay_manager import RelayManager
|
||||||
|
|
||||||
# from aes import AESCipher
|
# from aes import AESCipher
|
||||||
from . import cbc
|
from . import cbc
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,23 @@ class Delegation:
|
||||||
delegator_pubkey: str
|
delegator_pubkey: str
|
||||||
delegatee_pubkey: str
|
delegatee_pubkey: str
|
||||||
event_kind: int
|
event_kind: int
|
||||||
duration_secs: int = 30*24*60 # default to 30 days
|
duration_secs: int = 30 * 24 * 60 # default to 30 days
|
||||||
signature: str = None # set in PrivateKey.sign_delegation
|
signature: str = None # set in PrivateKey.sign_delegation
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def expires(self) -> int:
|
def expires(self) -> int:
|
||||||
return int(time.time()) + self.duration_secs
|
return int(time.time()) + self.duration_secs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def conditions(self) -> str:
|
def conditions(self) -> str:
|
||||||
return f"kind={self.event_kind}&created_at<{self.expires}"
|
return f"kind={self.event_kind}&created_at<{self.expires}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def delegation_token(self) -> str:
|
def delegation_token(self) -> str:
|
||||||
return f"nostr:delegation:{self.delegatee_pubkey}:{self.conditions}"
|
return f"nostr:delegation:{self.delegatee_pubkey}:{self.conditions}"
|
||||||
|
|
||||||
def get_tag(self) -> list[str]:
|
def get_tag(self) -> list[str]:
|
||||||
""" Called by Event """
|
"""Called by Event"""
|
||||||
return [
|
return [
|
||||||
"delegation",
|
"delegation",
|
||||||
self.delegator_pubkey,
|
self.delegator_pubkey,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import time
|
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from typing import List
|
|
||||||
from secp256k1 import PublicKey
|
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from secp256k1 import PublicKey
|
||||||
|
|
||||||
from .message_type import ClientMessageType
|
from .message_type import ClientMessageType
|
||||||
|
|
||||||
|
|
|
||||||
13
nostr/key.py
13
nostr/key.py
|
|
@ -1,14 +1,15 @@
|
||||||
import secrets
|
|
||||||
import base64
|
import base64
|
||||||
import secp256k1
|
import secrets
|
||||||
from cffi import FFI
|
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
||||||
from cryptography.hazmat.primitives import padding
|
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
|
import secp256k1
|
||||||
|
from cffi import FFI
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
|
from . import bech32
|
||||||
from .delegation import Delegation
|
from .delegation import Delegation
|
||||||
from .event import EncryptedDirectMessage, Event, EventKind
|
from .event import EncryptedDirectMessage, Event, EventKind
|
||||||
from . import bech32
|
|
||||||
|
|
||||||
|
|
||||||
class PublicKey:
|
class PublicKey:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import json
|
import json
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from .message_type import RelayMessageType
|
|
||||||
from .event import Event
|
from .event import Event
|
||||||
|
from .message_type import RelayMessageType
|
||||||
|
|
||||||
|
|
||||||
class EventMessage:
|
class EventMessage:
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ class ClientMessageType:
|
||||||
REQUEST = "REQ"
|
REQUEST = "REQ"
|
||||||
CLOSE = "CLOSE"
|
CLOSE = "CLOSE"
|
||||||
|
|
||||||
|
|
||||||
class RelayMessageType:
|
class RelayMessageType:
|
||||||
EVENT = "EVENT"
|
EVENT = "EVENT"
|
||||||
NOTICE = "NOTICE"
|
NOTICE = "NOTICE"
|
||||||
|
|
@ -10,6 +11,10 @@ class RelayMessageType:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_valid(type: str) -> bool:
|
def is_valid(type: str) -> bool:
|
||||||
if type == RelayMessageType.EVENT or type == RelayMessageType.NOTICE or type == RelayMessageType.END_OF_STORED_EVENTS:
|
if (
|
||||||
|
type == RelayMessageType.EVENT
|
||||||
|
or type == RelayMessageType.NOTICE
|
||||||
|
or type == RelayMessageType.END_OF_STORED_EVENTS
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
11
nostr/pow.py
11
nostr/pow.py
|
|
@ -1,7 +1,9 @@
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .event import Event
|
from .event import Event
|
||||||
from .key import PrivateKey
|
from .key import PrivateKey
|
||||||
|
|
||||||
|
|
||||||
def zero_bits(b: int) -> int:
|
def zero_bits(b: int) -> int:
|
||||||
n = 0
|
n = 0
|
||||||
|
|
||||||
|
|
@ -14,10 +16,11 @@ def zero_bits(b: int) -> int:
|
||||||
|
|
||||||
return 7 - n
|
return 7 - n
|
||||||
|
|
||||||
|
|
||||||
def count_leading_zero_bits(hex_str: str) -> int:
|
def count_leading_zero_bits(hex_str: str) -> int:
|
||||||
total = 0
|
total = 0
|
||||||
for i in range(0, len(hex_str) - 2, 2):
|
for i in range(0, len(hex_str) - 2, 2):
|
||||||
bits = zero_bits(int(hex_str[i:i+2], 16))
|
bits = zero_bits(int(hex_str[i : i + 2], 16))
|
||||||
total += bits
|
total += bits
|
||||||
|
|
||||||
if bits != 8:
|
if bits != 8:
|
||||||
|
|
@ -25,7 +28,10 @@ def count_leading_zero_bits(hex_str: str) -> int:
|
||||||
|
|
||||||
return total
|
return total
|
||||||
|
|
||||||
def mine_event(content: str, difficulty: int, public_key: str, kind: int, tags: list=[]) -> Event:
|
|
||||||
|
def mine_event(
|
||||||
|
content: str, difficulty: int, public_key: str, kind: int, tags: list = []
|
||||||
|
) -> Event:
|
||||||
all_tags = [["nonce", "1", str(difficulty)]]
|
all_tags = [["nonce", "1", str(difficulty)]]
|
||||||
all_tags.extend(tags)
|
all_tags.extend(tags)
|
||||||
|
|
||||||
|
|
@ -43,6 +49,7 @@ def mine_event(content: str, difficulty: int, public_key: str, kind: int, tags:
|
||||||
|
|
||||||
return Event(public_key, content, created_at, kind, all_tags, event_id)
|
return Event(public_key, content, created_at, kind, all_tags, event_id)
|
||||||
|
|
||||||
|
|
||||||
def mine_key(difficulty: int) -> PrivateKey:
|
def mine_key(difficulty: int) -> PrivateKey:
|
||||||
sk = PrivateKey()
|
sk = PrivateKey()
|
||||||
num_leading_zero_bits = count_leading_zero_bits(sk.public_key.hex())
|
num_leading_zero_bits = count_leading_zero_bits(sk.public_key.hex())
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ import json
|
||||||
import time
|
import time
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from websocket import WebSocketApp
|
from websocket import WebSocketApp
|
||||||
|
|
||||||
from .event import Event
|
from .event import Event
|
||||||
from .filter import Filters
|
from .filter import Filters
|
||||||
from .message_pool import MessagePool
|
from .message_pool import MessagePool
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
from .filter import Filters
|
from .filter import Filters
|
||||||
|
|
||||||
|
|
||||||
class Subscription:
|
class Subscription:
|
||||||
def __init__(self, id: str, filters: Filters=None) -> None:
|
def __init__(self, id: str, filters: Filters = None) -> None:
|
||||||
self.id = id
|
self.id = id
|
||||||
self.filters = filters
|
self.filters = filters
|
||||||
|
|
||||||
def to_json_object(self):
|
def to_json_object(self):
|
||||||
return {
|
return {"id": self.id, "filters": self.filters.to_json_array()}
|
||||||
"id": self.id,
|
|
||||||
"filters": self.filters.to_json_array()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@ import json
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
|
||||||
from fastapi import WebSocket, WebSocketDisconnect
|
from fastapi import WebSocket, WebSocketDisconnect
|
||||||
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
from .nostr.client.client import NostrClient as NostrClientLib
|
|
||||||
|
|
||||||
from .models import Event, Filter, Filters, Relay, RelayList
|
from .models import Event, Filter, Filters, Relay, RelayList
|
||||||
|
from .nostr.client.client import NostrClient as NostrClientLib
|
||||||
from .nostr.event import Event as NostrEvent
|
from .nostr.event import Event as NostrEvent
|
||||||
from .nostr.filter import Filter as NostrFilter
|
from .nostr.filter import Filter as NostrFilter
|
||||||
from .nostr.filter import Filters as NostrFilters
|
from .nostr.filter import Filters as NostrFilters
|
||||||
from .nostr.message_pool import EndOfStoredEventsMessage, EventMessage, NoticeMessage
|
from .nostr.message_pool import EndOfStoredEventsMessage, EventMessage, NoticeMessage
|
||||||
|
|
||||||
|
|
||||||
received_subscription_events: dict[str, list[Event]] = {}
|
received_subscription_events: dict[str, list[Event]] = {}
|
||||||
received_subscription_notices: dict[str, list[NoticeMessage]] = {}
|
received_subscription_notices: dict[str, list[NoticeMessage]] = {}
|
||||||
received_subscription_eosenotices: dict[str, EndOfStoredEventsMessage] = {}
|
received_subscription_eosenotices: dict[str, EndOfStoredEventsMessage] = {}
|
||||||
|
|
|
||||||
6
tasks.py
6
tasks.py
|
|
@ -2,20 +2,18 @@ import asyncio
|
||||||
import ssl
|
import ssl
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from .crud import get_relays
|
||||||
from .nostr.event import Event
|
from .nostr.event import Event
|
||||||
from .nostr.key import PublicKey
|
from .nostr.key import PublicKey
|
||||||
from .nostr.message_pool import EndOfStoredEventsMessage, EventMessage, NoticeMessage
|
from .nostr.message_pool import EndOfStoredEventsMessage, EventMessage, NoticeMessage
|
||||||
from .nostr.relay_manager import RelayManager
|
from .nostr.relay_manager import RelayManager
|
||||||
from .services import (
|
from .services import (
|
||||||
nostr,
|
nostr,
|
||||||
received_subscription_events,
|
|
||||||
received_subscription_eosenotices,
|
received_subscription_eosenotices,
|
||||||
|
received_subscription_events,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
from .crud import get_relays
|
|
||||||
|
|
||||||
|
|
||||||
async def init_relays():
|
async def init_relays():
|
||||||
# reinitialize the entire client
|
# reinitialize the entire client
|
||||||
nostr.__init__()
|
nostr.__init__()
|
||||||
|
|
|
||||||
|
|
@ -75,12 +75,17 @@
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
</q-table>
|
</q-table>
|
||||||
|
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-weight-bold"> Your endpoint:
|
<div class="text-weight-bold">
|
||||||
<q-badge outline class="q-ml-sm text-subtitle2" color="primary" :label="`wss://${host}/nostrclient/api/v1/relay`" />
|
Your endpoint:
|
||||||
</div>
|
<q-badge
|
||||||
|
outline
|
||||||
|
class="q-ml-sm text-subtitle2"
|
||||||
|
color="primary"
|
||||||
|
:label="`wss://${host}/nostrclient/api/v1/relay`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
<q-card>
|
<q-card>
|
||||||
|
|
@ -88,7 +93,13 @@
|
||||||
<q-form class="q-gutter-md q-y-md" @submit="addRelay">
|
<q-form class="q-gutter-md q-y-md" @submit="addRelay">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col q-mx-md q-my-sm">
|
<div class="col q-mx-md q-my-sm">
|
||||||
<q-input outlined v-model="relayToAdd" dense filled label="Relay URL"></q-input>
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="relayToAdd"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
label="Relay URL"
|
||||||
|
></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col q-mx-md items-align flex items-center justify-right">
|
<div class="col q-mx-md items-align flex items-center justify-right">
|
||||||
<q-btn unelevated color="primary" type="submit">Add relay </q-btn>
|
<q-btn unelevated color="primary" type="submit">Add relay </q-btn>
|
||||||
|
|
@ -104,17 +115,21 @@
|
||||||
<h6 class="text-subtitle1 q-my-none">Nostrclient Extension</h6>
|
<h6 class="text-subtitle1 q-my-none">Nostrclient Extension</h6>
|
||||||
<p>
|
<p>
|
||||||
This extension is a always-on nostr client that other extensions can
|
This extension is a always-on nostr client that other extensions can
|
||||||
use to send and receive events on nostr.
|
use to send and receive events on nostr. Add multiple nostr relays to
|
||||||
|
connect to. The extension then opens a websocket for you to use at
|
||||||
Add multiple nostr relays to connect to. The extension then opens a websocket for you to use
|
|
||||||
at
|
|
||||||
<p>
|
|
||||||
<!-- wss://{{host}}nostrclient/api/v1/relay -->
|
|
||||||
<q-badge outline class="q-ml-sm text-subtitle2" color="primary" :label="`wss://${host}/nostrclient/api/v1/relay`" />
|
|
||||||
</p>
|
|
||||||
Only Admin users can manage
|
|
||||||
this extension.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<!-- wss://{{host}}nostrclient/api/v1/relay -->
|
||||||
|
<q-badge
|
||||||
|
outline
|
||||||
|
class="q-ml-sm text-subtitle2"
|
||||||
|
color="primary"
|
||||||
|
:label="`wss://${host}/nostrclient/api/v1/relay`"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
Only Admin users can manage this extension.
|
||||||
|
|
||||||
<q-card-section></q-card-section>
|
<q-card-section></q-card-section>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
@ -217,7 +232,7 @@
|
||||||
message: `Invalid relay URL.`,
|
message: `Invalid relay URL.`,
|
||||||
caption: "Should start with 'wss://'' or 'ws://'"
|
caption: "Should start with 'wss://'' or 'ws://'"
|
||||||
})
|
})
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
console.log('ADD RELAY ' + this.relayToAdd)
|
console.log('ADD RELAY ' + this.relayToAdd)
|
||||||
let that = this
|
let that = this
|
||||||
|
|
@ -229,7 +244,7 @@
|
||||||
{url: this.relayToAdd}
|
{url: this.relayToAdd}
|
||||||
)
|
)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
console.log("response:", response)
|
console.log('response:', response)
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
response.data.map(maplrelays)
|
response.data.map(maplrelays)
|
||||||
that.nostrrelayLinks = response.data
|
that.nostrrelayLinks = response.data
|
||||||
|
|
@ -239,7 +254,7 @@
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
})
|
})
|
||||||
return false;
|
return false
|
||||||
},
|
},
|
||||||
deleteRelay(url) {
|
deleteRelay(url) {
|
||||||
console.log('DELETE RELAY ' + url)
|
console.log('DELETE RELAY ' + url)
|
||||||
|
|
|
||||||
3
views.py
3
views.py
|
|
@ -6,11 +6,12 @@ from fastapi import Request
|
||||||
from fastapi.param_functions import Query
|
from fastapi.param_functions import Query
|
||||||
from fastapi.params import Depends
|
from fastapi.params import Depends
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from starlette.responses import HTMLResponse
|
||||||
|
|
||||||
from lnbits.core.crud import update_payment_status
|
from lnbits.core.crud import update_payment_status
|
||||||
from lnbits.core.models import User
|
from lnbits.core.models import User
|
||||||
from lnbits.core.views.api import api_payment
|
from lnbits.core.views.api import api_payment
|
||||||
from lnbits.decorators import check_admin, check_user_exists
|
from lnbits.decorators import check_admin, check_user_exists
|
||||||
from starlette.responses import HTMLResponse
|
|
||||||
|
|
||||||
from . import nostr_renderer, nostrclient_ext
|
from . import nostr_renderer, nostrclient_ext
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ from http import HTTPStatus
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import Depends, WebSocket
|
from fastapi import Depends, WebSocket
|
||||||
from lnbits.decorators import check_admin
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
|
from lnbits.decorators import check_admin
|
||||||
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
from . import nostrclient_ext
|
from . import nostrclient_ext
|
||||||
from .crud import add_relay, delete_relay, get_relays
|
from .crud import add_relay, delete_relay, get_relays
|
||||||
from .models import Relay, RelayList
|
from .models import Relay, RelayList
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue