feat: mage block/allow accounts

This commit is contained in:
Vlad Stan 2023-02-16 14:17:21 +02:00
parent 2c5dfbbf92
commit 5a984bddcd
5 changed files with 188 additions and 58 deletions

View file

@ -299,12 +299,6 @@ class NostrClientConnection:
if self._exceeded_max_events_per_second(): if self._exceeded_max_events_per_second():
return False, f"Exceeded max events per second limit'!" return False, f"Exceeded max events per second limit'!"
if not self.client_config.is_author_allowed(e.pubkey):
return (
False,
f"Public key '{e.pubkey}' is not allowed in relay '{self.relay_id}'!",
)
try: try:
e.check_signature() e.check_signature()
except ValueError: except ValueError:
@ -326,7 +320,13 @@ class NostrClientConnection:
if not account: if not account:
account = NostrAccount.null_account() account = NostrAccount.null_account()
if not account.paid_to_join and self.client_config.is_paid_relay: if account.blocked:
return (
False,
f"Public key '{pubkey}' is not allowed in relay '{self.relay_id}'!",
)
if not account.can_join and self.client_config.is_paid_relay:
return False, f"This is a paid relay: '{self.relay_id}'" return False, f"This is a paid relay: '{self.relay_id}'"
stored_bytes = await get_storage_for_public_key(self.relay_id, pubkey) stored_bytes = await get_storage_for_public_key(self.relay_id, pubkey)

View file

@ -83,18 +83,6 @@ class PaymentSpec(BaseModel):
storage_cost_unit = Field("MB", alias="storageCostUnit") storage_cost_unit = Field("MB", alias="storageCostUnit")
class AuthorSpec(Spec):
allowed_public_keys = Field([], alias="allowedPublicKeys")
blocked_public_keys = Field([], alias="blockedPublicKeys")
def is_author_allowed(self, p: str) -> bool:
if p in self.blocked_public_keys:
return False
if len(self.allowed_public_keys) == 0:
return True
# todo: check payment
return p in self.allowed_public_keys
class WalletSpec(Spec): class WalletSpec(Spec):
wallet = Field("") wallet = Field("")
@ -108,7 +96,7 @@ class RelayPublicSpec(FilterSpec, EventSpec, StorageSpec, PaymentSpec):
self.free_storage_value == 0 and not self.is_paid_relay self.free_storage_value == 0 and not self.is_paid_relay
class RelaySpec(RelayPublicSpec, AuthorSpec, WalletSpec, AuthSpec): class RelaySpec(RelayPublicSpec, WalletSpec, AuthSpec):
pass pass
@ -357,6 +345,11 @@ class NostrAccount(BaseModel):
storage = 0 storage = 0
paid_to_join = False paid_to_join = False
@property
def can_join(self):
"""If an account is explicitly allowed then it does not need to pay"""
return self.paid_to_join or self.allowed
@classmethod @classmethod
def null_account(cls) -> "NostrAccount": def null_account(cls) -> "NostrAccount":
return NostrAccount(pubkey="") return NostrAccount(pubkey="")

View file

@ -526,33 +526,101 @@
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="accounts"> <q-tab-panel name="accounts">
<div v-if="relay"> <div v-if="relay">
<q-card
><q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col-2 q-pr-lg">Public Key:</div>
<div class="col-6 q-pr-lg">
<q-input
filled
dense
v-model.trim="accountPubkey"
type="text"
></q-input>
</div>
<div class="col-2 q-pr-md">
<q-btn
unelevated
color="green"
class="float-right"
@click="allowPublicKey(accountPubkey, true)"
>Allow</q-btn
>
</div>
<div class="col-2">
<q-btn
unelevated
color="pink"
class="float-right"
@click="blockPublicKey(accountPubkey, true)"
>Block</q-btn
>
</div>
</div>
</q-card-section>
</q-card>
<q-separator></q-separator>
<div class="row items-center no-wrap q-mb-md"> <div class="row items-center no-wrap q-mb-md">
<div class="col-2 q-pr-lg">Public Key:</div> <div class="col-3 q-pr-lg">Filter:</div>
<div class="col-6 q-pr-lg"> <div class="col-9 q-pr-lg">
<q-input <q-toggle
filled size="sm"
color="secodary"
class="q-mr-lg"
v-model="showAllowedAccounts"
@input="getAccounts()"
>Show Allowed Account</q-toggle
>
<q-toggle
size="sm"
color="secodary"
class="q-mr-lg"
v-model="showBlockedAccounts"
@input="getAccounts()"
>
Show Blocked Accounts</q-toggle
>
</div>
</div>
<q-separator></q-separator>
<div class="row items-center no-wrap q-mb-md">
<div class="col-12 q-pr-lg">
<q-table
flat
dense dense
v-model.trim="accountPubkey" :data="accounts"
type="text" row-key="pubkey"
></q-input> :columns="accountsTable.columns"
</div> :pagination.sync="accountsTable.pagination"
<div class="col-2 q-pr-md"> :filter="accountsFilter"
<q-btn
unelevated
color="green"
class="float-right"
@click="allowPublicKey(true)"
>Allow</q-btn
>
</div>
<div class="col-2">
<q-btn
unelevated
color="pink"
class="float-right"
@click="blockPublicKey(true)"
>Block</q-btn
> >
<template v-slot:body="props">
<q-tr :props="props">
<q-td key="pubkey" :props="props">
{{props.row.pubkey}}
</q-td>
<q-td key="allowed" :props="props">
<q-toggle
size="sm"
color="secodary"
v-model="props.row.allowed"
@input="togglePublicKey(props.row, 'allow')"
></q-toggle>
</q-td>
<q-td key="blocked" :props="props">
<q-toggle
size="sm"
color="secodary"
v-model="props.row.blocked"
@input="togglePublicKey(props.row, 'block')"
></q-toggle>
</q-td>
<q-td auto-width> {{props.row.paid_to_join}} </q-td>
<q-td auto-width> {{props.row.sats}} </q-td>
<q-td auto-width> {{props.row.storage}} </q-td>
</q-tr>
</template>
</q-table>
</div> </div>
</div> </div>
</div> </div>

View file

@ -9,6 +9,7 @@ async function relayDetails(path) {
return { return {
tab: 'info', tab: 'info',
relay: null, relay: null,
accounts: [],
accountPubkey: '', accountPubkey: '',
formDialogItem: { formDialogItem: {
show: false, show: false,
@ -17,6 +18,52 @@ async function relayDetails(path) {
description: '' description: ''
} }
}, },
accountsFilter: '',
showBlockedAccounts: true,
showAllowedAccounts: false,
accountsTable: {
columns: [
{
name: 'pubkey',
align: 'left',
label: 'Public Key',
field: 'pubkey'
},
{
name: 'allowed',
align: 'left',
label: 'Allowed',
field: 'allowed'
},
{
name: 'blocked',
align: 'left',
label: 'Blocked',
field: 'blocked'
},
{
name: 'paid_to_join',
align: 'left',
label: 'Paid to join',
field: 'paid_to_join'
},
{
name: 'sats',
align: 'left',
label: 'Spent Sats',
field: 'sats'
},
{
name: 'storage',
align: 'left',
label: 'Storage',
field: 'storage'
}
],
pagination: {
rowsPerPage: 10
}
},
skipEventKind: 0, skipEventKind: 0,
forceEventKind: 0 forceEventKind: 0
} }
@ -113,11 +160,39 @@ async function relayDetails(path) {
this.relay.config.wallet = this.relay.config.wallet =
this.relay.config.wallet || this.walletOptions[0].value this.relay.config.wallet || this.walletOptions[0].value
}, },
allowPublicKey: async function (allowed) { getAccounts: async function () {
await this.updatePublicKey({allowed}) try {
const {data} = await LNbits.api.request(
'GET',
`/nostrrelay/api/v1/account?relay_id=${this.relay.id}&allowed=${this.showAllowedAccounts}&blocked=${this.showBlockedAccounts}`,
this.inkey
)
this.accounts = data
console.log('### this.accounts', this.accounts)
} catch (error) {
LNbits.utils.notifyApiError(error)
}
}, },
blockPublicKey: async function (blocked = true) { allowPublicKey: async function (pubkey, allowed) {
await this.updatePublicKey({blocked}) await this.updatePublicKey({pubkey, allowed})
},
blockPublicKey: async function (pubkey, blocked = true) {
await this.updatePublicKey({pubkey, blocked})
},
togglePublicKey: async function (account, action) {
if (action === 'allow') {
await this.updatePublicKey({
pubkey: account.pubkey,
allowed: account.allowed
})
}
if (action === 'block') {
await this.updatePublicKey({
pubkey: account.pubkey,
blocked: account.blocked
})
}
}, },
updatePublicKey: async function (ops) { updatePublicKey: async function (ops) {
try { try {
@ -127,7 +202,7 @@ async function relayDetails(path) {
this.adminkey, this.adminkey,
{ {
relay_id: this.relay.id, relay_id: this.relay.id,
pubkey: this.accountPubkey, pubkey: ops.pubkey,
allowed: ops.allowed, allowed: ops.allowed,
blocked: ops.blocked blocked: ops.blocked
} }
@ -138,18 +213,11 @@ async function relayDetails(path) {
timeout: 5000 timeout: 5000
}) })
this.accountPubkey = '' this.accountPubkey = ''
await this.getAccounts()
} catch (error) { } catch (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
} }
}, },
deleteAllowedPublicKey: function (pubKey) {
this.relay.config.allowedPublicKeys =
this.relay.config.allowedPublicKeys.filter(p => p !== pubKey)
},
deleteBlockedPublicKey: function (pubKey) {
this.relay.config.blockedPublicKeys =
this.relay.config.blockedPublicKeys.filter(p => p !== pubKey)
},
addSkipAuthForEvent: function () { addSkipAuthForEvent: function () {
value = +this.skipEventKind value = +this.skipEventKind
@ -179,6 +247,7 @@ async function relayDetails(path) {
created: async function () { created: async function () {
await this.getRelay() await this.getRelay()
await this.getAccounts()
} }
}) })
} }

View file

@ -182,7 +182,7 @@ async def api_create_or_update_account(
@nostrrelay_ext.get("/api/v1/account") @nostrrelay_ext.get("/api/v1/account")
async def api_get_accounts( async def api_get_accounts(
relay_id: str, allowed: bool, blocked: bool, wallet: WalletTypeInfo = Depends(require_invoice_key) relay_id: str, allowed = False, blocked = True, wallet: WalletTypeInfo = Depends(require_invoice_key)
) -> List[NostrAccount]: ) -> List[NostrAccount]:
try: try:
# make sure the user has access to the relay # make sure the user has access to the relay