feat: update to v1.0.0 (#30)

This commit is contained in:
dni ⚡ 2024-11-08 14:32:04 +01:00 committed by GitHub
parent 2bdbbb274d
commit 73054fd5ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 2029 additions and 2132 deletions

View file

@ -1,10 +1,10 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
<div class="row q-col-gutter-md">
<div class="col-12 col-md-7 q-gutter-y-md">
<q-card>
<q-card-section>
{% raw %}
<q-btn unelevated color="primary" @click="openCreateRelayDialog()"
>New relay
</q-btn>
@ -49,10 +49,10 @@
<q-table
flat
dense
:data="relayLinks"
:rows="relayLinks"
row-key="id"
:columns="relaysTable.columns"
:pagination.sync="relaysTable.pagination"
v-model:pagination="relaysTable.pagination"
:filter="filter"
>
<template v-slot:body="props">
@ -69,28 +69,37 @@
</q-td>
<q-td key="id" :props="props">
<a style="color: unset" :href="props.row.id" target="_blank">
{{props.row.id}}</a
>
<a
style="color: unset"
:href="props.row.id"
target="_blank"
v-text="props.row.id"
></a>
</q-td>
<q-td key="toggle" :props="props">
<q-toggle
size="sm"
color="secodary"
v-model="props.row.active"
@input="showToggleRelayDialog(props.row)"
@update:model-value="showToggleRelayDialog(props.row)"
></q-toggle>
</q-td>
<q-td auto-width> {{props.row.name}} </q-td>
<q-td key="description" :props="props">
{{props.row.description}}
</q-td>
<q-td key="pubkey" :props="props">
<div>{{props.row.pubkey}}</div>
</q-td>
<q-td key="contact" :props="props">
<div>{{props.row.contact}}</div>
</q-td>
<q-td auto-width v-text="props.row.name"></q-td>
<q-td
key="description"
:props="props"
v-text="props.row.description"
></q-td>
<q-td
key="pubkey"
:props="props"
v-text="props.row.pubkey"
></q-td>
<q-td
key="contact"
:props="props"
v-text="props.row.contact"
></q-td>
</q-tr>
<q-tr v-if="props.row.expanded" :props="props">
<q-td colspan="100%">
@ -109,7 +118,6 @@
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
@ -188,9 +196,14 @@
</q-card>
</q-dialog>
</div>
{% endblock %} {% block vue_templates %}
<template id="relay-details">
{% include("nostrrelay/relay-details.html") %}
</template>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script src="{{ url_for('nostrrelay_static', path='js/utils.js') }}"></script>
<script src="{{ url_for('nostrrelay_static', path='components/relay-details/relay-details.js') }}"></script>
<script src="{{ url_for('nostrrelay_static', path='js/index.js') }}"></script>
<script src="{{ static_url_for('nostrrelay/static', path='js/utils.js') }}"></script>
<script src="{{ static_url_for('nostrrelay/static', path='js/index.js') }}"></script>
<script src="{{ static_url_for('nostrrelay/static', path='components/relay-details.js') }}"></script>
{% endblock %}

View file

@ -43,7 +43,7 @@
</div>
</div>
</q-card-section>
<q-card-section v-if="relay.config.isPaidRelay">
<q-card-section v-if="relay.meta.isPaidRelay">
<div class="row">
<div class="col-2 q-pt-sm">
<span class="text-bold">Public Key:</span>
@ -60,19 +60,19 @@
<div class="col-2"></div>
</div>
</q-card-section>
<q-card-section v-if="relay.config.isPaidRelay">
<q-card-section v-if="relay.meta.isPaidRelay">
<div class="row">
<div class="col-2">
<span class="text-bold">Cost to join: </span>
</div>
<div class="col-6">
<div>
<span v-text="relay.config.costToJoin"></span>
<span v-text="relay.meta.costToJoin"></span>
<span class="text-bold q-ml-sm">sats</span>
</div>
</div>
<div class="col-4">
<div v-if="relay.config.costToJoin">
<div v-if="relay.meta.costToJoin">
<q-btn
@click="createInvoice('join')"
unelevated
@ -89,37 +89,37 @@
</div>
</div>
</q-card-section>
<q-card-section v-if="relay.config.isPaidRelay">
<q-card-section v-if="relay.meta.isPaidRelay">
<div class="row q-mt-md q-mb-md">
<div class="col-2 q-pt-sm">
<span class="text-bold">Storage cost: </span>
</div>
<div class="col-3 q-pt-sm">
<span v-text="relay.config.storageCostValue"></span>
<span v-text="relay.meta.storageCostValue"></span>
<span class="text-bold q-ml-sm"> sats per</span>
<q-badge color="orange">
<span v-text="relay.config.storageCostUnit"></span>
<span v-text="relay.meta.storageCostUnit"></span>
</q-badge>
</div>
<div class="col-2">
<q-input
v-if="relay.config.storageCostValue"
v-if="relay.meta.storageCostValue"
filled
dense
v-model="unitsToBuy"
type="number"
min="0"
:label="relay.config.storageCostUnit"
:label="relay.meta.storageCostUnit"
></q-input>
</div>
<div class="col-2 q-pt-sm">
<div v-if="relay.config.storageCostValue">
<div v-if="relay.meta.storageCostValue">
<span class="text-bold q-ml-md" v-text="storageCost"></span>
<span>sats</span>
</div>
</div>
<div class="col-3">
<div v-if="relay.config.storageCostValue">
<div v-if="relay.meta.storageCostValue">
<q-btn
@click="createInvoice('storage')"
unelevated
@ -197,12 +197,10 @@
</q-page-container>
{% endblock %} {% block scripts %}
<script>
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
window.app = Vue.createApp({
el: '#vue',
mixins: [windowMixin],
data: function () {
data() {
return {
relay: JSON.parse('{{relay | tojson | safe}}'),
pubkey: '',
@ -213,14 +211,14 @@
},
computed: {
storageCost: function () {
if (!this.relay || !this.relay.config.storageCostValue) return 0
return this.unitsToBuy * this.relay.config.storageCostValue
if (!this.relay || !this.relay.meta.storageCostValue) return 0
return this.unitsToBuy * this.relay.meta.storageCostValue
},
wssLink: function () {
this.relay.config.domain =
this.relay.config.domain || window.location.hostname
this.relay.meta.domain =
this.relay.meta.domain || window.location.hostname
return (
'wss://' + this.relay.config.domain + '/nostrrelay/' + this.relay.id
'wss://' + this.relay.meta.domain + '/nostrrelay/' + this.relay.id
)
}
},
@ -272,7 +270,7 @@
wsConnection.close()
}
} catch (error) {
this.$q.notify({
Quasar.Notify.create({
timeout: 5000,
type: 'warning',
message: 'Failed to get invoice status',
@ -280,8 +278,7 @@
})
}
}
},
created: function () {}
}
})
</script>
{% endblock %}

View file

@ -0,0 +1,681 @@
<div>
<q-tabs v-model="tab" no-caps class="bg-dark text-white shadow-2">
<q-tab name="info" label="Relay Info"></q-tab>
<q-tab name="payment" label="Payment"></q-tab>
<q-tab name="config" label="Config"></q-tab>
<q-tab name="accounts" label="Accounts"></q-tab>
</q-tabs>
<q-tab-panels v-model="tab">
<q-tab-panel name="info">
<div v-if="relay">
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Name:</div>
<div class="col-6 col-sm-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.name"
type="text"
></q-input>
</div>
<div class="col-3 col-sm-1"></div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Description:</div>
<div class="col-6 col-sm-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.description"
type="text"
></q-input>
</div>
<div class="col-3 col-sm-1"></div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Relay Public Key:</div>
<div class="col-6 col-sm-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.pubkey"
type="text"
></q-input>
</div>
<div class="col-3 col-sm-1"></div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Contact:</div>
<div class="col-6 col-sm-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.contact"
type="text"
></q-input>
</div>
<div class="col-3 col-sm-1"></div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Domain:</div>
<div class="col-6 col-sm-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.domain"
type="text"
></q-input>
</div>
<div class="col-3 col-sm-1"></div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Web Socket Link:</div>
<div class="col-6 col-sm-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="wssLink"
type="text"
readonly
></q-input>
</div>
<div class="col-3 col-sm-1">
<q-btn outline color="grey" @click="copyText(wssLink)">Copy</q-btn>
</div>
</div>
</div>
</q-tab-panel>
<q-tab-panel name="payment">
<div v-if="relay">
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg q-pb-md">Free Storage:</div>
<div class="col-md-2 col-sm-4 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.freeStorageValue"
type="number"
hint="Value"
min="0"
></q-input>
</div>
<div class="col-md-2 col-sm-4 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.freeStorageUnit"
type="text"
hint="Unit"
:options="storageUnits"
></q-select>
</div>
<div class="col-1 q-pr-lg">
<q-icon name="info" class="cursor-pointer q-pb-md">
<q-tooltip>
How much data a client can store. This can be extended with the
Paid Plan.
</q-tooltip></q-icon
>
</div>
<div class="col-md-4 col-sm-2">
<q-badge
v-if="relay.meta.freeStorageValue == 0"
color="orange"
class="float-right q-mb-md"
><span>No free storage</span>
</q-badge>
</div>
</div>
<q-separator></q-separator>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Paid Plan:</div>
<div class="col-md-3 q-pr-lg">
<q-toggle
color="secodary"
v-model="relay.meta.isPaidRelay"
@update:model-value="togglePaidRelay"
></q-toggle>
</div>
<div class="col-6">
<q-badge
v-if="!relay.meta.isPaidRelay && relay.meta.freeStorageValue == 0"
color="orange"
class="float-right q-mb-md"
><span>No data will be stored. Read-only relay.</span>
</q-badge>
</div>
</div>
<div v-if="relay.meta.isPaidRelay && relay.meta.wallet">
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Wallet:</div>
<div class="col-md-6 col-sm-8 q-pr-lg">
<q-select
filled
dense
emit-value
v-model="relay.meta.wallet"
:options="walletOptions"
label="Wallet *"
>
</q-select>
</div>
<div class="col-md-3 col-sm-1">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
Wallet where the paiments will be sent to.
</q-tooltip></q-icon
>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Cost to join (sats):</div>
<div class="col-md-2 col-sm-4 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.costToJoin"
type="number"
hint="sats"
min="0"
></q-input>
</div>
<div class="col-1 q-pr-lg">
<q-icon name="info" class="cursor-pointer q-pb-md">
<q-tooltip>
Ask a fee for clients to join. Expected to be paid only once.
</q-tooltip></q-icon
>
</div>
<div class="col-md-6 col-sm-4">
<q-badge
v-if="relay.meta.costToJoin == 0"
color="green"
class="float-right"
><span>Free to join</span>
</q-badge>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Storage Cost (sats):</div>
<div class="col-md-2 col-sm-4 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.storageCostValue"
type="number"
hint="sats"
min="0"
></q-input>
</div>
<div class="col-md-2 col-sm-4 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.storageCostUnit"
type="text"
hint="Unit"
:options="storageUnits"
></q-select>
</div>
<div class="col-1 q-pr-lg">
<q-icon name="info" class="cursor-pointer q-pb-md">
<q-tooltip>
Cost for clients to buy additional storage.
</q-tooltip></q-icon
>
</div>
<div class="col-md-4 col-sm-0">
<q-badge
v-if="relay.meta.storageCostValue == 0"
color="green"
class="float-right"
><span>Unlimited storage</span>
</q-badge>
</div>
</div>
</div>
</div>
</q-tab-panel>
<q-tab-panel name="config">
<div v-if="relay">
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Created At in Past:</div>
<div class="col-2 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.createdAtDaysPast"
type="number"
min="0"
hint="Days"
></q-input>
</div>
<div class="col-2 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.createdAtHoursPast"
type="number"
hint="Hours"
:options="hours"
></q-select>
</div>
<div class="col-2 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.createdAtMinutesPast"
type="number"
hint="Minutes"
:options="range60"
></q-select>
</div>
<div class="col-2 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.createdAtSecondsPast"
type="number"
hint="Seconds"
:options="range60"
></q-select>
</div>
<div class="col-1 q-pb-md">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
NIP 22: Lower limit within which a relay will consider an
event's created_at to be acceptable.
</q-tooltip></q-icon
>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Created At in Future:</div>
<div class="col-2 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.createdAtDaysFuture"
type="number"
min="0"
hint="Days"
></q-input>
</div>
<div class="col-2 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.createdAtHoursFuture"
type="number"
hint="Hours"
:options="hours"
></q-select>
</div>
<div class="col-2 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.createdAtMinutesFuture"
type="number"
hint="Minutes"
:options="range60"
></q-select>
</div>
<div class="col-2 q-pr-lg">
<q-select
filled
dense
v-model="relay.meta.createdAtSecondsFuture"
type="number"
hint="Seconds"
:options="range60"
></q-select>
</div>
<div class="col-1 q-pb-md">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
NIP 22: Upper limit within which a relay will consider an
event's created_at to be acceptable.
</q-tooltip></q-icon
>
</div>
</div>
<q-separator></q-separator>
<div class="row items-center no-wrap q-mb-md q-mt-md">
<div class="col-3 q-pr-lg">Require Auth :</div>
<div class="col-2 col-sm-4 q-pr-lg">
<q-toggle
color="secodary"
class="q-ml-md q-mr-md"
v-model="relay.meta.requireAuthFilter"
>For Filters</q-toggle
>
</div>
<div class="col-2 col-sm-4 q-pr-lg">
<q-toggle
color="secodary"
class="q-ml-md q-mr-md"
v-model="relay.meta.requireAuthEvents"
>For All Events</q-toggle
>
</div>
<div class="col-5 col-sm-5">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
Require client authentication for accessing different types of
resources.
</q-tooltip></q-icon
>
</div>
</div>
<div
v-if="relay.meta.requireAuthEvents"
class="row items-center no-wrap q-mb-md q-mt-md"
>
<div class="col-3 q-pr-lg">Skip Auth For Events:</div>
<div class="col-1">
<q-input
filled
dense
v-model.trim="skipEventKind"
type="number"
min="0"
></q-input>
</div>
<div class="col-1">
<q-btn
unelevated
color="secondary"
icon="add"
@click="addSkipAuthForEvent()"
></q-btn>
</div>
<div class="col-7">
<q-chip
v-for="e in relay.meta.skipedAuthEvents"
:key="e"
removable
@remove="removeSkipAuthForEvent(e)"
color="primary"
text-color="white"
>
<span v-text="e"></span>
</q-chip>
</div>
</div>
<div v-else class="row items-center no-wrap q-mb-md q-mt-md">
<div class="col-3 q-pr-lg">Force Auth For Events:</div>
<div class="col-1">
<q-input
filled
dense
v-model.trim="forceEventKind"
type="number"
min="0"
></q-input>
</div>
<div class="col-1">
<q-btn
unelevated
color="secondary"
icon="add"
@click="addForceAuthForEvent()"
></q-btn>
</div>
<div class="col-7">
<q-chip
v-for="e in relay.meta.forcedAuthEvents"
:key="e"
removable
@remove="removeForceAuthForEvent(e)"
color="primary"
text-color="white"
>
<span v-text="e"></span>
</q-chip>
</div>
</div>
<q-separator></q-separator>
<div class="row items-center no-wrap q-mb-md q-mt-md">
<div class="col-3 q-pr-lg">Full Storage Action:</div>
<div class="col-3 col-sm-4 q-pr-lg">
<q-select
filled
dense
emit-value
v-model="relay.meta.fullStorageAction"
type="text"
:options="fullStorageActions"
></q-select>
</div>
<div class="col-6 col-sm-5">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
Action to be taken when the storage limit (if any) has been
reached.
</q-tooltip></q-icon
>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Limit per filter:</div>
<div class="col-3 col-sm-4 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.limitPerFilter"
type="number"
min="0"
></q-input>
</div>
<div class="col-6 col-sm-5">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
Maximum number of events to be returned in the initial query
(default 1000)
</q-tooltip></q-icon
>
<q-badge
v-if="relay.meta.limitPerFilter == 0"
color="green"
class="float-right"
><span>No Limit</span>
</q-badge>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Max Filters (per client):</div>
<div class="col-3 col-sm-4 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.maxClientFilters"
type="number"
min="0"
></q-input>
</div>
<div class="col-6 col-sm-5">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
Limit the number of filters that a client can have. Prevents
relay from being abused by clients with extremly high number of
fiters.
</q-tooltip></q-icon
>
<q-badge
v-if="relay.meta.maxClientFilters == 0"
color="green"
class="float-right"
><span>Unlimited Filters</span>
</q-badge>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Max events per hour:</div>
<div class="col-3 col-sm-4 q-pr-lg">
<q-input
filled
dense
v-model.trim="relay.meta.maxEventsPerHour"
type="number"
min="0"
></q-input>
</div>
<div class="col-6 col-sm-5">
<q-icon name="info" class="cursor-pointer">
<q-tooltip>
Limits the rate at which events are accepted by the relay.
Prevent clients from clogging the relay.
</q-tooltip></q-icon
>
<q-badge
v-if="relay.meta.maxEventsPerHour == 0"
color="green"
class="float-right"
><span>No Limit</span>
</q-badge>
</div>
</div>
</div>
</q-tab-panel>
<q-tab-panel name="accounts">
<div v-if="relay">
<q-card class="q-mr-lg q-mb-md"
><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-5 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-left"
@click="allowPublicKey(accountPubkey, true)"
>Allow</q-btn
>
</div>
<div class="col-3">
<q-btn
unelevated
color="pink"
class="float-left"
@click="blockPublicKey(accountPubkey, true)"
>Block</q-btn
>
</div>
</div>
</q-card-section>
</q-card>
<q-card class="q-mr-lg q-mb-dm"
><q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col-3 q-pr-lg">Filter:</div>
<div class="col-9 q-pr-lg">
<q-toggle
size="sm"
color="secodary"
class="q-mr-lg"
v-model="showAllowedAccounts"
@update:model-value="getAccounts()"
>Show Allowed Account</q-toggle
>
<q-toggle
size="sm"
color="secodary"
class="q-mr-lg"
v-model="showBlockedAccounts"
@update:model-value="getAccounts()"
>
Show Blocked Accounts</q-toggle
>
</div>
</div>
</q-card-section></q-card
>
<div class="row items-center no-wrap q-mb-md">
<div class="col-12 q-pr-lg">
<q-table
flat
dense
:rows="accounts"
row-key="pubkey"
:columns="accountsTable.columns"
:pagination.sync="accountsTable.pagination"
>
<template v-slot:body="props">
<q-tr :props="props">
<q-td key="action" :props="props">
<q-btn
dense
color="pink"
class="float-right"
@click="removePublicKey(props.row.pubkey)"
size="sm"
>Delete</q-btn
>
</q-td>
<q-td key="pubkey" :props="props">
<span v-text="props.row.pubkey"></span>
</q-td>
<q-td key="allowed" :props="props">
<q-toggle
size="sm"
color="secodary"
v-model="props.row.allowed"
@update:model-value="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"
@update:model-value="togglePublicKey(props.row, 'block')"
></q-toggle>
</q-td>
<q-td auto-width
><span v-text="props.row.paid_to_join"></span>
</q-td>
<q-td auto-width> <span v-text="props.row.sats"></span></q-td>
<q-td auto-width
><span v-text="props.row.storage"></span>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</div>
</div>
</q-tab-panel>
</q-tab-panels>
<div class="row items-center q-mt-md q-mb-lg">
<div class="col-6 q-pr-lg">
<q-btn
unelevated
color="secondary"
class="float-left"
@click="updateRelay()"
>Update Relay</q-btn
>
</div>
<div class="col-6">
<q-btn
unelevated
color="pink"
icon="cancel"
class="float-right"
@click="deleteRelay()"
>Delete Relay</q-btn
>
</div>
</div>
</div>