Fixed docker issue temporarily
This commit is contained in:
10
.env
10
.env
@@ -2,8 +2,7 @@ RUST_LOG=warn,api=info
|
||||
|
||||
HTTPD_DOMAIN=localhost
|
||||
HTTPD_PROTOCOL=http
|
||||
HTTPD_HTTP_PORT=8080
|
||||
HTTPD_HTTPS_PORT=8443
|
||||
HTTPD_PORT=8080
|
||||
HTTPD_MINIO_HOST=host.docker.internal
|
||||
HTTPD_API_HOST=host.docker.internal
|
||||
HTTPD_UI_HOST=host.docker.internal
|
||||
@@ -24,18 +23,19 @@ MINIO_BUCKET=aviation
|
||||
MINIO_PROTOCOL=http
|
||||
MINIO_PORT=9000
|
||||
MINIO_PORT_INTERNAL=9001
|
||||
MINIO_BROWSER_REDIRECT_URL=${HTTPD_PROTOCOL}://${HTTPD_DOMAIN}:${HTTPD_HTTP_PORT}/minio/
|
||||
MINIO_BROWSER_REDIRECT_URL=${HTTPD_PROTOCOL}://${HTTPD_DOMAIN}:${HTTPD_PORT}/minio/
|
||||
|
||||
UI_PROTOCOL=http
|
||||
UI_PORT=3000
|
||||
|
||||
API_PROTOCOL=http
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=5000
|
||||
|
||||
VITE_API_URL=${HTTPD_PROTOCOL}://${HTTPD_DOMAIN}:${HTTPD_HTTP_PORT}/api
|
||||
VITE_API_URL=${HTTPD_PROTOCOL}://${HTTPD_DOMAIN}:${HTTPD_PORT}/api
|
||||
|
||||
ENVIRONMENT=development
|
||||
ADMIN_EMAIL=admin@example.com
|
||||
ADMIN_PASSWORD=CHANGEME
|
||||
|
||||
GOV_API_URL=https://aviationweather.gov/cgi-bin/data
|
||||
AVIATION_WEATHER_URL=https://aviationweather.gov/api/data
|
||||
|
||||
@@ -11,26 +11,14 @@ COPY Cargo.toml ./
|
||||
RUN apt-get update && apt-get install -y cmake
|
||||
RUN cargo build --release
|
||||
|
||||
# ======
|
||||
# Keys
|
||||
# ======
|
||||
FROM debian:bookworm-slim AS keys
|
||||
WORKDIR /keys
|
||||
|
||||
RUN apt-get update && apt-get install -y openssl libpq-dev
|
||||
RUN openssl genrsa -out access.pem 4096
|
||||
RUN openssl rsa -in access.pem -pubout -outform PEM -out access.pem.pub
|
||||
RUN openssl genrsa -out refresh.pem 4096
|
||||
RUN openssl rsa -in refresh.pem -pubout -outform PEM -out refresh.pem.pub
|
||||
|
||||
# =========
|
||||
# Runtime
|
||||
# =========
|
||||
FROM keys AS runtime
|
||||
FROM debian:bookworm-slim AS runtime
|
||||
WORKDIR /api
|
||||
RUN apt-get update && apt-get install -y openssl libpq-dev
|
||||
USER root
|
||||
|
||||
COPY --from=builder /builder/target/release/api /usr/local/bin/api
|
||||
COPY --from=keys /keys /keys
|
||||
|
||||
CMD ["api"]
|
||||
|
||||
@@ -17,6 +17,15 @@ CREATE TABLE IF NOT EXISTS airports (
|
||||
public BOOLEAN DEFAULT false
|
||||
);
|
||||
|
||||
CREATE INDEX ON airports (iata);
|
||||
CREATE INDEX ON airports (local);
|
||||
CREATE INDEX ON airports (name);
|
||||
CREATE INDEX ON airports (category);
|
||||
CREATE INDEX ON airports (iso_country);
|
||||
CREATE INDEX ON airports (iso_region);
|
||||
CREATE INDEX ON airports (municipality);
|
||||
CREATE INDEX ON airports (longitude, latitude);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS runways (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
icao TEXT NOT NULL,
|
||||
@@ -26,6 +35,9 @@ CREATE TABLE IF NOT EXISTS runways (
|
||||
surface TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX ON runways (icao);
|
||||
CREATE INDEX ON runways (surface);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS frequencies (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
icao TEXT NOT NULL,
|
||||
@@ -33,6 +45,9 @@ CREATE TABLE IF NOT EXISTS frequencies (
|
||||
frequency_mhz REAL NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX ON frequencies (icao);
|
||||
CREATE INDEX ON frequencies (frequency_mhz);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS metars (
|
||||
icao TEXT NOT NULL,
|
||||
observation_time TIMESTAMPTZ NOT NULL,
|
||||
@@ -40,6 +55,8 @@ CREATE TABLE IF NOT EXISTS metars (
|
||||
data JSONB NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX ON metars (observation_time DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
email TEXT PRIMARY KEY NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use futures_util::try_join;
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Postgres, QueryBuilder};
|
||||
use crate::airports::{
|
||||
@@ -194,7 +195,7 @@ impl From<AirportRow> for Airport {
|
||||
}
|
||||
|
||||
impl Airport {
|
||||
pub async fn select(icao: &str, metar: bool) -> Option<Self> {
|
||||
pub async fn select(client: &Client, icao: &str, metar: bool) -> Option<Self> {
|
||||
let pool = db::pool();
|
||||
|
||||
let airport_fut = async {
|
||||
@@ -206,7 +207,7 @@ impl Airport {
|
||||
|
||||
let metar_fut = async {
|
||||
if metar {
|
||||
match Metar::find_all(&vec![icao.to_string()]).await {
|
||||
match Metar::find_all(client, &vec![icao.to_string()], &false).await {
|
||||
Ok(m) => Some(m.into_iter().nth(0)),
|
||||
Err(err) => {
|
||||
log::error!("{}", err);
|
||||
@@ -269,7 +270,7 @@ impl Airport {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn select_all(query: &AirportQuery) -> ApiResult<Vec<Self>> {
|
||||
pub async fn select_all(client: &Client, query: &AirportQuery) -> ApiResult<Vec<Self>> {
|
||||
let pool = db::pool();
|
||||
|
||||
let mut builder = QueryBuilder::<Postgres>::new("SELECT * FROM ");
|
||||
@@ -337,7 +338,7 @@ impl Airport {
|
||||
let runway_future = Runway::select_all_map(icaos.clone());
|
||||
let frequency_future = Frequency::select_all_map(icaos.clone());
|
||||
let metar_future = if query.metars.unwrap_or(false) {
|
||||
Some(Metar::find_all(&icaos))
|
||||
Some(Metar::find_all(client, &icaos, &false))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::{
|
||||
airports::Airport,
|
||||
db::Paged,
|
||||
auth::{Auth, verify_role},
|
||||
AppState,
|
||||
};
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::{delete, get, post, put, web, HttpResponse, HttpRequest, ResponseError};
|
||||
@@ -53,7 +54,7 @@ async fn import_airports(mut payload: Multipart, auth: Auth) -> HttpResponse {
|
||||
}
|
||||
|
||||
#[get("")]
|
||||
async fn get_airports(req: HttpRequest) -> HttpResponse {
|
||||
async fn get_airports(data: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||
let mut query = match web::Query::<AirportQuery>::from_query(req.query_string()) {
|
||||
Ok(q) => q.into_inner(),
|
||||
Err(err) => {
|
||||
@@ -71,7 +72,8 @@ async fn get_airports(req: HttpRequest) -> HttpResponse {
|
||||
query.limit = Some(limit);
|
||||
query.page = Some(page);
|
||||
|
||||
match Airport::select_all(&query).await {
|
||||
let client = &data.client;
|
||||
match Airport::select_all(client, &query).await {
|
||||
Ok(airports) => HttpResponse::Ok().json(Paged {
|
||||
data: airports,
|
||||
page,
|
||||
@@ -86,7 +88,11 @@ async fn get_airports(req: HttpRequest) -> HttpResponse {
|
||||
}
|
||||
|
||||
#[get("/{icao}")]
|
||||
async fn get_airport(icao: web::Path<String>, req: HttpRequest) -> HttpResponse {
|
||||
async fn get_airport(
|
||||
data: web::Data<AppState>,
|
||||
icao: web::Path<String>,
|
||||
req: HttpRequest,
|
||||
) -> HttpResponse {
|
||||
let metar = match web::Query::<AirportQuery>::from_query(req.query_string()) {
|
||||
Ok(q) => q.metars.unwrap_or_else(|| false),
|
||||
Err(err) => {
|
||||
@@ -95,7 +101,8 @@ async fn get_airport(icao: web::Path<String>, req: HttpRequest) -> HttpResponse
|
||||
}
|
||||
};
|
||||
|
||||
match Airport::select(&icao.into_inner(), metar).await {
|
||||
let client = &data.client;
|
||||
match Airport::select(client, &icao.into_inner(), metar).await {
|
||||
Some(airport) => HttpResponse::Ok().json(airport),
|
||||
None => HttpResponse::NotFound().finish(),
|
||||
}
|
||||
|
||||
@@ -97,7 +97,18 @@ impl From<std::env::VarError> for Error {
|
||||
|
||||
impl From<reqwest::Error> for Error {
|
||||
fn from(error: reqwest::Error) -> Self {
|
||||
Self::new(500, format!("Unknown reqwest error: {}", error))
|
||||
match error.status() {
|
||||
Some(status_code) => {
|
||||
if status_code.is_client_error() {
|
||||
Self::new(500, format!("Client reqwest error: {}", error))
|
||||
} else if status_code.is_server_error() {
|
||||
Self::new(500, format!("Server reqwest error: {}", error))
|
||||
} else {
|
||||
Self::new(500, format!("Unknown reqwest error: {:?}", error))
|
||||
}
|
||||
}
|
||||
_ => Self::new(500, format!("Unknown reqwest error: {:?}", error)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::env;
|
||||
|
||||
use std::time::Duration;
|
||||
use actix_cors::Cors;
|
||||
use actix_web::{App, HttpServer, middleware::Logger, web};
|
||||
use dotenv::from_filename;
|
||||
@@ -14,15 +14,17 @@ mod metars;
|
||||
mod scheduler;
|
||||
mod users;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct AppState {
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
initialize_environment()?;
|
||||
db::initialize().await?;
|
||||
// scheduler::update_airports();
|
||||
|
||||
let host = "0.0.0.0".to_string();
|
||||
let port = env::var("API_PORT").unwrap_or("5000".to_string());
|
||||
|
||||
// Initialize admin user
|
||||
let admin_email = env::var("ADMIN_EMAIL");
|
||||
let admin_password = env::var("ADMIN_PASSWORD");
|
||||
@@ -55,6 +57,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(10))
|
||||
.no_proxy()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.expect("Failed to create reqwest client");
|
||||
|
||||
let state = AppState { client };
|
||||
let host = env::var("API_HOST").unwrap_or("localhost".to_string());
|
||||
let port = env::var("API_PORT").unwrap_or("5000".to_string());
|
||||
|
||||
let server = match HttpServer::new(move || {
|
||||
let cors = Cors::default()
|
||||
.allow_any_origin()
|
||||
@@ -62,18 +75,22 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.allow_any_header()
|
||||
.supports_credentials()
|
||||
.max_age(3600);
|
||||
App::new().wrap(cors).wrap(Logger::default()).service(
|
||||
web::scope("api")
|
||||
.configure(airports::init_routes)
|
||||
.configure(metars::init_routes)
|
||||
.configure(auth::init_routes)
|
||||
.configure(users::init_routes),
|
||||
)
|
||||
App::new()
|
||||
.wrap(cors)
|
||||
.wrap(Logger::default())
|
||||
.app_data(web::Data::new(state.clone()))
|
||||
.service(
|
||||
web::scope("api")
|
||||
.configure(airports::init_routes)
|
||||
.configure(metars::init_routes)
|
||||
.configure(auth::init_routes)
|
||||
.configure(users::init_routes),
|
||||
)
|
||||
})
|
||||
.bind(format!("{}:{}", host, port))
|
||||
{
|
||||
Ok(b) => {
|
||||
log::info!("Binding server to {}:{}", host, port);
|
||||
log::info!("Server bound to {}:{}", host, port);
|
||||
b
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::{error::ApiResult, db};
|
||||
use chrono::{DateTime, Datelike, Utc};
|
||||
use std::collections::HashSet;
|
||||
use redis::{AsyncCommands, RedisResult};
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::db::redis_async_connection;
|
||||
|
||||
@@ -845,8 +846,8 @@ impl Metar {
|
||||
missing_metar_icaos
|
||||
}
|
||||
|
||||
async fn get_remote_metars(icaos: &[&str]) -> ApiResult<Vec<Metar>> {
|
||||
let gov_api_url = std::env::var("GOV_API_URL").expect("GOV_API_URL must be set");
|
||||
async fn get_remote_metars(client: &Client, icaos: &[&str]) -> ApiResult<Vec<Metar>> {
|
||||
let base_url = std::env::var("AVIATION_WEATHER_URL").expect("GOV_API_URL must be set");
|
||||
// Query the remote API for the missing METAR data 10 at a time
|
||||
let icao_chunks = icaos
|
||||
.chunks(10)
|
||||
@@ -854,14 +855,14 @@ impl Metar {
|
||||
.collect::<Vec<String>>();
|
||||
let mut metars: Vec<Metar> = vec![];
|
||||
for icao_chunk in icao_chunks {
|
||||
let url = format!("{}/metar.php?ids={}", gov_api_url, icao_chunk);
|
||||
let mut m = match reqwest::get(url).await {
|
||||
let url = format!("{}/metar?ids={}&order=id", base_url, icao_chunk);
|
||||
let mut m = match client.get(url).send().await {
|
||||
Ok(r) => {
|
||||
// Check if the status code is 200
|
||||
if r.status() != 200 {
|
||||
return Err(Error::new(
|
||||
500,
|
||||
format!("Unable to get METAR request: {}", r.status()),
|
||||
format!("Request returned status {}", r.status()),
|
||||
));
|
||||
}
|
||||
match r.text().await {
|
||||
@@ -876,20 +877,10 @@ impl Metar {
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(Error::new(
|
||||
500,
|
||||
format!("Unable to parse METAR request: {}", err),
|
||||
))
|
||||
}
|
||||
Err(err) => return Err(Error::new(500, format!("METAR parse failed: {}", err))),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(Error::new(
|
||||
500,
|
||||
format!("Unable to get METAR request: {}", err),
|
||||
))
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
metars.append(&mut m);
|
||||
}
|
||||
@@ -911,7 +902,11 @@ impl Metar {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn find_all(icao_list: &Vec<String>) -> ApiResult<Vec<Self>> {
|
||||
pub async fn find_all(
|
||||
client: &Client,
|
||||
icao_list: &Vec<String>,
|
||||
force: &bool,
|
||||
) -> ApiResult<Vec<Self>> {
|
||||
if icao_list.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
@@ -937,17 +932,21 @@ impl Metar {
|
||||
if !missing_icao_list.is_empty() {
|
||||
let mut updated_missing_icao_list: Vec<&str> = Vec::new();
|
||||
for icao in &missing_icao_list {
|
||||
let result: RedisResult<Option<bool>> = conn.get(icao).await;
|
||||
match result {
|
||||
Ok(Some(value)) => {
|
||||
if value {
|
||||
if *force {
|
||||
updated_missing_icao_list.push(icao);
|
||||
} else {
|
||||
let result: RedisResult<Option<bool>> = conn.get(icao).await;
|
||||
match result {
|
||||
Ok(Some(value)) => {
|
||||
if value {
|
||||
updated_missing_icao_list.push(icao);
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
updated_missing_icao_list.push(icao);
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
Ok(None) => {
|
||||
updated_missing_icao_list.push(icao);
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
if !updated_missing_icao_list.is_empty() {
|
||||
@@ -955,7 +954,7 @@ impl Metar {
|
||||
"Retrieving missing METAR data for {:?}",
|
||||
updated_missing_icao_list
|
||||
);
|
||||
let mut missing_icao_list = Self::get_remote_metars(&updated_missing_icao_list)
|
||||
let mut missing_icao_list = Self::get_remote_metars(client, &updated_missing_icao_list)
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
log::warn!("Unable to get remote METAR data; {}", err);
|
||||
|
||||
@@ -2,14 +2,16 @@ use crate::metars::Metar;
|
||||
use actix_web::{get, web, HttpResponse, HttpRequest};
|
||||
use log::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::AppState;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct FindAllParameters {
|
||||
icaos: Option<String>,
|
||||
force: Option<bool>,
|
||||
}
|
||||
|
||||
#[get("metars")]
|
||||
async fn find_all(req: HttpRequest) -> HttpResponse {
|
||||
async fn find_all(data: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||
let parameters = web::Query::<FindAllParameters>::from_query(req.query_string()).unwrap();
|
||||
let icao_option = ¶meters.icaos;
|
||||
let icao_string = match icao_option {
|
||||
@@ -17,8 +19,10 @@ async fn find_all(req: HttpRequest) -> HttpResponse {
|
||||
None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"),
|
||||
};
|
||||
let icaos: Vec<String> = icao_string.split(',').map(|s| s.to_string()).collect();
|
||||
let force = ¶meters.force.unwrap_or(false);
|
||||
|
||||
let metars = match Metar::find_all(&icaos).await {
|
||||
let client = &data.client;
|
||||
let metars = match Metar::find_all(client, &icaos, force).await {
|
||||
Ok(a) => a,
|
||||
Err(err) => {
|
||||
error!("{}", err);
|
||||
|
||||
@@ -5,11 +5,12 @@ meta {
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{API_URL}}/metars?icaos=KJYO,KOKV,KMRB,KHEF,KIAD
|
||||
url: {{API_URL}}/metars?icaos=KJYO,KOKV,KMRB,KHEF,KIAD&force=true
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
params:query {
|
||||
icaos: KJYO,KOKV,KMRB,KHEF,KIAD
|
||||
force: true
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
vars {
|
||||
BASE_URL: http://localhost:8080
|
||||
~BASE_URL: http://localhost:5000
|
||||
~BASE_URL: http://127.0.0.1:5000
|
||||
~BASE_URL: http://[::1]:5000
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ x-env_file: &env
|
||||
- path: .env.local
|
||||
required: false
|
||||
|
||||
x-restart: &default_restart
|
||||
restart: unless-stopped
|
||||
|
||||
name: aviation
|
||||
services:
|
||||
httpd:
|
||||
@@ -14,12 +17,11 @@ services:
|
||||
dockerfile: Dockerfile
|
||||
env_file: *env
|
||||
ports:
|
||||
- "${HTTPD_HTTP_PORT:-8080}:80"
|
||||
- "${HTTPD_HTTPS_PORT:-8443}:443"
|
||||
- "${HTTPD_PORT:-8080}:80"
|
||||
networks:
|
||||
- frontend
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
<<: *default_restart
|
||||
|
||||
postgres:
|
||||
image: postgis/postgis:17-3.5
|
||||
@@ -38,7 +40,7 @@ services:
|
||||
- backend
|
||||
profiles:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
<<: *default_restart
|
||||
|
||||
redis:
|
||||
image: redis:8.0-M03 # Replace with valkey?
|
||||
@@ -56,7 +58,7 @@ services:
|
||||
- backend
|
||||
profiles:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
<<: *default_restart
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-02-28T09-55-16Z
|
||||
@@ -76,7 +78,7 @@ services:
|
||||
profiles:
|
||||
- backend
|
||||
command: server --console-address ":9001" /data
|
||||
restart: unless-stopped
|
||||
<<: *default_restart
|
||||
|
||||
api:
|
||||
image: aviation-api:latest
|
||||
@@ -86,6 +88,7 @@ services:
|
||||
dockerfile: Dockerfile
|
||||
env_file: *env
|
||||
environment:
|
||||
API_HOST: 0.0.0.0
|
||||
POSTGRES_HOST: aviation-postgres
|
||||
POSTGRES_PORT: 5432
|
||||
REDIS_HOST: aviation-redis
|
||||
@@ -103,7 +106,7 @@ services:
|
||||
- backend
|
||||
profiles:
|
||||
- api
|
||||
restart: unless-stopped
|
||||
<<: *default_restart
|
||||
|
||||
ui:
|
||||
image: aviation-ui:latest
|
||||
@@ -125,7 +128,7 @@ services:
|
||||
profiles:
|
||||
- frontend
|
||||
command: ["npm", "run", "dev"]
|
||||
restart: unless-stopped
|
||||
<<: *default_restart
|
||||
|
||||
volumes:
|
||||
postgres:
|
||||
|
||||
@@ -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
|
||||
@@ -229,7 +229,7 @@ Group www-data
|
||||
# e-mailed. This address appears on some server-generated pages, such
|
||||
# as error documents. e.g. admin@your-domain.com
|
||||
#
|
||||
ServerAdmin you@example.com
|
||||
ServerAdmin ben@bensherrif.com
|
||||
|
||||
#
|
||||
# ServerName gives the name and port that the server uses to identify itself.
|
||||
|
||||
@@ -50,7 +50,9 @@ body,
|
||||
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background-color 0.2s, color 0.2s;
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
}
|
||||
|
||||
.map-button.active {
|
||||
|
||||
@@ -42,14 +42,14 @@ function App() {
|
||||
|
||||
useEffect(() => {
|
||||
if (showRadar) {
|
||||
getWeatherMapUrl().then(url => {
|
||||
getWeatherMapUrl().then((url) => {
|
||||
setRainViewerUrl(url);
|
||||
});
|
||||
}
|
||||
}, [showRadar]);
|
||||
|
||||
function toggleRadar() {
|
||||
setShowRadar(prev => {
|
||||
setShowRadar((prev) => {
|
||||
const newValue = !prev;
|
||||
Cookies.set('showRadar', newValue.toString(), { expires: 7 });
|
||||
return newValue;
|
||||
@@ -96,7 +96,7 @@ function App() {
|
||||
<TileLayer url={darkLayerUrl} />
|
||||
</LayersControl.BaseLayer>
|
||||
</LayersControl>
|
||||
{rainViewerUrl && showRadar && <TileLayer url={rainViewerUrl} opacity={0.5} zIndex={5} />}
|
||||
{rainViewerUrl && showRadar && <TileLayer url={rainViewerUrl} opacity={0.5} zIndex={10} />}
|
||||
<ZoomControl position={'bottomright'} />
|
||||
<AirportLayer setAirport={setAirport} />
|
||||
<BaseLayerChangeHandler />
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function AirportMarker({
|
||||
mouseout: () => markerRef.current?.closePopup()
|
||||
}}
|
||||
>
|
||||
<Popup closeButton={false}>
|
||||
<Popup closeButton={false} autoPan={false}>
|
||||
{airport.icao} - {airport.name}
|
||||
</Popup>
|
||||
</Marker>
|
||||
|
||||
@@ -4,7 +4,7 @@ const weatherMapsUrl = 'https://api.rainviewer.com/public/weather-maps.json';
|
||||
|
||||
async function getWeatherMaps(): Promise<WeatherMaps | undefined> {
|
||||
const response = await fetch(`${weatherMapsUrl}`, {
|
||||
method: 'GET',
|
||||
method: 'GET'
|
||||
});
|
||||
if (response?.status === 200) {
|
||||
return response.json();
|
||||
@@ -18,16 +18,16 @@ export async function getWeatherMapUrl(): Promise<string | null> {
|
||||
if (weatherMaps != undefined) {
|
||||
let url = weatherMaps.host;
|
||||
// url = 'https://cdn.rainviewer.com';
|
||||
let latest = "";
|
||||
let latest = '';
|
||||
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}/2/1_1.png';
|
||||
// url += latest + "/256/{z}/{x}/{y}/255/1_1_1_0.webp";
|
||||
return url;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@ export interface SatelliteObject {
|
||||
export interface FrameObject {
|
||||
time: number;
|
||||
path: string;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user