This commit is contained in:
philipp 2023-02-09 17:08:07 +01:00
parent 9a72413934
commit 432962f5a9
11 changed files with 156 additions and 20 deletions

BIN
db.sqlite

Binary file not shown.

View File

@ -18,11 +18,7 @@ impl MigrationTrait for Migration {
.integer() .integer()
.default(0), .default(0),
) )
.col( .col(ColumnDef::new(Day::PlannedStartingTime).string())
ColumnDef::new(Day::PlannedStartingTime)
.string()
.default(""),
)
.col( .col(
ColumnDef::new(Day::OpenRegistration) ColumnDef::new(Day::OpenRegistration)
.boolean() .boolean()

View File

@ -1,4 +1,5 @@
use sea_orm_migration::prelude::*; use sea_orm_migration::prelude::*;
use sea_query::Expr;
use crate::m20230208_114547_create_day::Day; use crate::m20230208_114547_create_day::Day;
use crate::m20230209_063357_create_user::User; use crate::m20230209_063357_create_user::User;
@ -40,7 +41,7 @@ impl MigrationTrait for Migration {
ColumnDef::new(Trip::Created) ColumnDef::new(Trip::Created)
.timestamp() .timestamp()
.not_null() .not_null()
.default("CURRENT_TIMESTAMP"), .default(Expr::current_timestamp()),
) )
.primary_key(Index::create().col(Trip::Day).col(Trip::UserId)) .primary_key(Index::create().col(Trip::Day).col(Trip::UserId))
.to_owned(), .to_owned(),

View File

@ -18,6 +18,9 @@ pub struct Model {
pub is_admin: bool, pub is_admin: bool,
} }
#[derive(Serialize)]
pub struct AdminUser(Model);
impl Model { impl Model {
pub async fn find_or_create_user(name: &str, db: &DatabaseConnection) -> Model { pub async fn find_or_create_user(name: &str, db: &DatabaseConnection) -> Model {
let user = Entity::find() let user = Entity::find()
@ -42,6 +45,7 @@ impl Model {
#[derive(Debug)] #[derive(Debug)]
pub enum UserError { pub enum UserError {
NoCookieSet, NoCookieSet,
NoAdmin,
} }
#[rocket::async_trait] #[rocket::async_trait]
@ -61,6 +65,27 @@ impl<'r> FromRequest<'r> for Model {
} }
} }
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AdminUser {
type Error = UserError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
match req.cookies().get("name") {
Some(name) => {
let db = req.guard::<&'r State<DatabaseConnection>>();
let name = name.value();
let user = Model::find_or_create_user(name, db.await.unwrap().inner()).await;
if user.is_admin {
Outcome::Success(AdminUser(user))
} else {
Outcome::Failure((Status::Unauthorized, UserError::NoAdmin))
}
}
None => Outcome::Failure((Status::Unauthorized, UserError::NoCookieSet)),
}
}
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {} pub enum Relation {}

View File

@ -1,9 +1,10 @@
mod restday; mod restday;
mod restreg; mod restreg;
mod restuser;
use std::ops::Deref; use std::ops::Deref;
use chrono::{Duration, Local, NaiveDate}; use chrono::{Datelike, Duration, Local, NaiveDate};
use rocket::{ use rocket::{
form::{self, Form, ValueField}, form::{self, Form, ValueField},
fs::FileServer, fs::FileServer,
@ -37,7 +38,17 @@ impl Deref for NaiveDateForm {
#[get("/")] #[get("/")]
async fn index(db: &State<DatabaseConnection>, user: user::Model) -> Template { async fn index(db: &State<DatabaseConnection>, user: user::Model) -> Template {
let mut data = Vec::new(); let mut data = Vec::new();
for i in 0..6 {
let mut show_next_n_days = 6;
if user.is_cox {
let end_of_year = NaiveDate::from_ymd_opt(Local::now().year(), 5, 31).unwrap();
show_next_n_days = end_of_year
.signed_duration_since(Local::now().date_naive())
.num_days()
+ 1;
}
for i in 0..show_next_n_days {
let date = (Local::now() + Duration::days(i)).date_naive(); let date = (Local::now() + Duration::days(i)).date_naive();
let day = day::Model::find_or_create_day(date, db.inner()).await; let day = day::Model::find_or_create_day(date, db.inner()).await;
data.push(DayWithTrips::new(day, db.inner()).await); data.push(DayWithTrips::new(day, db.inner()).await);
@ -62,6 +73,12 @@ fn savename(name: Form<NameForm>, cookies: &CookieJar) -> Redirect {
Redirect::to("/") Redirect::to("/")
} }
#[get("/logout")]
fn logout(cookies: &CookieJar) -> Redirect {
cookies.remove(Cookie::new("name", ""));
Redirect::to("/")
}
#[catch(401)] //unauthorized #[catch(401)] //unauthorized
fn unauthorized_error() -> Redirect { fn unauthorized_error() -> Redirect {
Redirect::to("/name") Redirect::to("/name")
@ -72,8 +89,9 @@ pub async fn start() -> Rocket<Build> {
.attach(Template::fairing()) .attach(Template::fairing())
.manage(Database::connect("sqlite://db.sqlite").await.unwrap()) .manage(Database::connect("sqlite://db.sqlite").await.unwrap())
.mount("/public", FileServer::from("static/")) .mount("/public", FileServer::from("static/"))
.mount("/", routes![index, name, savename]) .mount("/", routes![index, name, savename, logout])
.mount("/day", restday::routes()) .mount("/day", restday::routes())
.mount("/register", restreg::routes()) .mount("/register", restreg::routes())
.mount("/user", restuser::routes())
.register("/", catchers![unauthorized_error]) .register("/", catchers![unauthorized_error])
} }

View File

@ -20,6 +20,10 @@ async fn register(db: &State<DatabaseConnection>, register: Form<RegisterForm>)
.unwrap() .unwrap()
.expect("There's no trip on this date (yet)"); .expect("There's no trip on this date (yet)");
if !day.open_registration {
return Redirect::to("/");
}
let user = user::Model::find_or_create_user(&register.name, db.inner()).await; let user = user::Model::find_or_create_user(&register.name, db.inner()).await;
let day = format!("{}", day.day.format("%Y-%m-%d")); let day = format!("{}", day.day.format("%Y-%m-%d"));

42
src/rest/restuser.rs Normal file
View File

@ -0,0 +1,42 @@
use rocket::{form::Form, response::Redirect, Route, State};
use rocket_dyn_templates::{context, Template};
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set};
use crate::models::{day, user};
use super::NaiveDateForm;
#[get("/")]
async fn index(db: &State<DatabaseConnection>, user: user::AdminUser) -> Template {
let users = user::Entity::find().all(db.inner()).await.unwrap();
Template::render("user/index", context! {user, users})
}
#[derive(FromForm)]
struct UserEditForm {
is_cox: bool,
is_admin: bool,
}
#[put("/<id>", data = "<data>")]
async fn update(
db: &State<DatabaseConnection>,
id: i32,
data: Form<UserEditForm>,
_user: user::AdminUser,
) -> Redirect {
let new_user = user::ActiveModel {
id: Set(id),
is_cox: Set(data.is_cox),
is_admin: Set(data.is_admin),
..Default::default()
};
new_user.update(db.inner()).await.unwrap();
Redirect::to("/user")
}
pub fn routes() -> Vec<Route> {
routes![index, update]
}

View File

@ -37,6 +37,12 @@
<!-- Primary Page Layout <!-- Primary Page Layout
--> -->
<div class="container"> <div class="container">
{% if user %}
{% if user.is_admin %}
<a href="/user">USER</a>
{% endif %}
<a href="/logout">LOGOUT</a>
{% endif %}
<div class="row"> <div class="row">
<div class="column"> <div class="column">
{% block content %} {% block content %}

View File

@ -5,22 +5,30 @@
{% set day = day_with_trip.day %} {% set day = day_with_trip.day %}
{% set day_string = day.day | date(format="%Y-%m-%d") %} {% set day_string = day.day | date(format="%Y-%m-%d") %}
{% set trips = day_with_trip.trips %} {% set trips = day_with_trip.trips %}
{{ day.day | date(format="%d.%m.%Y")}} <h1>{{ day.day | date(format="%d.%m.%Y")}}</h1>
<br /> <br />
{% if day.planned_amount_cox > 0%} {% if day.planned_amount_cox > 0%}
Geplante Steuerpersonen: {{ day.planned_amount_cox}}<br />
{% set cox = trips | filter(attribute="user.is_cox", value=true) %}
{% set amount_cox = cox | length %}
{% set rowers = trips | filter(attribute="user.is_cox", value=false) %}
{% if amount_cox < day.planned_amount_cox %}
{% set cox_left = day.planned_amount_cox - amount_cox %}
Es {{ cox_left | pluralize(singular="wird", plural="werden")}} noch {{ cox_left }} Steuerperson{{ cox_left | pluralize(plural="en")}} gesucht!<br />
{% endif %}
Geplante Abfahrtszeit: {{ day.planned_starting_time }}<br /> Geplante Abfahrtszeit: {{ day.planned_starting_time }}<br />
Angemeldete Personen: {{ trips | length }} angemeldete Person{{ trips | length | pluralize(plural="en") }}: {{ cox | length }} Steuerperson{{ cox | length | pluralize(plural="en") }} ({% for c in cox %}{{ c.user.name }} {% endfor %}), {{ rowers | length }} Ruderer:
<ol> <ol>
{% for trip in trips %} {% for r in rowers %}
<li>{{ trip.user.name }}</li> <li>{{ r.user.name }} (angemeldet seit {{ r.trip.created }})</li>
{% endfor %} {% endfor %}
</ol> </ol>
{% if day.open_registration %} {% if day.open_registration or user.is_cox %}
<details> <details>
<summary class="button">&plus;</summary> <summary class="button">&plus;</summary>
<form method="post" action="/register"> <form method="post" action="/register">
@ -44,6 +52,7 @@
(Noch) keine Ausfahrt geplant (Noch) keine Ausfahrt geplant
{% endif %} {% endif %}
{% if user.is_admin %}
<details> <details>
<summary class="button">&#x270e;</summary> <summary class="button">&#x270e;</summary>
<form method="post" action="/day"> <form method="post" action="/day">
@ -69,7 +78,7 @@
</div> </div>
</form> </form>
</details> </details>
{% endif %}
<hr /> <hr />
{% endfor %} {% endfor %}

View File

@ -1,11 +1,11 @@
{% extends "base" %} {% extends "base" %}
{% block content %} {% block content %}
What's your name?
<form action="/name" method="post"> <form action="/name" method="post">
<input type="hidden" name="_method" value="put" /> <input type="hidden" name="_method" value="put" />
<input type="text" name="name"/> <label for="name">Bitte deinen Namen eingeben</label>
<input type="submit" value="Speichern"/> <input type="text" id="name" name="name"/>
<input type="submit" value="Weiter"/>
</form> </form>
{% endblock content %} {% endblock content %}

View File

@ -0,0 +1,35 @@
{% extends "base" %}
{% block content %}
<table class="u-full-width">
<thead>
<tr>
<th>Name</th>
<th>Cox</th>
<th>Admin</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<form action="/user/{{ user.id }}" method="post">
<input type="hidden" name="_method" value="put" />
<td>{{user.name}}</td>
<td>
<input type="checkbox" name="is_cox" {% if user.is_cox %} checked="true"{% endif %}
</td>
<td>
<input type="checkbox" name="is_admin" {% if user.is_admin %} checked="true"{% endif %}
</td>
<td>
<input class="button-primary" type="submit" value="Speichern">
</td>
</form>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}