From 05b5ceafe2d2f41c58e36dc5c4fdb77ee43f2401 Mon Sep 17 00:00:00 2001 From: Ben Sherriff Date: Fri, 11 Apr 2025 16:26:18 -0400 Subject: [PATCH] Updated UI --- .gitignore | 1 + Makefile | 4 ++ httpd/aviation.conf | 6 +++ httpd/httpd.conf | 6 +-- scripts/generate_cert.sh | 32 +++++++++++++++ ui/src/App.css | 36 ++++++++++++++++ ui/src/App.tsx | 66 +++++++++++++++++++++--------- ui/src/components/AirportLayer.tsx | 6 +-- ui/src/components/Header/index.tsx | 3 -- ui/src/lib/rainViewer.ts | 9 ++-- 10 files changed, 135 insertions(+), 34 deletions(-) create mode 100755 scripts/generate_cert.sh diff --git a/.gitignore b/.gitignore index 90507cb..b642382 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ node_modules target/ dist/ Cargo.lock +ssl/ .DS_Store diff --git a/Makefile b/Makefile index 03e6dc9..316e6c6 100644 --- a/Makefile +++ b/Makefile @@ -109,3 +109,7 @@ build: ## Build a specific docker image (`make build f=httpd`) ${folder} docker-build: build + +cert: domain=$(if $(d),$(d),aviation.bensherriff.com) +cert: ## Generate a cert for the given domain + @./scripts/generate_cert.sh ${domain} diff --git a/httpd/aviation.conf b/httpd/aviation.conf index f54f3f3..e6629c3 100644 --- a/httpd/aviation.conf +++ b/httpd/aviation.conf @@ -4,6 +4,12 @@ ProxyPreserveHost On LogLevel warn + #SSLEngine on + #SSLCertificateFile /path/to/your/cert.pem + #SSLCertificateKeyFile /path/to/your/privkey.pem + + #Protocols h2 http/1.1 + ProxyPass "/api" "${API_PROTOCOL}://${HTTPD_API_HOST}:${API_PORT}/api" ProxyPassReverse "/api" "${API_PROTOCOL}://${HTTPD_API_HOST}:${API_PORT}/api" diff --git a/httpd/httpd.conf b/httpd/httpd.conf index 6ba9eb5..6e82d94 100644 --- a/httpd/httpd.conf +++ b/httpd/httpd.conf @@ -158,14 +158,14 @@ LoadModule proxy_http_module modules/mod_proxy_http.so #LoadModule session_dbd_module modules/mod_session_dbd.so #LoadModule slotmem_shm_module modules/mod_slotmem_shm.so #LoadModule slotmem_plain_module modules/mod_slotmem_plain.so -#LoadModule ssl_module modules/mod_ssl.so +LoadModule ssl_module modules/mod_ssl.so #LoadModule optional_hook_export_module modules/mod_optional_hook_export.so #LoadModule optional_hook_import_module modules/mod_optional_hook_import.so #LoadModule optional_fn_import_module modules/mod_optional_fn_import.so #LoadModule optional_fn_export_module modules/mod_optional_fn_export.so #LoadModule dialup_module modules/mod_dialup.so -#LoadModule http2_module modules/mod_http2.so -#LoadModule proxy_http2_module modules/mod_proxy_http2.so +LoadModule http2_module modules/mod_http2.so +LoadModule proxy_http2_module modules/mod_proxy_http2.so #LoadModule md_module modules/mod_md.so #LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so #LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so diff --git a/scripts/generate_cert.sh b/scripts/generate_cert.sh new file mode 100755 index 0000000..985abbf --- /dev/null +++ b/scripts/generate_cert.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Usage: ./generate_cert.sh example.com +if [ "$#" -lt 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +DOMAIN=$1 +DAYS=365 +SSL_DIR="./ssl" + +# Create directory if it doesn't exist +mkdir -p "$SSL_DIR" + +KEY_PATH="${SSL_DIR}/${DOMAIN}.key" +CRT_PATH="${SSL_DIR}/${DOMAIN}.crt" + +echo "Generating self-signed certificate for ${DOMAIN}..." + +openssl req -x509 -nodes -days ${DAYS} -newkey rsa:2048 \ + -keyout "${KEY_PATH}" \ + -out "${CRT_PATH}" \ + -subj "/CN=${DOMAIN}" + +if [ $? -eq 0 ]; then + echo "Successfully generated certificate:" + echo "Private Key: ${KEY_PATH}" + echo "Certificate: ${CRT_PATH}" +else + echo "Certificate generation failed." +fi \ No newline at end of file diff --git a/ui/src/App.css b/ui/src/App.css index c2430c7..8ba6fba 100644 --- a/ui/src/App.css +++ b/ui/src/App.css @@ -29,3 +29,39 @@ body, height: 100%; width: 100%; } + +.map-button { + position: absolute; + right: 12px; + z-index: 1000; + + color: #000; + background: #fff; + border-radius: 3px; + border: 1px solid #ccc; + border-bottom-width: 2px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); + + height: 30px; + width: 30px; + text-align: center; + line-height: 30px; /* Vertically center text */ + font-weight: bold; + + cursor: pointer; + user-select: none; + transition: background-color 0.2s, color 0.2s; +} + +.map-button.active { + background-color: #228be6; + color: #fff; +} + +.map-button.active:hover { + background-color: #187ed7; +} + +.map-button:hover { + background: #e6e6e6; +} diff --git a/ui/src/App.tsx b/ui/src/App.tsx index a71635c..8ee8c06 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,4 +1,4 @@ -import { MapContainer, TileLayer, ZoomControl } from 'react-leaflet'; +import { LayersControl, MapContainer, TileLayer, useMapEvents, ZoomControl } from 'react-leaflet'; import '@mantine/core/styles.css'; import 'leaflet/dist/leaflet.css'; import './App.css'; @@ -12,8 +12,8 @@ import { useEffect, useState } from 'react'; import { Airport } from '@lib/airport.types.ts'; import AirportDrawer from '@components/AirportDrawer.tsx'; import { getWeatherMapUrl } from '@lib/rainViewer.ts'; -import { Switch } from '@mantine/core'; - +import { IconRadar } from '@tabler/icons-react'; +import Cookies from 'js-cookie'; // Fix Leaflet's default icon path issues with Webpack // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error @@ -26,13 +26,19 @@ L.Icon.Default.mergeOptions({ }); const openStreetMapUrl = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'; +const lightLayerUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png'; +const darkLayerUrl = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png'; +// const dark1Url = 'https://maps.rainviewer.com/data/v3/5/10/11.pbf'; +// const dark2Url = 'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/2/0/3.pbf'; const defaultZoom = 6; const defaultCenter: L.LatLngExpression = [38.944444, -77.455833]; function App() { const [airport, setAirport] = useState(null); const [rainViewerUrl, setRainViewerUrl] = useState(null); - const [showRadar, setShowRadar] = useState(false); + const initialRadarValue = Cookies.get('showRadar') === 'true'; + const [showRadar, setShowRadar] = useState(initialRadarValue); + const [baseLayer, setBaseLayer] = useState(Cookies.get('selectedBaseLayer') || 'Open Street Map'); useEffect(() => { if (showRadar) { @@ -40,7 +46,25 @@ function App() { setRainViewerUrl(url); }); } - }, [showRadar]) + }, [showRadar]); + + function toggleRadar() { + setShowRadar(prev => { + const newValue = !prev; + Cookies.set('showRadar', newValue.toString(), { expires: 7 }); + return newValue; + }); + } + + function BaseLayerChangeHandler() { + useMapEvents({ + baselayerchange: (e) => { + setBaseLayer(e.name); + Cookies.set('selectedBaseLayer', e.name, { expires: 7 }); + } + }); + return null; + } return (
@@ -61,23 +85,27 @@ function App() { scrollWheelZoom={true} zoomControl={false} > + + + + + + + + + + + + {rainViewerUrl && showRadar && } - - { rainViewerUrl && showRadar && ( - - )} + -
- setShowRadar(event.currentTarget.checked)} - /> -
+
); diff --git a/ui/src/components/AirportLayer.tsx b/ui/src/components/AirportLayer.tsx index 33548a1..4b37fb3 100644 --- a/ui/src/components/AirportLayer.tsx +++ b/ui/src/components/AirportLayer.tsx @@ -84,9 +84,9 @@ export default function AirportLayer({ setAirport }: { setAirport: (airport: Air return ( <> - {sortedAirports.map((airport, index) => { - return ; - })} + {sortedAirports.map((airport, index) => ( + + ))} ); } diff --git a/ui/src/components/Header/index.tsx b/ui/src/components/Header/index.tsx index a9f4e0b..f260820 100644 --- a/ui/src/components/Header/index.tsx +++ b/ui/src/components/Header/index.tsx @@ -126,9 +126,6 @@ export function Header() { return false; } - console.log(Cookies.get('logged_in')); - console.log(Cookies.get('session')); - return ( <> diff --git a/ui/src/lib/rainViewer.ts b/ui/src/lib/rainViewer.ts index eeeb32c..b590b12 100644 --- a/ui/src/lib/rainViewer.ts +++ b/ui/src/lib/rainViewer.ts @@ -13,22 +13,19 @@ async function getWeatherMaps(): Promise { } } -// const rainViewerUrl = 'https://tilecache.rainviewer.com/v2/radar/1744386000/256/{z}/{x}/{y}/2/1_1.png'; -// const rainViewerUrl = 'https://tilecache.rainviewer.com/v2/radar/1744386000/256/10/290/391/2/1_1.png' -// https://api.rainviewer.com/public/weather-maps.json export async function getWeatherMapUrl(): Promise { const weatherMaps = await getWeatherMaps(); if (weatherMaps != undefined) { let url = weatherMaps.host; + // url = 'https://cdn.rainviewer.com'; let latest = ""; - if (weatherMaps.radar.nowcast.length > 0) { - latest = weatherMaps.radar.nowcast[weatherMaps.radar.nowcast.length - 1].path; - } else if (weatherMaps.radar.past.length > 0) { + if (weatherMaps.radar.past.length > 0) { latest = weatherMaps.radar.past[weatherMaps.radar.past.length - 1].path; } else { return null; } url += latest + "/256/{z}/{x}/{y}/2/1_1.png"; + // url += latest + "/256/{z}/{x}/{y}/255/1_1_1_0.webp"; return url; } else { return null;