This commit is contained in:
Stefan Schwarz 2020-08-21 18:53:33 +02:00
parent cf185c833b
commit c4d44c68a0
9 changed files with 146 additions and 87551 deletions

View file

@ -48,9 +48,9 @@ LEFT OUTER JOIN
alive_hosts al alive_hosts al
ON ON
mtn.macaddr = al.macaddr mtn.macaddr = al.macaddr
AND al.erfda > NOW() - INTERVAL 24 DAY AND al.erfda > NOW() - INTERVAL 30 MINUTE
WHERE WHERE
nickname = ? nickname LIKE ?
ORDER BY ORDER BY
al.erfda DESC al.erfda DESC
", ",

View file

@ -5,13 +5,12 @@ use std::io;
use tide::sessions::{MemoryStore, SessionMiddleware}; use tide::sessions::{MemoryStore, SessionMiddleware};
mod db; mod db;
mod forms; mod forms;
mod middleware;
mod routes; mod routes;
mod session; mod session;
mod templates; mod templates;
pub use session::AppSession as Session; pub const USER: &str = "hansi";
pub const USER: &str = "foosinn";
/// Configuration /// Configuration
#[derive(FromArgs, Debug)] #[derive(FromArgs, Debug)]
@ -30,6 +29,10 @@ struct Config {
/// session secret /// session secret
#[argh(option, default = "\"thisisnotasecretthisisnotasecret\".into()")] #[argh(option, default = "\"thisisnotasecretthisisnotasecret\".into()")]
session_secret: String, session_secret: String,
/// debug
#[argh(switch)]
log: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -44,6 +47,16 @@ pub enum Level {
Error, Error,
} }
impl Level {
fn color(&self) -> &'static str {
match self {
Level::Info => "success",
Level::Warn => "warning",
Level::Error => "danger",
}
}
}
pub type Message = (Level, String); pub type Message = (Level, String);
pub type Request = tide::Request<State>; pub type Request = tide::Request<State>;
@ -51,6 +64,9 @@ pub type Request = tide::Request<State>;
#[async_std::main] #[async_std::main]
async fn main() -> Result<(), io::Error> { async fn main() -> Result<(), io::Error> {
let config: Config = argh::from_env(); let config: Config = argh::from_env();
if config.log {
tide::log::start();
}
let pool = MySqlPool::connect(&config.dsn) let pool = MySqlPool::connect(&config.dsn)
.await .await
@ -60,6 +76,7 @@ async fn main() -> Result<(), io::Error> {
SessionMiddleware::new(MemoryStore::new(), config.session_secret.as_bytes()); 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(middleware::ErrorHandler::default());
app.with(session_store); app.with(session_store);
app.at("/").get(routes::index); app.at("/").get(routes::index);
app.at("/change").post(routes::change); app.at("/change").post(routes::change);

23
src/middleware.rs Normal file
View file

@ -0,0 +1,23 @@
use crate::Request;
use crate::State;
use tide::log;
use tide::utils::async_trait;
use tide::{Middleware, Next, Redirect, Result};
#[derive(Default)]
pub struct ErrorHandler {}
#[async_trait]
impl Middleware<State> for ErrorHandler {
async fn handle(&self, request: Request, next: Next<'_, State>) -> Result {
let mut resp = next.run(request).await;
if let Some(err) = resp.take_error() {
log::error!("middleware caught error", { error: err.to_string() });
return Ok(Redirect::see_other("/").into());
}
Ok(resp)
}
fn name(&self) -> &str {
"ErrorHandler"
}
}

View file

@ -1,7 +1,7 @@
use crate::db; use crate::db;
use crate::forms; use crate::forms::ChangeForm;
use crate::templates; use crate::session::Session;
use crate::Session; use crate::templates::IndexTemplate;
use crate::USER; use crate::USER;
use tide::Redirect; use tide::Redirect;
@ -19,11 +19,11 @@ pub async fn index(mut request: crate::Request) -> tide::Result {
.await .await
.map_err(|err| dbg!(err))?; .map_err(|err| dbg!(err))?;
let messages = Session::from(&mut request).pop_messages(); let messages = Session::from(&mut request).pop_messages();
Ok(templates::IndexTemplate::new(my, unassinged, messages).into()) Ok(IndexTemplate::new(my, unassinged, messages).into())
} }
pub async fn change(mut request: crate::Request) -> tide::Result { pub async fn change(mut request: crate::Request) -> tide::Result {
let form: forms::ChangeForm = request.body_form().await?; let form: ChangeForm = request.body_form().await?;
let message = form.handle(&request).await; let message = form.handle(&request).await;
Session::from(&mut request).add_message(message); Session::from(&mut request).add_message(message);
Ok(Redirect::see_other("/").into()) Ok(Redirect::see_other("/").into())

View file

@ -2,12 +2,12 @@ use crate::Message;
use crate::Request; use crate::Request;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub struct AppSession<'r> { pub struct Session<'r> {
request: &'r mut Request, request: &'r mut Request,
session: Inner, session: Inner,
} }
impl<'r> AppSession<'r> { impl<'r> Session<'r> {
pub fn add_message(&mut self, message: Message) { pub fn add_message(&mut self, message: Message) {
self.session.add_message(message); self.session.add_message(message);
} }
@ -17,14 +17,14 @@ impl<'r> AppSession<'r> {
} }
} }
impl<'r> From<&'r mut Request> for AppSession<'r> { impl<'r> From<&'r mut Request> for Session<'r> {
fn from(request: &'r mut Request) -> Self { fn from(request: &'r mut Request) -> Self {
let session = Inner::from(&*request); let session = Inner::from(&*request);
Self { request, session } Self { request, session }
} }
} }
impl<'r> Drop for AppSession<'r> { impl<'r> Drop for Session<'r> {
fn drop(&mut self) { fn drop(&mut self) {
self.session.commit(self.request); self.session.commit(self.request);
} }

View file

@ -1,3 +0,0 @@
module.exports = {
plugins: [require("tailwindcss"), require("autoprefixer")],
};

View file

@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

File diff suppressed because it is too large Load diff

View file

@ -5,102 +5,109 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>macnickenson</title> <title>macnickenson</title>
<link rel="stylesheet" href="/static/style.css"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head> </head>
<body>
<body class="bg-gray-100"> <div class="container">
<h1>macnickenson</h1>
<div class="rounded-lg bg-white m-5 shadow-lg"> {% for message in messages %}
<h1 class="text-xl underline text-center p-2"> <div class="alert alert-{{ message.0.color() }}">{{ message.1 }}</div>
macnickenson {% endfor %}
</h1>
<div class="p-2">
{% for message in messages %}
{{ message.1 }}
{% endfor %}
</div>
<div class="p-2"> <div class="mt-5">
<h2 class="underline">Your Devices:</h2> <h2>Your Devices:</h2>
<table class="table">
<thead><tr>
<th scope="col">Mac-Address</th>
<th scope="col">Description</th>
<th scope="col">Privacy</th>
<th scope="col">Commands</th>
</tr></thead>
<tbody>
{% for device in my %} {% for device in my %}
<form action="/change" method="POST"> <tr><form action="/change" method="POST">
<div class="grid grid-cols-3 grid-gap-1"> <td>
<div class="font-mono"> {{ device.macaddr }}
{{ device.macaddr }} {{ device.present }} {% if device.present %}
<input type="hidden" name="macaddr" value="{{ device.macaddr }}" /> <span class="badge badge-success">on</span>
</div> {% endif %}
<div> <input type="hidden" name="macaddr" value="{{ device.macaddr }}" />
<input class="focus:shadow-outline border border-gray-300 px-2" </td>
name="descr" <td><input name="descr" required value="{{ device.descr }}" /></td>
value="{{ device.descr }}" /> <td>
<select class="focus:shadow-outline border border-gray-300 px-2" <select name="privacy">
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> <option value="1"
<option value="1" {{ device.privacy.selected(crate::db::PrivacyLevel::ShowUser) }}
{{ device.privacy.selected(crate::db::PrivacyLevel::ShowUser) }} >Show User</option>
>Show User</option> <option value="2"
<option value="2" {{ device.privacy.selected(crate::db::PrivacyLevel::ShowAnonymous) }}
{{ device.privacy.selected(crate::db::PrivacyLevel::ShowAnonymous) }} >Show Anonymous</option>
>Show Anonymous</option> <option value="3"
<option value="3" {{ device.privacy.selected(crate::db::PrivacyLevel::HideUser) }}
{{ device.privacy.selected(crate::db::PrivacyLevel::HideUser) }} >Hide User</option>
>Hide User</option> <option value="4"
<option value="4" {{ device.privacy.selected(crate::db::PrivacyLevel::DontLog) }}
{{ device.privacy.selected(crate::db::PrivacyLevel::DontLog) }} >Dont Log</option>
>Dont Log</option> </select>
</select> </td>
</div> <td>
<div> <div class="btn-group">
<button type="submit" name="action" value="update" <button type="submit" name="action" value="update"
class="rounded bg-gray-300 font-bold px-2 bg-green-500"> class="btn btn-primary">Update</button>
Update <button type="submit" name="action" value="delete"
</button> class="btn btn-danger">Delete</button>
<button type="submit"name="action" value="delete" </div>
class="rounded bg-gray-300 font-bold px-2 bg-red-500"> </td>
Delete </form></tr>
</button>
</div>
</div>
</form>
{% endfor %} {% endfor %}
</tbody>
</table>
</div> </div>
<div class="p-2"> <div class="mt-5">
<h2 class="underline">Unregistred Devices:</h2> <h2>Unregistred Devices:</h2>
<table class="table">
<thead><tr>
<th>MAC-Address</th>
<th>IP-Address</th>
<th>Description</th>
<th>Privacy</th>
<th>Commands</th>
</tr></thead>
<tbody>
{% for device in unassinged %} {% for device in unassinged %}
<form action="/change" method="POST"> <tr><form action="/change" method="POST">
<div class="grid grid-cols-4 grid-gap-1 p-2"> <td>
<div class="font-mono"> {{ device.macaddr }}
{{ device.macaddr }} <input type="hidden" name="macaddr" value="{{ device.macaddr }}" />
<input type="hidden" name="macaddr" value="{{ device.macaddr }}" /> </td>
</div> <td>{{ device.ip() }}</td>
<div class="font-mono">{{ device.ip() }}</div> <td>
<div> <input placeholder="awesome new device" required name="descr" />
<input class="focus:shadow-outline border border-gray-300 px-2" </td>
placeholder="awesome new device" <td>
name="descr" /> <select name="privacy">
<select class="focus:shadow-outline border border-gray-300 px-2" <option value="0">Show User and Device</option>
name="privacy"> <option value="1">Show User</option>
<option value="0">Show User and Device</option> <option value="2" selected>Show Anonymous</option>
<option value="1">Show User</option> <option value="3">Hide User</option>
<option value="2" selected>Show Anonymous</option> <option value="4">Dont Log</option>
<option value="3">Hide User</option> </select>
<option value="4">Dont Log</option> </td>
</select> <td>
</div> <button name="action" value="register" type="submit"
<div> class="btn btn-success">Register</button>
<button class="rounded bg-gray-300 font-bold px-2 bg-blue-300" </td>
name="action" value="register" type="submit"> </form></tr>
Register
</button>
</div>
</div>
</form>
{% endfor %} {% endfor %}
</table>
</div> </div>
<div class="shadow-lg p-2"> <div>
footer footer
</div> </div>
</div> </div>