v0.14.15-fix

This commit is contained in:
Mateusz Gruszczyński
2026-09-16 17:07:17 +02:00
parent 1862fed87f
commit c7d47db64e
26 changed files with 324 additions and 377 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ async fn export_configuration(
fn validate_configuration_header(export: &ConfigurationExport) -> Result<(), AppError> {
if export.format_version != 3 {
return Err(AppError::BadRequest("unsupported configuration export version; version 3 is required by GREE Controller 0.14.14".into()));
return Err(AppError::BadRequest("unsupported configuration export version; version 3 is required by GREE Controller 0.14.15".into()));
}
if export.settings.control_strategy != "setpoint" {
return Err(AppError::BadRequest(
+2 -1
View File
@@ -2,6 +2,7 @@ async fn security_headers(request: Request, next: Next) -> Response {
let path = request.uri().path();
let is_api = path == "/api" || path.starts_with("/api/");
let is_api_docs = path == "/api-docs" || path.starts_with("/api-docs/");
let is_custom_chart = path.starts_with("/charts/custom/");
let mut response = next.run(request).await;
@@ -24,7 +25,7 @@ async fn security_headers(request: Request, next: Next) -> Response {
);
headers.insert(
header::HeaderName::from_static("referrer-policy"),
HeaderValue::from_static(if path.starts_with("/charts/custom/") {
HeaderValue::from_static(if is_custom_chart {
"no-referrer"
} else {
"same-origin"
+2
View File
@@ -15,6 +15,7 @@ struct SystemInfoResponse {
simulator_count: usize,
bind: String,
base_path: String,
public_chart_base_url: String,
gree_interface: String,
gree_received_frames: u64,
gree_received_frames_by_device: std::collections::HashMap<String, u64>,
@@ -191,6 +192,7 @@ fn build_system_info(state: &AppState, devices: &[Device]) -> SystemInfoResponse
} else {
state.config.base_path.clone()
},
public_chart_base_url: state.config.public_chart_base_url.clone(),
gree_interface: if state.config.gree_interface.trim().is_empty() {
"auto".to_string()
} else {
+24
View File
@@ -21,6 +21,8 @@ pub struct Config {
pub app_token: String,
#[arg(long, env = "GREE_CONTROLLER_BASE_PATH", default_value = "")]
pub base_path: String,
#[arg(long, env = "GREE_CONTROLLER_PUBLIC_CHART_BASE_URL", default_value = "")]
pub public_chart_base_url: String,
#[arg(long, env = "GREE_CONTROLLER_SIMULATE", default_value_t = false)]
pub simulate: bool,
#[arg(long, env = "GREE_CONTROLLER_AUTO_SEED", default_value_t = false)]
@@ -60,6 +62,7 @@ impl Config {
dotenvy::dotenv().ok();
let mut config = Self::parse();
config.base_path = normalize_base_path(&config.base_path)?;
config.public_chart_base_url = normalize_public_chart_base_url(&config.public_chart_base_url)?;
if let Some(parent) = config.database.parent() {
std::fs::create_dir_all(parent).with_context(|| {
format!("cannot create database directory {}", parent.display())
@@ -282,6 +285,27 @@ fn normalize_base_path(value: &str) -> Result<String> {
Ok(format!("/{}", value.trim_matches('/')))
}
fn normalize_public_chart_base_url(value: &str) -> Result<String> {
let value = value.trim();
if value.is_empty() {
return Ok(String::new());
}
let parsed = url::Url::parse(value)
.with_context(|| "GREE_CONTROLLER_PUBLIC_CHART_BASE_URL must be an absolute HTTP(S) URL")?;
if !matches!(parsed.scheme(), "http" | "https")
|| parsed.host_str().is_none()
|| !parsed.username().is_empty()
|| parsed.password().is_some()
|| parsed.query().is_some()
|| parsed.fragment().is_some()
{
anyhow::bail!("GREE_CONTROLLER_PUBLIC_CHART_BASE_URL must be an absolute HTTP(S) URL without credentials, query or fragment");
}
Ok(value.trim_end_matches('/').to_string())
}
fn env_bool(name: &str) -> Option<bool> {
env::var(name)
.ok()