forked from Ruderverein-Donau-Linz/rowt
		
	push
This commit is contained in:
		@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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(),
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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])
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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(®ister.name, db.inner()).await;
 | 
			
		||||
 | 
			
		||||
    let day = format!("{}", day.day.format("%Y-%m-%d"));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								src/rest/restuser.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/rest/restuser.rs
									
									
									
									
									
										Normal 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]
 | 
			
		||||
}
 | 
			
		||||
@@ -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 %}
 | 
			
		||||
 
 | 
			
		||||
@@ -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">+</summary>
 | 
			
		||||
				<form method="post" action="/register">
 | 
			
		||||
@@ -43,7 +51,8 @@
 | 
			
		||||
	{% else %}
 | 
			
		||||
		(Noch) keine Ausfahrt geplant
 | 
			
		||||
	{% endif %}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	{% if user.is_admin %}
 | 
			
		||||
		<details>
 | 
			
		||||
			<summary class="button">✎</summary>
 | 
			
		||||
			<form method="post" action="/day">
 | 
			
		||||
@@ -69,7 +78,7 @@
 | 
			
		||||
				</div>
 | 
			
		||||
			</form>
 | 
			
		||||
		</details>
 | 
			
		||||
	
 | 
			
		||||
	{% endif %}
 | 
			
		||||
	<hr />
 | 
			
		||||
{% endfor %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								templates/user/index.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								templates/user/index.html.tera
									
									
									
									
									
										Normal 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 %}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user