From 11615fdc0d3d50a8091b96b77b6e03a9205073a0 Mon Sep 17 00:00:00 2001 From: Ruffin Date: Mon, 22 May 2023 16:10:54 +0200 Subject: [PATCH] Improve usability of PWAs in Dev Containers (#3576) * Add WIP icons * Add WIP icons for gzweb * Add WIP icons for glances * Set cross origin to use credentials ensuring auth cookie is included in request header when requesting for web app manifest file thus avoiding CORS policy violations in browser when accessing forwarded codespaces ports from the web > The request for the manifest is made without credentials (even if it's on the same domain), thus if the manifest requires credentials, you must include `crossorigin="use-credentials"` in the manifest tag. - https://web.dev/add-manifest/ - https://stackoverflow.com/a/57184506/2577586 * Use ReqHost variable in templates to account for X-Forwarded-Host value in header * Delete duplicate manifest * Set id property in app manifests so we can address them independently from their start_url - https://developer.chrome.com/blog/pwa-manifest-id/ * Ensure apps are uniquely identifies by adding trailing slash to id and thus different URI directories * Refactor root landing page into nav2 app by moving page file into nav2 sub folder adding root redirect pointing to /nav2/ and updating html, markdown, manifest files respectively * Fix https detection for Caddy reverse proxies by also checking X-Forwarded-Proto in request header * Remove unnecessary files * Prune smaller images * Prune duplicate icon * Clean up html tags * Update manifest icons * Rename icons * Revert "Prune duplicate icon" This reverts commit 571040173ca83716dfd2f6d5db4b351389a557a8. * Add back favicon for shortcut * Add self index for completeness and bookmarking * Simplify icon linking * Delete binary files * Fix hyperlink path * Include image files using gitattributes to track these binary files via git LFS * Add icons using git lfs * Standardized all icon paths * Use external links for icons to avoid the need for using git LFS although this is a bit of a hack * Stage any and maskable icons * Use any and masked icons * Set colors to match maskable icon colors * Update icon * Use lossless compression without removing background - https://shortpixel.com/online-image-compression * Use WebP instead of PNG for smaller file sizes - https://en.wikipedia.org/wiki/WebP * Move icons into icons folder * Use _SRV environment variables for service paths * Download media files from github during docker image build to avoid adding always online dependencies when creating or starting dev containers * Delete media icons from git repo now that we download media from anonymized URLs on github - https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/attaching-files * Add comments * Enable file browsing for non app paths for remote debugging of media and asset files * Consolidate assets into single folder * Add links for file browser paths to Server Diagnostics * Delete unused symlink * Update landing page to match manifest by including same shortcuts and start url * Patch gzweb to disable modelList avoiding 404s for thumbnails as they are hardcoded into js * Update comments * Simplify Caddyfile by reverting to symlinking but add ROOT_SRV env for custom overriding * Loop over nav2 srv folders when symlinking to generalize over folder names * Add matcher for file browsing root directory while still redirecting to nav2 app by default * Use placeholders for root variable to consolidate env default fallback settings e.g `:/srv` * Promote file browser in Nav2 app shortcuts * Fix and update SRV envs * Postpone symlinking for Nav2 web app to when post-create-command script then runs given full repo is not copied into builder stage in Dockerfile. While this could be postponed to update-content-command leaving it here avoids blowing user changes after the container has been created or modified. * Add guard to check if srv folder exists * Add refresh rate shortcuts to glances * Add file browser shortcut to nav2 * Set scope for nav2 PWA to root to allow for opening child apps inside nav2 app * Display child apps in fullscreen mode by default as users can still open them in standalone via nav2 app given the nav2 app's scope is the parent root path * Update shortcuts and landing page * Document PWA scope and installation order when using Nav2 PWA scoped as root * Revert setting scope for nav2 PWA to root path as adding file browser shortcut to nav2 PWA is not worth the trouble of having to explain installation order caveats and URL launch behavior. File browser shortcut is still accessible from inside nav2 pwa launcher but merely displays in browser preview given root / is out of scope for /nav2/ * Update server diagnostics for troubleshooting * Verify checksum of archive before extraction incase anonymized URL changes expected archive --- .devcontainer/caddy/Caddyfile | 56 +++++++++++-------- .../caddy/srv/assets/foxglove/manifest.json | 40 +++++++++++++ .../nav2_layout.json} | 0 .../caddy/srv/assets/glances/manifest.json | 40 +++++++++++++ .../caddy/srv/assets/gzweb/manifest.json | 23 ++++++++ .../caddy/srv/assets/nav2/manifest.json | 23 ++++++++ .devcontainer/caddy/srv/index.md | 48 ---------------- .../srv/manifests/foxglove/manifest.json | 32 ----------- .../caddy/srv/manifests/glances/manifest.json | 15 ----- .../caddy/srv/manifests/gzweb/manifest.json | 15 ----- .../caddy/srv/manifests/manifest.json | 8 --- .../caddy/srv/{ => nav2}/github-markdown.css | 0 .devcontainer/caddy/srv/{ => nav2}/index.html | 2 + .devcontainer/caddy/srv/nav2/index.md | 51 +++++++++++++++++ .devcontainer/post-create-command.sh | 7 +++ Dockerfile | 21 +++++-- 16 files changed, 235 insertions(+), 146 deletions(-) create mode 100644 .devcontainer/caddy/srv/assets/foxglove/manifest.json rename .devcontainer/caddy/srv/assets/{nav2_foxglove_layout.json => foxglove/nav2_layout.json} (100%) create mode 100644 .devcontainer/caddy/srv/assets/glances/manifest.json create mode 100644 .devcontainer/caddy/srv/assets/gzweb/manifest.json create mode 100644 .devcontainer/caddy/srv/assets/nav2/manifest.json delete mode 100644 .devcontainer/caddy/srv/index.md delete mode 100644 .devcontainer/caddy/srv/manifests/foxglove/manifest.json delete mode 100644 .devcontainer/caddy/srv/manifests/glances/manifest.json delete mode 100644 .devcontainer/caddy/srv/manifests/gzweb/manifest.json delete mode 100644 .devcontainer/caddy/srv/manifests/manifest.json rename .devcontainer/caddy/srv/{ => nav2}/github-markdown.css (100%) rename .devcontainer/caddy/srv/{ => nav2}/index.html (92%) create mode 100644 .devcontainer/caddy/srv/nav2/index.md diff --git a/.devcontainer/caddy/Caddyfile b/.devcontainer/caddy/Caddyfile index 74a9df93b2..1150579a47 100644 --- a/.devcontainer/caddy/Caddyfile +++ b/.devcontainer/caddy/Caddyfile @@ -7,7 +7,7 @@ # Matcher for http request scheme. E.g. "http" or "https" @http_scheme { - expression {http.request.scheme}=="https" || {header.X-Forwarded-Scheme}=="https" + expression {http.request.scheme}=="https" || {header.X-Forwarded-Scheme}=="https" || {header.X-Forwarded-Proto}=="https" } # If any http scheme is "https", then use "wss" vars @http_scheme WsScheme "wss" @@ -19,9 +19,9 @@ header X-Forwarded-Host * } # If http headers exists, then use them - vars @host_forwarded WsHost {header.X-Forwarded-Host} + vars @host_forwarded ReqHost {header.X-Forwarded-Host} # Else default to host in request - vars WsHost {http.request.hostport} + vars ReqHost {http.request.hostport} # Matcher for websocket connection upgrade requests @websockets { @@ -37,8 +37,8 @@ # E.g auto redirect websocket URL to match request scheme (redirect) { # Configure redirect to match request scheme - vars LayoutUrl "/assets/nav2_foxglove_layout.json" - vars DataSourceUrl "{vars.WsScheme}://{vars.WsHost}{args.0}/" + vars LayoutUrl "/assets/foxglove/nav2_layout.json" + vars DataSourceUrl "{vars.WsScheme}://{vars.ReqHost}{args.0}/" redir /autoconnect "{args.0}/?ds=foxglove-websocket&ds.url={vars.DataSourceUrl}" redir /autolayout "{args.0}/?ds=foxglove-websocket&ds.url={vars.DataSourceUrl}&layoutUrl={vars.LayoutUrl}" } @@ -55,30 +55,26 @@ route / { # Inject link to manifest just after tag # https://developer.mozilla.org/docs/Web/Manifest - replace `` `` + replace `` `` } # Redirect relative handle_path'ed manifest.json to /manifests directory - redir /manifest.json /manifests{http.request.orig_uri.path.dir}manifest.json + redir /manifest.json /assets{http.request.orig_uri.path.dir}manifest.json } # Snippet for hosted web app using websockets # to serve static files and reverse proxying connections # E.g. for serving GzWeb and Foxglove web apps (app) { - # Redirect implicit directory requests twards index.html - redir {args.0} {args.0}/ # handle and strip path prefix from redirect handle_path {args.0}/* { # Set root directory for static files - root * {args.1} - # Serve static files - file_server + root * {http.vars.root}{args.0} # Enable mobile web app features import mobile # Reverse proxy websockets to backend address - reverse_proxy @websockets {args.2} + reverse_proxy @websockets {args.1} # Import custom snippets - import {args.3} {args.0} + import {args.2} {args.0} } } @@ -88,24 +84,38 @@ :8080 { # Include global matchers and variables import globals + root * {$ROOT_SRV:/srv} + file_server browse - # Handle main landing page + # Handle root content + # I.e. assets internal to workspace handle /* { - # Use relative path for root directory - root * srv - file_server - import mobile - # Render markdown files as html - templates # Template manifest.json files templates */manifest.json { mime application/json } } + # Handle nav2 web app + # I.e. main landing page + handle_path /nav2/* { + root * {http.vars.root}/nav2 + import mobile + # Render markdown files as html + templates + } + + # Matcher for requests without browse query + @no_browse { + path / + not query browse=true + } + # Redirect to nav2 web app by default + redir @no_browse /nav2/ + # Import app snippets for web apps - import app "/gzweb" "{$GZWEB_WS}/http/client" "localhost:9090" "dummy" - import app "/foxglove" "{$FOXGLOVE_WS}" "localhost:8765" "redirect" + import app "/gzweb" "localhost:9090" "dummy" + import app "/foxglove" "localhost:8765" "redirect" # Handle glances web app redir /glances /glances/ diff --git a/.devcontainer/caddy/srv/assets/foxglove/manifest.json b/.devcontainer/caddy/srv/assets/foxglove/manifest.json new file mode 100644 index 0000000000..0b9b8b84d7 --- /dev/null +++ b/.devcontainer/caddy/srv/assets/foxglove/manifest.json @@ -0,0 +1,40 @@ +{ + "name": "Foxglove: {{placeholder "http.vars.ReqHost"}}", + "short_name": "Foxglove: {{placeholder "http.vars.ReqHost"}}", + "icons": [ + { + "src": "/media/icons/foxglove/any_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "any" + }, + { + "src": "/media/icons/foxglove/maskable_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "maskable" + } + ], + "id": "/foxglove/", + "start_url": "/foxglove/autoconnect", + "theme_color": "#6F3BE8", + "background_color": "#6F3BE8", + "display": "fullscreen", + "shortcuts" : [ + { + "name": "Auto Connect", + "url": "/foxglove/autoconnect", + "description": "Auto connect to default data source" + }, + { + "name": "Auto Layout", + "url": "/foxglove/autolayout", + "description": "Auto connect using default layout" + }, + { + "name": "Manual Connect", + "url": "/foxglove/", + "description": "Manually connect to data source" + } + ] +} diff --git a/.devcontainer/caddy/srv/assets/nav2_foxglove_layout.json b/.devcontainer/caddy/srv/assets/foxglove/nav2_layout.json similarity index 100% rename from .devcontainer/caddy/srv/assets/nav2_foxglove_layout.json rename to .devcontainer/caddy/srv/assets/foxglove/nav2_layout.json diff --git a/.devcontainer/caddy/srv/assets/glances/manifest.json b/.devcontainer/caddy/srv/assets/glances/manifest.json new file mode 100644 index 0000000000..78d27cb0bd --- /dev/null +++ b/.devcontainer/caddy/srv/assets/glances/manifest.json @@ -0,0 +1,40 @@ +{ + "name": "Glances: {{placeholder "http.vars.ReqHost"}}", + "short_name": "Glances: {{placeholder "http.vars.ReqHost"}}", + "icons": [ + { + "src": "/media/icons/glances/any_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "any" + }, + { + "src": "/media/icons/glances/maskable_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "maskable" + } + ], + "id": "/glances/", + "start_url": "/glances/", + "theme_color": "#2C363F", + "background_color": "#2C363F", + "display": "fullscreen", + "shortcuts" : [ + { + "name": "Refresh 1sec", + "url": "/glances/1", + "description": "Refresh page every 1 second" + }, + { + "name": "Refresh 5sec", + "url": "/glances/5", + "description": "Refresh page every 5 seconds" + }, + { + "name": "Refresh 10sec", + "url": "/glances/10", + "description": "Refresh page every 10 seconds" + } + ] +} diff --git a/.devcontainer/caddy/srv/assets/gzweb/manifest.json b/.devcontainer/caddy/srv/assets/gzweb/manifest.json new file mode 100644 index 0000000000..0596075f15 --- /dev/null +++ b/.devcontainer/caddy/srv/assets/gzweb/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "Gzweb: {{placeholder "http.vars.ReqHost"}}", + "short_name": "Gzweb: {{placeholder "http.vars.ReqHost"}}", + "icons": [ + { + "src": "/media/icons/gzweb/any_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "any" + }, + { + "src": "/media/icons/gzweb/maskable_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "maskable" + } + ], + "id": "/gzweb/", + "start_url": "/gzweb/", + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "fullscreen" +} diff --git a/.devcontainer/caddy/srv/assets/nav2/manifest.json b/.devcontainer/caddy/srv/assets/nav2/manifest.json new file mode 100644 index 0000000000..f482a47e80 --- /dev/null +++ b/.devcontainer/caddy/srv/assets/nav2/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "Nav2: {{placeholder "http.vars.ReqHost"}}", + "short_name": "Nav2: {{placeholder "http.vars.ReqHost"}}", + "icons": [ + { + "src": "/media/icons/nav2/any_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "any" + }, + { + "src": "/media/icons/nav2/maskable_icon_x512.webp", + "sizes": "512x512", + "type": "image/webp", + "purpose": "maskable" + } + ], + "id": "/nav2/", + "start_url": "/nav2/", + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/.devcontainer/caddy/srv/index.md b/.devcontainer/caddy/srv/index.md deleted file mode 100644 index acaec1c52c..0000000000 --- a/.devcontainer/caddy/srv/index.md +++ /dev/null @@ -1,48 +0,0 @@ -{ - "title": "Web Server" -} -## Hosted Apps - -Redirects for available web apps: - -| App | Links | -|-|-| -| [![Foxglove](foxglove/favicon.ico)](foxglove/autolayout) | [**Foxglove Studio**](foxglove/autolayout) | -| [![Gzweb](gzweb/favicon.ico)](gzweb) | [**Gzweb Client**](gzweb) | -| [![Glances](https://nicolargo.github.io/glances/favicon.ico)](glances) | [**System Monitor**](glances) | - -## External Resources - -For more related documentation: - -- [Nav2 Documentation](https://navigation.ros.org) - - [Development Guides](https://navigation.ros.org/development_guides) - - [Dev Containers](https://navigation.ros.org/development_guides/devcontainer_docs) - -## Session Info - -Useful information about host server and remote client: - -|Key | Value | -|-|-| -| Host | `{{.Host}}` | -| Remote IP | `{{placeholder "http.request.remote.host"}}` | -| Date | `{{now}}` | - -### Server Diagnostics - -
-Websocket Debug - -For troubleshooting websocket connections: - -|Key | Value | -|-|-| -| `header.X-Forwarded-Host` | `{{placeholder "header.X-Forwarded-Host"}}` | -| `header.X-Forwarded-Scheme` | `{{placeholder "header.X-Forwarded-Scheme"}}` | -| `http.request.hostport` | `{{placeholder "http.request.hostport"}}` | -| `http.request.scheme` | `{{placeholder "http.request.scheme"}}` | -| `http.vars.WsHost` | `{{placeholder "http.vars.WsHost"}}` | -| `http.vars.WsScheme` | `{{placeholder "http.vars.WsScheme"}}` | - -
diff --git a/.devcontainer/caddy/srv/manifests/foxglove/manifest.json b/.devcontainer/caddy/srv/manifests/foxglove/manifest.json deleted file mode 100644 index 24237b34f4..0000000000 --- a/.devcontainer/caddy/srv/manifests/foxglove/manifest.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "Foxglove: {{placeholder "http.request.hostport"}}", - "short_name": "Foxglove: {{placeholder "http.request.hostport"}}", - "icons": [ - { - "src": "/foxglove/apple-touch-icon.png", - "sizes": "180x180", - "type": "image/png" - } - ], - "start_url": "/foxglove/autoconnect", - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone", - "shortcuts" : [ - { - "name": "Auto Connect", - "url": "/foxglove/autoconnect", - "description": "Auto connect to default data source" - }, - { - "name": "Auto Layout", - "url": "/foxglove/autolayout", - "description": "Auto connect using default layout" - }, - { - "name": "Manual Connect", - "url": "/foxglove/", - "description": "Manually connect to data source" - } - ] -} diff --git a/.devcontainer/caddy/srv/manifests/glances/manifest.json b/.devcontainer/caddy/srv/manifests/glances/manifest.json deleted file mode 100644 index 7578cc2faf..0000000000 --- a/.devcontainer/caddy/srv/manifests/glances/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Glances: {{placeholder "http.request.hostport"}}", - "short_name": "Glances: {{placeholder "http.request.hostport"}}", - "icons": [ - { - "src": "/glances/favicon.ico", - "sizes": "32x32", - "type": "image/ico" - } - ], - "start_url": "/glances", - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/.devcontainer/caddy/srv/manifests/gzweb/manifest.json b/.devcontainer/caddy/srv/manifests/gzweb/manifest.json deleted file mode 100644 index d97b278a29..0000000000 --- a/.devcontainer/caddy/srv/manifests/gzweb/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Gzweb: {{placeholder "http.request.hostport"}}", - "short_name": "Gzweb: {{placeholder "http.request.hostport"}}", - "icons": [ - { - "src": "/gzweb/favicon.ico", - "sizes": "64x64", - "type": "image/ico" - } - ], - "start_url": "/gzweb", - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/.devcontainer/caddy/srv/manifests/manifest.json b/.devcontainer/caddy/srv/manifests/manifest.json deleted file mode 100644 index 84bdd22d8a..0000000000 --- a/.devcontainer/caddy/srv/manifests/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "Nav2: {{placeholder "http.request.hostport"}}", - "short_name": "Nav2: {{placeholder "http.request.hostport"}}", - "start_url": "/", - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/.devcontainer/caddy/srv/github-markdown.css b/.devcontainer/caddy/srv/nav2/github-markdown.css similarity index 100% rename from .devcontainer/caddy/srv/github-markdown.css rename to .devcontainer/caddy/srv/nav2/github-markdown.css diff --git a/.devcontainer/caddy/srv/index.html b/.devcontainer/caddy/srv/nav2/index.html similarity index 92% rename from .devcontainer/caddy/srv/index.html rename to .devcontainer/caddy/srv/nav2/index.html index da489d9c0c..4bebe27f4c 100644 --- a/.devcontainer/caddy/srv/index.html +++ b/.devcontainer/caddy/srv/nav2/index.html @@ -12,6 +12,8 @@ {{$title}} + +