messages
This commit is contained in:
parent
288f193754
commit
fc3a6fe2a0
8 changed files with 372 additions and 50 deletions
95
Cargo.lock
generated
95
Cargo.lock
generated
|
@ -254,6 +254,17 @@ dependencies = [
|
||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-sqlx-session"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf95404b3deed3c55b22ec8dffe85e94ade9166e65c923270ddfdfe637a63b9f"
|
||||||
|
dependencies = [
|
||||||
|
"async-session",
|
||||||
|
"async-std",
|
||||||
|
"sqlx 0.3.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-sse"
|
name = "async-sse"
|
||||||
version = "4.0.0"
|
version = "4.0.0"
|
||||||
|
@ -293,6 +304,27 @@ dependencies = [
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22068c0c19514942eefcfd4daf8976ef1aad84e61539f95cd200c35202f80af5"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream-impl",
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-stream-impl"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-task"
|
name = "async-task"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
|
@ -1058,10 +1090,12 @@ dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
"askama",
|
"askama",
|
||||||
"askama_tide",
|
"askama_tide",
|
||||||
|
"async-sqlx-session",
|
||||||
"async-std",
|
"async-std",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"http-types",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx 0.4.0-beta.1",
|
||||||
"tide",
|
"tide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1735,14 +1769,50 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8974cacd80085fbe49e778708d660dec6fb351604dc34c3905b26efb2803b038"
|
||||||
|
dependencies = [
|
||||||
|
"sqlx-core 0.3.5",
|
||||||
|
"sqlx-macros 0.3.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx"
|
name = "sqlx"
|
||||||
version = "0.4.0-beta.1"
|
version = "0.4.0-beta.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cb7b012f28c74075d6b11172ba1874f4376a255509462eaf2ef25068b31729f"
|
checksum = "8cb7b012f28c74075d6b11172ba1874f4376a255509462eaf2ef25068b31729f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sqlx-core",
|
"sqlx-core 0.4.0-beta.1",
|
||||||
"sqlx-macros",
|
"sqlx-macros 0.4.0-beta.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-core"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88ac5a436f941c42eac509471a730df5c3c58e1450e68cd39afedbd948206273"
|
||||||
|
dependencies = [
|
||||||
|
"async-native-tls",
|
||||||
|
"async-std",
|
||||||
|
"async-stream",
|
||||||
|
"bitflags",
|
||||||
|
"byteorder",
|
||||||
|
"chrono",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"hex",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"percent-encoding",
|
||||||
|
"sqlformat",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1791,6 +1861,23 @@ dependencies = [
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlx-macros"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de2ae78b783af5922d811b14665a5a3755e531c3087bb805cf24cf71f15e6780"
|
||||||
|
dependencies = [
|
||||||
|
"async-std",
|
||||||
|
"dotenv",
|
||||||
|
"futures",
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"sqlx-core 0.3.5",
|
||||||
|
"syn",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros"
|
name = "sqlx-macros"
|
||||||
version = "0.4.0-beta.1"
|
version = "0.4.0-beta.1"
|
||||||
|
@ -1804,7 +1891,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sqlx-core",
|
"sqlx-core 0.4.0-beta.1",
|
||||||
"sqlx-rt",
|
"sqlx-rt",
|
||||||
"syn",
|
"syn",
|
||||||
"url",
|
"url",
|
||||||
|
|
|
@ -9,8 +9,10 @@ anyhow = "1.0"
|
||||||
argh = "0.1"
|
argh = "0.1"
|
||||||
askama_tide = "0.10"
|
askama_tide = "0.10"
|
||||||
askama = { version = "0.10", features = ["with-tide"]}
|
askama = { version = "0.10", features = ["with-tide"]}
|
||||||
|
async-sqlx-session = "0.2"
|
||||||
async-std = { version = "1.6", features = ["attributes"] }
|
async-std = { version = "1.6", features = ["attributes"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
http-types = "2.4"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
sqlx = { version = "0.4.0-beta.1", features = ["mysql", "chrono", "macros"] }
|
sqlx = { version = "0.4.0-beta.1", features = ["mysql", "chrono", "macros"] }
|
||||||
tide = "0.13"
|
tide = "0.13"
|
||||||
|
|
101
src/db.rs
101
src/db.rs
|
@ -1,5 +1,6 @@
|
||||||
use chrono::{DateTime, Utc};
|
use anyhow::{anyhow, Result};
|
||||||
use sqlx::database::HasArguments;
|
use sqlx::database::HasArguments;
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
type QueryAs<'q, T> =
|
type QueryAs<'q, T> =
|
||||||
|
@ -8,28 +9,32 @@ type Query<'q> = sqlx::query::Query<'q, sqlx::MySql, <sqlx::MySql as HasArgument
|
||||||
|
|
||||||
#[derive(sqlx::FromRow, Debug)]
|
#[derive(sqlx::FromRow, Debug)]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub id: i32,
|
pub id: Option<i32>,
|
||||||
pub macaddr: String,
|
pub macaddr: String,
|
||||||
pub nickname: String,
|
pub nickname: String,
|
||||||
pub descr: String,
|
pub descr: String,
|
||||||
pub privacy: PrivacyLevel,
|
pub privacy: PrivacyLevel,
|
||||||
pub created: DateTime<Utc>,
|
|
||||||
pub present: bool,
|
pub present: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Debug, Clone, Copy)]
|
|
||||||
#[repr(i8)]
|
|
||||||
pub enum PrivacyLevel {
|
|
||||||
ShowUserAndDevice = 0,
|
|
||||||
ShowUser = 1,
|
|
||||||
ShowAnonymous = 2,
|
|
||||||
HideUser = 3,
|
|
||||||
DontLog = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'q> Device {
|
impl<'q> Device {
|
||||||
pub fn all() -> QueryAs<'q, Self> {
|
pub fn create(&'q self) -> Result<Query<'q>> {
|
||||||
sqlx::query_as("SELECT * FROM mac_to_nick")
|
if let Some(_) = self.id {
|
||||||
|
return Err(anyhow!("device has already been created"));
|
||||||
|
}
|
||||||
|
Ok(sqlx::query(
|
||||||
|
"
|
||||||
|
INSERT
|
||||||
|
INTO mac_to_nick
|
||||||
|
(macaddr, nickname, descr, privacy, created)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, NOW())
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.bind(&self.macaddr)
|
||||||
|
.bind(&self.nickname)
|
||||||
|
.bind(&self.descr)
|
||||||
|
.bind(self.privacy))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_user(user: &'q str) -> QueryAs<'q, Self> {
|
pub fn for_user(user: &'q str) -> QueryAs<'q, Self> {
|
||||||
|
@ -54,18 +59,52 @@ ORDER BY
|
||||||
.bind(user)
|
.bind(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(mac: &'q str, user: &'q str) -> Query<'q> {
|
pub fn for_mac(macaddr: &'q str) -> QueryAs<'q, Self> {
|
||||||
sqlx::query(
|
dbg!(&macaddr);
|
||||||
|
sqlx::query_as(
|
||||||
"
|
"
|
||||||
INSERT
|
SELECT DISTINCT
|
||||||
INTO mac_to_nick
|
*,
|
||||||
(macaddr, nickname, descr, privacy, created)
|
FALSE present
|
||||||
VALUES
|
FROM
|
||||||
(?, ?, ?, ?, NOW())
|
mac_to_nick
|
||||||
|
WHERE
|
||||||
|
macaddr = ?
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.bind(mac)
|
.bind(macaddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update(&'q self) -> Result<Query<'q>> {
|
||||||
|
let id = match self.id {
|
||||||
|
Some(id) => id,
|
||||||
|
None => return Err(anyhow!("selected device has no id")),
|
||||||
|
};
|
||||||
|
Ok(sqlx::query(
|
||||||
|
"
|
||||||
|
UPDATE
|
||||||
|
mac_to_nick
|
||||||
|
SET
|
||||||
|
privacy = ?,
|
||||||
|
descr = ?
|
||||||
|
WHERE
|
||||||
|
id = ?
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.bind(self.privacy as u8)
|
||||||
|
.bind(&self.descr)
|
||||||
|
.bind(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(sqlx::Type, Debug, Clone, Copy)]
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum PrivacyLevel {
|
||||||
|
ShowUserAndDevice = 0,
|
||||||
|
ShowUser = 1,
|
||||||
|
ShowAnonymous = 2,
|
||||||
|
HideUser = 3,
|
||||||
|
DontLog = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrivacyLevel {
|
impl PrivacyLevel {
|
||||||
|
@ -82,6 +121,22 @@ impl PrivacyLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<i8> for PrivacyLevel {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(i: i8) -> Result<Self, Self::Error> {
|
||||||
|
let level = match i {
|
||||||
|
0 => PrivacyLevel::ShowUserAndDevice,
|
||||||
|
1 => PrivacyLevel::ShowUser,
|
||||||
|
2 => PrivacyLevel::ShowAnonymous,
|
||||||
|
3 => PrivacyLevel::HideUser,
|
||||||
|
4 => PrivacyLevel::DontLog,
|
||||||
|
_ => return Err("invalid privacy level"),
|
||||||
|
};
|
||||||
|
Ok(level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(sqlx::FromRow, Debug)]
|
#[derive(sqlx::FromRow, Debug)]
|
||||||
pub struct AliveDevice {
|
pub struct AliveDevice {
|
||||||
pub macaddr: String,
|
pub macaddr: String,
|
||||||
|
|
78
src/forms.rs
Normal file
78
src/forms.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use crate::db;
|
||||||
|
use crate::Level;
|
||||||
|
use crate::Message;
|
||||||
|
use crate::USER;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct RegisterForm {
|
||||||
|
macaddr: String,
|
||||||
|
descr: String,
|
||||||
|
privacy: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterForm {
|
||||||
|
pub async fn handle(self, request: &crate::Request) -> Message {
|
||||||
|
let privacy = match db::PrivacyLevel::try_from(self.privacy) {
|
||||||
|
Ok(privacy) => privacy,
|
||||||
|
Err(_) => return (Level::Error, "unable to parse privacy level".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dbresult = db::Device {
|
||||||
|
id: None,
|
||||||
|
macaddr: self.macaddr,
|
||||||
|
nickname: USER.to_string(),
|
||||||
|
descr: self.descr,
|
||||||
|
privacy,
|
||||||
|
present: false,
|
||||||
|
};
|
||||||
|
let dbresult = dbresult
|
||||||
|
.create()
|
||||||
|
.unwrap()
|
||||||
|
.execute(&request.state().pool)
|
||||||
|
.await;
|
||||||
|
return match dbresult {
|
||||||
|
Ok(_) => (Level::Info, "assinged device".to_string()),
|
||||||
|
Err(_) => (Level::Error, "unable to create device".to_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct UpdateForm {
|
||||||
|
macaddr: String,
|
||||||
|
descr: String,
|
||||||
|
privacy: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateForm {
|
||||||
|
pub async fn handle(self, request: &crate::Request) -> Message {
|
||||||
|
let mut device = match db::Device::for_mac(&self.macaddr)
|
||||||
|
.fetch_one(&request.state().pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(device) => device,
|
||||||
|
Err(_) => {
|
||||||
|
return (
|
||||||
|
Level::Error,
|
||||||
|
"unable to load device from database".to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
device.privacy = match db::PrivacyLevel::try_from(self.privacy) {
|
||||||
|
Ok(privacy) => privacy,
|
||||||
|
Err(_) => return (Level::Error, "unable to parse privacy level".to_string()),
|
||||||
|
};
|
||||||
|
device.descr = self.descr;
|
||||||
|
match device
|
||||||
|
.update()
|
||||||
|
.unwrap()
|
||||||
|
.execute(&request.state().pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => (Level::Info, "updated device".to_string()),
|
||||||
|
Err(_) => (Level::Error, "unable to update device".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
src/main.rs
52
src/main.rs
|
@ -1,10 +1,15 @@
|
||||||
use argh::FromArgs;
|
use argh::FromArgs;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::MySqlPool;
|
use sqlx::MySqlPool;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use tide::sessions::{MemoryStore, SessionMiddleware};
|
||||||
mod db;
|
mod db;
|
||||||
|
mod forms;
|
||||||
mod routes;
|
mod routes;
|
||||||
mod templates;
|
mod templates;
|
||||||
|
|
||||||
|
pub const USER: &str = "foosinn";
|
||||||
|
|
||||||
/// Configuration
|
/// Configuration
|
||||||
#[derive(FromArgs, Debug)]
|
#[derive(FromArgs, Debug)]
|
||||||
struct Config {
|
struct Config {
|
||||||
|
@ -18,6 +23,10 @@ struct Config {
|
||||||
default = "\"mysql://administration:foosinn123@127.0.0.1/administration\".to_string()"
|
default = "\"mysql://administration:foosinn123@127.0.0.1/administration\".to_string()"
|
||||||
)]
|
)]
|
||||||
dsn: String,
|
dsn: String,
|
||||||
|
|
||||||
|
/// session secret
|
||||||
|
#[argh(option, default = "\"thisisnotasecretthisisnotasecret\".into()")]
|
||||||
|
session_secret: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -25,6 +34,43 @@ pub struct State {
|
||||||
pool: MySqlPool,
|
pool: MySqlPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub enum Level {
|
||||||
|
Info,
|
||||||
|
Warn,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Message = (Level, String);
|
||||||
|
|
||||||
|
#[derive(Default, Deserialize, Serialize)]
|
||||||
|
pub struct AppSession {
|
||||||
|
messages: Vec<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppSession {
|
||||||
|
pub fn add_message(mut self, message: Message) -> Self {
|
||||||
|
self.messages.push(message);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_messages(&mut self) -> Vec<Message> {
|
||||||
|
let mut messages: Vec<Message> = Vec::new();
|
||||||
|
std::mem::swap(&mut messages, &mut self.messages);
|
||||||
|
messages
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit(self, request: &mut Request) {
|
||||||
|
request.session_mut().insert("app", self).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&mut Request> for AppSession {
|
||||||
|
fn from(request: &mut Request) -> Self {
|
||||||
|
request.session().get("app").unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type Request = tide::Request<State>;
|
pub type Request = tide::Request<State>;
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
|
@ -35,11 +81,15 @@ async fn main() -> Result<(), io::Error> {
|
||||||
.await
|
.await
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
|
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
|
||||||
|
|
||||||
|
let session_store =
|
||||||
|
SessionMiddleware::new(MemoryStore::new(), config.session_secret.as_bytes());
|
||||||
|
|
||||||
let mut app = tide::with_state(State { pool });
|
let mut app = tide::with_state(State { pool });
|
||||||
|
app.with(session_store);
|
||||||
app.at("/").get(routes::index);
|
app.at("/").get(routes::index);
|
||||||
app.at("/register").post(routes::register);
|
app.at("/register").post(routes::register);
|
||||||
|
app.at("/update").post(routes::update);
|
||||||
app.at("/healthz").get(routes::healthz);
|
app.at("/healthz").get(routes::healthz);
|
||||||
app.at("/static").serve_dir("static/")?;
|
app.at("/static").serve_dir("static/")?;
|
||||||
|
|
||||||
app.listen(config.listen).await
|
app.listen(config.listen).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
use crate::forms;
|
||||||
use crate::templates;
|
use crate::templates;
|
||||||
use tide::prelude::*;
|
use crate::AppSession;
|
||||||
|
use crate::USER;
|
||||||
const USER: &str = "foosinn";
|
use tide::Redirect;
|
||||||
|
|
||||||
pub async fn healthz(_request: crate::Request) -> tide::Result {
|
pub async fn healthz(_request: crate::Request) -> tide::Result {
|
||||||
Ok("ok".into())
|
Ok("ok".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn index(request: crate::Request) -> tide::Result {
|
pub async fn index(mut request: crate::Request) -> tide::Result {
|
||||||
let my = db::Device::for_user(USER)
|
let my = db::Device::for_user(USER)
|
||||||
.fetch_all(&request.state().pool)
|
.fetch_all(&request.state().pool)
|
||||||
.await
|
.await
|
||||||
|
@ -17,15 +18,28 @@ pub async fn index(request: crate::Request) -> tide::Result {
|
||||||
.fetch_all(&request.state().pool)
|
.fetch_all(&request.state().pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| dbg!(err))?;
|
.map_err(|err| dbg!(err))?;
|
||||||
Ok(templates::IndexTemplate::new(my, unassinged).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
let mut session = AppSession::from(&mut request);
|
||||||
struct RegisterForm {
|
let messages = session.pop_messages();
|
||||||
macaddr: String,
|
session.commit(&mut request);
|
||||||
|
|
||||||
|
Ok(templates::IndexTemplate::new(my, unassinged, messages).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn register(mut request: crate::Request) -> tide::Result {
|
pub async fn register(mut request: crate::Request) -> tide::Result {
|
||||||
let form: RegisterForm = request.body_form().await?;
|
let form: forms::RegisterForm = request.body_form().await?;
|
||||||
unimplemented!();
|
let message = form.handle(&request).await;
|
||||||
|
AppSession::from(&mut request)
|
||||||
|
.add_message(message)
|
||||||
|
.commit(&mut request);
|
||||||
|
Ok(Redirect::see_other("/").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update(mut request: crate::Request) -> tide::Result {
|
||||||
|
let form: forms::UpdateForm = request.body_form().await?;
|
||||||
|
let message = form.handle(&request).await;
|
||||||
|
AppSession::from(&mut request)
|
||||||
|
.add_message(message)
|
||||||
|
.commit(&mut request);
|
||||||
|
Ok(Redirect::see_other("/").into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
use crate::Message;
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
|
||||||
#[derive(Template, Default)]
|
#[derive(Template, Default)]
|
||||||
|
@ -6,10 +7,19 @@ use askama::Template;
|
||||||
pub struct IndexTemplate {
|
pub struct IndexTemplate {
|
||||||
my: Vec<db::Device>,
|
my: Vec<db::Device>,
|
||||||
unassinged: Vec<db::AliveDevice>,
|
unassinged: Vec<db::AliveDevice>,
|
||||||
|
messages: Vec<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexTemplate {
|
impl IndexTemplate {
|
||||||
pub fn new(my: Vec<db::Device>, unassinged: Vec<db::AliveDevice>) -> Self {
|
pub fn new(
|
||||||
Self { my, unassinged }
|
my: Vec<db::Device>,
|
||||||
|
unassinged: Vec<db::AliveDevice>,
|
||||||
|
messages: Vec<Message>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
my,
|
||||||
|
unassinged,
|
||||||
|
messages,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,16 +15,27 @@
|
||||||
<h1 class="text-xl underline text-center p-2">
|
<h1 class="text-xl underline text-center p-2">
|
||||||
macnickenson
|
macnickenson
|
||||||
</h1>
|
</h1>
|
||||||
|
<div>
|
||||||
|
{% for message in messages %}
|
||||||
|
{{ message.1 }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<h2 class="underline">Your Devices:</h2>
|
<h2 class="underline">Your Devices:</h2>
|
||||||
{% for device in my %}
|
{% for device in my %}
|
||||||
|
<form action="/update" method="POST">
|
||||||
<div class="grid grid-cols-3 grid-gap-1">
|
<div class="grid grid-cols-3 grid-gap-1">
|
||||||
<div class="font-mono">{{ device.macaddr }} {{ device.present }}</div>
|
<div class="font-mono">
|
||||||
|
{{ device.macaddr }} {{ device.present }}
|
||||||
|
<input type="hidden" name="macaddr" value="{{ device.macaddr }}" />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input class="focus:shadow-outline border border-gray-300 px-2"
|
<input class="focus:shadow-outline border border-gray-300 px-2"
|
||||||
value="{{ device.descr }}"/>
|
name="descr"
|
||||||
<select class="focus:shadow-outline border border-gray-300 px-2">
|
value="{{ device.descr }}" />
|
||||||
|
<select class="focus:shadow-outline border border-gray-300 px-2"
|
||||||
|
name="privacy">
|
||||||
<option value="0"
|
<option value="0"
|
||||||
{{ device.privacy.selected(crate::db::PrivacyLevel::ShowUserAndDevice) }}
|
{{ device.privacy.selected(crate::db::PrivacyLevel::ShowUserAndDevice) }}
|
||||||
>Show User and Device</option>
|
>Show User and Device</option>
|
||||||
|
@ -47,30 +58,45 @@
|
||||||
<button class="rounded bg-gray-300 font-bold px-2 bg-red-500">Delete</button>
|
<button class="rounded bg-gray-300 font-bold px-2 bg-red-500">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<h2 class="underline">Unregistred Devices:</h2>
|
<h2 class="underline">Unregistred Devices:</h2>
|
||||||
{% for device in unassinged %}
|
{% for device in unassinged %}
|
||||||
|
<form action="/register" method="POST">
|
||||||
<div class="grid grid-cols-4 grid-gap-1 p-2">
|
<div class="grid grid-cols-4 grid-gap-1 p-2">
|
||||||
<div class="font-mono">{{ device.macaddr }}</div>
|
<div class="font-mono">
|
||||||
|
{{ device.macaddr }}
|
||||||
|
<input type="hidden" name="macaddr" value="{{ device.macaddr }}" />
|
||||||
|
</div>
|
||||||
<div class="font-mono">{{ device.ip() }}</div>
|
<div class="font-mono">{{ device.ip() }}</div>
|
||||||
<div>
|
<div>
|
||||||
<input class="focus:shadow-outline border border-gray-300 px-2"
|
<input class="focus:shadow-outline border border-gray-300 px-2"
|
||||||
placeholder="awesome new device"/>
|
placeholder="awesome new device"
|
||||||
|
name="descr" />
|
||||||
|
<select class="focus:shadow-outline border border-gray-300 px-2"
|
||||||
|
name="privacy">
|
||||||
|
<option value="0">Show User and Device</option>
|
||||||
|
<option value="1">Show User</option>
|
||||||
|
<option value="2" selected>Show Anonymous</option>
|
||||||
|
<option value="3">Hide User</option>
|
||||||
|
<option value="4">Dont Log</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="rounded bg-gray-300 font-bold px-2 bg-blue-300">Register</button>
|
<button class="rounded bg-gray-300 font-bold px-2 bg-blue-300"
|
||||||
|
type="submit">
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="shadow-lg p-2">
|
<div class="shadow-lg p-2">
|
||||||
footer
|
footer
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue