Forgot to add
This commit is contained in:
parent
83d712b930
commit
427a3095f8
15 changed files with 1992 additions and 124 deletions
1640
Cargo.lock
generated
1640
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
17
Cargo.toml
17
Cargo.toml
|
@ -6,7 +6,24 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.71"
|
||||||
|
axum = { version = "0.6.18", features = ["http2", "headers", "macros"] }
|
||||||
|
bytes = { version = "1.4.0", features = ["serde"] }
|
||||||
|
chrono = { version = "0.4.26", features = ["serde"] }
|
||||||
|
clap = { version = "4.3.8", features = ["derive", "env"] }
|
||||||
|
dotenvy = "0.15.7"
|
||||||
|
fred = { version = "6.3.0", features = ["nom"] }
|
||||||
|
heapless = { version = "0.7.16", features = ["ufmt-impl"] }
|
||||||
hex = { version = "0.4.3", default-features = false }
|
hex = { version = "0.4.3", default-features = false }
|
||||||
|
hifitime = "3.8.2"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
nom = { version = "7.1.3", default-features = false, features = ["std", "alloc"] }
|
nom = { version = "7.1.3", default-features = false, features = ["std", "alloc"] }
|
||||||
|
phf = { version = "0.11.2", features = ["serde", "macros"] }
|
||||||
|
regex = "1.8.4"
|
||||||
rust_decimal = { version = "1.30.0", features = ["rkyv", "rkyv-safe"] }
|
rust_decimal = { version = "1.30.0", features = ["rkyv", "rkyv-safe"] }
|
||||||
|
serde = { version = "1.0.164", features = ["derive", "alloc"] }
|
||||||
|
serde_json = "1.0.99"
|
||||||
|
smallstr = { version = "0.3.0", features = ["std", "union"] }
|
||||||
thiserror = "1.0.40"
|
thiserror = "1.0.40"
|
||||||
|
tokio = { version = "1.28.2", features = ["full"] }
|
||||||
|
ufmt = "0.2.0"
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use phf::phf_map;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum SupportedUnit {
|
||||||
|
Celsius, // Needs verification > 273.15
|
||||||
|
Percentage, // Needs verification >= 100 && <= 0
|
||||||
|
MillimeterHg, // Needs verification
|
||||||
|
UVIndex, // Needs verification
|
||||||
|
Boolean, // Needs verification
|
||||||
|
Kbps,
|
||||||
|
Volume,
|
||||||
|
KWh,
|
||||||
|
Amps,
|
||||||
|
Volts,
|
||||||
|
Watts,
|
||||||
|
Seconds,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SupportedUnit {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(match self {
|
||||||
|
SupportedUnit::Celsius => "C",
|
||||||
|
SupportedUnit::Percentage => "%",
|
||||||
|
SupportedUnit::MillimeterHg => "mmHg",
|
||||||
|
SupportedUnit::UVIndex => "UV",
|
||||||
|
SupportedUnit::Boolean => "bool",
|
||||||
|
SupportedUnit::Kbps => "Kbps",
|
||||||
|
SupportedUnit::Volume => "m3",
|
||||||
|
SupportedUnit::Amps => "A",
|
||||||
|
SupportedUnit::Volts => "V",
|
||||||
|
SupportedUnit::Watts => "W",
|
||||||
|
SupportedUnit::Seconds => "s",
|
||||||
|
SupportedUnit::KWh => "KWh"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SupportedUnit {
|
||||||
|
fn deserialize<'a, D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let color_str = Cow::<'a, str>::deserialize(deserializer)?;
|
||||||
|
match STR_TO_UNITS.get(color_str.as_ref()) {
|
||||||
|
Some(v) => Ok(v.clone()),
|
||||||
|
None => Err(serde::de::Error::custom("Invalid unit")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static STR_TO_UNITS: phf::Map<&'static str, SupportedUnit> = phf_map! {
|
||||||
|
"C" => SupportedUnit::Celsius,
|
||||||
|
"%" => SupportedUnit::Percentage,
|
||||||
|
"mmHg" => SupportedUnit::MillimeterHg,
|
||||||
|
"UV" => SupportedUnit::UVIndex,
|
||||||
|
"bool" => SupportedUnit::Boolean,
|
||||||
|
"Kbps" => SupportedUnit::Kbps,
|
||||||
|
"m3" => SupportedUnit::Volume,
|
||||||
|
"A" => SupportedUnit::Amps,
|
||||||
|
"V" => SupportedUnit::Volts,
|
||||||
|
"W" => SupportedUnit::Watts,
|
||||||
|
"s" => SupportedUnit::Seconds,
|
||||||
|
"KWh" => SupportedUnit::Seconds,
|
||||||
|
};
|
143
src/main.rs
143
src/main.rs
|
@ -1,3 +1,4 @@
|
||||||
|
#![feature(impl_trait_in_fn_trait_return)]
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Три датчика реалтайм:
|
Три датчика реалтайм:
|
||||||
|
@ -33,98 +34,30 @@ C названием и координатами:
|
||||||
##
|
##
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
|
mod hashes;
|
||||||
|
mod web_server;
|
||||||
|
mod protocol;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||||
use std::mem::{size_of, size_of_val};
|
use std::mem::{size_of, size_of_val};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use nom::{InputTake, IResult, Needed, Parser};
|
use std::str::FromStr;
|
||||||
use nom::branch::alt;
|
use axum::Router;
|
||||||
use nom::bytes::complete::{take_until, take_while_m_n};
|
use axum::routing::post;
|
||||||
use nom::bytes::streaming::{take_till1, take};
|
use hifitime::Epoch;
|
||||||
use nom::bytes::complete::tag;
|
|
||||||
use nom::bytes::streaming::take_till;
|
|
||||||
use nom::character::streaming::{anychar, char, hex_digit0, newline};
|
|
||||||
use nom::character::complete::hex_digit1;
|
|
||||||
|
|
||||||
use nom::combinator::{map, map_opt, map_parser, opt, recognize, rest};
|
|
||||||
use nom::Err as NomErr;
|
|
||||||
use nom::error::{Error, ErrorKind as NomErrorKind};
|
|
||||||
use nom::multi::{count, separated_list1};
|
|
||||||
use nom::sequence::{delimited, preceded, separated_pair};
|
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use thiserror::Error;
|
use crate::protocol::{NarodMonPacket, NarodMonValues};
|
||||||
|
use crate::protocol::error::Error;
|
||||||
struct NarodMonPacketSerializer {
|
use crate::protocol::error::Error::TimestampParseError;
|
||||||
|
use crate::web_server::old_app_api::old_api_handler;
|
||||||
}
|
use crate::web_server::server_main;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
struct NarodMonValues<'a> {
|
|
||||||
mac: Cow<'a, str>,
|
|
||||||
value: Decimal,
|
|
||||||
time: Option<u64>,
|
|
||||||
name: Option<u64>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Copy, Clone)]
|
|
||||||
enum ValueMac<'a> {
|
|
||||||
Temperature(Cow<'a, str>, Option<u128>)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct NarodMonPacket<'a> {
|
|
||||||
mac: [u8; 6],
|
|
||||||
name: Option<Cow<'a, str>>,
|
|
||||||
values: HashSet<NarodMonValues<'a>>
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_mac_address(input: &str) -> IResult<&str, [u8; 6]> {
|
|
||||||
let mut mac = [0u8; 6];
|
|
||||||
|
|
||||||
let mut counter = 0;
|
|
||||||
|
|
||||||
let (leftovers, i) = take(17usize)(input)?;
|
|
||||||
|
|
||||||
let (_, out) = count(|inp| {
|
|
||||||
//dbg!(inp);
|
|
||||||
let (mut i, o) = map_parser(take(2usize), hex_digit1)(inp)?;
|
|
||||||
if counter != 5 {
|
|
||||||
(i, _) = tag("-")(i)?;
|
|
||||||
}
|
|
||||||
counter+=1;
|
|
||||||
Ok((i, o))
|
|
||||||
}, 6)(i)?;
|
|
||||||
|
|
||||||
//dbg!(&out);
|
|
||||||
|
|
||||||
hex::decode_to_slice(out.join(""), &mut mac).unwrap();
|
|
||||||
|
|
||||||
Ok((leftovers, mac))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_packet(input: &str) -> IResult<&str, NarodMonPacket> {
|
|
||||||
let (input, _) = tag("#")(input)?;
|
|
||||||
|
|
||||||
let (input, mac) = parse_mac_address(input)?;
|
|
||||||
|
|
||||||
dbg!(input);
|
|
||||||
|
|
||||||
let (input, opt_name) = opt(delimited(tag("#"), take_till1(|c| c == '\n'), tag("\n")))(input)?;
|
|
||||||
|
|
||||||
|
|
||||||
|
/*fn parse_sensor_value(input: Vec<&str>) -> MyIError<Vec<&str>, NarodMonValues> {
|
||||||
let (input, values) = parse_sensor_values(input)?;
|
|
||||||
|
|
||||||
Ok((input,
|
|
||||||
NarodMonPacket {
|
|
||||||
mac,
|
|
||||||
name: opt_name,
|
|
||||||
values,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_sensor_value(input: Vec<&str>) -> IResult<&str, NarodMonValues> {
|
|
||||||
Ok(
|
Ok(
|
||||||
(input, NarodMonValues {
|
(input, NarodMonValues {
|
||||||
mac: Default::default(),
|
mac: Default::default(),
|
||||||
|
@ -133,44 +66,46 @@ fn parse_sensor_value(input: Vec<&str>) -> IResult<&str, NarodMonValues> {
|
||||||
name: None,
|
name: None,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fn parse_sensor_values(input: &str) -> IResult<&str, HashSet<NarodMonValues>> {
|
|
||||||
let (input, asd) = map_parser(take_until("##"),separated_list1(tag("\n"), rest))(input)?;
|
|
||||||
|
|
||||||
|
|
||||||
for line in asd {
|
|
||||||
let one = NarodMonValues::default();
|
|
||||||
let (line, _) = tag("#")(line)?;
|
|
||||||
let (line, sensor_mac) = take_till1(|c| c == '\n' || c == "#")(line);
|
|
||||||
let (line, _) = preceded(tag("#"), take_till1(|c| c == '\n' || c == "#")(line);
|
|
||||||
|
|
||||||
|
|
||||||
|
struct Params {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((input,
|
|
||||||
Default::default()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
//dotenvy::dotenv().unwrap();
|
||||||
|
|
||||||
|
let web_server_hndl = tokio::spawn(server_main());
|
||||||
|
|
||||||
|
web_server_hndl.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::protocol::parser::{parse_mac_address, parse_packet};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mac() {
|
fn test_mac() {
|
||||||
assert_eq!(parse_mac_address("12-34-AA-12-55-AA"), Ok(("", [18, 52, 170, 18, 85, 170])))
|
//assert_eq!(parse_mac_address("12-34-AA-12-55-AA"), Ok(("", [18, 52, 170, 18, 85, 170])) );
|
||||||
|
|
||||||
|
println!("{:?}", parse_mac_address("12-34-AA-12-55-AA"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packet() {
|
fn test_packet() {
|
||||||
let inp = "#26-94-1D-75-C2-F8#Метео\n";
|
let inp = r#"#26-94-1D-75-C2-F8#Метео
|
||||||
|
#OWNER#nm17
|
||||||
|
#T1#1.12
|
||||||
|
#T2#10000#1231410321#sensorName
|
||||||
|
#T2#1.2#3400005345
|
||||||
|
##"#;
|
||||||
|
|
||||||
println!("{:?}", parse_packet(inp));
|
|
||||||
|
println!("{:#?}", parse_packet(inp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
mod packet_types;
|
||||||
|
pub mod error;
|
||||||
|
pub mod parser;
|
||||||
|
mod server;
|
||||||
|
|
||||||
|
pub use packet_types::*;
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use hifitime::Epoch;
|
||||||
|
use nom::{InputTake, Needed, Parser};
|
||||||
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::{take_until, take_until1, take_while_m_n};
|
||||||
|
use nom::bytes::complete::{take_till1, take, take_while, take_while1};
|
||||||
|
use nom::bytes::complete::tag;
|
||||||
|
use nom::bytes::streaming::take_till;
|
||||||
|
use nom::character::streaming::{anychar, char, hex_digit0, newline};
|
||||||
|
use nom::character::complete::hex_digit1;
|
||||||
|
|
||||||
|
use nom::combinator::{map, map_opt, map_parser, opt, recognize, rest};
|
||||||
|
use nom::Err as NomErr;
|
||||||
|
use nom::error::{context, ContextError, ErrorKind as NomErrorKind, ParseError as NomParseError, Error as NomError, VerboseError};
|
||||||
|
use nom::multi::{count, separated_list0, separated_list1};
|
||||||
|
use nom::sequence::{delimited, preceded, separated_pair};
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
use crate::protocol::error::Error;
|
||||||
|
use crate::protocol::{NarodMonPacket, NarodMonValues};
|
||||||
|
use crate::protocol::error::Error::TimestampParseError;
|
||||||
|
|
||||||
|
type MyIError<I, O> = Result<(I, O), Error<I>>;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn parse_mac_address(input: &str) -> MyIError<&str, [u8; 6]> {
|
||||||
|
let mut mac = [0u8; 6];
|
||||||
|
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
let (leftovers, i) = context("17 символов для MAC адреса", take(17usize))(input)?;
|
||||||
|
|
||||||
|
let (_, out) = count(|inp| {
|
||||||
|
//dbg!(inp);
|
||||||
|
let (mut i, o) = context("Октет", map_parser(take(2usize), hex_digit1))(inp)?;
|
||||||
|
if counter != 5 {
|
||||||
|
(i, _) = tag("-")(i)?;
|
||||||
|
}
|
||||||
|
counter += 1;
|
||||||
|
Ok((i, o))
|
||||||
|
}, 6)(i)?;
|
||||||
|
|
||||||
|
//dbg!(&out);
|
||||||
|
|
||||||
|
hex::decode_to_slice(out.join(""), &mut mac).unwrap();
|
||||||
|
|
||||||
|
Ok((leftovers, mac))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_packet(input: &str) -> MyIError<&str, NarodMonPacket> {
|
||||||
|
let (input, _) = tag("#")(input)?;
|
||||||
|
|
||||||
|
let (input, mac) = parse_mac_address(input)?;
|
||||||
|
|
||||||
|
let (input, opt_name) = opt(delimited(tag("#"), take_while(|c| c != '\n'), tag("\n")))(input)?;
|
||||||
|
|
||||||
|
let mut packet = NarodMonPacket::default();
|
||||||
|
packet.mac = mac;
|
||||||
|
|
||||||
|
let mut hs = HashSet::new();
|
||||||
|
|
||||||
|
let (input, asd) = context(
|
||||||
|
"Получение значений до тега терминатора",
|
||||||
|
map_parser(
|
||||||
|
take_until1("##"),
|
||||||
|
separated_list0(tag("\n"), take_while1(|c| c != '\n'))
|
||||||
|
)
|
||||||
|
)(input)?;
|
||||||
|
|
||||||
|
|
||||||
|
println!("ASDASD: {:?}", asd);
|
||||||
|
|
||||||
|
for line in asd {
|
||||||
|
let (line, _) = tag("#")(line)?;
|
||||||
|
let (line, sensor_mac) = take_while1(|c| c != '\n' && c != '#')(line)?;
|
||||||
|
|
||||||
|
let (line, _) = tag("#")(line)?;
|
||||||
|
|
||||||
|
match sensor_mac {
|
||||||
|
"OWNER" => {
|
||||||
|
let (line, owner_value) = take_while1(|c| c != '\n')(line)?;
|
||||||
|
|
||||||
|
packet.owner = Some(owner_value.into())
|
||||||
|
|
||||||
|
//hs.insert(NarodMonValues::Owner(owner_value.into()));
|
||||||
|
//let (line, _) = tag("\n")(line)?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let (line, sensor_value) = take_while1(|c| c != '\n' && c != '#')(line)?;
|
||||||
|
let (line, sensor_time) = opt(preceded(tag("#"), take_while1(|c| c != '\n' && c != '#')))(line)?;
|
||||||
|
let (line, sensor_name) = opt(preceded(tag("#"), take_while1(|c| c != '\n')))(line)?;
|
||||||
|
|
||||||
|
|
||||||
|
let sensor_time = match sensor_time {
|
||||||
|
Some(v) => Some(Epoch::from_unix_seconds(v.parse().map_err(|e| TimestampParseError(e))?)),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
hs.insert(NarodMonValues {
|
||||||
|
mac: sensor_mac.into(),
|
||||||
|
value: Decimal::from_str(sensor_value).unwrap(),
|
||||||
|
time: sensor_time, // TODO
|
||||||
|
unit: None,
|
||||||
|
name: sensor_name.map(|v| Cow::from(v)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (input, _) = tag("##")(input)?;
|
||||||
|
|
||||||
|
packet.name = opt_name.map(|v| Cow::from(v));
|
||||||
|
packet.values = hs;
|
||||||
|
|
||||||
|
Ok((input, packet))
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
async fn main() {
|
||||||
|
|
||||||
|
}
|
|
@ -25,6 +25,11 @@ pub enum AppError {
|
||||||
#[error("Fuck")]
|
#[error("Fuck")]
|
||||||
RequestTooLarge,
|
RequestTooLarge,
|
||||||
|
|
||||||
|
#[error("Api")]
|
||||||
|
ApiKeyInvalid {
|
||||||
|
reason: &'static str
|
||||||
|
},
|
||||||
|
|
||||||
#[error("Fuck")]
|
#[error("Fuck")]
|
||||||
UnitValidationFailed {
|
UnitValidationFailed {
|
||||||
max: Option<Decimal>,
|
max: Option<Decimal>,
|
||||||
|
@ -51,6 +56,9 @@ impl IntoResponse for AppError {
|
||||||
},
|
},
|
||||||
AppError::ServerRedisError(_) => {
|
AppError::ServerRedisError(_) => {
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")
|
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")
|
||||||
|
},
|
||||||
|
AppError::ApiKeyInvalid { .. } => {
|
||||||
|
(StatusCode::BAD_REQUEST, "API Key invalid")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,6 +84,9 @@ impl IntoResponse for AppError {
|
||||||
AppError::RequestTooLarge => {
|
AppError::RequestTooLarge => {
|
||||||
headers.insert("X-Max-Request-Size", HeaderValue::try_from("10 KiB = 10240 bytes").unwrap());
|
headers.insert("X-Max-Request-Size", HeaderValue::try_from("10 KiB = 10240 bytes").unwrap());
|
||||||
},
|
},
|
||||||
|
AppError::ApiKeyInvalid { reason } => {
|
||||||
|
headers.insert("X-Error-Description", HeaderValue::try_from(reason).unwrap());
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,47 @@ use axum::{
|
||||||
};
|
};
|
||||||
use axum::error_handling::{HandleError, HandleErrorLayer};
|
use axum::error_handling::{HandleError, HandleErrorLayer};
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use crate::server::old_app_api::old_api_handler;
|
use axum::routing::post;
|
||||||
|
use fred::bytes_utils::Str;
|
||||||
|
use fred::prelude::*;
|
||||||
|
use crate::web_server::old_app_api::old_api_handler;
|
||||||
|
|
||||||
pub mod old_app_api;
|
pub mod old_app_api;
|
||||||
|
mod utils;
|
||||||
|
pub(crate) mod app_error;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct NMAppState {
|
pub struct NMAppState {
|
||||||
redis_client: RedisClient
|
pub redis_client: RedisClient
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn main()
|
use heapless::String as HeaplessString;
|
||||||
|
|
||||||
|
pub async fn server_main() {
|
||||||
|
let config = RedisConfig::default();
|
||||||
|
let perf = PerformanceConfig::default();
|
||||||
|
let policy = ReconnectPolicy::default();
|
||||||
|
let client = RedisClient::new(config, Some(perf), Some(policy));
|
||||||
|
|
||||||
|
// connect to the server, returning a handle to the task that drives the connection
|
||||||
|
let _ = client.connect();
|
||||||
|
let _ = client.wait_for_connect().await.unwrap();
|
||||||
|
|
||||||
|
let asd: Str = client.ping().await.unwrap();
|
||||||
|
|
||||||
|
println!("Ping result: {}", asd);
|
||||||
|
|
||||||
|
let state = NMAppState {
|
||||||
|
redis_client: client
|
||||||
|
};
|
||||||
|
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/api", post(old_api_handler))
|
||||||
|
.with_state(state);
|
||||||
|
|
||||||
|
|
||||||
|
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,35 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use axum::body::{Body, Bytes, HttpBody};
|
use axum::body::{Body, Bytes, HttpBody};
|
||||||
|
use axum::extract::State;
|
||||||
use axum::http::Request;
|
use axum::http::Request;
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
use axum::response::{IntoResponse, Response};
|
use axum::response::{IntoResponse, Response};
|
||||||
use nom::AsBytes;
|
use nom::AsBytes;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use crate::server::old_app_api::app_error::AppError;
|
use crate::web_server::app_error::AppError;
|
||||||
use crate::server::old_app_api::handlers::app_init;
|
use crate::web_server::NMAppState;
|
||||||
use crate::server::old_app_api::types::{AppInitRequest, MandatoryParams};
|
use crate::web_server::old_app_api::handlers::app_init;
|
||||||
|
use crate::web_server::old_app_api::types::{AppInitRequest, MandatoryParams};
|
||||||
|
use crate::web_server::utils::redis::is_api_key_valid;
|
||||||
|
|
||||||
|
|
||||||
pub async fn old_api_handler(
|
pub async fn old_api_handler(
|
||||||
|
app_state: State<NMAppState>,
|
||||||
body_bytes: Bytes,
|
body_bytes: Bytes,
|
||||||
) -> Result<impl IntoResponse, AppError> {
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
if body_bytes.len() > 10 * 1024 { // 10 KiB
|
if body_bytes.len() > 10 * 1024 { // 10 KiB
|
||||||
return Err(AppError::RequestTooLarge)
|
return Err(AppError::RequestTooLarge)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mandatory_params: MandatoryParams<'_> = serde_json::from_slice(body_bytes.as_bytes())?;
|
let mandatory_params: MandatoryParams<'_> = serde_json::from_slice(body_bytes.as_bytes())?; // TODO: Simd-JSON
|
||||||
|
|
||||||
|
is_api_key_valid(&app_state.redis_client, mandatory_params.api_key.as_ref()).await?;
|
||||||
|
|
||||||
return match mandatory_params.cmd.as_ref() {
|
return match mandatory_params.cmd.as_ref() {
|
||||||
"appInit" => {
|
"appInit" => {
|
||||||
let body: AppInitRequest = serde_json::from_slice(body_bytes.as_bytes())?;
|
let body: AppInitRequest = serde_json::from_slice(body_bytes.as_bytes())?;
|
||||||
|
|
||||||
Ok(app_init(body).await)
|
Ok(app_init(body, app_state).await)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
Err(AppError::UnknownMethod(mandatory_params.cmd.to_string()))
|
Err(AppError::UnknownMethod(mandatory_params.cmd.to_string()))
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
use axum::body::Body;
|
use axum::body::Body;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::http::Request;
|
use axum::http::{Request, StatusCode};
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
|
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
use crate::server::old_app_api::types::AppInitRequest;
|
use crate::web_server::old_app_api::types::AppInitRequest;
|
||||||
use heapless::String as HeaplessString;
|
use heapless::String as HeaplessString;
|
||||||
use ufmt::uwrite;
|
use ufmt::uwrite;
|
||||||
|
use crate::web_server::NMAppState;
|
||||||
|
use crate::web_server::app_error::AppError;
|
||||||
|
|
||||||
|
use fred::interfaces::KeysInterface;
|
||||||
|
|
||||||
pub async fn app_init(body: AppInitRequest<'_>, State(appState): State<>) -> impl IntoResponse {
|
pub async fn app_init(body: AppInitRequest<'_>, State(appState): State<NMAppState>) -> Result<impl IntoResponse, AppError> {
|
||||||
|
let _: () = appState.redis_client.set("test", 123, None, None, true).await?;
|
||||||
|
|
||||||
|
Ok((StatusCode::OK, "Hello, World!").into_response())
|
||||||
"Hello, World!"
|
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
mod types;
|
mod types;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod config_app;
|
mod config_app;
|
||||||
mod app_error;
|
|
||||||
|
|
||||||
pub use config_app::old_api_handler;
|
pub use config_app::old_api_handler;
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@ pub struct AppInitRequest<'a> {
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
pub model: Cow<'a, str>,
|
pub model: Cow<'a, str>,
|
||||||
|
|
||||||
#[serde(borrow)]
|
pub width: u64
|
||||||
pub width: Cow<'a, str>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod redis;
|
|
@ -0,0 +1,31 @@
|
||||||
|
use fred::prelude::*;
|
||||||
|
use heapless::String as HeaplessString;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use regex::Regex;
|
||||||
|
use ufmt::uwrite;
|
||||||
|
use crate::web_server::app_error::AppError;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ALLOWED_API_KEY_CHARACTERS: Regex = Regex::new("[a-zA-Z0-9]{13}").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ApiKeyDescription {
|
||||||
|
apikey_owner: i64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_api_key_valid(client: &RedisClient, api_key: &str) -> Result<ApiKeyDescription, AppError> {
|
||||||
|
if !ALLOWED_API_KEY_CHARACTERS.is_match(api_key) {
|
||||||
|
return Err(AppError::ApiKeyInvalid { reason: "Invalid characters present in the API key." })
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut key_buffer = HeaplessString::<{7 + 13}>::new();
|
||||||
|
uwrite!(key_buffer, "apikey_{}", api_key);
|
||||||
|
|
||||||
|
let valid: Option<i64> = client.hget(key_buffer.as_str(), "owner").await?;
|
||||||
|
|
||||||
|
valid.map(|uid| {
|
||||||
|
ApiKeyDescription {
|
||||||
|
apikey_owner: uid
|
||||||
|
}
|
||||||
|
}).ok_or(AppError::ApiKeyInvalid { reason: "Unknown API key" })
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue