Update frontend dockerfile
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dominic Grimm 2022-11-13 18:41:53 +01:00
parent 2b568d37f6
commit 7e1eda596c
No known key found for this signature in database
GPG key ID: 6F294212DEAAC530
8 changed files with 111 additions and 44 deletions

View file

@ -20,44 +20,52 @@
CREATE TYPE user_roles AS ENUM ('student', 'teacher'); CREATE TYPE user_roles AS ENUM ('student', 'teacher');
CREATE TABLE users( CREATE TABLE users(
id SERIAL PRIMARY KEY, id serial PRIMARY KEY,
username TEXT UNIQUE NOT NULL, username text UNIQUE NOT NULL,
role user_roles NOT NULL, role user_roles NOT NULL,
admin BOOLEAN NOT NULL, admin boolean NOT NULL
jti uuid UNIQUE );
CREATE TABLE tokens(
id uuid PRIMARY KEY,
iat timestamp NOT NULL,
exp timestamp NOT NULL,
user_id int NOT NULL REFERENCES users(id)
); );
CREATE TABLE teachers( CREATE TABLE teachers(
id SERIAL PRIMARY KEY, id serial PRIMARY KEY,
user_id INT NOT NULL UNIQUE REFERENCES users(id), user_id int NOT NULL UNIQUE REFERENCES users(id),
max_students INT NOT NULL max_students int NOT NULL
); );
CREATE TABLE students( CREATE TABLE students(
id SERIAL PRIMARY KEY, id serial PRIMARY KEY,
user_id INT NOT NULL UNIQUE REFERENCES users(id) user_id int NOT NULL UNIQUE REFERENCES users(id)
); );
CREATE TABLE votes( CREATE TABLE votes(
id SERIAL PRIMARY KEY, id serial PRIMARY KEY,
student_id INT NOT NULL UNIQUE REFERENCES students(id) student_id int NOT NULL UNIQUE REFERENCES students(id)
); );
CREATE TABLE teacher_votes( CREATE TABLE teacher_votes(
id SERIAL PRIMARY KEY, id serial PRIMARY KEY,
vote_id INT NOT NULL REFERENCES votes(id), vote_id int NOT NULL REFERENCES votes(id),
teacher_id INT NOT NULL REFERENCES teachers(id), teacher_id int NOT NULL REFERENCES teachers(id),
priority INT NOT NULL priority int NOT NULL
); );
CREATE TABLE assignments( CREATE TABLE assignments(
id SERIAL PRIMARY KEY, id serial PRIMARY KEY,
student_id INT NOT NULL REFERENCES students(id), student_id int NOT NULL REFERENCES students(id),
teacher_id INT NOT NULL REFERENCES teachers(id) teacher_id int NOT NULL REFERENCES teachers(id)
); );
-- +micrate Down -- +micrate Down
-- SQL section ' Down ' is executed when this migration is rolled back -- SQL section ' Down ' is executed when this migration is rolled back
DROP TABLE assignments;
DROP TABLE teacher_votes; DROP TABLE teacher_votes;
DROP TABLE votes; DROP TABLE votes;
@ -68,6 +76,8 @@ DROP TABLE teachers;
DROP TABLE students; DROP TABLE students;
DROP TABLE tokens;
DROP TABLE users; DROP TABLE users;
DROP TYPE user_roles; DROP TYPE user_roles;

View file

@ -31,16 +31,12 @@ module Backend
struct Token struct Token
include JSON::Serializable include JSON::Serializable
getter iss : String
getter vrs : String
getter iat : Int64 getter iat : Int64
getter exp : Int64 getter exp : Int64
getter jti : UUID getter jti : UUID
getter context : Context getter context : Context
def initialize( def initialize(
@iss : String,
@vrs : String,
@iat : Int64, @iat : Int64,
@exp : Int64, @exp : Int64,
@jti : UUID, @jti : UUID,
@ -54,8 +50,6 @@ module Backend
def self.from_hash(token : Hash(String, JSON::Any)) : self def self.from_hash(token : Hash(String, JSON::Any)) : self
self.new( self.new(
iss: token["iss"].as_s,
vrs: token["vrs"].as_s,
iat: token["iat"].as_i64, iat: token["iat"].as_i64,
exp: token["exp"].as_i64, exp: token["exp"].as_i64,
jti: UUID.new(token["jti"].as_s), jti: UUID.new(token["jti"].as_s),

View file

@ -62,10 +62,7 @@ module Backend
rescue rescue
@status = Status::JWTError @status = Status::JWTError
else else
pp! payload if @user = Db::User.find(payload.context.user)
if payload.iss != "Mentorenwahl" || payload.vrs != Backend::VERSION
@status = Status::JWTError
elsif @user = Db::User.find(payload.context.user)
@admin = user.not_nil!.admin @admin = user.not_nil!.admin
@role = user.not_nil!.role.to_api @role = user.not_nil!.role.to_api
@external = @external =

View file

@ -32,15 +32,20 @@ module Backend
user = Db::User.query.find { var(:username) == username } user = Db::User.query.find { var(:username) == username }
raise Errors::Authentication.new unless user && Ldap.authenticate?(Ldap::DN.uid(username), password) raise Errors::Authentication.new unless user && Ldap.authenticate?(Ldap::DN.uid(username), password)
jti = UUID.random(Random::Secure) token = Db::Token.create!(
id: UUID.random(Random::Secure),
iat: Time.utc,
exp: Time.utc + Backend.config.api.jwt_expiration.minutes,
user_id: user.id
)
pp! token, typeof(token.id)
LoginPayload.new( LoginPayload.new(
user: User.new(user), user: User.new(user),
token: Auth::Token.new( token: Auth::Token.new(
iss: "Mentorenwahl", iat: token.iat.to_unix,
vrs: Backend::VERSION, exp: token.exp.to_unix,
iat: Time.utc.to_unix, jti: token.id.not_nil!,
exp: (Time.utc + Backend.config.api.jwt_expiration.minutes).to_unix,
jti: jti,
context: Auth::Context.new(user.id.not_nil!) context: Auth::Context.new(user.id.not_nil!)
).encode ).encode
) )

View file

@ -0,0 +1,13 @@
module Backend::Db
class Token
include Clear::Model
self.table = :tokens
primary_key type: :uuid
column iat : Time
column exp : Time
belongs_to user : User
end
end

View file

@ -30,10 +30,11 @@ module Backend
column username : String column username : String
column role : UserRole column role : UserRole
column admin : Bool = false column admin : Bool = false
column jti : UUID?
has_one student : Student?, foreign_key: :user_id has_one student : Student?, foreign_key: :user_id
has_one teacher : Teacher?, foreign_key: :user_id has_one teacher : Teacher?, foreign_key: :user_id
has_many tokens : Token, foreign_key: :user_id
end end
end end
end end

View file

@ -26,8 +26,9 @@ WORKDIR /usr/src/public
COPY --from=builder /usr/src/frontend/dist . COPY --from=builder /usr/src/frontend/dist .
RUN minify . -r -o . RUN minify . -r -o .
FROM alpine as binaryen # FROM alpine as binaryen
RUN apk add --no-cache binaryen # RUN apk add --no-cache binaryen
FROM niklasei/wasm-opt-action:v2.1.0 as binaryen
WORKDIR /usr/src/public WORKDIR /usr/src/public
COPY --from=public /usr/src/public . COPY --from=public /usr/src/public .
RUN find . -name "*.wasm" -type f | xargs -I % wasm-opt % -o % -O --intrinsic-lowering -Oz RUN find . -name "*.wasm" -type f | xargs -I % wasm-opt % -o % -O --intrinsic-lowering -Oz

View file

@ -24,6 +24,9 @@ pub enum Msg {
}, },
Submit, Submit,
Vote(Option<Vec<String>>), Vote(Option<Vec<String>>),
AddSlot,
RemoveSlot,
Reset,
} }
#[derive(Properties, PartialEq)] #[derive(Properties, PartialEq)]
@ -37,7 +40,8 @@ pub struct StudentVote {
can_vote: bool, can_vote: bool,
errors: Option<Vec<String>>, errors: Option<Vec<String>>,
min: usize, min: usize,
teachers: Option<Vec<graphql::queries::teachers::teachers::TeachersTeachers>>, slots: usize,
teachers: Vec<graphql::queries::teachers::teachers::TeachersTeachers>,
votes: HashMap<usize, Option<i64>>, votes: HashMap<usize, Option<i64>>,
} }
@ -68,7 +72,8 @@ impl Component for StudentVote {
can_vote: false, can_vote: false,
errors: None, errors: None,
min: 0, min: 0,
teachers: None, slots: 0,
teachers: vec![],
votes: HashMap::new(), votes: HashMap::new(),
} }
} }
@ -109,6 +114,7 @@ impl Component for StudentVote {
Msg::DoneFetchingConfig { errors, min } => { Msg::DoneFetchingConfig { errors, min } => {
self.errors = errors; self.errors = errors;
self.min = min; self.min = min;
self.slots = self.min;
let client = graphql::client(Some(&ctx.props().token)).unwrap(); let client = graphql::client(Some(&ctx.props().token)).unwrap();
ctx.link().send_future(async move { ctx.link().send_future(async move {
@ -131,7 +137,7 @@ impl Component for StudentVote {
Msg::DoneFetchingTeachers { errors, teachers } => { Msg::DoneFetchingTeachers { errors, teachers } => {
self.fetching = false; self.fetching = false;
self.errors = errors; self.errors = errors;
self.teachers = Some(teachers); self.teachers = teachers;
true true
} }
Msg::RadioSelect { priority, teacher } => { Msg::RadioSelect { priority, teacher } => {
@ -178,6 +184,27 @@ impl Component for StudentVote {
true true
} }
} }
Msg::AddSlot => {
if self.slots < self.teachers.len() {
self.slots += 1;
true
} else {
false
}
}
Msg::RemoveSlot => {
if self.slots > self.min {
self.votes.remove(&(self.slots - 1));
self.slots -= 1;
true
} else {
false
}
}
Msg::Reset => {
self.slots = self.min;
true
}
} }
} }
@ -202,7 +229,7 @@ impl Component for StudentVote {
<> <>
<h3>{ "Wähle deine Wunschmentoren aus:" }</h3> <h3>{ "Wähle deine Wunschmentoren aus:" }</h3>
<form {onsubmit}> <form {onsubmit}>
{ for (0..self.min).map(|i| { { for (0..self.slots).map(|i| {
let curr_t = self.votes.get(&i); let curr_t = self.votes.get(&i);
if let Some(te) = curr_t { if let Some(te) = curr_t {
if let Some(t) = te { if let Some(t) = te {
@ -216,7 +243,7 @@ impl Component for StudentVote {
<fieldset> <fieldset>
<legend>{ format!("{}. Wahl", i + 1) }</legend> <legend>{ format!("{}. Wahl", i + 1) }</legend>
{ for self.teachers.as_ref().unwrap().iter().enumerate().filter_map(|(j, t)| { { for self.teachers.iter().enumerate().filter_map(|(j, t)| {
let checked = curr_t == Some(&Some(t.id)); let checked = curr_t == Some(&Some(t.id));
if teachers.contains(&t.id) && !checked { if teachers.contains(&t.id) && !checked {
@ -243,16 +270,35 @@ impl Component for StudentVote {
} }
}) } }) }
</fieldset> </fieldset>
if i < self.min - 1 { if i < self.slots - 1 {
<br /> <br />
} }
</> </>
} }
}) } }) }
<div>
<button
onclick={ctx.link().callback(|e: MouseEvent| {
e.prevent_default();
Msg::AddSlot
})}
>
{ "+" }
</button>
<button
onclick={ctx.link().callback(|e: MouseEvent| {
e.prevent_default();
Msg::RemoveSlot
})}
>
{ "-" }
</button>
</div>
<div> <div>
<input type="submit" value="Submit" /> <input type="submit" value="Submit" />
<input type="reset" value="Reset" /> <input type="reset" value="Reset" onclick={ctx.link().callback(|_| Msg::Reset)} />
</div> </div>
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} /> <components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />