diff --git a/client_manager.py b/client_manager.py index ccffe7a..3a2cd12 100644 --- a/client_manager.py +++ b/client_manager.py @@ -106,8 +106,6 @@ class NostrClientConnection: ] = None self.get_client_config: Optional[Callable[[], RelaySpec]] = None - - async def start(self): await self.websocket.accept() while True: @@ -177,14 +175,15 @@ class NostrClientConnection: self.authenticated = True return None - if not self.authenticated and self.client_config.event_requires_auth(e.kind): await self._send_msg(["AUTH", self._current_auth_challenge()]) - resp_nip20 += [False, f"restricted: Relay requires authentication for events of kind '{e.kind}'"] + resp_nip20 += [ + False, + f"restricted: Relay requires authentication for events of kind '{e.kind}'", + ] await self._send_msg(resp_nip20) - return None + return None - valid, message = await self._validate_write(e) if not valid: resp_nip20 += [valid, message] @@ -259,7 +258,7 @@ class NostrClientConnection: self._remove_filter(subscription_id) def _handle_auth(self): - raise ValueError('Not supported') + raise ValueError("Not supported") def _can_add_filter(self) -> bool: return ( @@ -276,7 +275,7 @@ class NostrClientConnection: challenge_tag = e.tag_values("challenge") if len(relay_tag) == 0 or len(challenge_tag) == 0: return False, "error: NIP42 tags are missing for auth event" - + if self.client_config.domain != extract_domain(relay_tag[0]): return False, "error: wrong relay domain for auth event" @@ -317,10 +316,12 @@ class NostrClientConnection: return True, "" - async def _validate_storage(self, pubkey: str, event_size_bytes: int) -> Tuple[bool, str]: + async def _validate_storage( + self, pubkey: str, event_size_bytes: int + ) -> Tuple[bool, str]: if self.client_config.is_read_only_relay: return False, "Cannot write event, relay is read-only" - + account = await get_account(self.relay_id, pubkey) if not account: account = NostrAccount.null_account() @@ -329,7 +330,9 @@ class NostrClientConnection: return False, f"This is a paid relay: '{self.relay_id}'" stored_bytes = await get_storage_for_public_key(self.relay_id, pubkey) - total_available_storage = account.storage + self.client_config.free_storage_bytes_value + total_available_storage = ( + account.storage + self.client_config.free_storage_bytes_value + ) if (stored_bytes + event_size_bytes) <= total_available_storage: return True, "" @@ -375,11 +378,13 @@ class NostrClientConnection: if self._auth_challenge_created_at == 0: return True current_time_seconds = round(time.time()) - chanllenge_max_age_seconds = 300 # 5 min - return (current_time_seconds - self._auth_challenge_created_at) >= chanllenge_max_age_seconds + chanllenge_max_age_seconds = 300 # 5 min + return ( + current_time_seconds - self._auth_challenge_created_at + ) >= chanllenge_max_age_seconds def _current_auth_challenge(self): if self._auth_challenge_expired(): self._auth_challenge = self.relay_id + ":" + urlsafe_short_hash() self._auth_challenge_created_at = round(time.time()) - return self._auth_challenge \ No newline at end of file + return self._auth_challenge diff --git a/crud.py b/crud.py index 83cb7fb..5b986c4 100644 --- a/crud.py +++ b/crud.py @@ -13,6 +13,7 @@ from .models import ( ########################## RELAYS #################### + async def create_relay(user_id: str, r: NostrRelay) -> NostrRelay: await db.execute( """ @@ -326,9 +327,9 @@ def build_select_events_query(relay_id: str, filter: NostrFilter): return query, values - ########################## ACCOUNTS #################### + async def create_account(relay_id: str, a: NostrAccount) -> NostrAccount: await db.execute( """ @@ -357,25 +358,19 @@ async def update_account(relay_id: str, a: NostrAccount) -> NostrAccount: SET (sats, storage, paid_to_join, allowed, blocked) = (?, ?, ?, ?, ?) WHERE relay_id = ? AND pubkey = ? """, - ( - a.sats, - a.storage, - a.paid_to_join, - a.allowed, - a.blocked, - relay_id, - a.pubkey - ), + (a.sats, a.storage, a.paid_to_join, a.allowed, a.blocked, relay_id, a.pubkey), ) return a -async def get_account(relay_id: str, pubkey: str,) -> Optional[NostrAccount]: +async def get_account( + relay_id: str, + pubkey: str, +) -> Optional[NostrAccount]: row = await db.fetchone( "SELECT * FROM nostrrelay.accounts WHERE relay_id = ? AND pubkey = ?", (relay_id, pubkey), ) return NostrAccount.from_row(row) if row else None - diff --git a/helpers.py b/helpers.py index 8e0b15d..5f2b065 100644 --- a/helpers.py +++ b/helpers.py @@ -20,5 +20,6 @@ def normalize_public_key(pubkey: str) -> str: int(pubkey, 16) return pubkey + def extract_domain(url: str) -> str: - return urlparse(url).netloc \ No newline at end of file + return urlparse(url).netloc diff --git a/models.py b/models.py index da2c673..c00cd15 100644 --- a/models.py +++ b/models.py @@ -73,6 +73,7 @@ class AuthSpec(BaseModel): return False return kind not in self.skiped_auth_events + class PaymentSpec(BaseModel): is_paid_relay = Field(False, alias="isPaidRelay") cost_to_join = Field(0, alias="costToJoin") @@ -93,23 +94,23 @@ class AuthorSpec(Spec): # todo: check payment return p in self.allowed_public_keys + class WalletSpec(Spec): wallet = Field("") class RelayPublicSpec(FilterSpec, EventSpec, StorageSpec, PaymentSpec): - domain: str = '' + domain: str = "" @property def is_read_only_relay(self): self.free_storage_value == 0 and not self.is_paid_relay + class RelaySpec(RelayPublicSpec, AuthorSpec, WalletSpec, AuthSpec): pass - - class NostrRelay(BaseModel): id: str name: str @@ -183,7 +184,7 @@ class NostrEvent(BaseModel): @property def is_auth_response_event(self) -> bool: return self.kind == 22242 - + @property def is_delete_event(self) -> bool: return self.kind == 5 @@ -340,7 +341,8 @@ class BuyOrder(BaseModel): units_to_buy = 0 def is_valid_action(self): - return self.action in ['join', 'storage'] + return self.action in ["join", "storage"] + class NostrAccount(BaseModel): pubkey: str @@ -356,4 +358,4 @@ class NostrAccount(BaseModel): @classmethod def from_row(cls, row: Row) -> "NostrAccount": - return cls(**dict(row)) \ No newline at end of file + return cls(**dict(row)) diff --git a/tasks.py b/tasks.py index d6dff60..88c21fc 100644 --- a/tasks.py +++ b/tasks.py @@ -35,13 +35,16 @@ async def on_invoice_paid(payment: Payment): await invoice_paid_for_storage(relay_id, pubkey, storage_to_buy) return + async def invoice_paid_to_join(relay_id: str, pubkey: str): try: account = await get_account(relay_id, pubkey) if not account: - await create_account(relay_id, NostrAccount(pubkey=pubkey, paid_to_join=True)) + await create_account( + relay_id, NostrAccount(pubkey=pubkey, paid_to_join=True) + ) return - + if account.blocked or account.paid_to_join: return @@ -56,9 +59,11 @@ async def invoice_paid_for_storage(relay_id: str, pubkey: str, storage_to_buy: i try: account = await get_account(relay_id, pubkey) if not account: - await create_account(relay_id, NostrAccount(pubkey=pubkey, storage=storage_to_buy)) + await create_account( + relay_id, NostrAccount(pubkey=pubkey, storage=storage_to_buy) + ) return - + if account.blocked: return @@ -66,4 +71,4 @@ async def invoice_paid_for_storage(relay_id: str, pubkey: str, storage_to_buy: i await update_account(relay_id, account) except Exception as ex: - logger.warning(ex) \ No newline at end of file + logger.warning(ex) diff --git a/views_api.py b/views_api.py index f44ca95..ca45aa1 100644 --- a/views_api.py +++ b/views_api.py @@ -48,7 +48,9 @@ async def websocket_endpoint(relay_id: str, websocket: WebSocket): @nostrrelay_ext.post("/api/v1/relay") async def api_create_relay( - data: NostrRelay, request: Request, wallet: WalletTypeInfo = Depends(require_admin_key) + data: NostrRelay, + request: Request, + wallet: WalletTypeInfo = Depends(require_admin_key), ) -> NostrRelay: if len(data.id): await check_admin(UUID4(wallet.wallet.user)) @@ -166,11 +168,11 @@ async def api_pay_to_join(data: BuyOrder): detail="Relay not found", ) - if data.action == 'join' and relay.is_free_to_join: + if data.action == "join" and relay.is_free_to_join: raise ValueError("Relay is free to join") storage_to_buy = 0 - if data.action == 'storage': + if data.action == "storage": if relay.config.storage_cost_value == 0: raise ValueError("Relay storage cost is zero. Cannot buy!") if data.units_to_buy == 0: @@ -188,7 +190,7 @@ async def api_pay_to_join(data: BuyOrder): "action": data.action, "relay_id": relay.id, "pubkey": pubkey, - "storage_to_buy": storage_to_buy + "storage_to_buy": storage_to_buy, }, ) print("### payment_request", payment_request)