summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2021-07-15 20:34:00 +0200
committerDaniel Friesel <derf@finalrewind.org>2021-07-15 20:34:00 +0200
commit83f54386b7877ec66c70b79962d3cd73a17ac8a5 (patch)
treefec84d2a72685f355c27ef393523f39c7fbe23a5
parente258e3e1eb2d2dafc92c016253fd29d48869af73 (diff)
add error handling for hafas and efa requests
-rwxr-xr-xbin/nvm58
-rw-r--r--sass/app.scss8
-rw-r--r--static/v0/css/dark.min.css2
-rw-r--r--static/v0/css/light.min.css2
-rw-r--r--static/v0/js/geolocation.js6
-rw-r--r--templates/departure_list.html9
-rw-r--r--templates/landing_page.html9
7 files changed, 83 insertions, 11 deletions
diff --git a/bin/nvm b/bin/nvm
index 74cf531..bc502ba 100755
--- a/bin/nvm
+++ b/bin/nvm
@@ -87,7 +87,10 @@ class EFA:
async with aiohttp.ClientSession() as session:
async with session.post(self.url, data=self.post_data) as response:
# EFA may return JSON with a text/html Content-Type, which response.json() does not like.
- departures = json.loads(await response.text())
+ try:
+ departures = json.loads(await response.text())
+ except json.decoder.JSONDecodeError:
+ raise RuntimeError(response) from None
return list(map(EFADeparture, departures["departureList"]))
@@ -288,16 +291,26 @@ async def show_departure_board(request, eva=None):
efa_by_iso8601 = dict()
+ warning = None
if len(departures) and ", " in station_name:
name, place = station_name.split(", ")
efa_endpoint = apis.get_efa(departures[0].location)
if efa_endpoint:
efa = EFA(efa_endpoint["endpoint"])
- efa_departures = await efa.get_departures(place, name, now)
- for departure in efa_departures:
- if departure.iso8601 not in efa_by_iso8601:
- efa_by_iso8601[departure.iso8601] = list()
- efa_by_iso8601[departure.iso8601].append(departure)
+ try:
+ efa_departures = await efa.get_departures(place, name, now)
+ for departure in efa_departures:
+ if departure.iso8601 not in efa_by_iso8601:
+ efa_by_iso8601[departure.iso8601] = list()
+ efa_by_iso8601[departure.iso8601].append(departure)
+ except RuntimeError as e:
+ (response,) = e.args
+ text = await response.text()
+ warning = {
+ "lead": "Detailabfrage fehlgeschlagen:",
+ "body": "Angaben sind möglicherweise unvollständig",
+ "code": f"""EFA server {efa_endpoint["endpoint"]} returned HTTP {response.status} '{text[:10224]}'""",
+ }
for departure in departures:
departure.add_efa(efa_by_iso8601.get(departure.iso8601, list()))
@@ -306,7 +319,9 @@ async def show_departure_board(request, eva=None):
departure_board = env.get_template("departure_list.html")
return web.Response(
- body=departure_board.render(title=station_name, departures=departures),
+ body=departure_board.render(
+ title=station_name, departures=departures, warning=warning
+ ),
headers=headers,
)
@@ -314,9 +329,24 @@ async def show_departure_board(request, eva=None):
async def redirect_to_departure_board(request):
stop_name = request.query["name"]
request_url = f"{db_rest_api}/locations?query={stop_name}&poi=false&addresses=false"
- logging.debug(f"Requesting stops matcihng '{stop_name}' from {request_url}")
+ logging.debug(f"Requesting stops matching '{stop_name}' from {request_url}")
async with aiohttp.ClientSession() as session:
async with session.get(request_url) as response:
+ if response.status != 200:
+ error = await response.text()
+ landing_page = env.get_template("landing_page.html")
+ return web.Response(
+ body=landing_page.render(
+ title="NVM",
+ error={
+ "lead": "Haltestellensuche fehlgeschlagen",
+ "body": "",
+ "code": f"Server returned HTTP {response.status} '{error[:10224]}'",
+ },
+ ),
+ headers=headers,
+ status=500,
+ )
stops = await response.json()
for stop in stops:
@@ -348,6 +378,18 @@ async def ajax_geolocation(request):
logging.debug(f"Requesting stops near {lat}/{lon} from {request_url}")
async with aiohttp.ClientSession() as session:
async with session.get(request_url) as response:
+ if response.status != 200:
+ text = await response.text()
+ return web.Response(
+ body=json.dumps(
+ {
+ "error": True,
+ "msg": f"HTTP {response.status} '{text[:1024]}'",
+ }
+ ),
+ headers=ajax_headers,
+ status=500,
+ )
departures = await response.json()
return web.Response(
body=json.dumps(departures),
diff --git a/sass/app.scss b/sass/app.scss
index 73793ad..a4f6840 100644
--- a/sass/app.scss
+++ b/sass/app.scss
@@ -37,13 +37,19 @@ html {
.warning {
padding: 15px;
- margin-bottom: 20px;
border: 1px solid #faebcc;
border-radius: 4px;
color: #8a6d3b;
background-color: #fcf8e3;
margin-left: auto;
margin-right: auto;
+
+ .errcode {
+ font-family: Monospace;
+ margin-top: 2em;
+ font-size: 100%;
+ color: #aaaaaa;
+ }
}
.error {
diff --git a/static/v0/css/dark.min.css b/static/v0/css/dark.min.css
index 40255e2..b27cede 100644
--- a/static/v0/css/dark.min.css
+++ b/static/v0/css/dark.min.css
@@ -1 +1 @@
-body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;margin-bottom:20px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
+body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
diff --git a/static/v0/css/light.min.css b/static/v0/css/light.min.css
index 40255e2..b27cede 100644
--- a/static/v0/css/light.min.css
+++ b/static/v0/css/light.min.css
@@ -1 +1 @@
-body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;margin-bottom:20px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
+body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
diff --git a/static/v0/js/geolocation.js b/static/v0/js/geolocation.js
index 1ea4ee5..4f520b9 100644
--- a/static/v0/js/geolocation.js
+++ b/static/v0/js/geolocation.js
@@ -11,6 +11,12 @@ document.addEventListener("DOMContentLoaded", function() {
const processResult = function(results) {
const list = document.createElement("ul");
list.className = "stops";
+
+ if (results.error) {
+ showError("Backend-Fehler:", results.msg);
+ return;
+ }
+
for (var result in results) {
result = results[result];
const listentry = document.createElement("li");
diff --git a/templates/departure_list.html b/templates/departure_list.html
index 3762a03..42ce550 100644
--- a/templates/departure_list.html
+++ b/templates/departure_list.html
@@ -10,6 +10,15 @@
<div class="container">
<div class="content">
+ {% if warning %}
+ <div class="content">
+ <div class="warning">
+ <strong>{{ warning["lead"] }}</strong>
+ {{ warning["body"] }}
+ <div class="errcode">{{ warning["code"] }}</div>
+ </div>
+ </div>
+ {% endif %}
{% if not departures %}
<div class="globalnote">Keine Abfahrten innerhalb der nächsten zwei Stunden</div>
{% endif %}
diff --git a/templates/landing_page.html b/templates/landing_page.html
index c5a04fd..b6a28cd 100644
--- a/templates/landing_page.html
+++ b/templates/landing_page.html
@@ -8,6 +8,15 @@
<body>
{% include 'navbar.html' %}
<div class="container">
+ {% if error %}
+ <div class="content">
+ <div class="error">
+ <strong>{{ error["lead"] }}</strong>
+ {{ error["body"] }}
+ <div class="errcode">{{ error["code"] }}</div>
+ </div>
+ </div>
+ {% endif %}
<div class="content">
<div class="geolocation">
<button id="geolocationsearch">Haltestellen in der Umgebung suchen</button>