dev/nm17 #2
18 changed files with 223 additions and 191 deletions
62
Cargo.lock
generated
62
Cargo.lock
generated
|
@ -984,6 +984,37 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iotishnik-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bstr",
|
||||||
|
"bytes",
|
||||||
|
"chrono",
|
||||||
|
"clap",
|
||||||
|
"derive_more",
|
||||||
|
"dotenvy",
|
||||||
|
"fred",
|
||||||
|
"heapless",
|
||||||
|
"hex",
|
||||||
|
"hifitime",
|
||||||
|
"lazy_static",
|
||||||
|
"nom",
|
||||||
|
"ntex",
|
||||||
|
"phf",
|
||||||
|
"regex",
|
||||||
|
"rust_decimal",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_qs",
|
||||||
|
"serde_with",
|
||||||
|
"smallstr",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"ufmt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
@ -1156,37 +1187,6 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "narodmon-server"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"bstr",
|
|
||||||
"bytes",
|
|
||||||
"chrono",
|
|
||||||
"clap",
|
|
||||||
"derive_more",
|
|
||||||
"dotenvy",
|
|
||||||
"fred",
|
|
||||||
"heapless",
|
|
||||||
"hex",
|
|
||||||
"hifitime",
|
|
||||||
"lazy_static",
|
|
||||||
"nom",
|
|
||||||
"ntex",
|
|
||||||
"phf",
|
|
||||||
"regex",
|
|
||||||
"rust_decimal",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_qs",
|
|
||||||
"serde_with",
|
|
||||||
"smallstr",
|
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
|
||||||
"ufmt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "narodmon-server"
|
name = "iotishnik-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
22
README.md
Normal file
22
README.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# IoTishnik
|
||||||
|
Платформа для обработки данных окружающей среды с IoT устройств.
|
||||||
|
|
||||||
|
## Описание идеи
|
||||||
|
> ### Что есть сейчас:
|
||||||
|
>
|
||||||
|
> Есть куча девайсов, которые сейчас со своих сенсоров, для публичного и в некоторых случаях частного использования выкладывают на сервер narodmon.ru данные.
|
||||||
|
>
|
||||||
|
> ### В чём проблема narodmon.ru:
|
||||||
|
>
|
||||||
|
>1. Там всё такоееее легаси, что ты не представляешь. Речь идёт о cgi скриптах написанных на bash.
|
||||||
|
>2. Разрабы ищут помощь в виде лиц, в которых можно плевать без последствий сколько угодно.
|
||||||
|
>3. Отсутствие тех поддержки для обычных пользователей от слову совсем.
|
||||||
|
>4. Ужасное API которое ужасное не только из-за своей кривизны. В нём нужно отправлять телеметрию устройств, админ панель разработчика считай отсутствует. О OAuth2 вообще говорить не стоит. И если ты отправишь неправильные запросы с клиента своего, твой "api ключ" приложения заблокируют, и пофиг на то, что его можно спиздить с чужих приложений.
|
||||||
|
>5. Интерфейс просто ужас. Можешь сам зайти и посмотреть, даже писать про это не буду. Даже сам UX в изоляции от UI ужасный.
|
||||||
|
>6. Документация API не соблюдается. Даже когда мы с Андреем написали, что, мол, у вас поля изменились, им было плевать.
|
||||||
|
>7. Работа с приватными датчиками ужастная и требует денег для того, чтобы оно вообще работало.
|
||||||
|
>8. Некоторые очень важные API для устройств (такие как MQTT, на которых работают большое количество готовых продуктов не заточенные под narodmon.ru) доступны тоже только по подписке разрабам.
|
||||||
|
>
|
||||||
|
> ### Наше решение:
|
||||||
|
>
|
||||||
|
> Сделать с нуля своё решение, которое будет горизонтально масштабируемое, с поддержкой старых API для поддержки устройств сделанных под narodmon.ru . Выдать разрабам которые хотят новое и мощное API это API вместе с SDK в виде либ под ардуинку и т.п. Сделать поддержку OAuth для входа используя чужие сервисы и не только. И многое другое что я мб забыл упомянуть
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Модуль для парсинга всего, что связанно с данными от устройств.
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod packet_types;
|
mod packet_types;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
use crate::hashes::SupportedUnit;
|
//! Сборник типов для внутренней обработки данных с датчиков.
|
||||||
|
//!
|
||||||
|
//! Предполагается, что struct-ы будут совместимы с JSON API для передачи данных датчиков.
|
||||||
|
|
||||||
|
// Не забывайте про:
|
||||||
|
// #[serde(rename = "...")]
|
||||||
|
// #[serde(alias = "...")]
|
||||||
|
|
||||||
|
use crate::utils::SupportedUnit;
|
||||||
use crate::ingest_protocol::error::Error;
|
use crate::ingest_protocol::error::Error;
|
||||||
use crate::ingest_protocol::parser::parse_mac_address;
|
use crate::ingest_protocol::parser::parse_mac_address;
|
||||||
|
|
||||||
|
@ -11,6 +19,17 @@ use serde_with::serde_as;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
/// Данные с одного датчика.
|
||||||
|
///
|
||||||
|
/// Основной идентификатор - поле `mac`. В случае с [crate::web_server::old_device_sensor_api],
|
||||||
|
/// `mac` может быть не полем, а названием ключа в параметрах. Из-за двусмысленности документации
|
||||||
|
/// NarodMon, `mac` может означать EUI-48 совместимый MAC адрес, или же просто
|
||||||
|
/// уникальный идентификатор.
|
||||||
|
///
|
||||||
|
/// Парсинг этих данных отличается в разных транспортных протоколах.
|
||||||
|
/// Для HTTP /post или /get: см. [crate::web_server::old_device_sensor_api]
|
||||||
|
/// Для TCP/UDP: TODO
|
||||||
|
/// Для MQTT: TODO
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct SensorValue {
|
pub struct SensorValue {
|
||||||
pub mac: String,
|
pub mac: String,
|
||||||
|
@ -26,14 +45,9 @@ impl Hash for SensorValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DashSeparator {}
|
/// Функция-помощник для [MacAsArray], предназначенный для использования с [serde_with].
|
||||||
|
///
|
||||||
impl Separator for DashSeparator {
|
/// Преобразует MAC-адрес.
|
||||||
fn separator() -> &'static str {
|
|
||||||
"-"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mac_as_array(value: &str) -> Result<[u8; 6], Error<&str>> {
|
fn mac_as_array(value: &str) -> Result<[u8; 6], Error<&str>> {
|
||||||
Ok(parse_mac_address(value)?.1)
|
Ok(parse_mac_address(value)?.1)
|
||||||
}
|
}
|
||||||
|
|
60
src/main.rs
60
src/main.rs
|
@ -1,69 +1,13 @@
|
||||||
/*
|
#![doc = include_str!("../README.md")]
|
||||||
Три датчика реалтайм:
|
|
||||||
#26-94-1D-75-C2-F8
|
|
||||||
#T1#6.93
|
|
||||||
#H1#21
|
|
||||||
#P1#700.91
|
|
||||||
##
|
|
||||||
|
|
||||||
Формат пакета данных:
|
|
||||||
#MAC[#NAME]\n
|
|
||||||
#mac1#value1[#time1][#name1]\n
|
|
||||||
...
|
|
||||||
#macN#valueN[#timeN][#nameN]\n
|
|
||||||
##
|
|
||||||
|
|
||||||
Загрузка истории показаний:
|
|
||||||
#26-94-1D-75-C2-F8
|
|
||||||
#T1#6.93#1687006667
|
|
||||||
#T1#10.17#1687006067
|
|
||||||
#T1#27.26#1687005467
|
|
||||||
##
|
|
||||||
|
|
||||||
C названием и координатами:
|
|
||||||
#26-94-1D-75-C2-F8#Метео
|
|
||||||
#OWNER#nm17
|
|
||||||
#T1#6.93#Улица
|
|
||||||
#T2#27.26#Дом
|
|
||||||
#P1#700.91#Барометр
|
|
||||||
#LAT#54.308997
|
|
||||||
#LON#48.395861
|
|
||||||
#ALT#233
|
|
||||||
##
|
|
||||||
*/
|
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
||||||
mod hashes;
|
mod utils;
|
||||||
mod ingest_protocol;
|
mod ingest_protocol;
|
||||||
mod web_server;
|
mod web_server;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::web_server::server_main;
|
use crate::web_server::server_main;
|
||||||
|
|
||||||
|
|
||||||
/*fn parse_sensor_value(input: Vec<&str>) -> MyIError<Vec<&str>, NarodMonValues> {
|
|
||||||
Ok(
|
|
||||||
(input, NarodMonValues {
|
|
||||||
mac: Default::default(),
|
|
||||||
value: Default::default(),
|
|
||||||
time: None,
|
|
||||||
name: None,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
struct Params {}
|
struct Params {}
|
||||||
|
|
||||||
#[ntex::main]
|
#[ntex::main]
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
|
//! Глобальный модуль для вспомогательных типов и утилит.
|
||||||
|
//!
|
||||||
|
|
||||||
|
|
||||||
use phf::phf_map;
|
use phf::phf_map;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
|
||||||
|
/// Поддерживаемые типы.
|
||||||
|
///
|
||||||
|
/// TODO: Решить необходимо ли к данным прикреплять единицы измерения.
|
||||||
|
/// TODO: Сейчас вообще сомнительно оставлять ли это или нет.
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum SupportedUnit {
|
pub enum SupportedUnit {
|
||||||
Celsius, // Needs verification > 273.15
|
Celsius, // Needs verification > -273.15
|
||||||
Percentage, // Needs verification >= 100 && <= 0
|
Percentage, // Needs verification >= 100 && <= 0
|
||||||
MillimeterHg, // Needs verification
|
MillimeterHg, // Needs verification
|
||||||
UVIndex, // Needs verification
|
UVIndex, // Needs verification
|
||||||
|
@ -56,6 +65,7 @@ impl<'de> Deserialize<'de> for SupportedUnit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Таблица преобразования текстового представления единиц в значения [SupportedUnit].
|
||||||
static STR_TO_UNITS: phf::Map<&'static str, SupportedUnit> = phf_map! {
|
static STR_TO_UNITS: phf::Map<&'static str, SupportedUnit> = phf_map! {
|
||||||
"C" => SupportedUnit::Celsius,
|
"C" => SupportedUnit::Celsius,
|
||||||
"%" => SupportedUnit::Percentage,
|
"%" => SupportedUnit::Percentage,
|
|
@ -12,11 +12,14 @@ use thiserror::Error;
|
||||||
|
|
||||||
|
|
||||||
use crate::insert_header;
|
use crate::insert_header;
|
||||||
use crate::web_server::old_device_sensor_api::QSParserError;
|
use crate::web_server::old_device_sensor_api::qs_parser::QSParserError;
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
|
||||||
|
/// Главный объект ошибки [std::error::Error] для всего Web API.
|
||||||
|
///
|
||||||
|
/// В целом, все Result у Web сервера должны использовать этот Error.
|
||||||
#[derive(Debug, Error, Display)]
|
#[derive(Debug, Error, Display)]
|
||||||
pub enum AppError {
|
pub enum AppError {
|
||||||
#[display(fmt = "IDK")]
|
#[display(fmt = "IDK")]
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
use crate::web_server::old_app_api::old_api_handler;
|
//! Модуль веб сервера.
|
||||||
|
//!
|
||||||
|
//! Все модули отвечают только за Web сторону. Такие вещи как
|
||||||
|
//! [crate::web_server::old_device_sensor_api] отвечают только за веб версию.
|
||||||
|
//!
|
||||||
|
//! TODO: Начать работу над TCP/UDP и MQTT сервером
|
||||||
|
|
||||||
|
use old_app_api::old_api_handler;
|
||||||
use fred::bytes_utils::Str;
|
use fred::bytes_utils::Str;
|
||||||
use fred::prelude::*;
|
use fred::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub(crate) mod app_error;
|
pub mod app_error;
|
||||||
pub mod old_app_api;
|
pub mod old_app_api;
|
||||||
mod old_device_sensor_api;
|
pub mod old_device_sensor_api;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
use crate::web_server::app_error::AppError;
|
|
||||||
use crate::web_server::old_app_api::handlers::{app_init, version};
|
|
||||||
use crate::web_server::old_app_api::types::{AppInitRequest, MandatoryParams};
|
|
||||||
use crate::web_server::utils::redis::is_api_key_valid;
|
|
||||||
use crate::web_server::NMAppState;
|
|
||||||
use nom::AsBytes;
|
|
||||||
use ntex::util::Bytes;
|
|
||||||
use ntex::web;
|
|
||||||
use ntex::web::types::State;
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn old_api_handler(
|
|
||||||
app_state: State<NMAppState>,
|
|
||||||
body_bytes: Bytes,
|
|
||||||
) -> Result<impl web::Responder, AppError> {
|
|
||||||
if body_bytes.len() > 10 * 1024 {
|
|
||||||
// 10 KiB
|
|
||||||
return Err(AppError::RequestTooLarge);
|
|
||||||
}
|
|
||||||
|
|
||||||
let body_bytes = body_bytes.as_bytes();
|
|
||||||
|
|
||||||
let mandatory_params: MandatoryParams<'_> = serde_json::from_slice(body_bytes)?; // TODO: Simd-JSON
|
|
||||||
|
|
||||||
// Ignore clippy singlematch
|
|
||||||
match mandatory_params.cmd.as_ref() {
|
|
||||||
"version" => return version((), &app_state).await,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
is_api_key_valid(&app_state.redis_client, mandatory_params.api_key.as_ref()).await?;
|
|
||||||
|
|
||||||
match mandatory_params.cmd.as_ref() {
|
|
||||||
"appInit" => {
|
|
||||||
let body: AppInitRequest = serde_json::from_slice(body_bytes)?;
|
|
||||||
|
|
||||||
app_init(body, &app_state).await
|
|
||||||
}
|
|
||||||
_ => Err(AppError::UnknownMethod(mandatory_params.cmd.to_string())),
|
|
||||||
}
|
|
||||||
|
|
||||||
//Ok("fuck")
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
use crate::web_server::app_error::AppError;
|
|
||||||
use crate::web_server::old_app_api::types::AppInitRequest;
|
|
||||||
use crate::web_server::NMAppState;
|
|
||||||
|
|
||||||
use serde_json::{json};
|
|
||||||
|
|
||||||
|
|
||||||
use crate::insert_header;
|
|
||||||
use fred::interfaces::KeysInterface;
|
|
||||||
use ntex::http::StatusCode;
|
|
||||||
use ntex::web;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn app_init(
|
|
||||||
_body: AppInitRequest<'_>,
|
|
||||||
app_state: &NMAppState,
|
|
||||||
) -> Result<web::HttpResponse, AppError> {
|
|
||||||
let _: () = app_state
|
|
||||||
.redis_client
|
|
||||||
.set("test", 123, None, None, true)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(web::HttpResponse::build(StatusCode::OK).body("Hello world!"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn version(_body: (), _app_state: &NMAppState) -> Result<web::HttpResponse, AppError> {
|
|
||||||
let mut resp = web::HttpResponse::build(StatusCode::OK).json(&json!({
|
|
||||||
"version": "indev",
|
|
||||||
"iotishnik": true
|
|
||||||
}));
|
|
||||||
|
|
||||||
insert_header!(resp.headers_mut(), "Cache-Control", "no-cache");
|
|
||||||
|
|
||||||
Ok(resp)
|
|
||||||
}
|
|
|
@ -1,7 +1,36 @@
|
||||||
mod methods;
|
use crate::web_server::app_error::AppError;
|
||||||
|
use crate::web_server::old_app_api::types::AppInitRequest;
|
||||||
|
use crate::web_server::NMAppState;
|
||||||
|
|
||||||
|
use serde_json::{json};
|
||||||
|
|
||||||
|
|
||||||
pub use methods::*;
|
use crate::insert_header;
|
||||||
|
use fred::interfaces::KeysInterface;
|
||||||
|
use ntex::http::StatusCode;
|
||||||
|
use ntex::web;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn app_init(
|
||||||
|
_body: AppInitRequest<'_>,
|
||||||
|
app_state: &NMAppState,
|
||||||
|
) -> Result<web::HttpResponse, AppError> {
|
||||||
|
let _: () = app_state
|
||||||
|
.redis_client
|
||||||
|
.set("test", 123, None, None, true)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(web::HttpResponse::build(StatusCode::OK).body("Hello world!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn version(_body: (), _app_state: &NMAppState) -> Result<web::HttpResponse, AppError> {
|
||||||
|
let mut resp = web::HttpResponse::build(StatusCode::OK).json(&json!({
|
||||||
|
"version": "indev",
|
||||||
|
"iotishnik": true
|
||||||
|
}));
|
||||||
|
|
||||||
|
insert_header!(resp.headers_mut(), "Cache-Control", "no-cache");
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,53 @@
|
||||||
mod config_app;
|
//! Модуль обработки данных для приложений конечных пользователей, которые используют старый API.
|
||||||
|
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use config_app::old_api_handler;
|
use ntex::web::types::State;
|
||||||
|
use ntex::util::Bytes;
|
||||||
|
use ntex::web;
|
||||||
|
use nom::AsBytes;
|
||||||
|
use crate::web_server::app_error::AppError;
|
||||||
|
use crate::web_server::NMAppState;
|
||||||
|
use crate::web_server::old_app_api::handlers::{app_init, version};
|
||||||
|
use crate::web_server::old_app_api::types::{AppInitRequest, MandatoryParams};
|
||||||
|
use crate::web_server::utils::redis::is_api_key_valid;
|
||||||
|
|
||||||
|
|
||||||
|
/// Обработчик запросов от приложений.
|
||||||
|
///
|
||||||
|
/// Отвечает за разделение на функции по `cmd`.
|
||||||
|
///
|
||||||
|
/// Вызывается напрямую из ntex приложения.
|
||||||
|
pub async fn old_api_handler(
|
||||||
|
app_state: State<NMAppState>,
|
||||||
|
body_bytes: Bytes,
|
||||||
|
) -> Result<impl web::Responder, AppError> {
|
||||||
|
if body_bytes.len() > 10 * 1024 {
|
||||||
|
// 10 KiB
|
||||||
|
return Err(AppError::RequestTooLarge);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body_bytes = body_bytes.as_bytes();
|
||||||
|
|
||||||
|
let mandatory_params: MandatoryParams<'_> = serde_json::from_slice(body_bytes)?; // TODO: Simd-JSON
|
||||||
|
|
||||||
|
// Ignore clippy singlematch
|
||||||
|
match mandatory_params.cmd.as_ref() {
|
||||||
|
"version" => return version((), &app_state).await,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_api_key_valid(&app_state.redis_client, mandatory_params.api_key.as_ref()).await?;
|
||||||
|
|
||||||
|
match mandatory_params.cmd.as_ref() {
|
||||||
|
"appInit" => {
|
||||||
|
let body: AppInitRequest = serde_json::from_slice(body_bytes)?;
|
||||||
|
|
||||||
|
app_init(body, &app_state).await
|
||||||
|
}
|
||||||
|
_ => Err(AppError::UnknownMethod(mandatory_params.cmd.to_string())),
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ok("fuck")
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
@ -24,6 +23,11 @@ pub struct AddLikeRequest {
|
||||||
pub version: u64,
|
pub version: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Обязательные параметры у JSON app API.
|
||||||
|
///
|
||||||
|
/// При обработке входящих данных производиться два запроса [serde_json::from_str]. Один вызов пытается
|
||||||
|
/// получить [MandatoryParams], другой в зависимости от `cmd`. Это позволяет не добвалять поля из
|
||||||
|
/// этой структуры в каждом специфичном типе.
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct MandatoryParams<'a> {
|
pub struct MandatoryParams<'a> {
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
|
@ -32,6 +36,11 @@ pub struct MandatoryParams<'a> {
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
pub lang: Cow<'a, str>,
|
pub lang: Cow<'a, str>,
|
||||||
|
|
||||||
|
/// Уникальный ID клиента.
|
||||||
|
///
|
||||||
|
/// Используется на подобии как куки PHPSESSID в php.
|
||||||
|
///
|
||||||
|
/// См. также: <https://www.php.net/manual/en/book.session.php>
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
pub uuid: Cow<'a, str>,
|
pub uuid: Cow<'a, str>,
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
mod qs_parser;
|
//! Модуль обработки данных с устройств, которые используют старый API.
|
||||||
|
|
||||||
|
pub mod qs_parser;
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use crate::ingest_protocol::{NMDeviceDataPacket, NMJsonPacket};
|
use crate::ingest_protocol::{NMDeviceDataPacket, NMJsonPacket};
|
||||||
|
@ -13,6 +15,7 @@ use hifitime::Epoch;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use ufmt::uwrite;
|
use ufmt::uwrite;
|
||||||
use crate::web_server::NMAppState;
|
use crate::web_server::NMAppState;
|
||||||
|
use crate::web_server::old_device_sensor_api::qs_parser::QSParserError;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -20,9 +23,17 @@ pub enum Error {
|
||||||
DeviceNotFound(String),
|
DeviceNotFound(String),
|
||||||
#[error("Time sent with the device is way to behind now")]
|
#[error("Time sent with the device is way to behind now")]
|
||||||
TimeIsLongBehindNow,
|
TimeIsLongBehindNow,
|
||||||
|
#[error("{0}")]
|
||||||
|
QSParserError(#[from] QSParserError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Обработчик данных датчиков с устройств.
|
||||||
|
///
|
||||||
|
/// Слушает /post и /get.
|
||||||
|
/// Для того чтобы пользователям было легче, на оба пути можно отправлять и POST и GET.
|
||||||
|
///
|
||||||
|
/// На POST можно отправлять JSON или url-encoded тело, на GET - только через Query String.
|
||||||
pub async fn device_handler<'a>(
|
pub async fn device_handler<'a>(
|
||||||
request: web::HttpRequest,
|
request: web::HttpRequest,
|
||||||
body: Bytes,
|
body: Bytes,
|
||||||
|
|
|
@ -40,6 +40,11 @@ impl From<serde_qs::Error> for QSParserError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Преобразование оставшихся параметров в urlencoded теле или query string в данные с датчиков
|
||||||
|
/// [SensorValue].
|
||||||
|
///
|
||||||
|
/// Формат: `<SENSOR_MAC>=<SENSOR_VALUE>`.
|
||||||
|
/// Других данных на подобии названия и времени нет.
|
||||||
pub fn qs_rest_to_values(parsed: HashMap<String, String>) -> Result<HashSet<SensorValue>, QSParserError> {
|
pub fn qs_rest_to_values(parsed: HashMap<String, String>) -> Result<HashSet<SensorValue>, QSParserError> {
|
||||||
let mut hashset = HashSet::new();
|
let mut hashset = HashSet::new();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//! Сборник полезных функций которые используются в многих местах одновременно или слишком
|
||||||
|
//! неспециализированные.
|
||||||
|
|
||||||
pub mod redis;
|
pub mod redis;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Сборник утилит для работы с Redis.
|
||||||
|
|
||||||
use crate::web_server::app_error::AppError;
|
use crate::web_server::app_error::AppError;
|
||||||
use fred::prelude::*;
|
use fred::prelude::*;
|
||||||
use heapless::String as HeaplessString;
|
use heapless::String as HeaplessString;
|
||||||
|
@ -6,13 +8,17 @@ use regex::Regex;
|
||||||
use ufmt::uwrite;
|
use ufmt::uwrite;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
/// Разрешённые знаки для API ключа.
|
||||||
static ref ALLOWED_API_KEY_CHARACTERS: Regex = Regex::new("[a-zA-Z0-9]{13}").unwrap();
|
static ref ALLOWED_API_KEY_CHARACTERS: Regex = Regex::new("[a-zA-Z0-9]{13}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Описание полей в KV DB у `apikey_{}`.
|
||||||
pub struct ApiKeyDescription {
|
pub struct ApiKeyDescription {
|
||||||
|
/// ID владельца API ключа.
|
||||||
apikey_owner: i64,
|
apikey_owner: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Проверка API ключа на валидность.
|
||||||
pub async fn is_api_key_valid(
|
pub async fn is_api_key_valid(
|
||||||
client: &RedisClient,
|
client: &RedisClient,
|
||||||
api_key: &str,
|
api_key: &str,
|
||||||
|
|
Loading…
Add table
Reference in a new issue