mirror of
https://github.com/aljazceru/nostr-watch.git
synced 2025-12-17 05:24:19 +01:00
add routing
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
"vue-final-modal": "3",
|
"vue-final-modal": "3",
|
||||||
"vue-grid-responsive": "1.3.0",
|
"vue-grid-responsive": "1.3.0",
|
||||||
"vue-nav-tabs": "0.5.7",
|
"vue-nav-tabs": "0.5.7",
|
||||||
|
"vue-router": "4.1.6",
|
||||||
"vue-simple-maps": "1.1.3",
|
"vue-simple-maps": "1.1.3",
|
||||||
"vue3-popper": "1.5.0",
|
"vue3-popper": "1.5.0",
|
||||||
"yaml-loader": "^0.6.0",
|
"yaml-loader": "^0.6.0",
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<RelayTableComponent />
|
<router-view></router-view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RelayTableComponent from './components/RelayTableComponent.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {}
|
||||||
RelayTableComponent,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -51,29 +51,23 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getLatLng(geo){
|
getLatLng(geo){
|
||||||
console.log('meow', [geo.lat, geo.lon])
|
|
||||||
return [geo.lat, geo.lon]
|
return [geo.lat, geo.lon]
|
||||||
},
|
},
|
||||||
getCircleColor(relay){
|
getCircleColor(relay){
|
||||||
|
|
||||||
if(this.result[relay]?.aggregate == 'public') {
|
if(this.result[relay]?.aggregate == 'public') {
|
||||||
console.log('woof', relay, this.result[relay]?.aggregate)
|
|
||||||
return '#00AA00'
|
return '#00AA00'
|
||||||
}
|
}
|
||||||
else if(this.result[relay]?.aggregate == 'restricted') {
|
else if(this.result[relay]?.aggregate == 'restricted') {
|
||||||
console.log('woof', relay, this.result[relay]?.aggregate)
|
|
||||||
return '#FFA500'
|
return '#FFA500'
|
||||||
}
|
}
|
||||||
else if(this.result[relay]?.aggregate == 'offline') {
|
else if(this.result[relay]?.aggregate == 'offline') {
|
||||||
console.log('woof', relay, this.result[relay]?.aggregate)
|
|
||||||
return '#FF0000'
|
return '#FF0000'
|
||||||
}
|
}
|
||||||
return 'transparent'
|
return 'transparent'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {},
|
||||||
console.log('GEO', Object.entries(this.geo))
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
geo: {
|
geo: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
101
src/components/LeafletSingleComponent.vue
Normal file
101
src/components/LeafletSingleComponent.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<l-map
|
||||||
|
ref="map"
|
||||||
|
v-model:zoom="zoom"
|
||||||
|
:center="[47.41322, -1.219482]"
|
||||||
|
:minZoom="zoom"
|
||||||
|
:maxZoom="zoom"
|
||||||
|
:zoomControl="false"
|
||||||
|
style="height:50vh"
|
||||||
|
>
|
||||||
|
|
||||||
|
<l-tile-layer
|
||||||
|
url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"
|
||||||
|
layer-type="base"
|
||||||
|
name="OpenStreetMap"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- <l-marker v-for="([relay, result]) in Object.entries(geo)" :lat-lng="getLatLng(result)" :key="relay">
|
||||||
|
<l-popup>
|
||||||
|
{{ relay }}
|
||||||
|
</l-popup>
|
||||||
|
</l-marker> -->
|
||||||
|
|
||||||
|
<l-circle-marker
|
||||||
|
:lat-lng="getLatLng(entry)"
|
||||||
|
:radius="3"
|
||||||
|
:weight="6"
|
||||||
|
:color="getCircleColor(relay)"
|
||||||
|
:fillOpacity="1"
|
||||||
|
:class="relay"
|
||||||
|
>
|
||||||
|
<l-popup>
|
||||||
|
{{ relay }}
|
||||||
|
meopw
|
||||||
|
</l-popup>
|
||||||
|
</l-circle-marker>
|
||||||
|
</l-map>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import "leaflet/dist/leaflet.css"
|
||||||
|
import { LMap, LTileLayer, LCircleMarker } from "@vue-leaflet/vue-leaflet";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
LMap,
|
||||||
|
LTileLayer,
|
||||||
|
LCircleMarker,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getLatLng(geo){
|
||||||
|
return [geo.lat, geo.lon]
|
||||||
|
},
|
||||||
|
getCircleColor(relay){
|
||||||
|
|
||||||
|
if(this.result[relay]?.aggregate == 'public') {
|
||||||
|
return '#00AA00'
|
||||||
|
}
|
||||||
|
else if(this.result[relay]?.aggregate == 'restricted') {
|
||||||
|
return '#FFA500'
|
||||||
|
}
|
||||||
|
else if(this.result[relay]?.aggregate == 'offline') {
|
||||||
|
return '#FF0000'
|
||||||
|
}
|
||||||
|
return 'transparent'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
console.log('GEO', Object.entries(this.geo))
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
geo: {
|
||||||
|
type: Object,
|
||||||
|
default(){
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
type: Object,
|
||||||
|
default(){
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
zoom: 2
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.leaflet-container {
|
||||||
|
margin-top:37px;
|
||||||
|
height:250px !important;
|
||||||
|
}
|
||||||
|
.leaflet-control-zoom {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -55,14 +55,14 @@
|
|||||||
<strong v-if="result.info?.pubkey">Public Key:</strong> {{ result.info?.pubkey }} <br/>
|
<strong v-if="result.info?.pubkey">Public Key:</strong> {{ result.info?.pubkey }} <br/>
|
||||||
<strong v-if="result.info?.contact">Contact:</strong> <SafeMail :email="result.info?.contact" v-if="result.info?.contact" />
|
<strong v-if="result.info?.contact">Contact:</strong> <SafeMail :email="result.info?.contact" v-if="result.info?.contact" />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div>
|
<div>
|
||||||
<h4>Status</h4>
|
<h4>Status</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Connected</strong> <span :class="getResultClass(relay, 'connect')" class="connect indicator"></span></li>
|
<li><strong>Connected</strong> <span :class="getResultClass(relay, 'connect')" class="connect indicator"></span></li>
|
||||||
<li><strong>Read</strong> <span :class="getResultClass(relay, 'read')" class="read indicator"></span></li>
|
<li><strong>Read</strong> <span :class="getResultClass(relay, 'read')" class="read indicator"></span></li>
|
||||||
<li><strong>Write</strong> <span :class="getResultClass(relay, 'write')" class="write indicator"></span></li>
|
<li><strong>Write</strong> <span :class="getResultClass(relay, 'write')" class="write indicator"></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div> -->
|
</div>
|
||||||
<h4>Relay Info</h4>
|
<h4>Relay Info</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Software:</strong> {{ result.info?.software }} </li>
|
<li><strong>Software:</strong> {{ result.info?.software }} </li>
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
import "./styles/main.scss"
|
import "./styles/main.scss"
|
||||||
import directives from "./directives/"
|
import directives from "./directives/"
|
||||||
import titleMixin from './mixins/titleMixin'
|
import titleMixin from './mixins/titleMixin'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.mixin(titleMixin)
|
app
|
||||||
|
.use(router)
|
||||||
|
.mixin(titleMixin)
|
||||||
|
|
||||||
directives(app);
|
directives(app);
|
||||||
|
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
228
src/pages/HomePage.vue
Normal file
228
src/pages/HomePage.vue
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
<template>
|
||||||
|
<!-- <NavComponent /> -->
|
||||||
|
<div id="wrapper" :class="loadingComplete()">
|
||||||
|
|
||||||
|
<row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12" class="title-card">
|
||||||
|
<h1>nostr.watch<sup>{{version}}</sup></h1>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12">
|
||||||
|
<LeafletComponent
|
||||||
|
:geo="geo"
|
||||||
|
:result="result"
|
||||||
|
/>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12">
|
||||||
|
<div class="block">
|
||||||
|
<table>
|
||||||
|
|
||||||
|
<RelayListComponent
|
||||||
|
section="public"
|
||||||
|
:relays="relays"
|
||||||
|
:result="result"
|
||||||
|
:geo="geo"
|
||||||
|
:messages="messages"
|
||||||
|
:alerts="alerts"
|
||||||
|
:connections="connections"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RelayListComponent
|
||||||
|
section="restricted"
|
||||||
|
:relays="relays"
|
||||||
|
:result="result"
|
||||||
|
:geo="geo"
|
||||||
|
:messages="messages"
|
||||||
|
:alerts="alerts"
|
||||||
|
:connections="connections"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RelayListComponent
|
||||||
|
section="offline"
|
||||||
|
:relays="relays"
|
||||||
|
:result="result"
|
||||||
|
:geo="geo"
|
||||||
|
:messages="messages"
|
||||||
|
:alerts="alerts"
|
||||||
|
:connections="connections"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- <RelayListComponent
|
||||||
|
section="processing"
|
||||||
|
:relays="relays"
|
||||||
|
:result="result"
|
||||||
|
:messages="messages"
|
||||||
|
:alerts="alerts"
|
||||||
|
:connections="connections"
|
||||||
|
:showJson="false"
|
||||||
|
/> -->
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12" class="processing-card loading">
|
||||||
|
<span v-if="(relaysTotal()-relaysConnected()>0)">Processing {{ relaysConnected() }}/{{ relaysTotal() }}</span>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent} from 'vue'
|
||||||
|
import RelayListComponent from '../components/RelayListComponent.vue'
|
||||||
|
import LeafletComponent from '../components/LeafletComponent.vue'
|
||||||
|
// import NavComponent from './NavComponent.vue'
|
||||||
|
|
||||||
|
import { Row, Column } from 'vue-grid-responsive';
|
||||||
|
|
||||||
|
import { version } from '../../package.json'
|
||||||
|
|
||||||
|
import { relays } from '../../relays.yaml'
|
||||||
|
import { geo } from '../../geo.yaml'
|
||||||
|
import { messages as RELAY_MESSAGES, codes as RELAY_CODES } from '../../codes.yaml'
|
||||||
|
|
||||||
|
import { Inspector, InspectorObservation } from 'nostr-relay-inspector'
|
||||||
|
// import { Inspector, InspectorObservation } from '../../lib/nostr-relay-inspector'
|
||||||
|
|
||||||
|
import crypto from "crypto"
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
title: "nostr.watch registry & network status",
|
||||||
|
name: 'RelayTableComponent',
|
||||||
|
components: {
|
||||||
|
Row,
|
||||||
|
Column,
|
||||||
|
RelayListComponent,
|
||||||
|
LeafletComponent
|
||||||
|
// NavComponent
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
relays,
|
||||||
|
result: {},
|
||||||
|
messages: {},
|
||||||
|
connections: {},
|
||||||
|
nips: {},
|
||||||
|
alerts: {},
|
||||||
|
timeouts: {},
|
||||||
|
lastPing: Date.now(),
|
||||||
|
nextPing: Date.now() + (60*1000),
|
||||||
|
count: 0,
|
||||||
|
geo,
|
||||||
|
version: version
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
this.relays.forEach(relay => {
|
||||||
|
this.check(relay)
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
check(relay){
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
checkLatency: true,
|
||||||
|
setIP: false,
|
||||||
|
setGeo: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
let inspect = new Inspector(relay, opts)
|
||||||
|
.on('run', (result) => {
|
||||||
|
result.aggregate = 'processing'
|
||||||
|
})
|
||||||
|
.on('open', (e, result) => {
|
||||||
|
this.result[relay] = result
|
||||||
|
})
|
||||||
|
.on('complete', (instance) => {
|
||||||
|
this.result[relay] = instance.result
|
||||||
|
this.messages[relay] = instance.inbox
|
||||||
|
// this.setFlag(relay)
|
||||||
|
this.setAggregateResult(relay)
|
||||||
|
this.adjustResult(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[relay].observations.push( new InspectorObservation('notice', response_obj.code, response_obj.description, response_obj.relates_to) )
|
||||||
|
|
||||||
|
})
|
||||||
|
.on('close', () => {})
|
||||||
|
.on('error', () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
|
this.connections[relay] = inspect
|
||||||
|
},
|
||||||
|
|
||||||
|
adjustResult (relay) {
|
||||||
|
this.result[relay].observations.forEach( observation => {
|
||||||
|
if (observation.code == "BLOCKS_WRITE_STATUS_CHECK") {
|
||||||
|
this.result[relay].check.write = false
|
||||||
|
this.result[relay].aggregate = 'public'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
setAggregateResult (relay) {
|
||||||
|
let aggregateTally = 0
|
||||||
|
aggregateTally += this.result?.[relay]?.check.connect ? 1 : 0
|
||||||
|
aggregateTally += this.result?.[relay]?.check.read ? 1 : 0
|
||||||
|
aggregateTally += this.result?.[relay]?.check.write ? 1 : 0
|
||||||
|
if (aggregateTally == 3) {
|
||||||
|
this.result[relay].aggregate = 'public'
|
||||||
|
}
|
||||||
|
else if (aggregateTally == 0) {
|
||||||
|
this.result[relay].aggregate = 'offline'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.result[relay].aggregate = 'restricted'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
relaysTotal () {
|
||||||
|
return this.relays.length-1 //TODO: Figure out WHY?
|
||||||
|
},
|
||||||
|
|
||||||
|
relaysConnected () {
|
||||||
|
return Object.entries(this.result).length
|
||||||
|
},
|
||||||
|
|
||||||
|
sha1 (message) {
|
||||||
|
const hash = crypto.createHash('sha1').update(JSON.stringify(message)).digest('hex')
|
||||||
|
return hash
|
||||||
|
},
|
||||||
|
|
||||||
|
isDone(){
|
||||||
|
return this.relaysTotal()-this.relaysConnected() == 0
|
||||||
|
},
|
||||||
|
|
||||||
|
loadingComplete(){
|
||||||
|
return this.isDone() ? 'loaded' : ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
294
src/pages/SingleRelay.vue
Normal file
294
src/pages/SingleRelay.vue
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
<template>
|
||||||
|
<!-- <NavComponent /> -->
|
||||||
|
<div id="wrapper" :class="loadingComplete()">
|
||||||
|
|
||||||
|
<row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12" class="title-card">
|
||||||
|
<h1>nostr.watch<sup>{{version}}</sup></h1>
|
||||||
|
<h2>{{relayUrl()}}</h2>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12">
|
||||||
|
<LeafletSingleComponent
|
||||||
|
:geo="geo"
|
||||||
|
:relay="relay"
|
||||||
|
:result="result"
|
||||||
|
/>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12">
|
||||||
|
<div class="block">
|
||||||
|
<div v-if="result.info?.description">
|
||||||
|
{{ result.info?.description }} <br/>
|
||||||
|
<strong v-if="result.info?.pubkey">Public Key:</strong> {{ result.info?.pubkey }} <br/>
|
||||||
|
<strong v-if="result.info?.contact">Contact:</strong> <SafeMail :email="result.info?.contact" v-if="result.info?.contact" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>Status</h4>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Connected</strong> <span :class="getResultClass(relay, 'connect')" class="connect indicator"></span></li>
|
||||||
|
<li><strong>Read</strong> <span :class="getResultClass(relay, 'read')" class="read indicator"></span></li>
|
||||||
|
<li><strong>Write</strong> <span :class="getResultClass(relay, 'write')" class="write indicator"></span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<h4>Relay Info</h4>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Software:</strong> {{ result.info?.software }} </li>
|
||||||
|
<li><strong>Version</strong>: {{ result.info?.version }} </li>
|
||||||
|
</ul>
|
||||||
|
<h4>NIP Support</h4>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(nip) in result.info?.supported_nips" :key="`${relay}_${nip}`">
|
||||||
|
<a :href="nipLink(nip)" target="_blank">{{ nipFormatted(nip) }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</column>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<!-- <row container :gutter="12">
|
||||||
|
<column :xs="12" :md="12" :lg="12" class="processing-card loading">
|
||||||
|
<span v-if="(relaysTotal()-relaysConnected()>0)">Processing {{ relaysConnected() }}/{{ relaysTotal() }}</span>
|
||||||
|
</column>
|
||||||
|
</row> -->
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent} from 'vue'
|
||||||
|
import LeafletSingleComponent from '../components/LeafletSingleComponent.vue'
|
||||||
|
// import NavComponent from './NavComponent.vue'\
|
||||||
|
|
||||||
|
import { countryCodeEmoji } from 'country-code-emoji';
|
||||||
|
import emoji from 'node-emoji';
|
||||||
|
|
||||||
|
import { Row, Column } from 'vue-grid-responsive';
|
||||||
|
|
||||||
|
import { version } from '../../package.json'
|
||||||
|
|
||||||
|
import { relays } from '../../relays.yaml'
|
||||||
|
import { geo } from '../../geo.yaml'
|
||||||
|
import { messages as RELAY_MESSAGES, codes as RELAY_CODES } from '../../codes.yaml'
|
||||||
|
|
||||||
|
import { Inspector, InspectorObservation } from 'nostr-relay-inspector'
|
||||||
|
// import { Inspector, InspectorObservation } from '../../lib/nostr-relay-inspector'
|
||||||
|
|
||||||
|
import crypto from "crypto"
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
title: "nostr.watch registry & network status",
|
||||||
|
name: 'SingleRelay',
|
||||||
|
components: {
|
||||||
|
Row,
|
||||||
|
Column,
|
||||||
|
LeafletSingleComponent
|
||||||
|
// NavComponent
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
relays,
|
||||||
|
result: {},
|
||||||
|
messages: {},
|
||||||
|
connections: {},
|
||||||
|
nips: {},
|
||||||
|
alerts: {},
|
||||||
|
timeouts: {},
|
||||||
|
lastPing: Date.now(),
|
||||||
|
nextPing: Date.now() + (60*1000),
|
||||||
|
count: 0,
|
||||||
|
geo,
|
||||||
|
relay: "",
|
||||||
|
version: version
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
console.log('mounted')
|
||||||
|
this.relay = this.relayUrl()
|
||||||
|
this.check(this.relay)
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
relayUrl() {
|
||||||
|
// We will see what `params` is shortly
|
||||||
|
return `wss://${this.$route.params.relayUrl}`
|
||||||
|
},
|
||||||
|
check(relay){
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
checkLatency: true,
|
||||||
|
setIP: false,
|
||||||
|
setGeo: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
let inspect = new Inspector(relay, opts)
|
||||||
|
.on('run', (result) => {
|
||||||
|
result.aggregate = 'processing'
|
||||||
|
})
|
||||||
|
.on('open', (e, result) => {
|
||||||
|
this.result = result
|
||||||
|
})
|
||||||
|
.on('complete', (instance) => {
|
||||||
|
console.log('on_complete', instance.result.aggregate)
|
||||||
|
this.result = instance.result
|
||||||
|
this.messages[relay] = instance.inbox
|
||||||
|
// this.setFlag(relay)
|
||||||
|
this.setAggregateResult(relay)
|
||||||
|
// this.adjustResult(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) )
|
||||||
|
|
||||||
|
console.log(this.result.observations)
|
||||||
|
})
|
||||||
|
.on('close', () => {})
|
||||||
|
.on('error', () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
|
this.connections[relay] = inspect
|
||||||
|
},
|
||||||
|
getResultClass (url, key) {
|
||||||
|
let result = this.result?.check?.[key] === true
|
||||||
|
? 'success'
|
||||||
|
: this.result?.check?.[key] === false
|
||||||
|
? 'failure'
|
||||||
|
: 'pending'
|
||||||
|
return `indicator ${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 ? '✅ ' : ''
|
||||||
|
},
|
||||||
|
|
||||||
|
setCross (bool) {
|
||||||
|
return !bool ? '❌' : ''
|
||||||
|
},
|
||||||
|
setCaution (bool) {
|
||||||
|
return !bool ? '⚠️' : ''
|
||||||
|
},
|
||||||
|
identityList () {
|
||||||
|
let string = '',
|
||||||
|
extraString = '',
|
||||||
|
users = Object.entries(this.result.identities),
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
console.log(this.result.uri, 'admin', this.result.identities.serverAdmin)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
console.log('copy', text)
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
} catch($e) {
|
||||||
|
//console.log('Cannot copy');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// adjustResult (relay) {
|
||||||
|
// this.result.observations.forEach( observation => {
|
||||||
|
// if (observation.code == "BLOCKS_WRITE_STATUS_CHECK") {
|
||||||
|
// this.result.check.write = false
|
||||||
|
// this.result.aggregate = 'public'
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
|
||||||
|
setAggregateResult (relay) {
|
||||||
|
let aggregateTally = 0
|
||||||
|
aggregateTally += this.result?.[relay]?.check.connect ? 1 : 0
|
||||||
|
aggregateTally += this.result?.[relay]?.check.read ? 1 : 0
|
||||||
|
aggregateTally += this.result?.[relay]?.check.write ? 1 : 0
|
||||||
|
if (aggregateTally == 3) {
|
||||||
|
this.result.aggregate = 'public'
|
||||||
|
}
|
||||||
|
else if (aggregateTally == 0) {
|
||||||
|
this.result.aggregate = 'offline'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.result.aggregate = 'restricted'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
relaysTotal () {
|
||||||
|
return this.relays.length-1 //TODO: Figure out WHY?
|
||||||
|
},
|
||||||
|
|
||||||
|
relaysConnected () {
|
||||||
|
return Object.entries(this.result).length
|
||||||
|
},
|
||||||
|
|
||||||
|
sha1 (message) {
|
||||||
|
const hash = crypto.createHash('sha1').update(JSON.stringify(message)).digest('hex')
|
||||||
|
// //console.log(message, ':', hash)
|
||||||
|
return hash
|
||||||
|
},
|
||||||
|
|
||||||
|
isDone(){
|
||||||
|
return this.relaysTotal()-this.relaysConnected() == 0
|
||||||
|
},
|
||||||
|
|
||||||
|
loadingComplete(){
|
||||||
|
return this.isDone() ? 'loaded' : ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
27
src/router/index.js
Normal file
27
src/router/index.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// /router/index.js
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import HomePage from '../pages/HomePage.vue'
|
||||||
|
import SingleRelay from '../pages/SingleRelay.vue'
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'nostr.watch',
|
||||||
|
component: HomePage
|
||||||
|
},
|
||||||
|
// Added our new route file named profile.vue
|
||||||
|
{
|
||||||
|
path: '/:relayUrl',
|
||||||
|
name: 'nostr.watch - :relayUrl',
|
||||||
|
component: SingleRelay
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// Create Vue Router Object
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
base: process.env.BASE_URL,
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
3150
yarn-error.log
3150
yarn-error.log
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user