Update
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is passing

This commit is contained in:
Dominic Grimm 2022-12-16 19:23:38 +01:00
parent 9682962ac6
commit 44d6f59453
No known key found for this signature in database
GPG key ID: 6F294212DEAAC530
18 changed files with 104 additions and 97 deletions

View file

@ -14,12 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
FROM crystallang/crystal:1.6-alpine as micrate-deps
FROM crystallang/crystal:1.6.2-alpine as micrate-deps
WORKDIR /usr/src/micrate
COPY ./micrate/shard.yml ./micrate/shard.lock ./
RUN shards install --production
FROM crystallang/crystal:1.6-alpine as micrate-builder
FROM crystallang/crystal:1.6.2-alpine as micrate-builder
WORKDIR /usr/src/micrate
COPY --from=micrate-deps /usr/src/micrate/shard.yml /usr/src/micrate/shard.lock ./
COPY --from=micrate-deps /usr/src/micrate/lib ./lib
@ -31,12 +31,12 @@ WORKDIR /usr/src/public
COPY ./public ./src
RUN minify -r -o ./dist ./src
FROM crystallang/crystal:1.6-alpine as deps
FROM crystallang/crystal:1.6.2-alpine as deps
WORKDIR /usr/src/mentorenwahl
COPY ./shard.yml ./shard.lock ./
RUN shards install --production
FROM crystallang/crystal:1.6-alpine as builder
FROM crystallang/crystal:1.6.2-alpine as builder
WORKDIR /usr/src/mentorenwahl
RUN apk add --no-cache pcre2-dev
RUN mkdir deps

View file

@ -8,7 +8,7 @@ targets:
micrate:
main: src/micrate.cr
crystal: 1.5.0
crystal: 1.6.2
dependencies:
micrate:

View file

@ -26,7 +26,7 @@ targets:
backend:
main: src/backend.cr
crystal: 1.6.1
crystal: 1.6.2
dependencies:
clear:

View file

@ -34,13 +34,13 @@ module Backend
getter iat : Int64
getter exp : Int64
getter jti : UUID
getter context : Context
getter user_id : Int32
def initialize(
@iat : Int64,
@exp : Int64,
@jti : UUID,
@context : Context
@user_id : Int32
)
end
@ -53,7 +53,7 @@ module Backend
iat: token["iat"].as_i64,
exp: token["exp"].as_i64,
jti: UUID.new(token["jti"].as_s),
context: Context.from_hash(token["context"].as_h)
user_id: token["user_id"].as_i
)
end
@ -61,20 +61,6 @@ module Backend
self.from_hash(JWT.decode(jwt, Backend.config.api.jwt_secret, JWT::Algorithm::HS256)[0].as_h)
end
end
# JWT token context data
struct Context
include JSON::Serializable
getter user : Int32
def initialize(@user : Int32)
end
def self.from_hash(data : Hash(String, JSON::Any))
self.new(user: data["user"].as_i)
end
end
end
end
end

View file

@ -67,7 +67,7 @@ module Backend
rescue
@status = Status::JWTError
else
if @user = Db::User.find(payload.context.user)
if @user = Db::User.find(payload.user_id)
@jti = payload.jti
if Db::Token.query.where { (id == payload.jti) & active }.first.nil?
@status = Status::SessionExpired

View file

@ -46,7 +46,7 @@ module Backend
iat: token.iat.to_unix,
exp: token.exp.to_unix,
jti: token.id.not_nil!,
context: Auth::Context.new(user.id.not_nil!)
user_id: user.id.not_nil!
).encode
)
end

View file

@ -56,6 +56,9 @@ module Backend
raw("EXISTS (SELECT 1 FROM teacher_votes WHERE teacher_id = teachers.id)") &
max_students > 0
end
pp! teachers
students = Db::Student.query.with_vote(&.with_teacher_votes).to_a
pp! students
end
end
end

View file

@ -24,3 +24,5 @@ lazy_static = "1.4.0"
const_format = "0.2.30"
yew-agent = "0.1.0"
yew-side-effect = "0.2.0"
uuid = { version = "1.2.2", features = ["serde"] }
chrono = { version = "0.4.23", features = ["serde"] }

View file

@ -9,3 +9,25 @@ body {
#wrapper {
flex: 1;
}
.fieldset {
background-color: #fff;
border-radius: 6px;
box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.1),
0 0 0 1px rgba(10, 10, 10, 0.02);
color: #4a4a4a;
display: block;
padding: 1.25rem;
border: 1px solid #ccc;
}
.fieldset > legend {
color: #363636;
display: block;
font-size: 1rem;
font-weight: 700;
background-color: #fff;
padding: 0 5px;
width: max-content;
border: 0 none;
}

View file

@ -4,13 +4,12 @@ use yew_agent::{Dispatched, Dispatcher};
use yew_router::prelude::*;
use crate::agents;
use crate::components;
use crate::graphql;
use crate::routes;
pub enum Msg {
LogoutClicked,
Logout(graphql::Errors),
Logout,
BurgerClicked,
}
@ -18,14 +17,11 @@ pub enum Msg {
pub struct NavbarProps {
pub token: Option<String>,
pub logged_in: bool,
#[prop_or(true)]
pub default_theme: bool,
}
pub struct Navbar {
logged_in: bool,
logged_in_event_bus: Dispatcher<agents::logged_in::EventBus>,
errors: Option<Vec<String>>,
burger_active: bool,
}
@ -37,7 +33,6 @@ impl Component for Navbar {
Self {
logged_in: ctx.props().logged_in,
logged_in_event_bus: agents::logged_in::EventBus::dispatcher(),
errors: None,
burger_active: false,
}
}
@ -47,7 +42,7 @@ impl Component for Navbar {
Msg::LogoutClicked => {
let client = graphql::client(ctx.props().token.as_ref()).unwrap();
ctx.link().send_future(async move {
let response = post_graphql::<graphql::mutations::logout::Logout, _>(
post_graphql::<graphql::mutations::logout::Logout, _>(
&client,
graphql::URL.as_str(),
graphql::mutations::logout::logout::Variables,
@ -55,20 +50,15 @@ impl Component for Navbar {
.await
.unwrap();
Msg::Logout(graphql::convert(response.errors))
Msg::Logout
});
false
}
Msg::Logout(errors) => {
if errors.is_none() {
self.logged_in_event_bus
.send(agents::logged_in::Request::LoggedIn(false));
false
} else {
self.errors = errors;
true
}
Msg::Logout => {
self.logged_in_event_bus
.send(agents::logged_in::Request::LoggedIn(false));
false
}
Msg::BurgerClicked => {
self.burger_active = !self.burger_active;
@ -101,10 +91,15 @@ impl Component for Navbar {
</a>
</div>
<div class={classes!("navbar-menu", if self.burger_active { Some("is-active") } else { None })}>
<div class={classes!(
"navbar-menu",
"is-white",
if self.burger_active { Some("is-active") } else { None }
)}
>
<div class={classes!("navbar-start")}>
<Link<routes::Route> to={routes::Route::Home} classes={classes!("navbar-item")}>
<strong>{ "Home" }</strong>
{ "Home" }
</Link<routes::Route>>
<Link<routes::Route> to={routes::Route::Settings} classes={classes!("navbar-item")}>
@ -132,8 +127,6 @@ impl Component for Navbar {
</div>
</div>
</div>
<components::graphql_errors::GraphQLErrors errors={self.errors.to_owned()} />
</nav>
</header>
}

View file

@ -3,6 +3,7 @@ use std::path::Path;
pub mod mutations;
pub mod queries;
pub mod scalars;
lazy_static! {
pub static ref URL: String =

View file

@ -1,6 +1,8 @@
use graphql_client::GraphQLQuery;
type UUID = String;
use crate::graphql::scalars;
type UUID = scalars::UUID;
#[derive(GraphQLQuery)]
#[graphql(

View file

@ -1,6 +1,9 @@
use graphql_client::GraphQLQuery;
type UUID = String;
use crate::graphql::scalars;
// type UUID = String;
type UUID = scalars::UUID;
type Time = String;
#[derive(GraphQLQuery)]

View file

@ -0,0 +1,2 @@
pub type UUID = uuid::Uuid;
// pub type Time =

View file

@ -82,7 +82,7 @@ impl Component for Home {
html! {
<>
<section class={classes!("hero", "is-primary")}>
<section class={classes!("hero", "is-light")}>
<div class={classes!("hero-body")}>
<p class={classes!("title")}>{ format!("Hey, {}!", me.first_name) }</p>
</div>

View file

@ -28,21 +28,17 @@ impl Component for Index {
<div id="wrapper">
<section class={classes!("hero", "is-success", "is-fullheight")}>
<div class={classes!("hero-head")}>
<components::navbar::Navbar
token={ctx.props().token.to_owned()}
logged_in={ctx.props().logged_in}
default_theme=false
/>
<components::navbar::Navbar token={ctx.props().token.to_owned()} logged_in={ctx.props().logged_in} />
</div>
<div class={classes!("hero-body")}>
<div class={classes!("container", "has-text-centered")}>
<p class={classes!("title")}>
<h1 class={classes!("title")}>
{ "Mentorenwahl" }
</p>
<p class={classes!("subtitle")}>
{ "Programmierprojekt des Otto-Hahn-Gymnasiums Furtwangen vermarktet als GFS" }
</p>
</h1>
<h2 class={classes!("subtitle")}>
{ "Programmierprojekt für das Otto-Hahn-Gymnasium Furtwangen vermarktet als GFS" }
</h2>
</div>
</div>

View file

@ -82,11 +82,9 @@ pub fn switch(routes: &Route) -> Html {
}
Route::Info => {
html! {
<layouts::logged_in::LoggedIn {logged_in}>
<layouts::main::Main token={token.to_owned()} {logged_in}>
<h1>{ "Informationen" }</h1>
</layouts::main::Main>
</layouts::logged_in::LoggedIn>
<layouts::main::Main token={token.to_owned()} {logged_in}>
<h1>{ "Informationen" }</h1>
</layouts::main::Main>
}
}
Route::Login => html! {

View file

@ -10,10 +10,7 @@ pub enum Msg {
tokens: Vec<graphql::queries::tokens::tokens::TokensTokens>,
},
Revoke(usize),
RevokeDone {
errors: Option<Vec<String>>,
id: usize,
},
RevokeDone(Option<Vec<String>>),
}
#[derive(Properties, PartialEq, Eq)]
@ -64,6 +61,8 @@ impl Component for Tokens {
true
}
Msg::Revoke(id) => {
self.tokens.as_mut().unwrap().remove(id);
let client = graphql::client(Some(&ctx.props().token)).unwrap();
let token = self.tokens.as_ref().unwrap()[id].id.to_owned();
ctx.link().send_future(async move {
@ -76,17 +75,13 @@ impl Component for Tokens {
.await
.unwrap();
Msg::RevokeDone {
errors: graphql::convert(response.errors),
id,
}
Msg::RevokeDone(graphql::convert(response.errors))
});
false
true
}
Msg::RevokeDone { errors, id } => {
Msg::RevokeDone(errors) => {
self.errors = errors;
self.tokens.as_mut().unwrap().remove(id);
true
}
}
@ -94,32 +89,36 @@ impl Component for Tokens {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<fieldset>
<fieldset class={classes!("fieldset")}>
<legend>{ "Tokens" }</legend>
if self.fetching {
<p>{ "Fetching..." }</p>
} else {
<table>
<tr>
<th>{ "ID" }</th>
<th>{ "Issued at" }</th>
<th>{ "Expires at" }</th>
<th>{ "Revoke" }</th>
</tr>
{ for self.tokens.as_ref().unwrap().iter().enumerate().map(|(i, t)| {
html! {
<tr>
<td><code>{ &t.id }</code></td>
<td><code>{ &t.iat }</code></td>
<td><code>{ &t.exp }</code></td>
<td>
<button onclick={ctx.link().callback(move |_| Msg::Revoke(i))}>
{ "Revoke" }
</button>
</td>
</tr>
}
}) }
<table class={classes!("table")}>
<thead>
<tr>
<th><abbr title="JTI claim of the JWT">{ "ID" }</abbr></th>
<th><abbr title="IAT claim of the JWT">{ "Issued at" }</abbr></th>
<th><abbr title="EXP claim of the JWT">{ "Expires at" }</abbr></th>
<th>{ "Revoke" }</th>
</tr>
</thead>
<tbody>
{ for self.tokens.as_ref().unwrap().iter().enumerate().map(|(i, t)| {
html! {
<tr>
<th><code>{ &t.id }</code></th>
<td><code>{ &t.iat }</code></td>
<td><code>{ &t.exp }</code></td>
<td>
<button onclick={ctx.link().callback(move |_| Msg::Revoke(i))}>
{ "Revoke" }
</button>
</td>
</tr>
}
}) }
</tbody>
</table>
<components::graphql_errors::GraphQLErrors errors={self.errors.to_owned()} />