From 8055a5e4dbec6625843efa86a699914ea937a180 Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Tue, 17 Jan 2023 06:56:19 +0100 Subject: [PATCH] Update --- Makefile | 4 +- .../20220414171336_create_users.sql | 5 +- .../backend/web/controllers/api_controller.cr | 7 +- .../src/backend/worker/jobs/assignment_job.cr | 6 +- docker-compose.yml | 4 +- frontend/Cargo.toml | 8 +- .../queries/users_by_role_students.graphql | 17 + .../queries/users_by_role_teachers.graphql | 14 + frontend/graphql/schema.graphql | 78 ++++- frontend/src/agents/logged_in.rs | 47 --- frontend/src/agents/mod.rs | 1 - frontend/src/components/footer.rs | 10 +- frontend/src/components/logged_in_handler.rs | 54 --- frontend/src/components/mod.rs | 2 +- frontend/src/components/navbar.rs | 31 +- frontend/src/components/title.rs | 26 ++ frontend/src/cookies.rs | 20 +- frontend/src/graphql/queries/mod.rs | 1 + frontend/src/graphql/queries/tokens.rs | 1 - frontend/src/graphql/queries/users_by_role.rs | 19 + frontend/src/layouts/base.rs | 5 +- frontend/src/layouts/logged_in.rs | 46 +-- frontend/src/layouts/main.rs | 7 +- frontend/src/lib.rs | 2 +- frontend/src/main.rs | 22 +- frontend/src/routes/home/mod.rs | 3 +- frontend/src/routes/home/student_vote.rs | 52 +-- .../src/routes/home/teacher_registration.rs | 2 +- frontend/src/routes/index.rs | 7 +- frontend/src/routes/info.rs | 23 ++ frontend/src/routes/login.rs | 38 +- frontend/src/routes/mod.rs | 30 +- frontend/src/routes/not_found.rs | 7 +- frontend/src/routes/settings/assignments.rs | 40 ++- frontend/src/routes/settings/mod.rs | 24 +- .../src/routes/settings/new_user_modal.rs | 74 ++++ frontend/src/routes/settings/tokens.rs | 20 +- frontend/src/routes/settings/users.rs | 329 ++++++++++++++++++ frontend/src/stores.rs | 29 ++ scripts/backend.sh | 2 +- scripts/micrate.sh | 2 +- 41 files changed, 829 insertions(+), 290 deletions(-) create mode 100644 frontend/graphql/queries/users_by_role_students.graphql create mode 100644 frontend/graphql/queries/users_by_role_teachers.graphql delete mode 100644 frontend/src/agents/logged_in.rs delete mode 100644 frontend/src/agents/mod.rs delete mode 100644 frontend/src/components/logged_in_handler.rs create mode 100644 frontend/src/components/title.rs create mode 100644 frontend/src/graphql/queries/users_by_role.rs create mode 100644 frontend/src/routes/info.rs create mode 100644 frontend/src/routes/settings/new_user_modal.rs create mode 100644 frontend/src/routes/settings/users.rs create mode 100644 frontend/src/stores.rs diff --git a/Makefile b/Makefile index c5c4a0a..e2a7a73 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,10 @@ all: prod dev: - docker-compose build --build-arg BUILD_ENV=development + docker compose build --build-arg BUILD_ENV=development prod: - docker-compose build + docker compose build docs: cd docs && mdbook build diff --git a/backend/db/migrations/20220414171336_create_users.sql b/backend/db/migrations/20220414171336_create_users.sql index c92c438..a3a7d31 100644 --- a/backend/db/migrations/20220414171336_create_users.sql +++ b/backend/db/migrations/20220414171336_create_users.sql @@ -54,12 +54,13 @@ CREATE TABLE teacher_votes( id serial PRIMARY KEY, vote_id int NOT NULL REFERENCES votes(id), teacher_id int NOT NULL REFERENCES teachers(id), - priority int NOT NULL + priority int NOT NULL, + UNIQUE (vote_id, teacher_id, priority) ); CREATE TABLE assignments( id serial PRIMARY KEY, - student_id int NOT NULL REFERENCES students(id), + student_id int NOT NULL REFERENCES students(id) UNIQUE, teacher_id int NOT NULL REFERENCES teachers(id) ); diff --git a/backend/src/backend/web/controllers/api_controller.cr b/backend/src/backend/web/controllers/api_controller.cr index a6fd840..3277558 100644 --- a/backend/src/backend/web/controllers/api_controller.cr +++ b/backend/src/backend/web/controllers/api_controller.cr @@ -70,7 +70,12 @@ module Backend return ATH::Exceptions::BadRequest.new("No request body given") unless request.body query = GraphQLQuery.from_json(request.body.not_nil!) - ATH::StreamedResponse.new(headers: HTTP::Headers{"content-type" => "application/json"}) do |io| + ATH::StreamedResponse.new( + headers: HTTP::Headers{ + "content-type" => "application/json", + "cache-control" => ["no-cache", "no-store", "max-age=0", "must-revalidate"], + } + ) do |io| Api::Schema::SCHEMA.execute( io, query.query, diff --git a/backend/src/backend/worker/jobs/assignment_job.cr b/backend/src/backend/worker/jobs/assignment_job.cr index 275b998..bbd517b 100644 --- a/backend/src/backend/worker/jobs/assignment_job.cr +++ b/backend/src/backend/worker/jobs/assignment_job.cr @@ -19,11 +19,13 @@ module Backend module Jobs # Assigns students to teachers when all students voted class AssignmentJob < Mosquito::QueuedJob - # run_every 1.minute - alias TeacherVote = {student: Int32, priority: Int32} alias Assignment = {teacher: Int32, priority: Int32} + def rescheduleable? + false + end + # :ditto: def perform : Nil teacher_count = Db::Teacher.query.count diff --git a/docker-compose.yml b/docker-compose.yml index 69ae161..c7caced 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,7 @@ services: - frontend db: - image: postgres:alpine + image: docker.io/postgres:alpine restart: always networks: - db @@ -41,7 +41,7 @@ services: - db:/var/lib/postgresql/data adminer: - image: adminer:standalone + image: docker.io/adminer:standalone restart: always networks: - default diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 16e634d..b920810 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -10,10 +10,10 @@ opt-level = "z" panic = "abort" [dependencies] -yew = "0.19.3" +yew = { version = "0.20.0", features = ["csr"] } wasm-logger = "0.2.0" log = "0.4.6" -yew-router = "0.16.0" +yew-router = "0.17.0" wee_alloc = "0.4.5" graphql_client = { git = "https://github.com/graphql-rust/graphql-client.git", branch = "main", features = ["reqwest"] } reqwest = "0.11.12" @@ -22,7 +22,7 @@ web-sys = { version = "0.3.60", features = ["Window", "Location"] } wasm-cookies = "0.1.0" 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"] } +yewdux = "0.9.0" +bounce = { version = "0.6.0", features = ["helmet"] } diff --git a/frontend/graphql/queries/users_by_role_students.graphql b/frontend/graphql/queries/users_by_role_students.graphql new file mode 100644 index 0000000..bc55a7d --- /dev/null +++ b/frontend/graphql/queries/users_by_role_students.graphql @@ -0,0 +1,17 @@ +query Students { + students { + id + user { + id + firstName + lastName + username + email + role + admin + } + vote { + id + } + } +} diff --git a/frontend/graphql/queries/users_by_role_teachers.graphql b/frontend/graphql/queries/users_by_role_teachers.graphql new file mode 100644 index 0000000..58e0c11 --- /dev/null +++ b/frontend/graphql/queries/users_by_role_teachers.graphql @@ -0,0 +1,14 @@ +query Teachers { + teachers { + id + user { + id + firstName + lastName + username + email + role + admin + } + } +} diff --git a/frontend/graphql/schema.graphql b/frontend/graphql/schema.graphql index 224a00b..d358093 100644 --- a/frontend/graphql/schema.graphql +++ b/frontend/graphql/schema.graphql @@ -1,7 +1,19 @@ +"The `Boolean` scalar type represents `true` or `false`." +scalar Boolean + type Config { minimumTeacherSelectionCount: Int! } +"The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point)." +scalar Float + +"The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID." +scalar ID + +"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1." +scalar Int + type Query { admins: [User!] allStudentsVoted: Boolean @@ -23,6 +35,9 @@ type Query { votes: [Vote!] } +"The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text." +scalar String + type Student { id: Int! user: User! @@ -79,6 +94,67 @@ type Vote { teacherVotes: [TeacherVote!]! } +type __Directive { + args: [__InputValue!]! + description: String + locations: [String!]! + name: String! +} + +type __EnumValue { + deprecationReason: String + description: String + isDeprecated: Boolean! + name: String! +} + +type __Field { + args: [__InputValue!]! + deprecationReason: String + description: String + isDeprecated: Boolean! + name: String! + type: __Type! +} + +type __InputValue { + defaultValue: String + description: String + name: String! + type: __Type! +} + +type __Schema { + directives: [__Directive!]! + mutationType: __Type + queryType: __Type! + subscriptionType: __Type + types: [__Type!]! +} + +type __Type { + description: String + enumValues(includeDeprecated: Boolean! = false): [__EnumValue!] + fields(includeDeprecated: Boolean! = false): [__Field!] + inputFields: [__InputValue!] + interfaces: [__Type!] + kind: __TypeKind! + name: String + ofType: __Type + possibleTypes: [__Type!] +} + +enum __TypeKind { + ENUM + INPUT_OBJECT + INTERFACE + LIST + NON_NULL + OBJECT + SCALAR + UNION +} + type LoginPayload { bearer: String! token: String! @@ -108,4 +184,4 @@ input UserCreateInput { input VoteCreateInput { teacherIds: [Int!]! -} +} \ No newline at end of file diff --git a/frontend/src/agents/logged_in.rs b/frontend/src/agents/logged_in.rs deleted file mode 100644 index 9c49dc7..0000000 --- a/frontend/src/agents/logged_in.rs +++ /dev/null @@ -1,47 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use yew_agent::{Agent, AgentLink, Context, HandlerId}; - -#[derive(Serialize, Deserialize, Debug)] -pub enum Request { - LoggedIn(bool), -} - -pub struct EventBus { - link: AgentLink, - subscribers: HashSet, -} - -impl Agent for EventBus { - type Reach = Context; - type Message = (); - type Input = Request; - type Output = bool; - - fn create(link: AgentLink) -> Self { - Self { - link, - subscribers: HashSet::new(), - } - } - - fn update(&mut self, _msg: Self::Message) {} - - fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) { - match msg { - Request::LoggedIn(x) => { - for sub in self.subscribers.iter() { - self.link.respond(*sub, x); - } - } - } - } - - fn connected(&mut self, id: HandlerId) { - self.subscribers.insert(id); - } - - fn disconnected(&mut self, id: HandlerId) { - self.subscribers.remove(&id); - } -} diff --git a/frontend/src/agents/mod.rs b/frontend/src/agents/mod.rs deleted file mode 100644 index a5931af..0000000 --- a/frontend/src/agents/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod logged_in; diff --git a/frontend/src/components/footer.rs b/frontend/src/components/footer.rs index 67e2e5d..30df1e8 100644 --- a/frontend/src/components/footer.rs +++ b/frontend/src/components/footer.rs @@ -15,7 +15,7 @@ impl Component for Footer {