Update frontend dockerfile
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
2b568d37f6
commit
7e1eda596c
|
@ -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;
|
|
@ -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),
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
13
backend/src/backend/db/token.cr
Normal file
13
backend/src/backend/db/token.cr
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()} />
|
||||||
|
|
Loading…
Reference in a new issue