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()
.default(0),
)
.col(
ColumnDef::new(Day::PlannedStartingTime)
.string()
.default(""),
)
.col(ColumnDef::new(Day::PlannedStartingTime).string())
.col(
ColumnDef::new(Day::OpenRegistration)
.boolean()

View File

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

View File

@ -18,6 +18,9 @@ pub struct Model {
pub is_admin: bool,
}
#[derive(Serialize)]
pub struct AdminUser(Model);
impl Model {
pub async fn find_or_create_user(name: &str, db: &DatabaseConnection) -> Model {
let user = Entity::find()
@ -42,6 +45,7 @@ impl Model {
#[derive(Debug)]
pub enum UserError {
NoCookieSet,
NoAdmin,
}
#[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)]
pub enum Relation {}

View File

@ -1,9 +1,10 @@
mod restday;
mod restreg;
mod restuser;
use std::ops::Deref;
use chrono::{Duration, Local, NaiveDate};
use chrono::{Datelike, Duration, Local, NaiveDate};
use rocket::{
form::{self, Form, ValueField},
fs::FileServer,
@ -37,7 +38,17 @@ impl Deref for NaiveDateForm {
#[get("/")]
async fn index(db: &State<DatabaseConnection>, user: user::Model) -> Template {
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 day = day::Model::find_or_create_day(date, 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("/")
}
#[get("/logout")]
fn logout(cookies: &CookieJar) -> Redirect {
cookies.remove(Cookie::new("name", ""));
Redirect::to("/")
}
#[catch(401)] //unauthorized
fn unauthorized_error() -> Redirect {
Redirect::to("/name")
@ -72,8 +89,9 @@ pub async fn start() -> Rocket<Build> {
.attach(Template::fairing())
.manage(Database::connect("sqlite://db.sqlite").await.unwrap())
.mount("/public", FileServer::from("static/"))
.mount("/", routes![index, name, savename])
.mount("/", routes![index, name, savename, logout])
.mount("/day", restday::routes())
.mount("/register", restreg::routes())
.mount("/user", restuser::routes())
.register("/", catchers![unauthorized_error])
}

View File

@ -20,6 +20,10 @@ async fn register(db: &State<DatabaseConnection>, register: Form<RegisterForm>)
.unwrap()
.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 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
-->
<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="column">
{% block content %}

View File

@ -5,22 +5,30 @@
{% set day = day_with_trip.day %}
{% set day_string = day.day | date(format="%Y-%m-%d") %}
{% set trips = day_with_trip.trips %}
{{ day.day | date(format="%d.%m.%Y")}}
<h1>{{ day.day | date(format="%d.%m.%Y")}}</h1>
<br />
{% 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 />
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>
{% for trip in trips %}
<li>{{ trip.user.name }}</li>
{% for r in rowers %}
<li>{{ r.user.name }} (angemeldet seit {{ r.trip.created }})</li>
{% endfor %}
</ol>
{% if day.open_registration %}
{% if day.open_registration or user.is_cox %}
<details>
<summary class="button">&plus;</summary>
<form method="post" action="/register">
@ -43,7 +51,8 @@
{% else %}
(Noch) keine Ausfahrt geplant
{% endif %}
{% if user.is_admin %}
<details>
<summary class="button">&#x270e;</summary>
<form method="post" action="/day">
@ -69,7 +78,7 @@
</div>
</form>
</details>
{% endif %}
<hr />
{% endfor %}

View File

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