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"> | ||||
| 
 | ||||
|   import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import StatusIcon from "./StatusIcon.svelte" | ||||
|   import type { MCService } from "./MCService" | ||||
|   import ServiceIndicator from "./ServiceIndicator.svelte" | ||||
|  | @ -8,11 +8,36 @@ | |||
|   import Constants from "../../Models/Constants" | ||||
|   import { Utils } from "../../Utils" | ||||
|   import Loading from "../Base/Loading.svelte" | ||||
|   import Checkbox from "../Base/Checkbox.svelte" | ||||
| 
 | ||||
| 
 | ||||
|   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> { | ||||
|     return s.mapD(s => { | ||||
|  | @ -42,15 +67,13 @@ | |||
|             return "offline" | ||||
|         } | ||||
|       }), | ||||
|       message: osmApi | ||||
|       message: osmApi, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   { | ||||
|     const s = "https://studio.mapcomplete.org" | ||||
|     const status = UIEventSource.FromPromiseWithErr( | ||||
|       Utils.downloadJson(s + "/overview") | ||||
|     ) | ||||
|     const status = testDownload(s + "/overview") | ||||
|     services.push({ | ||||
|       name: s, | ||||
|       status: status.mapD(s => { | ||||
|  | @ -67,21 +90,19 @@ | |||
|         return "online" | ||||
|       }), | ||||
|       message: status.mapD(s => { | ||||
|         if(s["error"]){ | ||||
|         if (s["error"]) { | ||||
|           return s["error"] | ||||
|         } | ||||
|         const files: string[] = s["success"]["allFiles"] | ||||
|         return "Contains "+(files.length ?? "no")+" files" | ||||
|       }) | ||||
|         return "Contains " + (files.length ?? "no") + " files" | ||||
|       }), | ||||
|     }) | ||||
|   } | ||||
|   { | ||||
|     services.push( | ||||
|       { | ||||
|         name: Constants.GeoIpServer, | ||||
|         status: UIEventSource.FromPromiseWithErr( | ||||
|           Utils.downloadJson(Constants.GeoIpServer + "/status") | ||||
|         ).mapD(result => { | ||||
|         status: testDownload(Constants.GeoIpServer + "/status").mapD(result => { | ||||
|           if (result["success"].online) { | ||||
|             return "online" | ||||
|           } | ||||
|  | @ -91,18 +112,16 @@ | |||
|             return "degraded" | ||||
|           } | ||||
|         }), | ||||
|         message: simpleMessage(UIEventSource.FromPromiseWithErr( | ||||
|           Utils.downloadJson(Constants.GeoIpServer + "/ip") | ||||
|         )) | ||||
|       } | ||||
|         message: simpleMessage( | ||||
|           testDownload(Constants.GeoIpServer + "/ip"), | ||||
|         ), | ||||
|       }, | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   { | ||||
|     const s = Constants.ErrorReportServer | ||||
|     const status = UIEventSource.FromPromiseWithErr( | ||||
|       Utils.downloadJson(s.replace(/\/report$/, "/status")) | ||||
|     ) | ||||
|     const status = testDownload(s.replace(/\/report$/, "/status")) | ||||
|     services.push({ | ||||
|       name: s, | ||||
|       status: status.mapD(s => { | ||||
|  | @ -115,15 +134,13 @@ | |||
|         } | ||||
|         return "degraded" | ||||
|       }), | ||||
|       message: simpleMessage(status) | ||||
|       message: simpleMessage(status), | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   { | ||||
|     const s = Constants.linkedDataProxy.replace(/\/[^/]*$/, "") | ||||
|     const status = UIEventSource.FromPromiseWithErr( | ||||
|       Utils.downloadJson(s + "/status") | ||||
|     ) | ||||
|     const status = testDownload(s + "/status") | ||||
|     services.push({ | ||||
|       name: s, | ||||
|       status: status.mapD(s => { | ||||
|  | @ -136,15 +153,13 @@ | |||
|         } | ||||
|         return "online" | ||||
|       }), | ||||
|       message: simpleMessage(status) | ||||
|       message: simpleMessage(status), | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   { | ||||
|     const s = Constants.SummaryServer | ||||
|     const status = UIEventSource.FromPromiseWithErr( | ||||
|       Utils.downloadJson(s + "/summary/status.json") | ||||
|     ) | ||||
|     const status = testDownload(s + "/summary/status.json") | ||||
|     services.push({ | ||||
|       name: s, | ||||
|       status: status.mapD(s => { | ||||
|  | @ -177,15 +192,13 @@ | |||
| 
 | ||||
|         const json = JSON.stringify(s["success"], null, "  ") | ||||
|         return "Database is " + Math.floor(timediffDays) + " days out of sync\n\n" + json | ||||
|       }) | ||||
|       }), | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   { | ||||
|     const s = Constants.countryCoderEndpoint | ||||
|     const status = UIEventSource.FromPromiseWithErr( | ||||
|       Utils.downloadJson(s + "/0.0.0.json") | ||||
|     ) | ||||
|     const status = testDownload(s + "/0.0.0.json") | ||||
|     services.push({ | ||||
|       name: s, | ||||
|       status: status.mapD(s => { | ||||
|  | @ -198,7 +211,7 @@ | |||
|         } | ||||
|         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) { | ||||
|       const statusUrl = defaultOverpassUrl.replace(/\/interpreter$/, "/status") | ||||
|       const status = UIEventSource.FromPromiseWithErr( | ||||
|         Utils.download(statusUrl) | ||||
|       ) | ||||
|       const status = testDownload(statusUrl, true) | ||||
| 
 | ||||
|       services.push({ | ||||
|         name: "Overpass-server: " + defaultOverpassUrl, | ||||
|  | @ -228,7 +239,7 @@ | |||
| 
 | ||||
|           return "online" | ||||
|         }), | ||||
|         message: simpleMessage(status) | ||||
|         message: simpleMessage(status), | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
|  | @ -236,14 +247,12 @@ | |||
|   { | ||||
|     services.push({ | ||||
|       name: "Mangrove reviews", | ||||
|       status: UIEventSource.FromPromiseWithErr( | ||||
|         Utils.download("https://api.mangrove.reviews") | ||||
|       ).mapD(r => { | ||||
|       status: testDownload("https://api.mangrove.reviews", true).mapD(r => { | ||||
|         if (r["success"]) { | ||||
|           return "online" | ||||
|         } | ||||
|         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> | ||||
| 
 | ||||
| <h1>MapComplete status indicators</h1> | ||||
| 
 | ||||
| {#if $someLoading} | ||||
|   <Loading /> | ||||
| <div class="flex"> | ||||
| 
 | ||||
|   {#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} | ||||
| <StatusIcon status={$all} cls="w-16 h-16" /> | ||||
| 
 | ||||
| {#each services as service} | ||||
|   <ServiceIndicator {service} /> | ||||
|  |  | |||
|  | @ -13,13 +13,15 @@ | |||
| </script> | ||||
| 
 | ||||
| {#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"} | ||||
|   <Exclamation class={twJoin(cls,"rounded-full")} style="color: #eecc22"  /> | ||||
|   <Exclamation class={twJoin(cls,"rounded-full  shrink-0")} style="color: #eecc22" /> | ||||
| {: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} | ||||
|   <Loading /> | ||||
|   <div class={twJoin(cls,"rounded-full shrink-0")}> | ||||
|     <Loading /> | ||||
|   </div> | ||||
| {:else} | ||||
|   ? {status} | ||||
| {/if} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue