init
This commit is contained in:
commit
c5300b9912
5 changed files with 1173 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
1014
Cargo.lock
generated
Normal file
1014
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "rpictrl"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
ntex = { version = "2.12.3", default-features = false, features = ["tokio"] }
|
||||
tokio = { version = "1.44.1", features = ["fs", "process"] }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
lto = "fat"
|
||||
strip = "symbols"
|
102
src/main.rs
Normal file
102
src/main.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use ntex::{
|
||||
http::StatusCode,
|
||||
web::{self, App, HttpServer, Responder, WebResponseError},
|
||||
};
|
||||
|
||||
type Result<T> = core::result::Result<T, ErrorWrapper>;
|
||||
|
||||
#[ntex::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let bind = std::env::var("BIND").unwrap_or("10.3.141.1:5544".to_owned());
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.service(root)
|
||||
.service(poweroff)
|
||||
.service(ping)
|
||||
.service(clients)
|
||||
.service(temp)
|
||||
})
|
||||
.bind(bind)?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
#[web::get("/")]
|
||||
async fn root() -> impl Responder {
|
||||
include_bytes!("../static/index.html")
|
||||
.with_header("content-type", "text/html")
|
||||
.with_status(StatusCode::OK)
|
||||
}
|
||||
|
||||
#[web::get("/off")]
|
||||
async fn poweroff() -> Result<impl Responder> {
|
||||
tokio::process::Command::new("/sbin/poweroff")
|
||||
.kill_on_drop(false)
|
||||
.spawn()?;
|
||||
Ok("OK")
|
||||
}
|
||||
|
||||
#[web::get("/ping")]
|
||||
async fn ping() -> Result<impl Responder> {
|
||||
let out = tokio::process::Command::new("/bin/ping")
|
||||
.arg("-c")
|
||||
.arg("4")
|
||||
.arg("1.1.1.1")
|
||||
.output()
|
||||
.await?;
|
||||
Ok(format!(
|
||||
"exited with {}\n\nstdout:\n{}\n----\n\nstderr:\n{}",
|
||||
out.status,
|
||||
String::from_utf8_lossy(&out.stdout),
|
||||
String::from_utf8_lossy(&out.stderr),
|
||||
))
|
||||
}
|
||||
|
||||
#[web::get("/ips")]
|
||||
async fn clients() -> Result<impl Responder> {
|
||||
tokio::fs::read_to_string("/var/lib/misc/dnsmasq.leases")
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[web::get("/temp")]
|
||||
async fn temp() -> Result<impl Responder> {
|
||||
let out = tokio::process::Command::new("/usr/bin/vcgencmd")
|
||||
.arg("measure_temp")
|
||||
.output()
|
||||
.await?;
|
||||
Ok(String::from_utf8(out.stdout)?)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ErrorWrapper(std::io::Error);
|
||||
|
||||
impl From<std::io::Error> for ErrorWrapper {
|
||||
#[inline]
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for ErrorWrapper {
|
||||
fn from(value: std::string::FromUtf8Error) -> Self {
|
||||
Self(std::io::Error::new(std::io::ErrorKind::InvalidData, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ErrorWrapper {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl WebResponseError for ErrorWrapper {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
fn error_response(&self, _: &web::HttpRequest) -> web::HttpResponse {
|
||||
format!("{:?}", self.0).into()
|
||||
}
|
||||
}
|
42
static/index.html
Normal file
42
static/index.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>RPi Ctrl</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #222;
|
||||
color: #eee;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid #3684d3;
|
||||
border-radius: 0.25rem;
|
||||
text-decoration: none;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: #3684d3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/off">Power off</a>
|
||||
<a href="/ping">Ping 1.1.1.1</a>
|
||||
<a href="/ips">List IPs</a>
|
||||
<a href="/temp">Get CPU temp</a>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue