forked from MapComplete/MapComplete
Status page: Add support for personal traffic light and rechecking
This commit is contained in:
parent
740702e20f
commit
695c328bc0
2 changed files with 116 additions and 47 deletions
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import StatusIcon from "./StatusIcon.svelte"
|
import StatusIcon from "./StatusIcon.svelte"
|
||||||
import type { MCService } from "./MCService"
|
import type { MCService } from "./MCService"
|
||||||
import ServiceIndicator from "./ServiceIndicator.svelte"
|
import ServiceIndicator from "./ServiceIndicator.svelte"
|
||||||
|
@ -8,11 +8,36 @@
|
||||||
import Constants from "../../Models/Constants"
|
import Constants from "../../Models/Constants"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import Loading from "../Base/Loading.svelte"
|
import Loading from "../Base/Loading.svelte"
|
||||||
|
import Checkbox from "../Base/Checkbox.svelte"
|
||||||
|
|
||||||
|
|
||||||
let services: MCService[] = []
|
let services: MCService[] = []
|
||||||
|
|
||||||
let states = [undefined, "online", "degraded", "offline"]
|
let recheckSignal: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||||
|
let checkSignal = Stores.Chronic(10000)
|
||||||
|
let autoCheckAgain = new UIEventSource<boolean>(false)
|
||||||
|
|
||||||
|
function testDownload(url: string, raw: boolean = false): Store<{ success } | { error }> {
|
||||||
|
const src = new UIEventSource(undefined)
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
const promise = raw ? Utils.download(url) : Utils.downloadJson(url)
|
||||||
|
promise
|
||||||
|
?.then((d) => src.setData({ success: d }))
|
||||||
|
?.catch((err) => src.setData({ error: err }))
|
||||||
|
}
|
||||||
|
|
||||||
|
check()
|
||||||
|
recheckSignal.addCallback(_ => check())
|
||||||
|
checkSignal.addCallback(_ => {
|
||||||
|
if (autoCheckAgain.data) {
|
||||||
|
check()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return src
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function simpleMessage(s: Store<{ success: any } | { error: any }>): Store<string> {
|
function simpleMessage(s: Store<{ success: any } | { error: any }>): Store<string> {
|
||||||
return s.mapD(s => {
|
return s.mapD(s => {
|
||||||
|
@ -42,15 +67,13 @@
|
||||||
return "offline"
|
return "offline"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
message: osmApi
|
message: osmApi,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const s = "https://studio.mapcomplete.org"
|
const s = "https://studio.mapcomplete.org"
|
||||||
const status = UIEventSource.FromPromiseWithErr(
|
const status = testDownload(s + "/overview")
|
||||||
Utils.downloadJson(s + "/overview")
|
|
||||||
)
|
|
||||||
services.push({
|
services.push({
|
||||||
name: s,
|
name: s,
|
||||||
status: status.mapD(s => {
|
status: status.mapD(s => {
|
||||||
|
@ -67,21 +90,19 @@
|
||||||
return "online"
|
return "online"
|
||||||
}),
|
}),
|
||||||
message: status.mapD(s => {
|
message: status.mapD(s => {
|
||||||
if(s["error"]){
|
if (s["error"]) {
|
||||||
return s["error"]
|
return s["error"]
|
||||||
}
|
}
|
||||||
const files: string[] = s["success"]["allFiles"]
|
const files: string[] = s["success"]["allFiles"]
|
||||||
return "Contains "+(files.length ?? "no")+" files"
|
return "Contains " + (files.length ?? "no") + " files"
|
||||||
})
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
services.push(
|
services.push(
|
||||||
{
|
{
|
||||||
name: Constants.GeoIpServer,
|
name: Constants.GeoIpServer,
|
||||||
status: UIEventSource.FromPromiseWithErr(
|
status: testDownload(Constants.GeoIpServer + "/status").mapD(result => {
|
||||||
Utils.downloadJson(Constants.GeoIpServer + "/status")
|
|
||||||
).mapD(result => {
|
|
||||||
if (result["success"].online) {
|
if (result["success"].online) {
|
||||||
return "online"
|
return "online"
|
||||||
}
|
}
|
||||||
|
@ -91,18 +112,16 @@
|
||||||
return "degraded"
|
return "degraded"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
message: simpleMessage(UIEventSource.FromPromiseWithErr(
|
message: simpleMessage(
|
||||||
Utils.downloadJson(Constants.GeoIpServer + "/ip")
|
testDownload(Constants.GeoIpServer + "/ip"),
|
||||||
))
|
),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const s = Constants.ErrorReportServer
|
const s = Constants.ErrorReportServer
|
||||||
const status = UIEventSource.FromPromiseWithErr(
|
const status = testDownload(s.replace(/\/report$/, "/status"))
|
||||||
Utils.downloadJson(s.replace(/\/report$/, "/status"))
|
|
||||||
)
|
|
||||||
services.push({
|
services.push({
|
||||||
name: s,
|
name: s,
|
||||||
status: status.mapD(s => {
|
status: status.mapD(s => {
|
||||||
|
@ -115,15 +134,13 @@
|
||||||
}
|
}
|
||||||
return "degraded"
|
return "degraded"
|
||||||
}),
|
}),
|
||||||
message: simpleMessage(status)
|
message: simpleMessage(status),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const s = Constants.linkedDataProxy.replace(/\/[^/]*$/, "")
|
const s = Constants.linkedDataProxy.replace(/\/[^/]*$/, "")
|
||||||
const status = UIEventSource.FromPromiseWithErr(
|
const status = testDownload(s + "/status")
|
||||||
Utils.downloadJson(s + "/status")
|
|
||||||
)
|
|
||||||
services.push({
|
services.push({
|
||||||
name: s,
|
name: s,
|
||||||
status: status.mapD(s => {
|
status: status.mapD(s => {
|
||||||
|
@ -136,15 +153,13 @@
|
||||||
}
|
}
|
||||||
return "online"
|
return "online"
|
||||||
}),
|
}),
|
||||||
message: simpleMessage(status)
|
message: simpleMessage(status),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const s = Constants.SummaryServer
|
const s = Constants.SummaryServer
|
||||||
const status = UIEventSource.FromPromiseWithErr(
|
const status = testDownload(s + "/summary/status.json")
|
||||||
Utils.downloadJson(s + "/summary/status.json")
|
|
||||||
)
|
|
||||||
services.push({
|
services.push({
|
||||||
name: s,
|
name: s,
|
||||||
status: status.mapD(s => {
|
status: status.mapD(s => {
|
||||||
|
@ -177,15 +192,13 @@
|
||||||
|
|
||||||
const json = JSON.stringify(s["success"], null, " ")
|
const json = JSON.stringify(s["success"], null, " ")
|
||||||
return "Database is " + Math.floor(timediffDays) + " days out of sync\n\n" + json
|
return "Database is " + Math.floor(timediffDays) + " days out of sync\n\n" + json
|
||||||
})
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const s = Constants.countryCoderEndpoint
|
const s = Constants.countryCoderEndpoint
|
||||||
const status = UIEventSource.FromPromiseWithErr(
|
const status = testDownload(s + "/0.0.0.json")
|
||||||
Utils.downloadJson(s + "/0.0.0.json")
|
|
||||||
)
|
|
||||||
services.push({
|
services.push({
|
||||||
name: s,
|
name: s,
|
||||||
status: status.mapD(s => {
|
status: status.mapD(s => {
|
||||||
|
@ -198,7 +211,7 @@
|
||||||
}
|
}
|
||||||
return "degraded"
|
return "degraded"
|
||||||
}),
|
}),
|
||||||
message: status.map(s => JSON.stringify(s))
|
message: status.map(s => JSON.stringify(s)),
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -207,9 +220,7 @@
|
||||||
{
|
{
|
||||||
for (const defaultOverpassUrl of Constants.defaultOverpassUrls) {
|
for (const defaultOverpassUrl of Constants.defaultOverpassUrls) {
|
||||||
const statusUrl = defaultOverpassUrl.replace(/\/interpreter$/, "/status")
|
const statusUrl = defaultOverpassUrl.replace(/\/interpreter$/, "/status")
|
||||||
const status = UIEventSource.FromPromiseWithErr(
|
const status = testDownload(statusUrl, true)
|
||||||
Utils.download(statusUrl)
|
|
||||||
)
|
|
||||||
|
|
||||||
services.push({
|
services.push({
|
||||||
name: "Overpass-server: " + defaultOverpassUrl,
|
name: "Overpass-server: " + defaultOverpassUrl,
|
||||||
|
@ -228,7 +239,7 @@
|
||||||
|
|
||||||
return "online"
|
return "online"
|
||||||
}),
|
}),
|
||||||
message: simpleMessage(status)
|
message: simpleMessage(status),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,14 +247,12 @@
|
||||||
{
|
{
|
||||||
services.push({
|
services.push({
|
||||||
name: "Mangrove reviews",
|
name: "Mangrove reviews",
|
||||||
status: UIEventSource.FromPromiseWithErr(
|
status: testDownload("https://api.mangrove.reviews", true).mapD(r => {
|
||||||
Utils.download("https://api.mangrove.reviews")
|
|
||||||
).mapD(r => {
|
|
||||||
if (r["success"]) {
|
if (r["success"]) {
|
||||||
return "online"
|
return "online"
|
||||||
}
|
}
|
||||||
return "offline"
|
return "offline"
|
||||||
})
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,14 +280,72 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const trafficLightUrl = "http://traffic_light_bicycle.local/"
|
||||||
|
let trafficLightIsOnline = testDownload(trafficLightUrl + "/status", true)
|
||||||
|
let enableTrafficLight = new UIEventSource(true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* So... IN my room, there is a traffic light. Like, an actual traffic light with bicycles.
|
||||||
|
* I put in an ESP32 and can now control it remotely - which is precisely what this code does.
|
||||||
|
* @param state
|
||||||
|
*/
|
||||||
|
async function setTrafficLight(state: "online" | "degraded" | "offline") {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const url = trafficLightUrl
|
||||||
|
const status = await Utils.downloadJson(url + "status")
|
||||||
|
console.log(status)
|
||||||
|
if (!enableTrafficLight.data) {
|
||||||
|
await Utils.download(url + "configure?mode=0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch (state) {
|
||||||
|
|
||||||
|
case "offline":
|
||||||
|
await Utils.download(url + "configure?mode=3")
|
||||||
|
break
|
||||||
|
case "degraded":
|
||||||
|
await Utils.download(url + "configure?mode=2")
|
||||||
|
break
|
||||||
|
case "online":
|
||||||
|
await Utils.download(url + "configure?mode=1")
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
await Utils.download(url + "configure?mode=7")
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Could not connect to the traffic light")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
all.addCallbackAndRunD(state => {
|
||||||
|
setTrafficLight(state)
|
||||||
|
})
|
||||||
|
enableTrafficLight.addCallbackAndRunD(_ => {
|
||||||
|
setTrafficLight(all.data)
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>MapComplete status indicators</h1>
|
<h1>MapComplete status indicators</h1>
|
||||||
|
|
||||||
{#if $someLoading}
|
<div class="flex">
|
||||||
<Loading />
|
|
||||||
|
{#if $someLoading}
|
||||||
|
<Loading />
|
||||||
|
{/if}
|
||||||
|
<StatusIcon status={$all} cls="w-16 h-16" />
|
||||||
|
<button on:click={() => recheckSignal.ping()}>Check again</button>
|
||||||
|
<Checkbox selected={autoCheckAgain}>
|
||||||
|
Automatically check again every 10s
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if $trafficLightIsOnline?.["success"] }
|
||||||
|
<Checkbox selected={enableTrafficLight}>Enable traffic light</Checkbox>
|
||||||
{/if}
|
{/if}
|
||||||
<StatusIcon status={$all} cls="w-16 h-16" />
|
|
||||||
|
|
||||||
{#each services as service}
|
{#each services as service}
|
||||||
<ServiceIndicator {service} />
|
<ServiceIndicator {service} />
|
||||||
|
|
|
@ -13,13 +13,15 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if status === "online"}
|
{#if status === "online"}
|
||||||
<CheckCircle class={twJoin(cls,"rounded-full")} style="color: #22cc22" />
|
<CheckCircle class={twJoin(cls,"rounded-full shrink-0")} style="color: #22cc22" />
|
||||||
{:else if status === "degraded"}
|
{:else if status === "degraded"}
|
||||||
<Exclamation class={twJoin(cls,"rounded-full")} style="color: #eecc22" />
|
<Exclamation class={twJoin(cls,"rounded-full shrink-0")} style="color: #eecc22" />
|
||||||
{:else if status === "offline"}
|
{:else if status === "offline"}
|
||||||
<XCircleIcon class={twJoin(cls,"rounded-full")} style="color: #bb2222" />
|
<XCircleIcon class={twJoin(cls,"rounded-full shrink-0")} style="color: #bb2222" />
|
||||||
{:else if status === undefined}
|
{:else if status === undefined}
|
||||||
<Loading />
|
<div class={twJoin(cls,"rounded-full shrink-0")}>
|
||||||
|
<Loading />
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
? {status}
|
? {status}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue