mirror of
https://github.com/aljazceru/nostr-watch.git
synced 2025-12-18 22:04:23 +01:00
Merge branch 'main' into patch-1
This commit is contained in:
@@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
A client-side nostr network status built with Vue3, [nostr-js](https://github.com/jb55/nostr-js) and [nostr-relay-inspector](https://github.com/dskvr/nostr-relay-inspector). Goal is to produce a client-side app that collects detailed information about nostr relays and the network in general to assist users, developers and relay operators alike.
|
A client-side nostr network status built with Vue3, [nostr-js](https://github.com/jb55/nostr-js) and [nostr-relay-inspector](https://github.com/dskvr/nostr-relay-inspector). Goal is to produce a client-side app that collects detailed information about nostr relays and the network in general to assist users, developers and relay operators alike.
|
||||||
|
|
||||||
## Goals
|
|
||||||
- [x] Rapidly learn Nostr Protocol _Personal Goal_
|
|
||||||
- [ ] Tool that can assist in a visual understanding of Nostr for developers
|
|
||||||
- [ ] Tool that can assist in onboarding for Users
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- [x] Real-time relay status
|
- [x] Real-time relay status
|
||||||
- [x] Real-time, client-centric latency tests
|
- [x] Real-time, client-centric latency tests
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nostr-watch",
|
"name": "nostr-watch",
|
||||||
"version": "0.0.15",
|
"version": "0.0.16",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "node ./scripts/geo.js",
|
"prebuild": "node ./scripts/geo.js",
|
||||||
@@ -31,8 +31,7 @@
|
|||||||
"node-emoji": "1.11.0",
|
"node-emoji": "1.11.0",
|
||||||
"node-polyfill-webpack-plugin": "2.0.1",
|
"node-polyfill-webpack-plugin": "2.0.1",
|
||||||
"nostr": "0.2.5",
|
"nostr": "0.2.5",
|
||||||
"nostr-relay-inspector": "0.0.9",
|
"nostr-relay-inspector": "0.0.10",
|
||||||
"nostr-tools": "0.24.1",
|
|
||||||
"onion-regex": "2.0.8",
|
"onion-regex": "2.0.8",
|
||||||
"requests": "0.3.0",
|
"requests": "0.3.0",
|
||||||
"sass": "1.56.1",
|
"sass": "1.56.1",
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
relays:
|
relays:
|
||||||
- 'wss://rsslay.fiatjaf.com'
|
|
||||||
- 'wss://relayer.fiatjaf.com'
|
|
||||||
- 'wss://freedom-relay.herokuapp.com/ws'
|
- 'wss://freedom-relay.herokuapp.com/ws'
|
||||||
- 'wss://nostr-relay.freeberty.net'
|
- 'wss://nostr-relay.freeberty.net'
|
||||||
- 'wss://nostr-relay.wlvs.space'
|
- 'wss://nostr-relay.wlvs.space'
|
||||||
@@ -40,3 +38,4 @@ relays:
|
|||||||
- 'wss://nostr-2.zebedee.cloud'
|
- 'wss://nostr-2.zebedee.cloud'
|
||||||
- 'wss://nostr.shadownode.org'
|
- 'wss://nostr.shadownode.org'
|
||||||
- 'wss://nostr.nymsrelay.com/'
|
- 'wss://nostr.nymsrelay.com/'
|
||||||
|
- 'wss://expensive-relay.fiatjaf.com'
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
body { margin: 0 !important; }
|
||||||
#app {
|
#app {
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<section id="refresh">
|
<section id="refresh">
|
||||||
<span>Updated {{ refreshData?.sinceLast }} ago <button @click="invalidate(true)">Update Now</button></span>
|
<span>Updated {{ refreshData?.sinceLast }} ago <button @click="invalidate(true, this.relay)">Refresh{{ relay ? ` ${relay}` : "" }}</button></span>
|
||||||
<span v-if="preferences.refresh"> Next refresh in: {{ refreshData?.untilNext }}</span>
|
<span v-if="preferences.refresh"> Next refresh in: {{ refreshData?.untilNext }}</span>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -33,7 +33,7 @@ const localMethods = {
|
|||||||
this.refreshData.sinceLast = this.timeSinceRefresh()
|
this.refreshData.sinceLast = this.timeSinceRefresh()
|
||||||
|
|
||||||
if(this.isExpired() && this.preferences.refresh)
|
if(this.isExpired() && this.preferences.refresh)
|
||||||
this.invalidate()
|
this.invalidate(false, this.relay)
|
||||||
|
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
@@ -57,9 +57,7 @@ export default defineComponent({
|
|||||||
updated(){
|
updated(){
|
||||||
this.saveState('preferences')
|
this.saveState('preferences')
|
||||||
|
|
||||||
if(this.isDone()) {
|
this.saveState('lastUpdate')
|
||||||
this.saveState('lastUpdate')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refreshData.untilNext = this.timeUntilRefresh()
|
this.refreshData.untilNext = this.timeUntilRefresh()
|
||||||
this.refreshData.sinceLast = this.timeSinceRefresh()
|
this.refreshData.sinceLast = this.timeSinceRefresh()
|
||||||
@@ -67,6 +65,12 @@ export default defineComponent({
|
|||||||
computed: {},
|
computed: {},
|
||||||
methods: Object.assign(localMethods, sharedMethods),
|
methods: Object.assign(localMethods, sharedMethods),
|
||||||
props: {
|
props: {
|
||||||
|
relay: {
|
||||||
|
type: String,
|
||||||
|
default(){
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
relaysProp:{
|
relaysProp:{
|
||||||
type: Object,
|
type: Object,
|
||||||
default(){
|
default(){
|
||||||
|
|||||||
@@ -43,10 +43,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- <td class="nip nip-11">
|
|
||||||
<a v-if="result?.info" @click="showModal=true">✅ </a>
|
|
||||||
</td> -->
|
|
||||||
|
|
||||||
<vue-final-modal v-model="showModal" classes="modal-container" content-class="modal-content">
|
<vue-final-modal v-model="showModal" classes="modal-container" content-class="modal-content">
|
||||||
<div class="modal__title">
|
<div class="modal__title">
|
||||||
<span>{{ result?.info?.name }}</span>
|
<span>{{ result?.info?.name }}</span>
|
||||||
@@ -83,7 +79,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { defineComponent} from 'vue'
|
import { defineComponent} from 'vue'
|
||||||
import { VueFinalModal } from 'vue-final-modal'
|
import { VueFinalModal } from 'vue-final-modal'
|
||||||
import InspectorRelayResult from 'nostr-relay-inspector'
|
import { InspectorRelayResult } from 'nostr-relay-inspector'
|
||||||
import SafeMail from "@2alheure/vue-safe-mail";
|
import SafeMail from "@2alheure/vue-safe-mail";
|
||||||
import { countryCodeEmoji } from 'country-code-emoji';
|
import { countryCodeEmoji } from 'country-code-emoji';
|
||||||
import emoji from 'node-emoji';
|
import emoji from 'node-emoji';
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
</column>
|
</column>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
|
||||||
<row container :gutter="12">
|
<row container :gutter="12">
|
||||||
<column :xs="12" :md="12" :lg="12" class="title-card">
|
<column :xs="12" :md="12" :lg="12" class="title-card">
|
||||||
<div style="display: none">{{result}}</div> <!-- ? -->
|
<div style="display: none">{{result}}</div> <!-- ? -->
|
||||||
@@ -30,80 +29,89 @@
|
|||||||
<span><img :src="badgeCheck('read')" /></span>
|
<span><img :src="badgeCheck('read')" /></span>
|
||||||
<span><img :src="badgeCheck('write')" /></span>
|
<span><img :src="badgeCheck('write')" /></span>
|
||||||
</span>
|
</span>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<span v-if="result.info?.supported_nips" class="badges">
|
<row container :gutter="12" v-if="!result?.check?.connect">
|
||||||
<span v-for="(nip) in result.info.supported_nips" :key="`${relay}_${nip}`">
|
<column :xs="12" :md="12" :lg="12" class="title-card">
|
||||||
<a :href="nipLink(nip)" target="_blank"><img :src="badgeLink(nip)" /></a>
|
This relay appears to be offline.
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<row container :gutter="12" v-if="result?.check?.connect">
|
||||||
|
<column :xs="12" :md="12" :lg="12" class="title-card">
|
||||||
|
|
||||||
|
<span v-if="result.info?.supported_nips" class="badges">
|
||||||
|
<span v-for="(nip) in result.info.supported_nips" :key="`${relay}_${nip}`">
|
||||||
|
<a :href="nipLink(nip)" target="_blank"><img :src="badgeLink(nip)" /></a>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
|
|
||||||
<!--table>
|
<table v-if="result.info">
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="3"><h4>Status</h4></th>
|
<th colspan="2"><h4>Info</h4></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="result.checkClass">
|
<tbody v-if="result.info">
|
||||||
<td :class="result.checkClass.connect" class="connect indicator">Connected</td>
|
<tr v-for="(value, key) in Object.entries(result.info).filter(value => value[0] != 'id' && value[0] != 'supported_nips')" :key="`${value}_${key}`">
|
||||||
<td :class="result.checkClass.read" class="read indicator">Read</td>
|
<td>{{ value[0] }}</td>
|
||||||
<td :class="result.checkClass.write" class="write indicator">Write</td>
|
<td v-if="value[0]!='contact' && value[0]!='pubkey' && value[0]!='software' && value[0]!='version'">{{ value[1] }} </td>
|
||||||
</tr>
|
<td v-if="value[0]=='contact'"><SafeMail :email="value[1]" /></td>
|
||||||
</table-->
|
<td v-if="value[0]=='pubkey' || value[0]=='version'"><code>{{ value[1] }}</code></td>
|
||||||
|
<td v-if="value[0]=='software'"><a :href="value[1]">{{ value[1] }}</a></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tr v-if="Object.entries(result.info).length == 0 && result.check.connect">
|
||||||
|
Relay does not have NIP-11 support, or the administrator has not configured the relay to return information.
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h4>Identities</h4>
|
||||||
<table v-if="result.info">
|
<table v-if="result.identities">
|
||||||
<tr>
|
<tr v-for="(value, key) in Object.entries(result?.identities)" :key="`${value}_${key}`">
|
||||||
<th colspan="2"><h4>Info</h4></th>
|
|
||||||
</tr>
|
|
||||||
<tr v-for="(value, key) in Object.entries(result.info).filter(value => value[0] != 'id' && value[0] != 'supported_nips')" :key="`${value}_${key}`">
|
|
||||||
<td>{{ value[0] }}</td>
|
<td>{{ value[0] }}</td>
|
||||||
<td v-if="value[0]!='contact' && value[0]!='pubkey' && value[0]!='software' && value[0]!='version'">{{ value[1] }} </td>
|
<td><code>{{ value[1] }}</code></td>
|
||||||
<td v-if="value[0]=='contact'"><SafeMail :email="value[1]" /></td>
|
</tr>
|
||||||
<td v-if="value[0]=='pubkey' || value[0]=='version'"><code>{{ value[1] }}</code></td>
|
<tr v-if="Object.entries(result.identities).length==0">
|
||||||
<td v-if="value[0]=='software'"><a href="{{ value[1] }}">{{ value[1] }}</a></td>
|
Relay does not provide NIP-05 support and has not registered an administrator key.
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<h4>Identities</h4>
|
<div style="display: none">{{result}}</div> <!-- ? -->
|
||||||
<table v-if="result.identities">
|
</column>
|
||||||
|
<column :xs="12" :md="6" :lg="6" class="title-card">
|
||||||
|
|
||||||
<tr v-for="(value, key) in Object.entries(result?.identities)" :key="`${value}_${key}`">
|
<h4>GEO {{geo?.countryCode ? getFlag() : ''}}</h4>
|
||||||
|
<table v-if="geo[relay]">
|
||||||
|
<tr v-for="(value, key) in Object.entries(geo[relay])" :key="`${value}_${key}`">
|
||||||
|
<td>{{ value[0] }}</td>
|
||||||
|
<td>{{ value[1] }} </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<td>{{ value[0] }}</td>
|
</column>
|
||||||
<td><code>{{ value[1] }}</code></td>
|
<column :xs="12" :md="6" :lg="6" class="title-card">
|
||||||
</tr>
|
<h4>DNS</h4>
|
||||||
</table>
|
<table v-if="geo[relay]">
|
||||||
|
<tr v-for="(value, key) in Object.entries(geo[relay].dns)" :key="`${value}_${key}`">
|
||||||
|
<td>{{ value[0] }}</td>
|
||||||
|
<td>{{ value[1] }} </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<div style="display: none">{{result}}</div> <!-- ? -->
|
<div style="display: none">{{result}}</div> <!-- ? -->
|
||||||
</column>
|
|
||||||
</row>
|
</column>
|
||||||
|
|
||||||
<row container :gutter="12">
|
|
||||||
<column :xs="12" :md="6" :lg="6" class="title-card">
|
|
||||||
|
|
||||||
<h4>GEO {{geo?.countryCode ? getFlag() : ''}}</h4>
|
|
||||||
<table v-if="geo[relay]">
|
|
||||||
<tr v-for="(value, key) in Object.entries(geo[relay])" :key="`${value}_${key}`">
|
|
||||||
<td>{{ value[0] }}</td>
|
|
||||||
<td>{{ value[1] }} </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</column>
|
|
||||||
<column :xs="12" :md="6" :lg="6" class="title-card">
|
|
||||||
<h4>DNS</h4>
|
|
||||||
<table v-if="geo[relay]">
|
|
||||||
<tr v-for="(value, key) in Object.entries(geo[relay].dns)" :key="`${value}_${key}`">
|
|
||||||
<td>{{ value[0] }}</td>
|
|
||||||
<td>{{ value[1] }} </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div style="display: none">{{result}}</div> <!-- ? -->
|
|
||||||
|
|
||||||
</column>
|
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
|
||||||
|
<RefreshComponent
|
||||||
|
:relay="relay"
|
||||||
|
/>
|
||||||
<span class="credit"><a href="http://sandwich.farm">Another 🥪 by sandwich.farm</a>, built with <a href="https://github.com/jb55/nostr-js">nostr-js</a> and <a href="https://github.com/dskvr/nostr-relay-inspector">nostr-relay-inspector</a>, inspired by <a href="https://github.com/fiatjaf/nostr-relay-registry">nostr-relay-registry</a></span>
|
<span class="credit"><a href="http://sandwich.farm">Another 🥪 by sandwich.farm</a>, built with <a href="https://github.com/jb55/nostr-js">nostr-js</a> and <a href="https://github.com/dskvr/nostr-relay-inspector">nostr-relay-inspector</a>, inspired by <a href="https://github.com/fiatjaf/nostr-relay-registry">nostr-relay-registry</a></span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -116,25 +124,43 @@ import { useStorage } from "vue3-storage";
|
|||||||
|
|
||||||
import LeafletSingleComponent from '../components/LeafletSingleComponent.vue'
|
import LeafletSingleComponent from '../components/LeafletSingleComponent.vue'
|
||||||
import NavComponent from '../components/NavComponent.vue'
|
import NavComponent from '../components/NavComponent.vue'
|
||||||
|
import RefreshComponent from '../components/RefreshComponent.vue'
|
||||||
|
|
||||||
import { Row, Column } from 'vue-grid-responsive';
|
import { Row, Column } from 'vue-grid-responsive';
|
||||||
import SafeMail from "@2alheure/vue-safe-mail";
|
import SafeMail from "@2alheure/vue-safe-mail";
|
||||||
import emoji from 'node-emoji';
|
|
||||||
import { countryCodeEmoji } from 'country-code-emoji';
|
|
||||||
|
|
||||||
import { Inspector, InspectorObservation } from 'nostr-relay-inspector'
|
import sharedMethods from '../shared'
|
||||||
// import { Inspector, InspectorObservation } from '../../lib/nostr-relay-inspector'
|
|
||||||
// import { Inspector, InspectorObservation } from '../../lib/nostr-relay-inspector'
|
|
||||||
|
|
||||||
import { version } from '../../package.json'
|
import { version } from '../../package.json'
|
||||||
import { relays } from '../../relays.yaml'
|
import { relays } from '../../relays.yaml'
|
||||||
import { geo } from '../../geo.yaml'
|
import { geo } from '../../geo.yaml'
|
||||||
import { messages as RELAY_MESSAGES, codes as RELAY_CODES } from '../../codes.yaml'
|
|
||||||
|
|
||||||
|
const localMethods = {
|
||||||
|
relayUrl() {
|
||||||
|
// We will see what `params` is shortly
|
||||||
|
return `wss://${this.$route.params.relayUrl}`
|
||||||
|
},
|
||||||
|
|
||||||
|
badgeLink(nip){
|
||||||
|
return `https://img.shields.io/static/v1?style=for-the-badge&label=NIP&message=${this.nipSignature(nip)}&color=black`
|
||||||
|
},
|
||||||
|
|
||||||
|
badgeCheck(which){
|
||||||
|
return `https://img.shields.io/static/v1?style=for-the-badge&label=&message=${which}&color=${this.result?.check?.[which] ? 'green' : 'red'}`
|
||||||
|
},
|
||||||
|
|
||||||
import crypto from "crypto"
|
nipSignature(key){
|
||||||
|
return key.toString().length == 1 ? `0${key}` : key
|
||||||
|
},
|
||||||
|
|
||||||
|
nipFormatted(key){
|
||||||
|
return `NIP-${this.nipSignature(key)}`
|
||||||
|
},
|
||||||
|
|
||||||
|
nipLink(key){
|
||||||
|
return `https://github.com/nostr-protocol/nips/blob/master/${this.nipSignature(key)}.md`
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
title: "nostr.watch registry & network status",
|
title: "nostr.watch registry & network status",
|
||||||
@@ -145,6 +171,7 @@ export default defineComponent({
|
|||||||
LeafletSingleComponent,
|
LeafletSingleComponent,
|
||||||
NavComponent,
|
NavComponent,
|
||||||
SafeMail,
|
SafeMail,
|
||||||
|
RefreshComponent,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -173,215 +200,21 @@ export default defineComponent({
|
|||||||
|
|
||||||
this.storage = useStorage()
|
this.storage = useStorage()
|
||||||
this.lastUpdate = this.storage.getStorageSync('lastUpdate')
|
this.lastUpdate = this.storage.getStorageSync('lastUpdate')
|
||||||
|
this.preferences = this.storage.getStorageSync('preferences')
|
||||||
this.result = this.storage.getStorageSync(this.relay)
|
this.result = this.storage.getStorageSync(this.relay)
|
||||||
|
|
||||||
if(this.isExpired())
|
if(this.isExpired())
|
||||||
this.check(this.relay)
|
this.check(this.relay)
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {},
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
updated() {
|
updated() {
|
||||||
Object.keys(this.timeouts).forEach(timeout => clearTimeout(this.timeouts[timeout]))
|
Object.keys(this.timeouts).forEach(timeout => clearTimeout(this.timeouts[timeout]))
|
||||||
Object.keys(this.intervals).forEach(interval => clearInterval(this.intervals[interval]))
|
Object.keys(this.intervals).forEach(interval => clearInterval(this.intervals[interval]))
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: Object.assign(localMethods, sharedMethods),
|
||||||
isExpired(){
|
|
||||||
return typeof this.lastUpdate === 'undefined' || Date.now() - this.lastUpdate > this.preferences.cacheExpiration
|
|
||||||
},
|
|
||||||
|
|
||||||
saveState(relay){
|
|
||||||
this.storage
|
|
||||||
.setStorage({
|
|
||||||
key: relay,
|
|
||||||
data: this.result
|
|
||||||
})
|
|
||||||
.then(successCallback => {
|
|
||||||
console.log(successCallback.errMsg);
|
|
||||||
})
|
|
||||||
.catch(failCallback => {
|
|
||||||
console.log(failCallback.errMsg);
|
|
||||||
})
|
|
||||||
|
|
||||||
this.storage
|
|
||||||
.setStorage({
|
|
||||||
key: "lastUpdate",
|
|
||||||
data: Date.now()
|
|
||||||
})
|
|
||||||
.then(successCallback => {
|
|
||||||
console.log(successCallback.errMsg);
|
|
||||||
this.lastUpdate = Date.now()
|
|
||||||
})
|
|
||||||
.catch(failCallback => {
|
|
||||||
console.log(failCallback.errMsg);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
relayUrl() {
|
|
||||||
// We will see what `params` is shortly
|
|
||||||
return `wss://${this.$route.params.relayUrl}`
|
|
||||||
},
|
|
||||||
async check(relay){
|
|
||||||
//const self = this
|
|
||||||
/* return new Promise(function(resolve, reject) { */
|
|
||||||
/* let nip = new Array(99).fill(false);
|
|
||||||
nip[5] = true
|
|
||||||
nip[11] = true */
|
|
||||||
|
|
||||||
const opts = {
|
|
||||||
checkLatency: true,
|
|
||||||
checkNips: true,
|
|
||||||
/* checkNip: nip, */
|
|
||||||
/* debug: true */
|
|
||||||
}
|
|
||||||
|
|
||||||
let inspect = new Inspector(relay, opts)
|
|
||||||
.on('run', (result) => {
|
|
||||||
result.aggregate = 'processing'
|
|
||||||
})
|
|
||||||
.on('open', (e, result) => {
|
|
||||||
this.result = result
|
|
||||||
this.result.checkClass = {read: null, write: null, connect: null}
|
|
||||||
this.setResultClass('connect')
|
|
||||||
})
|
|
||||||
.on('complete', (instance) => {
|
|
||||||
this.result = instance.result
|
|
||||||
this.messages[this.relay] = instance.inbox
|
|
||||||
|
|
||||||
/* this.setFlag(relay) */
|
|
||||||
this.setAggregateResult()
|
|
||||||
/* this.adjustResult(relay) */
|
|
||||||
this.setResultClass('read')
|
|
||||||
this.setResultClass('write')
|
|
||||||
this.saveState(relay)
|
|
||||||
})
|
|
||||||
.on('notice', (notice) => {
|
|
||||||
const hash = this.sha1(notice)
|
|
||||||
let message_obj = RELAY_MESSAGES[hash]
|
|
||||||
let code_obj = RELAY_CODES[message_obj.code]
|
|
||||||
|
|
||||||
let response_obj = {...message_obj, ...code_obj}
|
|
||||||
|
|
||||||
this.result.observations.push( new InspectorObservation('notice', response_obj.code, response_obj.description, response_obj.relates_to) )
|
|
||||||
})
|
|
||||||
.on('close', (msg) => {
|
|
||||||
console.warn("CAUTION", msg)
|
|
||||||
})
|
|
||||||
.on('error', (err) => {
|
|
||||||
console.error("ERROR", err)
|
|
||||||
})
|
|
||||||
.run()
|
|
||||||
|
|
||||||
return inspect;
|
|
||||||
/* }) */
|
|
||||||
|
|
||||||
},
|
|
||||||
setResultClass (key) {
|
|
||||||
let result = this.result?.check?.[key] === true
|
|
||||||
? 'success'
|
|
||||||
: this.result?.check?.[key] === false
|
|
||||||
? 'failure'
|
|
||||||
: 'pending'
|
|
||||||
this.result.checkClass[key] = result
|
|
||||||
},
|
|
||||||
getLoadingClass () {
|
|
||||||
return this.result?.state == 'complete' ? "relay loaded" : "relay"
|
|
||||||
},
|
|
||||||
generateKey (url, key) {
|
|
||||||
return `${url}_${key}`
|
|
||||||
},
|
|
||||||
|
|
||||||
getFlag () {
|
|
||||||
return this.geo?.countryCode ? countryCodeEmoji(this.geo.countryCode) : emoji.get('shrug');
|
|
||||||
},
|
|
||||||
|
|
||||||
setCheck (bool) {
|
|
||||||
return bool ? '✅ ' : ''
|
|
||||||
},
|
|
||||||
|
|
||||||
badgeLink(nip){
|
|
||||||
return `https://img.shields.io/static/v1?style=for-the-badge&label=NIP&message=${this.nipSignature(nip)}&color=black`
|
|
||||||
},
|
|
||||||
|
|
||||||
badgeCheck(which){
|
|
||||||
return `https://img.shields.io/static/v1?style=for-the-badge&label=&message=${which}&color=${this.result?.check?.[which] ? 'green' : 'red'}`
|
|
||||||
},
|
|
||||||
|
|
||||||
setCross (bool) {
|
|
||||||
return !bool ? '❌' : ''
|
|
||||||
},
|
|
||||||
setCaution (bool) {
|
|
||||||
return !bool ? '⚠️' : ''
|
|
||||||
},
|
|
||||||
identityList () {
|
|
||||||
let string = '',
|
|
||||||
extraString = '',
|
|
||||||
users = Object.entries(this.result.identities),
|
|
||||||
count = 0
|
|
||||||
|
|
||||||
if(this.result.identities) {
|
|
||||||
if(this.result.identities.serverAdmin) {
|
|
||||||
string = `Relay has registered an administrator pubkey: ${this.result.identities.serverAdmin}. `
|
|
||||||
extraString = "Additionally, "
|
|
||||||
}
|
|
||||||
|
|
||||||
const total = users.filter(([key]) => key!='serverAdmin').length,
|
|
||||||
isOne = total==1
|
|
||||||
|
|
||||||
if(total) {
|
|
||||||
string = `${string}${extraString}Relay domain contains NIP-05 verification data for:`
|
|
||||||
users.forEach( ([key]) => {
|
|
||||||
if(key == "serverAdmin") return
|
|
||||||
count++
|
|
||||||
string = `${string} ${(count==total && !isOne) ? 'and' : ''} @${key}${(count!=total && !isOne) ? ', ' : ''}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string
|
|
||||||
},
|
|
||||||
nipSignature(key){
|
|
||||||
return key.toString().length == 1 ? `0${key}` : key
|
|
||||||
},
|
|
||||||
nipFormatted(key){
|
|
||||||
return `NIP-${this.nipSignature(key)}`
|
|
||||||
},
|
|
||||||
nipLink(key){
|
|
||||||
return `https://github.com/nostr-protocol/nips/blob/master/${this.nipSignature(key)}.md`
|
|
||||||
},
|
|
||||||
async copy(text) {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(text);
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setAggregateResult () {
|
|
||||||
if(!this.result) return
|
|
||||||
|
|
||||||
let aggregateTally = 0
|
|
||||||
aggregateTally += this.result?.check.connect ? 1 : 0
|
|
||||||
aggregateTally += this.result?.check.read ? 1 : 0
|
|
||||||
aggregateTally += this.result?.check.write ? 1 : 0
|
|
||||||
if (aggregateTally == 3) {
|
|
||||||
this.result.aggregate = 'public'
|
|
||||||
}
|
|
||||||
else if (aggregateTally == 0) {
|
|
||||||
this.result.aggregate = 'offline'
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.result.aggregate = 'restricted'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
sha1 (message) {
|
|
||||||
const hash = crypto.createHash('sha1').update(JSON.stringify(message)).digest('hex')
|
|
||||||
return hash
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,19 +4,22 @@ import { messages as RELAY_MESSAGES, codes as RELAY_CODES } from '../codes.yaml'
|
|||||||
import crypto from "crypto"
|
import crypto from "crypto"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
invalidate: function(force){
|
invalidate: async function(force, single){
|
||||||
if(!this.isExpired() && !force)
|
if(!this.isExpired() && !force)
|
||||||
return
|
return
|
||||||
|
|
||||||
this.relays.forEach(async relay => {
|
if(single) {
|
||||||
await this.check(relay)
|
await this.check(single)
|
||||||
this.relays[relay] = this.getState(relay)
|
this.relays[single] = this.getState(single)
|
||||||
this.messages[relay] = this.getState(`${relay}_inbox`)
|
this.messages[single] = this.getState(`${single}_inbox`)
|
||||||
})
|
}
|
||||||
|
else {
|
||||||
// if(this.preferences.refresh)
|
this.relays.forEach(async relay => {
|
||||||
// this.timeouts.invalidate = setTimeout(()=> this.invalidate(), 1000)
|
await this.check(relay)
|
||||||
|
this.relays[relay] = this.getState(relay)
|
||||||
|
this.messages[relay] = this.getState(`${relay}_inbox`)
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isExpired: function(){
|
isExpired: function(){
|
||||||
|
|||||||
Reference in New Issue
Block a user