forked from coracle/flotilla
Create health check framework
This commit is contained in:
@@ -1,140 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import {derived} from "svelte/store"
|
|
||||||
import {shuffle, partition, ifLet} from "@welshman/lib"
|
|
||||||
import {RelayMode} from "@welshman/util"
|
|
||||||
import {
|
|
||||||
pubkey,
|
|
||||||
derivePubkeyRelays,
|
|
||||||
getRelay,
|
|
||||||
setWriteRelays,
|
|
||||||
setReadRelays,
|
|
||||||
setSearchRelays,
|
|
||||||
setMessagingRelays,
|
|
||||||
} from "@welshman/app"
|
|
||||||
import Stars from "@assets/icons/stars.svg?dataurl"
|
|
||||||
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
|
|
||||||
import DangerTriangle from "@assets/icons/danger-triangle.svg?dataurl"
|
|
||||||
import Stethoscope from "@assets/icons/stethoscope.svg?dataurl"
|
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
|
||||||
import Button from "@lib/components/Button.svelte"
|
|
||||||
import type {ActionItem} from "@app/components/RelaySettingsActionItem.svelte"
|
|
||||||
import RelaySettingsActionItem from "@app/components/RelaySettingsActionItem.svelte"
|
|
||||||
import {hasNip50, DEFAULT_RELAYS, DEFAULT_MESSAGING_RELAYS, PLATFORM_NAME} from "@app/core/state"
|
|
||||||
|
|
||||||
const readRelayUrls = derivePubkeyRelays($pubkey!, RelayMode.Read)
|
|
||||||
const writeRelayUrls = derivePubkeyRelays($pubkey!, RelayMode.Write)
|
|
||||||
const searchRelayUrls = derivePubkeyRelays($pubkey!, RelayMode.Search)
|
|
||||||
const messagingRelayUrls = derivePubkeyRelays($pubkey!, RelayMode.Messaging)
|
|
||||||
|
|
||||||
const actionItems = derived(
|
|
||||||
[readRelayUrls, writeRelayUrls, messagingRelayUrls, searchRelayUrls],
|
|
||||||
([$readRelayUrls, $writeRelayUrls, $messagingRelayUrls, $searchRelayUrls]) => {
|
|
||||||
const $actionItems: ActionItem[] = []
|
|
||||||
|
|
||||||
if ($readRelayUrls.length <= 1) {
|
|
||||||
$actionItems.push({
|
|
||||||
title: "Missing Inbox Relays",
|
|
||||||
subtitle: "Other people aren't currently able to reliably tag you in public notes.",
|
|
||||||
action: "Update",
|
|
||||||
apply: () => setReadRelays(DEFAULT_RELAYS),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($writeRelayUrls.length <= 1) {
|
|
||||||
$actionItems.push({
|
|
||||||
title: "Missing Outbox Relays",
|
|
||||||
subtitle: "Other people aren't currently able to reliably find your public notes.",
|
|
||||||
action: "Update",
|
|
||||||
apply: () => setWriteRelays(DEFAULT_RELAYS),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($messagingRelayUrls.length <= 1) {
|
|
||||||
$actionItems.push({
|
|
||||||
title: "Missing DM Relays",
|
|
||||||
subtitle: "You aren't currently able to reliably send or receive direct messages.",
|
|
||||||
action: "Update",
|
|
||||||
apply: () => setMessagingRelays(DEFAULT_MESSAGING_RELAYS),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($readRelayUrls.length > 8) {
|
|
||||||
$actionItems.push({
|
|
||||||
title: "Too Many Inbox Relays",
|
|
||||||
subtitle:
|
|
||||||
"You have more inbox relays than is really necessary, which can affect resource usage.",
|
|
||||||
action: "Prune Selections",
|
|
||||||
apply: () => setReadRelays(shuffle($readRelayUrls).slice(0, 5)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($writeRelayUrls.length > 8) {
|
|
||||||
$actionItems.push({
|
|
||||||
title: "Too Many Outbox Relays",
|
|
||||||
subtitle:
|
|
||||||
"You have more outbox relays than is really necessary, which can affect resource usage.",
|
|
||||||
action: "Prune Selections",
|
|
||||||
apply: () => setWriteRelays(shuffle($writeRelayUrls).slice(0, 5)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($messagingRelayUrls.length > 8) {
|
|
||||||
$actionItems.push({
|
|
||||||
title: "Too Many DM Relays",
|
|
||||||
subtitle:
|
|
||||||
"You have more DM relays than is really necessary, which can affect resource usage.",
|
|
||||||
action: "Prune Selections",
|
|
||||||
apply: () => setMessagingRelays(shuffle($messagingRelayUrls).slice(0, 5)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const [okSearchRelays, badSearchRelays] = partition(
|
|
||||||
url => Boolean(ifLet(getRelay(url), hasNip50)),
|
|
||||||
$searchRelayUrls,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (badSearchRelays.length > 0) {
|
|
||||||
$actionItems.push({
|
|
||||||
title: "Invalid Search Relays",
|
|
||||||
subtitle: `Some of your search relays don't support search.`,
|
|
||||||
action: "Remove Invalid",
|
|
||||||
apply: () => setSearchRelays(okSearchRelays),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return $actionItems
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const applyAll = () => {
|
|
||||||
for (const actionItem of $actionItems) {
|
|
||||||
actionItem.apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="card2 bg-alt flex flex-col gap-4 shadow-md">
|
|
||||||
<div class="flex justify-between items-center">
|
|
||||||
<strong class="flex items-center gap-3 text-lg">
|
|
||||||
<Icon icon={Stethoscope} />
|
|
||||||
Health Check
|
|
||||||
</strong>
|
|
||||||
<span class="flex items-center gap-2 text-sm">
|
|
||||||
<Icon icon={$actionItems.length === 0 ? CheckCircle : DangerTriangle} />
|
|
||||||
{$actionItems.length} Issue{$actionItems.length === 1 ? "" : "s"} Detected
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
{PLATFORM_NAME} actively checks your connection to the network in the background to discover relays
|
|
||||||
that are offline, that you don't have access to, or are otherwise causing trouble.
|
|
||||||
</p>
|
|
||||||
{#each $actionItems as actionItem}
|
|
||||||
<RelaySettingsActionItem {...actionItem} />
|
|
||||||
{/each}
|
|
||||||
{#if $actionItems.length > 0}
|
|
||||||
<Button class="btn btn-primary" onclick={applyAll}>
|
|
||||||
<Icon icon={Stars} />
|
|
||||||
Apply All Recommendations
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
+12
-13
@@ -1,27 +1,26 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
export type ActionItem = {
|
|
||||||
title: string
|
|
||||||
subtitle: string
|
|
||||||
action: string
|
|
||||||
apply: () => unknown
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Stars from "@assets/icons/stars.svg?dataurl"
|
import Stars from "@assets/icons/stars.svg?dataurl"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import type {HealthCheck} from "@app/util/health"
|
||||||
|
import {applyHealthCheck} from "@app/util/health"
|
||||||
|
|
||||||
const {title, action, subtitle, apply}: ActionItem = $props()
|
type Props = {
|
||||||
|
healthCheck: HealthCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
const {healthCheck}: Props = $props()
|
||||||
|
|
||||||
|
const apply = () => applyHealthCheck(healthCheck)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="card2 card2-sm bg-alt flex justify-between">
|
<div class="card2 card2-sm bg-alt flex justify-between">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<strong>{title}</strong>
|
<strong>{healthCheck.title}</strong>
|
||||||
<p class="text-sm">{subtitle}</p>
|
<p class="text-sm">{healthCheck.description}</p>
|
||||||
</div>
|
</div>
|
||||||
<Button class="btn btn-neutral btn-sm" onclick={apply}>
|
<Button class="btn btn-neutral btn-sm" onclick={apply}>
|
||||||
<Icon icon={Stars} />
|
<Icon icon={Stars} />
|
||||||
{action}
|
{healthCheck.action}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Stars from "@assets/icons/stars.svg?dataurl"
|
||||||
|
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
|
||||||
|
import DangerTriangle from "@assets/icons/danger-triangle.svg?dataurl"
|
||||||
|
import Stethoscope from "@assets/icons/stethoscope.svg?dataurl"
|
||||||
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import RelaySettingsHealthCheck from "@app/components/RelaySettingsHealthCheck.svelte"
|
||||||
|
import {PLATFORM_NAME} from "@app/core/state"
|
||||||
|
import {pendingHealthChecks, applyHealthCheck} from "@app/util/health"
|
||||||
|
|
||||||
|
const applyAll = () => {
|
||||||
|
for (const healthCheck of $pendingHealthChecks) {
|
||||||
|
applyHealthCheck(healthCheck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="card2 bg-alt flex flex-col gap-4 shadow-md">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<strong class="flex items-center gap-3 text-lg">
|
||||||
|
<Icon icon={Stethoscope} />
|
||||||
|
Health Check
|
||||||
|
</strong>
|
||||||
|
<span class="flex items-center gap-2 text-sm">
|
||||||
|
<Icon icon={$pendingHealthChecks.length === 0 ? CheckCircle : DangerTriangle} />
|
||||||
|
{$pendingHealthChecks.length} Issue{$pendingHealthChecks.length === 1 ? "" : "s"} Detected
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
{PLATFORM_NAME} actively checks your connection to the network in the background to discover relays
|
||||||
|
that are offline, that you don't have access to, or are otherwise causing trouble.
|
||||||
|
</p>
|
||||||
|
{#each $pendingHealthChecks as healthCheck}
|
||||||
|
<RelaySettingsHealthCheck {healthCheck} />
|
||||||
|
{/each}
|
||||||
|
{#if $pendingHealthChecks.length > 0}
|
||||||
|
<Button class="btn btn-primary" onclick={applyAll}>
|
||||||
|
<Icon icon={Stars} />
|
||||||
|
Apply All Recommendations
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import {derived, get} from "svelte/store"
|
||||||
|
import {not, ifLet, sample} from "@welshman/lib"
|
||||||
|
import {getRelaysFromList, RelayMode} from "@welshman/util"
|
||||||
|
import {
|
||||||
|
getRelay,
|
||||||
|
setWriteRelays,
|
||||||
|
setReadRelays,
|
||||||
|
setSearchRelays,
|
||||||
|
setMessagingRelays,
|
||||||
|
userRelayList,
|
||||||
|
userSearchRelayList,
|
||||||
|
userMessagingRelayList,
|
||||||
|
} from "@welshman/app"
|
||||||
|
import {hasNip50, DEFAULT_RELAYS, DEFAULT_MESSAGING_RELAYS} from "@app/core/state"
|
||||||
|
|
||||||
|
export type HealthCheckContext = {
|
||||||
|
readRelays: string[]
|
||||||
|
writeRelays: string[]
|
||||||
|
messagingRelays: string[]
|
||||||
|
searchRelays: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HealthCheck = {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
action: string
|
||||||
|
isPending: (context: HealthCheckContext) => boolean
|
||||||
|
apply: (context: HealthCheckContext) => unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export const healthCheckContext = derived(
|
||||||
|
[userRelayList, userSearchRelayList, userMessagingRelayList],
|
||||||
|
([$userRelayList, $userSearchRelayList, $userMessagingRelayList]) => {
|
||||||
|
return {
|
||||||
|
readRelays: getRelaysFromList($userRelayList, RelayMode.Read),
|
||||||
|
writeRelays: getRelaysFromList($userRelayList, RelayMode.Write),
|
||||||
|
searchRelays: getRelaysFromList($userSearchRelayList),
|
||||||
|
messagingRelays: getRelaysFromList($userMessagingRelayList),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const healthChecks: HealthCheck[] = [
|
||||||
|
{
|
||||||
|
title: "Missing Inbox Relays",
|
||||||
|
description: "Other people aren't currently able to reliably tag you in public notes.",
|
||||||
|
action: "Update",
|
||||||
|
isPending: context => context.readRelays.length <= 1,
|
||||||
|
apply: () => setReadRelays(DEFAULT_RELAYS),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Missing Outbox Relays",
|
||||||
|
description: "Other people aren't currently able to reliably find your public notes.",
|
||||||
|
action: "Update",
|
||||||
|
isPending: context => context.writeRelays.length <= 1,
|
||||||
|
apply: () => setWriteRelays(DEFAULT_RELAYS),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Missing DM Relays",
|
||||||
|
description: "You aren't currently able to reliably send or receive direct messages.",
|
||||||
|
action: "Update",
|
||||||
|
isPending: context => context.messagingRelays.length <= 1,
|
||||||
|
apply: () => setMessagingRelays(DEFAULT_MESSAGING_RELAYS),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Too Many Inbox Relays",
|
||||||
|
description:
|
||||||
|
"You have more inbox relays than is really necessary, which can affect resource usage.",
|
||||||
|
action: "Prune Selections",
|
||||||
|
isPending: context => context.readRelays.length > 8,
|
||||||
|
apply: context => setReadRelays(sample(5, context.readRelays)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Too Many Outbox Relays",
|
||||||
|
description:
|
||||||
|
"You have more outbox relays than is really necessary, which can affect resource usage.",
|
||||||
|
action: "Prune Selections",
|
||||||
|
isPending: context => context.writeRelays.length > 8,
|
||||||
|
apply: context => setWriteRelays(sample(5, context.writeRelays)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Too Many DM Relays",
|
||||||
|
description:
|
||||||
|
"You have more DM relays than is really necessary, which can affect resource usage.",
|
||||||
|
action: "Prune Selections",
|
||||||
|
isPending: context => context.messagingRelays.length > 8,
|
||||||
|
apply: context => setMessagingRelays(sample(5, context.messagingRelays)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Invalid Search Relays",
|
||||||
|
description: "Some of your search relays don't support search.",
|
||||||
|
action: "Remove Invalid",
|
||||||
|
isPending: context => context.searchRelays.some(url => not(ifLet(getRelay(url), hasNip50))),
|
||||||
|
apply: context =>
|
||||||
|
setSearchRelays(context.searchRelays.filter(url => ifLet(getRelay(url), hasNip50))),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const isHealthCheckPending = (healthCheck: HealthCheck) =>
|
||||||
|
healthCheck.isPending(get(healthCheckContext))
|
||||||
|
|
||||||
|
export const applyHealthCheck = (healthCheck: HealthCheck) =>
|
||||||
|
healthCheck.apply(get(healthCheckContext))
|
||||||
|
|
||||||
|
export const pendingHealthChecks = derived(healthCheckContext, ctx =>
|
||||||
|
healthChecks.filter(hc => hc.isPending(ctx)),
|
||||||
|
)
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
import ForbiddenCircle from "@assets/icons/forbidden-circle.svg?dataurl"
|
import ForbiddenCircle from "@assets/icons/forbidden-circle.svg?dataurl"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import RelaySettingsItem from "@app/components/RelaySettingsItem.svelte"
|
import RelaySettingsItem from "@app/components/RelaySettingsItem.svelte"
|
||||||
import RelaySettingsActionItems from "@app/components/RelaySettingsActionItems.svelte"
|
import RelaySettingsHealthChecks from "@app/components/RelaySettingsHealthChecks.svelte"
|
||||||
import {hasNip50} from "@app/core/state"
|
import {hasNip50} from "@app/core/state"
|
||||||
import {discoverRelays} from "@app/core/requests"
|
import {discoverRelays} from "@app/core/requests"
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="content flex flex-col gap-4">
|
<div class="content flex flex-col gap-4">
|
||||||
<RelaySettingsActionItems />
|
<RelaySettingsHealthChecks />
|
||||||
<div class="card2 bg-alt flex flex-col gap-4 shadow-md">
|
<div class="card2 bg-alt flex flex-col gap-4 shadow-md">
|
||||||
<strong class="flex items-center gap-3 text-lg">
|
<strong class="flex items-center gap-3 text-lg">
|
||||||
<Icon icon={Server} />
|
<Icon icon={Server} />
|
||||||
|
|||||||
Reference in New Issue
Block a user