jetzt mit Header, 3 Tabellen

This commit is contained in:
Eric Neuber committed 2025-11-20 20:12:25 +01:00
1 parent f2417ec65d
commit 220161a70a
10 files changed
+636 -102

No files matched your search

+138 -28
View File
@@ -1,6 +1,7 @@
use actix_web::{web, App, HttpResponse, HttpServer, Result};
use actix_files as fs;
use serde::{Deserialize, Serialize};
use std::fs;
use std::fs as std_fs;
use std::sync::Mutex;
use tera::{Context, Tera};
@@ -15,16 +16,38 @@ struct TableRow {
}
#[derive(Debug, Serialize, Deserialize, Clone)]
struct TableData {
rows: Vec<TableRow>,
struct Settings {
mqtt_broker: String,
mqtt_port: String,
influxdb_url: String,
influxdb_token: String,
}
impl Default for TableData {
impl Default for Settings {
fn default() -> Self {
TableData {
rows: vec![
Settings {
mqtt_broker: "localhost".to_string(),
mqtt_port: "1883".to_string(),
influxdb_url: "http://localhost:8086".to_string(),
influxdb_token: "".to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
struct AppConfig {
table1: Vec<TableRow>,
table2: Vec<TableRow>,
table3: Vec<TableRow>,
settings: Settings,
}
impl Default for AppConfig {
fn default() -> Self {
AppConfig {
table1: vec![
TableRow {
bezeichnung: "Sensor 1".to_string(),
bezeichnung: "Temp Sensor 1".to_string(),
adresse: "192.168.1.100".to_string(),
r#type: "Temperatur".to_string(),
faktor: "1.0".to_string(),
@@ -32,39 +55,52 @@ impl Default for TableData {
influxdb: false,
},
TableRow {
bezeichnung: "Sensor 2".to_string(),
bezeichnung: "Temp Sensor 2".to_string(),
adresse: "192.168.1.101".to_string(),
r#type: "Luftfeuchtigkeit".to_string(),
faktor: "0.5".to_string(),
r#type: "Temperatur".to_string(),
faktor: "1.0".to_string(),
mqtt: false,
influxdb: true,
},
],
table2: vec![
TableRow {
bezeichnung: "Sensor 3".to_string(),
adresse: "192.168.1.102".to_string(),
r#type: "Druck".to_string(),
faktor: "2.0".to_string(),
bezeichnung: "Humidity Sensor 1".to_string(),
adresse: "192.168.1.200".to_string(),
r#type: "Luftfeuchtigkeit".to_string(),
faktor: "0.5".to_string(),
mqtt: true,
influxdb: true,
},
],
table3: vec![
TableRow {
bezeichnung: "Pressure Sensor 1".to_string(),
adresse: "192.168.1.300".to_string(),
r#type: "Druck".to_string(),
faktor: "2.0".to_string(),
mqtt: true,
influxdb: false,
},
],
settings: Settings::default(),
}
}
}
struct AppState {
table: Mutex<TableData>,
config: Mutex<AppConfig>,
config_path: String,
templates: Tera,
}
impl AppState {
fn load_or_create(config_path: &str) -> Self {
let table = match fs::read_to_string(config_path) {
let config = match std_fs::read_to_string(config_path) {
Ok(content) => serde_json::from_str(&content).unwrap_or_default(),
Err(_) => {
let default = TableData::default();
let _ = fs::write(config_path, serde_json::to_string_pretty(&default).unwrap());
let default = AppConfig::default();
let _ = std_fs::write(config_path, serde_json::to_string_pretty(&default).unwrap());
default
}
};
@@ -78,24 +114,26 @@ impl AppState {
};
AppState {
table: Mutex::new(table),
config: Mutex::new(config),
config_path: config_path.to_string(),
templates: tera,
}
}
fn save(&self) -> Result<(), std::io::Error> {
let table = self.table.lock().unwrap();
let json = serde_json::to_string_pretty(&*table)?;
fs::write(&self.config_path, json)
let config = self.config.lock().unwrap();
let json = serde_json::to_string_pretty(&*config)?;
std_fs::write(&self.config_path, json)
}
}
async fn index(data: web::Data<AppState>) -> Result<HttpResponse> {
let table = data.table.lock().unwrap();
let config = data.config.lock().unwrap();
let mut context = Context::new();
context.insert("rows", &table.rows);
context.insert("rows", &config.table1);
context.insert("table_id", "table1");
context.insert("active_page", "table1");
let html = data.templates.render("index.html", &context)
.map_err(|e| {
@@ -106,13 +144,81 @@ async fn index(data: web::Data<AppState>) -> Result<HttpResponse> {
Ok(HttpResponse::Ok().content_type("text/html").body(html))
}
async fn table_page(data: web::Data<AppState>, path: web::Path<String>) -> Result<HttpResponse> {
let table_id = path.into_inner();
let config = data.config.lock().unwrap();
let rows = match table_id.as_str() {
"table1" => &config.table1,
"table2" => &config.table2,
"table3" => &config.table3,
_ => return Ok(HttpResponse::NotFound().body("Table not found")),
};
let mut context = Context::new();
context.insert("rows", rows);
context.insert("table_id", &table_id);
context.insert("active_page", &table_id);
let html = data.templates.render("index.html", &context)
.map_err(|e| {
eprintln!("Template error: {}", e);
actix_web::error::ErrorInternalServerError("Template error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(html))
}
async fn settings_page(data: web::Data<AppState>) -> Result<HttpResponse> {
let config = data.config.lock().unwrap();
let mut context = Context::new();
context.insert("settings", &config.settings);
context.insert("active_page", "settings");
let html = data.templates.render("settings.html", &context)
.map_err(|e| {
eprintln!("Template error: {}", e);
actix_web::error::ErrorInternalServerError("Template error")
})?;
Ok(HttpResponse::Ok().content_type("text/html").body(html))
}
#[derive(Deserialize)]
struct SaveTableRequest {
table_id: String,
rows: Vec<TableRow>,
}
async fn save_table(
data: web::Data<AppState>,
table_data: web::Json<TableData>,
req: web::Json<SaveTableRequest>,
) -> Result<HttpResponse> {
let mut table = data.table.lock().unwrap();
*table = table_data.into_inner();
drop(table);
let mut config = data.config.lock().unwrap();
match req.table_id.as_str() {
"table1" => config.table1 = req.rows.clone(),
"table2" => config.table2 = req.rows.clone(),
"table3" => config.table3 = req.rows.clone(),
_ => return Ok(HttpResponse::BadRequest().json(serde_json::json!({"status": "error", "message": "Invalid table_id"}))),
}
drop(config);
match data.save() {
Ok(_) => Ok(HttpResponse::Ok().json(serde_json::json!({"status": "success"}))),
Err(_) => Ok(HttpResponse::InternalServerError().json(serde_json::json!({"status": "error"}))),
}
}
async fn save_settings(
data: web::Data<AppState>,
settings: web::Json<Settings>,
) -> Result<HttpResponse> {
let mut config = data.config.lock().unwrap();
config.settings = settings.into_inner();
drop(config);
match data.save() {
Ok(_) => Ok(HttpResponse::Ok().json(serde_json::json!({"status": "success"}))),
@@ -131,7 +237,11 @@ async fn main() -> std::io::Result<()> {
App::new()
.app_data(app_state.clone())
.route("/", web::get().to(index))
.route("/table/{id}", web::get().to(table_page))
.route("/settings", web::get().to(settings_page))
.route("/api/save", web::post().to(save_table))
.route("/api/save-settings", web::post().to(save_settings))
.service(fs::Files::new("/static", "./static"))
})
.bind("0.0.0.0:8080")?
.run()