froms and db

This commit is contained in:
Stefan Schwarz 2020-08-20 23:08:18 +02:00
parent fc3a6fe2a0
commit a1abfad8d6
6 changed files with 141 additions and 54 deletions

View file

@ -2,7 +2,6 @@ use anyhow::{anyhow, Result};
use sqlx::database::HasArguments;
use std::convert::TryFrom;
use std::net::IpAddr;
type QueryAs<'q, T> =
sqlx::query::QueryAs<'q, sqlx::MySql, T, <sqlx::MySql as HasArguments<'q>>::Arguments>;
type Query<'q> = sqlx::query::Query<'q, sqlx::MySql, <sqlx::MySql as HasArguments<'q>>::Arguments>;
@ -45,7 +44,7 @@ SELECT DISTINCT
IF(al.iplong, TRUE, FALSE) present
FROM
mac_to_nick mtn
LEFT JOIN
LEFT OUTER JOIN
alive_hosts al
ON
mtn.macaddr = al.macaddr
@ -95,6 +94,23 @@ WHERE
.bind(&self.descr)
.bind(id))
}
pub fn delete(self) -> Result<Query<'q>> {
let id = match self.id {
Some(id) => id,
None => return Err(anyhow!("selected device has no id")),
};
Ok(sqlx::query(
"
DELETE FROM
mac_to_nick
WHERE
id = ?
LIMIT 1
",
)
.bind(id))
}
}
#[derive(sqlx::Type, Debug, Clone, Copy)]
@ -108,10 +124,6 @@ pub enum PrivacyLevel {
}
impl PrivacyLevel {
pub fn as_u8(&self) -> u8 {
*self as u8
}
pub fn selected(&self, level: &PrivacyLevel) -> &'static str {
if *self as u8 == *level as u8 {
"selected"
@ -149,16 +161,19 @@ impl<'q> AliveDevice {
"
SELECT DISTINCT
al.macaddr macaddr,
al.iplong iplong
al.iplong iplong,
mtn.nickname
FROM
alive_hosts al
NATURAL LEFT JOIN
LEFT OUTER JOIN
mac_to_nick mtn
ON
al.macaddr = mtn.macaddr
WHERE
mtn.nickname IS NULL
AND al.erfda > NOW() - INTERVAL 24 DAY
ORDER BY
al.erfda DESC
;
",
)
}

View file

@ -18,12 +18,11 @@ impl RegisterForm {
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,
descr: self.descr.clone(),
privacy,
present: false,
};
@ -33,7 +32,10 @@ impl RegisterForm {
.execute(&request.state().pool)
.await;
return match dbresult {
Ok(_) => (Level::Info, "assinged device".to_string()),
Ok(_) => (
Level::Info,
format!("assinged device \"{}\" to {}", self.descr, USER),
),
Err(_) => (Level::Error, "unable to create device".to_string()),
};
}
@ -76,3 +78,34 @@ impl UpdateForm {
}
}
}
#[derive(Deserialize)]
pub struct DeleteForm {
macaddr: String,
}
impl DeleteForm {
pub async fn handle(self, request: &crate::Request) -> Message {
let 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(),
)
}
};
match device
.delete()
.unwrap()
.execute(&request.state().pool)
.await
{
Ok(_) => (Level::Info, "delete device".to_string()),
Err(_) => (Level::Error, "unable to delete device".to_string()),
}
}
}

View file

@ -6,8 +6,11 @@ use tide::sessions::{MemoryStore, SessionMiddleware};
mod db;
mod forms;
mod routes;
mod session;
mod templates;
pub use session::AppSession as Session;
pub const USER: &str = "foosinn";
/// Configuration
@ -43,34 +46,6 @@ pub enum Level {
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>;
#[async_std::main]
@ -89,6 +64,7 @@ async fn main() -> Result<(), io::Error> {
app.at("/").get(routes::index);
app.at("/register").post(routes::register);
app.at("/update").post(routes::update);
app.at("/delete").post(routes::delete);
app.at("/healthz").get(routes::healthz);
app.at("/static").serve_dir("static/")?;
app.listen(config.listen).await

View file

@ -1,7 +1,7 @@
use crate::db;
use crate::forms;
use crate::templates;
use crate::AppSession;
use crate::Session;
use crate::USER;
use tide::Redirect;
@ -18,28 +18,27 @@ pub async fn index(mut request: crate::Request) -> tide::Result {
.fetch_all(&request.state().pool)
.await
.map_err(|err| dbg!(err))?;
let mut session = AppSession::from(&mut request);
let messages = session.pop_messages();
session.commit(&mut request);
let messages = Session::from(&mut request).pop_messages();
Ok(templates::IndexTemplate::new(my, unassinged, messages).into())
}
pub async fn register(mut request: crate::Request) -> tide::Result {
let form: forms::RegisterForm = request.body_form().await?;
let message = form.handle(&request).await;
AppSession::from(&mut request)
.add_message(message)
.commit(&mut request);
Session::from(&mut request).add_message(message);
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);
Session::from(&mut request).add_message(message);
Ok(Redirect::see_other("/").into())
}
pub async fn delete(mut request: crate::Request) -> tide::Result {
let form: forms::DeleteForm = request.body_form().await?;
let message = form.handle(&request).await;
Session::from(&mut request).add_message(message);
Ok(Redirect::see_other("/").into())
}

58
src/session.rs Normal file
View file

@ -0,0 +1,58 @@
use crate::Message;
use crate::Request;
use serde::{Deserialize, Serialize};
pub struct AppSession<'r> {
request: &'r mut Request,
session: Inner,
}
impl<'r> AppSession<'r> {
pub fn add_message(&mut self, message: Message) {
self.session.add_message(message);
}
pub fn pop_messages(&mut self) -> Vec<Message> {
self.session.pop_messages()
}
}
impl<'r> From<&'r mut Request> for AppSession<'r> {
fn from(request: &'r mut Request) -> Self {
let session = Inner::from(&*request);
Self { request, session }
}
}
impl<'r> Drop for AppSession<'r> {
fn drop(&mut self) {
self.session.commit(self.request);
}
}
#[derive(Default, Deserialize, Serialize)]
struct Inner {
messages: Vec<Message>,
}
impl Inner {
pub fn add_message(&mut self, message: Message) {
self.messages.push(message);
}
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(&mut self, request: &mut Request) {
request.session_mut().insert("app", self).unwrap()
}
}
impl From<&Request> for Inner {
fn from(request: &Request) -> Self {
request.session().get("app").unwrap_or_default()
}
}

View file

@ -54,8 +54,14 @@
</select>
</div>
<div>
<button class="rounded bg-gray-300 font-bold px-2 bg-green-500">Update</button>
<button class="rounded bg-gray-300 font-bold px-2 bg-red-500">Delete</button>
<button type="submit" name="action" value="update"
class="rounded bg-gray-300 font-bold px-2 bg-green-500">
Update
</button>
<button type="submit"name="action" value="update"
class="rounded bg-gray-300 font-bold px-2 bg-red-500">
Delete
</button>
</div>
</div>
</form>