Skip to content

Commit

Permalink
feat: added health page
Browse files Browse the repository at this point in the history
  • Loading branch information
onmax committed Aug 16, 2024
1 parent fe363fd commit add7687
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 8 deletions.
4 changes: 2 additions & 2 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ const { data: health } = useFetch('/api/health')
Go back
</NuxtLink>
<div flex-auto />
<div :class="{ 'bg-green/10 text-green': health?.isSynced, 'bg-red/10 text-red': !health?.isSynced}" px-12 py-4 rounded-full text-11 flex="~ items-center gap-6">
<NuxtLink to="/health" :class="{ 'bg-green/10 text-green': health?.isSynced, 'bg-red/10 text-red': !health?.isSynced}" px-12 py-4 rounded-full text-11 flex="~ items-center gap-6">
<div text-10 :class="health?.isSynced ? 'i-nimiq:check' : 'i-nimiq:alert'"/>
<span font-semibold>{{ health?.isSynced ? 'Synced' : 'Not Synced' }}</span>
</div>
</NuxtLink>
<NuxtLink to="https://github.com/onmax/nimiq-validators" i-nimiq:logos-github-mono target="_blank" />
<button i-nimiq:moon @click="() => toggleDark()" />
</header>
Expand Down
80 changes: 80 additions & 0 deletions app/pages/health.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script setup lang="ts">
const { data: health, status, error } = useFetch('/api/health')
const network = useRuntimeConfig().public.nimiqNetwork
</script>

<template>
<div class="nq-prose">
<div flex="~ items-center justify-between">
<h1 max-w-inherit my-32>Network <code px-4 rounded-6>{{ network }}</code></h1>
<NuxtLink to="/api/health" target="_blank" nq-arrow nq-pill-tertiary text-12 op-80>
API
</NuxtLink>
</div>

<div v-if="status === 'pending'">
<p>Loading...</p>
</div>

<div v-else-if="status === 'error'">
<p>Failed to fetch health data</p>
<pre>{{ error }}</pre>
</div>

<div v-else-if="status === 'success' && health" mt-12>
<p v-if="health.isSynced">The network is <span text-green font-bold>synced</span></p>
<p v-else>The network is <span text-red font-bold>not synced</span></p>

<p v-if="health.flags.length > 0">
{{ JSON.stringify(health.flags) }}
</p>

<div mt-32>
<p>
Range: [{{ health.range.fromEpoch }} - {{ health.range.toEpoch }}] ({{ health.range.epochCount }} epochs)
</p>
<p text-neutral-700 text-13 m-0>
The range of blocks used to compute the score of the validators. We don't consider the first epoch as it is an speacial epoch.
</p>
</div>


<div mt-16>
<p>
Current Epoch: {{ health.currentEpoch }}
</p>
<p m-0>
Latest Fetched Epoch: {{ health.latestFetchedEpoch }}
</p>
<p text-neutral-700 text-13 m-0>
We only use finished epochs to compute the score of the validators.
</p>
</div>

<template v-if="health.missingEpochs.length > 0">
<h3>Missing Epochs</h3>
<p v-for="epoch in health.missingEpochs" :key="epoch">{{ epoch }}</p>
</template>

<p mt-32>
{{ health.fetchedEpochs.length }} epochs fetched
</p>
<p text-11 m-0>
{{ health.fetchedEpochs.join(', ') }}
</p>

<p mt-32>
{{ health.missingEpochs.length }} epochs missing
</p>
<p v-if="health.missingEpochs.length > 0" text-11 m-0>
{{ health.missingEpochs.join(', ') }}
</p>

<details mt-32>
<summary>Details</summary>
<pre px-2 font-12>{{ JSON.stringify(health, null, 2) }}</pre>
</details>
</div>

</div>
</template>
23 changes: 17 additions & 6 deletions server/api/health.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ export enum HealthFlag {
export interface HealthStatus {
// TODO
// latestScoreEpoch: number | undefined
latestActivityEpoch: number | undefined
latestFetchedEpoch: number | undefined
totalValidators: number
headBlockNumber: number
currentEpoch: number
missingEpochs: number[]
fetchedEpochs: number[]
range: Range

isSynced: boolean
Expand All @@ -26,14 +27,17 @@ export interface HealthStatus {
export default defineEventHandler(async (event) => {
const db = await useDrizzle();

// Get the latest epoch number in the scores table
const rpcClient = await getRpcClient();

// Get the latest epoch number in the activity table
const latestActivityEpoch = await db
const latestActivityBlock = await db
.select({ epoch: max(tables.activity.epochBlockNumber) })
.from(tables.activity)
.get()
.then((row) => row?.epoch ?? -1);
const { data: latestFetchedEpoch, error: errorLatestFetchedEpoch } = await rpcClient.policy.getEpochAt(latestActivityBlock)
if (errorLatestFetchedEpoch)
throw errorLatestFetchedEpoch;

// Get the total number of validators
const totalValidators = await db
Expand All @@ -42,12 +46,18 @@ export default defineEventHandler(async (event) => {
.get()
.then((row) => row?.count ?? 0);

const rpcClient = await getRpcClient();
const fetchedEpochs = await db
.selectDistinct({ epoch: tables.activity.epochBlockNumber })
.from(tables.activity)
.orderBy(tables.activity.epochBlockNumber)
.all()
.then((rows) => rows.map(row => row.epoch));


const { data: headBlockNumber, error: errorHeadBlockNumber } = await rpcClient.blockchain.getBlockNumber()
if (errorHeadBlockNumber)
throw errorHeadBlockNumber;

const { data: currentEpoch, error: errorCurrentEpoch } = await rpcClient.blockchain.getEpochNumber()
if (errorCurrentEpoch)
throw errorCurrentEpoch;
Expand All @@ -61,14 +71,15 @@ export default defineEventHandler(async (event) => {

// Combine all the data into a HealthStatus object
const healthStatus: HealthStatus = {
latestActivityEpoch,
latestFetchedEpoch,
totalValidators,
headBlockNumber,
currentEpoch,
range,
missingEpochs,
isSynced,
flags,
fetchedEpochs,
};

// Return the health status
Expand Down

0 comments on commit add7687

Please sign in to comment.