From 860ae7ed5e3bf755e926ca0d0e04d131801f8eda Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Fri, 4 Nov 2022 21:23:36 +0100 Subject: [PATCH] Rewrite frontend in rust with yew --- backend/src/backend/api/auth.cr | 10 +- backend/src/backend/api/context.cr | 37 +- backend/src/backend/api/errors.cr | 57 +- backend/src/backend/api/schema/mutation.cr | 31 +- backend/src/backend/api/schema/query.cr | 8 + backend/src/backend/runner.cr | 2 +- .../backend/web/controllers/api_controller.cr | 2 +- config/nginx/nginx.conf | 2 +- docker-compose.yml | 2 - frontend/.cargo/config | 2 + frontend/.dockerignore | 30 +- frontend/.gitignore | 26 +- frontend/.nvmrc | 1 - frontend/Cargo.toml | 19 + frontend/Dockerfile | 32 +- frontend/graphql/queries/ok.graphql | 3 + frontend/graphql/schema.graphql | 92 + frontend/index.html | 13 + frontend/nginx.conf | 37 + .../{static/.keep => src/components/mod.rs} | 0 frontend/src/cookie_names.rs | 4 + frontend/src/graphql/mod.rs | 13 + frontend/src/graphql/queries.rs | 9 + frontend/src/layouts/main.rs | 56 + frontend/src/layouts/mod.rs | 1 + frontend/src/lib.rs | 5 + frontend/src/main.rs | 22 + frontend/src/routes/home.rs | 8 + frontend/src/routes/login.rs | 8 + frontend/src/routes/mod.rs | 29 + frontend/src/routes/not_found.rs | 8 + frontend_old/.dockerignore | 12 + {frontend => frontend_old}/.eslintrc.cjs | 0 frontend_old/.gitignore | 9 + {frontend => frontend_old}/.npmrc | 0 {frontend => frontend_old}/.prettierrc | 0 frontend_old/Dockerfile | 22 + {frontend => frontend_old}/README.md | 0 {frontend => frontend_old}/package.json | 2 +- {frontend => frontend_old}/src/app.d.ts | 0 {frontend => frontend_old}/src/app.html | 0 {frontend => frontend_old}/src/hooks.ts | 5 +- frontend_old/src/lib/StudentHome.svelte | 1 + frontend_old/src/lib/TeacherHome.svelte | 1 + .../src/lib/cookieNames.ts | 0 {frontend => frontend_old}/src/lib/graphql.ts | 0 .../src/routes/__layout.svelte | 0 .../src/routes/index.svelte | 8 +- .../src/routes/login.svelte | 0 frontend_old/static/.keep | 0 {frontend => frontend_old}/svelte.config.js | 0 frontend_old/test.json | 2085 +++++++++++++++++ {frontend => frontend_old}/tsconfig.json | 2 +- {frontend => frontend_old}/yarn.lock | 630 +++-- 54 files changed, 3135 insertions(+), 211 deletions(-) create mode 100644 frontend/.cargo/config delete mode 100644 frontend/.nvmrc create mode 100644 frontend/Cargo.toml create mode 100644 frontend/graphql/queries/ok.graphql create mode 100644 frontend/graphql/schema.graphql create mode 100644 frontend/index.html create mode 100644 frontend/nginx.conf rename frontend/{static/.keep => src/components/mod.rs} (100%) create mode 100644 frontend/src/cookie_names.rs create mode 100644 frontend/src/graphql/mod.rs create mode 100644 frontend/src/graphql/queries.rs create mode 100644 frontend/src/layouts/main.rs create mode 100644 frontend/src/layouts/mod.rs create mode 100644 frontend/src/lib.rs create mode 100644 frontend/src/main.rs create mode 100644 frontend/src/routes/home.rs create mode 100644 frontend/src/routes/login.rs create mode 100644 frontend/src/routes/mod.rs create mode 100644 frontend/src/routes/not_found.rs create mode 100644 frontend_old/.dockerignore rename {frontend => frontend_old}/.eslintrc.cjs (100%) create mode 100644 frontend_old/.gitignore rename {frontend => frontend_old}/.npmrc (100%) rename {frontend => frontend_old}/.prettierrc (100%) create mode 100644 frontend_old/Dockerfile rename {frontend => frontend_old}/README.md (100%) rename {frontend => frontend_old}/package.json (96%) rename {frontend => frontend_old}/src/app.d.ts (100%) rename {frontend => frontend_old}/src/app.html (100%) rename {frontend => frontend_old}/src/hooks.ts (77%) create mode 100644 frontend_old/src/lib/StudentHome.svelte create mode 100644 frontend_old/src/lib/TeacherHome.svelte rename {frontend => frontend_old}/src/lib/cookieNames.ts (100%) rename {frontend => frontend_old}/src/lib/graphql.ts (100%) rename {frontend => frontend_old}/src/routes/__layout.svelte (100%) rename {frontend => frontend_old}/src/routes/index.svelte (95%) rename {frontend => frontend_old}/src/routes/login.svelte (100%) create mode 100644 frontend_old/static/.keep rename {frontend => frontend_old}/svelte.config.js (100%) create mode 100644 frontend_old/test.json rename {frontend => frontend_old}/tsconfig.json (95%) rename {frontend => frontend_old}/yarn.lock (75%) diff --git a/backend/src/backend/api/auth.cr b/backend/src/backend/api/auth.cr index fa482a6..4c638d2 100644 --- a/backend/src/backend/api/auth.cr +++ b/backend/src/backend/api/auth.cr @@ -16,6 +16,7 @@ require "jwt" require "json" +require "uuid" module Backend module Api @@ -31,16 +32,18 @@ module Backend include JSON::Serializable getter iss : String + getter vrs : String getter iat : Int64 getter exp : Int64 - getter jti : String + getter jti : UUID getter context : Context def initialize( @iss : String, + @vrs : String, @iat : Int64, @exp : Int64, - @jti : String, + @jti : UUID, @context : Context ) end @@ -52,9 +55,10 @@ module Backend def self.from_hash(token : Hash(String, JSON::Any)) : self self.new( iss: token["iss"].as_s, + vrs: token["vrs"].as_s, iat: token["iat"].as_i64, exp: token["exp"].as_i64, - jti: token["jti"].as_s, + jti: UUID.new(token["jti"].as_s), context: Context.from_hash(token["context"].as_h) ) end diff --git a/backend/src/backend/api/context.cr b/backend/src/backend/api/context.cr index 3359f01..35776e2 100644 --- a/backend/src/backend/api/context.cr +++ b/backend/src/backend/api/context.cr @@ -62,6 +62,8 @@ module Backend rescue @status = Status::JWTError else + pp! payload + if @user = Db::User.find(payload.context.user) @admin = user.not_nil!.admin @role = user.not_nil!.role.to_api @@ -98,8 +100,10 @@ module Backend # :ditto: def authenticated! : Bool - raise "Session expired" if @status.session_expired? - raise "Not authenticated" unless authenticated? + # raise "Session expired" if @status.session_expired? + raise Errors::SessionExpired.new if @status.session_expired? + # raise "Not authenticated" unless authenticated? + raise Errors::NotAuthenticated.new unless authenticated? true end @@ -112,7 +116,8 @@ module Backend # :ditto: def admin! : Bool authenticated! - raise "Invalid permissions" unless admin? + # raise "Invalid permissions" unless admin? + raise Errors::InvalidPermissions.new unless admin? true end @@ -142,7 +147,8 @@ module Backend # :ditto: def role!(roles : Array(Schema::UserRole), external_check = true) : Bool authenticated! - raise "Invalid permissions" unless role?(roles, external_check) + # raise "Invalid permissions" unless role?(roles, external_check) + raise Errors::InvalidPermissions.new unless role?(roles, external_check) true end @@ -169,32 +175,31 @@ module Backend # Custom error handler def handle_exception(ex : Exception) : String? - pp! ex, ex.message - - # ex.message + # pp! ex, ex.message, ex.class, typeof(ex), ex.is_a? Errors::PublicError + # # ex.message case ex - when Errors::Error + when Errors::PublicError + ex.api_message + when Errors::PrivateError {% if !flag?(:release) %} if @development - ex.message + ex.api_message else - nil + Errors::UNKNOWN_PRIVATE_ERROR end {% else %} - nil + Errors::UNKNOWN_PRIVATE_ERROR {% end %} - when Errors::PublicError - ex.message else {% if !flag?(:release) %} if @development - ex.message + ex.message || Errors::UNKNOWN_PRIVATE_ERROR else - nil + Errors::UNKNOWN_PRIVATE_ERROR end {% else %} - nil + Errors::UNKNOWN_PRIVATE_ERROR {% end %} end end diff --git a/backend/src/backend/api/errors.cr b/backend/src/backend/api/errors.cr index 54b6008..f36eb95 100644 --- a/backend/src/backend/api/errors.cr +++ b/backend/src/backend/api/errors.cr @@ -1,5 +1,9 @@ module Backend::Api::Errors + UNKNOWN_PRIVATE_ERROR = "UNKNOWN_ERROR" + UNKNOWN_PUBLIC_ERROR = "UNKNOWN_PUBLIC_ERROR" + abstract class Error < Exception + abstract def api_message : String end abstract class PrivateError < Error @@ -8,6 +12,57 @@ module Backend::Api::Errors abstract class PublicError < Error end - class AuthenticationError < PublicError + class SessionExpired < PublicError + def api_message : String + "Session expired" + end + end + + class NotAuthenticated < PublicError + def api_message : String + "Not authenticated" + end + end + + class Authentication < PublicError + def api_message : String + "Invalid username or password" + end + end + + class InvalidPermissions < PublicError + def api_message : String + "Invalid permissions" + end + end + + class LdapUserDoesNotExist < PublicError + def api_message : String + "LDAP user does not exist" + end + end + + class DuplicateTeachers < PublicError + def api_message : String + "Duplicate teachers" + end + end + + class NotEnoughTeachers < PublicError + def api_message : String + "Not enough teachers" + end + end + + class TeachersNotRegistered < PublicError + def api_message : String + "Teachers not registered" + end + end + + class TeachersNotFound < PublicError + def api_message : String + "Teachers not found" + end end end diff --git a/backend/src/backend/api/schema/mutation.cr b/backend/src/backend/api/schema/mutation.cr index b676478..187a992 100644 --- a/backend/src/backend/api/schema/mutation.cr +++ b/backend/src/backend/api/schema/mutation.cr @@ -16,6 +16,8 @@ require "ldap" require "uuid" +require "uuid/json" +require "random/secure" module Backend module Api @@ -25,19 +27,20 @@ module Backend @[GraphQL::Field] # Logs in as *username* with credential *password* def login(username : String, password : String) : LoginPayload - raise Errors::AuthenticationError.new if username.empty? || password.empty? + raise Errors::Authentication.new if username.empty? || password.empty? user = Db::User.query.find { var(:username) == username } - raise Errors::AuthenticationError.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 + jti = UUID.random(Random::Secure) LoginPayload.new( user: User.new(user), token: Auth::Token.new( - iss: "mentorenwahl", + iss: "Mentorenwahl", + vrs: Backend::VERSION, iat: Time.utc.to_unix, exp: (Time.utc + Backend.config.api.jwt_expiration.minutes).to_unix, - jti: jti.hexstring, + jti: jti, context: Auth::Context.new(user.id.not_nil!) ).encode ) @@ -48,11 +51,11 @@ module Backend def create_user(context : Context, input : UserCreateInput, check_ldap : Bool = true) : User context.admin! - raise "LDAP user does not exist" if check_ldap && begin - !Ldap::User.from_username(input.username) - rescue LDAP::Client::AuthError - true - end + raise Errors::LdapUserDoesNotExist.new if check_ldap && begin + !Ldap::User.from_username(input.username) + rescue LDAP::Client::AuthError + true + end user = Db::User.create!(username: input.username, role: input.role.to_db, admin: input.admin) Worker::Jobs::CacheLdapUserJob.new(user.id.not_nil!.to_i).enqueue @@ -148,16 +151,16 @@ module Backend def create_vote(context : Context, input : VoteCreateInput) : Vote context.student! - raise "Duplicate teachers" if input.teacher_ids.uniq.size != input.teacher_ids.size - raise "Not enough teachers" if input.teacher_ids.size < Backend.config.minimum_teacher_selection_count + raise Errors::DuplicateTeachers.new if input.teacher_ids.uniq.size != input.teacher_ids.size + raise Errors::NotEnoughTeachers.new if input.teacher_ids.size < Backend.config.minimum_teacher_selection_count teacher_role_count = Db::User.query.where(role: Db::UserRole::Teacher).count - raise "Teachers not registered" if teacher_role_count != Db::Teacher.query.count || teacher_role_count.zero? + raise Errors::TeachersNotRegistered.new if teacher_role_count != Db::Teacher.query.count || teacher_role_count.zero? input.teacher_ids.each do |id| teacher = Db::Teacher.find(id) if teacher.nil? - raise "Teachers not found" + raise Errors::TeachersNotFound.new # elsif teacher.user.skif != context.user.not_nil!.skif # if teacher.user.skif # raise "Teacher is SKIF, student is not" diff --git a/backend/src/backend/api/schema/query.cr b/backend/src/backend/api/schema/query.cr index a4838f4..5cbdd27 100644 --- a/backend/src/backend/api/schema/query.cr +++ b/backend/src/backend/api/schema/query.cr @@ -125,6 +125,14 @@ module Backend students > 0 && votes >= students end + @[GraphQL::Field] + # Students can vote + def students_can_vote : Bool + teacher_role_count = Db::User.query.where(role: Db::UserRole::Teacher).count + + teacher_role_count > 0 && teacher_role_count == Db::Teacher.query.count + end + @[GraphQL::Field] # Teacher vote by ID def teacher_vote(context : Context, id : Int32) : TeacherVote diff --git a/backend/src/backend/runner.cr b/backend/src/backend/runner.cr index 40ef586..5446cfc 100644 --- a/backend/src/backend/runner.cr +++ b/backend/src/backend/runner.cr @@ -56,7 +56,7 @@ module Backend if ex raise ex else - raise Exception.new unless Backend.config.db.allow_old_schema + raise "Database schema is not up to date" unless Backend.config.db.allow_old_schema end end end diff --git a/backend/src/backend/web/controllers/api_controller.cr b/backend/src/backend/web/controllers/api_controller.cr index 74b2a73..a6fd840 100644 --- a/backend/src/backend/web/controllers/api_controller.cr +++ b/backend/src/backend/web/controllers/api_controller.cr @@ -33,7 +33,7 @@ module Backend @[ARTA::Get("")] def playground : ATH::Response - ATH::StreamedResponse.new(headers: HTTP::Headers{"Content-Type" => "text/html"}) do |io| + ATH::StreamedResponse.new(headers: HTTP::Headers{"Content-Type" => "text/html", "Access-Control-Allow-Origin" => "*"}) do |io| IO.copy(Public.get("index.html"), io) end end diff --git a/config/nginx/nginx.conf b/config/nginx/nginx.conf index 4a210e7..a9c7906 100644 --- a/config/nginx/nginx.conf +++ b/config/nginx/nginx.conf @@ -20,7 +20,7 @@ events { http { server { location / { - proxy_pass http://frontend:3000/; + proxy_pass http://frontend/; } location /graphql { diff --git a/docker-compose.yml b/docker-compose.yml index bde2849..d2fd727 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -104,8 +104,6 @@ services: - default depends_on: - backend - environment: - NODE_ENV: production networks: db: diff --git a/frontend/.cargo/config b/frontend/.cargo/config new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/frontend/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/frontend/.dockerignore b/frontend/.dockerignore index da9c4db..59bf0b2 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,14 +1,20 @@ -.DS_Store -node_modules -yarn-error.log -/build -/.svelte-kit -/package -.env -.env.* -!.env.example +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +dist/ Dockerfile -.dockerignore .gitignore -README.md -.nvmrc +.dockerignore +vendor/ diff --git a/frontend/.gitignore b/frontend/.gitignore index e1e4aff..a74235e 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -1,9 +1,17 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example -yarn-error.log +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +dist/ +vendor/ diff --git a/frontend/.nvmrc b/frontend/.nvmrc deleted file mode 100644 index e0325e5..0000000 --- a/frontend/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v16.17.1 diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml new file mode 100644 index 0000000..ac9e44c --- /dev/null +++ b/frontend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "frontend" +version = "0.1.0" +edition = "2021" + +[dependencies] +yew = "0.19.3" +wasm-logger = "0.2.0" +log = "0.4.6" +yew-router = "0.16.0" +wee_alloc = "0.4.5" +graphql_client = { version = "0.11.0", features = ["reqwest"] } +reqwest = "0.11.12" +wasm-bindgen-futures = "0.4.33" +serde = "1.0.147" +web-sys = { version = "0.3.60", features = ["Window", "Location"] } +wasm-cookies = "0.1.0" +lazy_static = "1.4.0" +const_format = "0.2.30" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index b7d83a6..801bad2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,8 +1,26 @@ -FROM node:16-alpine +FROM lukemathwalker/cargo-chef:latest-rust-1.65.0 as chef WORKDIR /usr/src/frontend -COPY ./package.json ./yarn.lock ./ -RUN yarn install --frozen-lockfile -COPY . . -RUN yarn build -EXPOSE 3000 -CMD ["yarn", "preview", "--host"] + +FROM chef as planner +WORKDIR /usr/src/frontend +RUN mkdir src && touch src/main.rs +COPY ./Cargo.toml . +RUN cargo chef prepare --recipe-path recipe.json + +FROM chef as builder +WORKDIR /usr/local/bin +ARG TRUNK_VERSION="v0.16.0" +RUN wget -qO- https://github.com/thedodd/trunk/releases/download/${TRUNK_VERSION}/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf- +WORKDIR /usr/src/frontend +RUN rustup target add wasm32-unknown-unknown +COPY ./.cargo ./.cargo +COPY --from=planner /usr/src/frontend/recipe.json . +RUN cargo chef cook --release --recipe-path recipe.json +COPY ./index.html . +COPY ./graphql ./graphql +COPY ./src ./src +RUN trunk build --release + +FROM nginx:alpine as runner +COPY ./nginx.conf /etc/nginx/nginx.conf +COPY --from=builder /usr/src/frontend/dist /var/www/html diff --git a/frontend/graphql/queries/ok.graphql b/frontend/graphql/queries/ok.graphql new file mode 100644 index 0000000..20e8420 --- /dev/null +++ b/frontend/graphql/queries/ok.graphql @@ -0,0 +1,3 @@ +query Ok { + ok +} diff --git a/frontend/graphql/schema.graphql b/frontend/graphql/schema.graphql new file mode 100644 index 0000000..aca8292 --- /dev/null +++ b/frontend/graphql/schema.graphql @@ -0,0 +1,92 @@ +type Config { + minimumTeacherSelectionCount: Int! +} + +type Query { + admins: [User!]! + allStudentsVoted: Boolean! + config: Config! + me: User! + ok: Boolean! + student(id: Int!): Student! + students: [Student!]! + studentsCanVote: Boolean! + teacher(id: Int!): Teacher! + teacherVote(id: Int!): TeacherVote! + teacherVotes: [TeacherVote!]! + teachers: [Teacher!]! + user(id: Int!): User! + userByUsername(username: String!): User! + users: [User!]! + vote(id: Int!): Vote! + votes: [Vote!]! +} + +type Student { + id: Int! + user: User! + vote: Vote +} + +type Teacher { + id: Int! + maxStudents: Int! + teacherVotes: [TeacherVote!]! + user: User! +} + +type TeacherVote { + id: Int! + priority: Int! + teacher: Teacher! + vote: Vote! +} + +type User { + admin: Boolean! + email: String! + externalId: Int! + firstName: String! + id: Int! + lastName: String! + name(formal: Boolean! = true): String! + role: UserRole! + student: Student + teacher: Teacher + username: String! +} + +enum UserRole { + Student + Teacher +} + +type Vote { + id: Int! + student: Student! + teacherVotes: [TeacherVote!]! +} + +type LoginPayload { + bearer: String! + token: String! + user: User! +} + +type Mutation { + assignStudents: Boolean! + createUser(checkLdap: Boolean! = true, input: UserCreateInput!): User! + createVote(input: VoteCreateInput!): Vote! + deleteUser(id: Int!): Int! + login(password: String!, username: String!): LoginPayload! +} + +input UserCreateInput { + username: String! + role: UserRole! + admin: Boolean! = false +} + +input VoteCreateInput { + teacherIds: [Int!]! +} diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..aefc1fe --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..7e00e07 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,37 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + server { + listen 80; + + root /var/www/html; + + location / { + try_files $uri /index.html; + } + } +} diff --git a/frontend/static/.keep b/frontend/src/components/mod.rs similarity index 100% rename from frontend/static/.keep rename to frontend/src/components/mod.rs diff --git a/frontend/src/cookie_names.rs b/frontend/src/cookie_names.rs new file mode 100644 index 0000000..18e5aa0 --- /dev/null +++ b/frontend/src/cookie_names.rs @@ -0,0 +1,4 @@ +use const_format::concatcp; + +pub const BASE: &str = "mentorenwahl_"; +pub const TOKEN: &str = concatcp!(BASE, "token"); diff --git a/frontend/src/graphql/mod.rs b/frontend/src/graphql/mod.rs new file mode 100644 index 0000000..0b1a318 --- /dev/null +++ b/frontend/src/graphql/mod.rs @@ -0,0 +1,13 @@ +use lazy_static::lazy_static; +use std::path::Path; + +pub mod queries; + +lazy_static! { + pub static ref URL: String = + Path::new(&web_sys::window().unwrap().location().origin().unwrap()) + .join("graphql") + .to_str() + .unwrap() + .to_string(); +} diff --git a/frontend/src/graphql/queries.rs b/frontend/src/graphql/queries.rs new file mode 100644 index 0000000..22cef99 --- /dev/null +++ b/frontend/src/graphql/queries.rs @@ -0,0 +1,9 @@ +use graphql_client::GraphQLQuery; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/queries/ok.graphql", + response_derives = "Debug" +)] +pub struct Ok; diff --git a/frontend/src/layouts/main.rs b/frontend/src/layouts/main.rs new file mode 100644 index 0000000..f2c3a8c --- /dev/null +++ b/frontend/src/layouts/main.rs @@ -0,0 +1,56 @@ +use graphql_client::reqwest::post_graphql; +use wasm_bindgen_futures; +use yew::prelude::*; +use yew_router::prelude::*; + +use crate::cookie_names; +use crate::graphql; +use crate::routes; + +#[derive(Properties, PartialEq)] +pub struct MainProps { + #[prop_or_default] + pub children: Children, +} + +#[function_component(Main)] +pub fn main(props: &MainProps) -> Html { + let client = reqwest::Client::new(); + wasm_bindgen_futures::spawn_local(async move { + let response = post_graphql::( + &client, + graphql::URL.as_str(), + graphql::queries::ok::Variables {}, + ) + .await + .unwrap(); + log::debug!("{:?}", response); + log::debug!("{:?}", wasm_cookies::get(cookie_names::TOKEN)); + }); + + let history = use_history().unwrap(); + let loginout_onclick = Callback::once(move |_| history.push(routes::Route::Login)); + + html! { + <> + + +
+ +
+ { for props.children.iter() } +
+ + } +} diff --git a/frontend/src/layouts/mod.rs b/frontend/src/layouts/mod.rs new file mode 100644 index 0000000..2a04341 --- /dev/null +++ b/frontend/src/layouts/mod.rs @@ -0,0 +1 @@ +pub mod main; diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs new file mode 100644 index 0000000..21496b8 --- /dev/null +++ b/frontend/src/lib.rs @@ -0,0 +1,5 @@ +pub mod components; +pub mod cookie_names; +pub mod graphql; +pub mod layouts; +pub mod routes; diff --git a/frontend/src/main.rs b/frontend/src/main.rs new file mode 100644 index 0000000..6aaac67 --- /dev/null +++ b/frontend/src/main.rs @@ -0,0 +1,22 @@ +use yew::prelude::*; +use yew_router::prelude::*; + +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +use frontend::routes; + +#[function_component(App)] +fn app() -> Html { + html! { + + render={Switch::render(routes::switch)} /> + + } +} + +fn main() { + wasm_logger::init(wasm_logger::Config::default()); + + yew::start_app::(); +} diff --git a/frontend/src/routes/home.rs b/frontend/src/routes/home.rs new file mode 100644 index 0000000..2d8f545 --- /dev/null +++ b/frontend/src/routes/home.rs @@ -0,0 +1,8 @@ +use yew::prelude::*; + +#[function_component(Home)] +pub fn home() -> Html { + html! { +

{ "HOME!" }

+ } +} diff --git a/frontend/src/routes/login.rs b/frontend/src/routes/login.rs new file mode 100644 index 0000000..72c9dea --- /dev/null +++ b/frontend/src/routes/login.rs @@ -0,0 +1,8 @@ +use yew::prelude::*; + +#[function_component(Login)] +pub fn login() -> Html { + html! { +

{ "LOGIN!" }

+ } +} diff --git a/frontend/src/routes/mod.rs b/frontend/src/routes/mod.rs new file mode 100644 index 0000000..82de03a --- /dev/null +++ b/frontend/src/routes/mod.rs @@ -0,0 +1,29 @@ +use yew::prelude::*; +use yew_router::prelude::*; + +use crate::layouts; + +pub mod home; +pub mod login; +pub mod not_found; + +#[derive(Clone, Routable, PartialEq)] +pub enum Route { + #[at("/")] + Home, + #[at("/login")] + Login, + #[not_found] + #[at("/404")] + NotFound, +} + +pub fn switch(routes: &Route) -> Html { + match routes { + Route::Home => html! { }, + Route::Login => html! { }, + Route::NotFound => { + html! { } + } + } +} diff --git a/frontend/src/routes/not_found.rs b/frontend/src/routes/not_found.rs new file mode 100644 index 0000000..94e91d9 --- /dev/null +++ b/frontend/src/routes/not_found.rs @@ -0,0 +1,8 @@ +use yew::prelude::*; + +#[function_component(NotFound)] +pub fn not_found() -> Html { + html! { +

{ "404" }

+ } +} diff --git a/frontend_old/.dockerignore b/frontend_old/.dockerignore new file mode 100644 index 0000000..adf689e --- /dev/null +++ b/frontend_old/.dockerignore @@ -0,0 +1,12 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +yarn-error.log +Dockerfile +.gitignore +.dockerignore diff --git a/frontend/.eslintrc.cjs b/frontend_old/.eslintrc.cjs similarity index 100% rename from frontend/.eslintrc.cjs rename to frontend_old/.eslintrc.cjs diff --git a/frontend_old/.gitignore b/frontend_old/.gitignore new file mode 100644 index 0000000..e1e4aff --- /dev/null +++ b/frontend_old/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +yarn-error.log diff --git a/frontend/.npmrc b/frontend_old/.npmrc similarity index 100% rename from frontend/.npmrc rename to frontend_old/.npmrc diff --git a/frontend/.prettierrc b/frontend_old/.prettierrc similarity index 100% rename from frontend/.prettierrc rename to frontend_old/.prettierrc diff --git a/frontend_old/Dockerfile b/frontend_old/Dockerfile new file mode 100644 index 0000000..5baa6b9 --- /dev/null +++ b/frontend_old/Dockerfile @@ -0,0 +1,22 @@ +FROM node:16-alpine as deps +WORKDIR /usr/src/frontend +COPY ./package.json ./yarn.lock ./ +RUN yarn install --frozen-lockfile + +FROM node:16-alpine as builder +WORKDIR /usr/src/frontend +COPY --from=deps /usr/src/frontend/package.json . +COPY --from=deps /usr/src/frontend/node_modules ./node_modules +COPY svelte.config.js tsconfig.json ./ +COPY ./static ./static +COPY ./src ./src +RUN yarn build + +FROM node:16-alpine as runner +WORKDIR /usr/src/frontend +COPY --from=deps /usr/src/frontend/package.json . +COPY --from=deps /usr/src/frontend/node_modules ./node_modules +COPY svelte.config.js . +COPY --from=builder /usr/src/frontend/.svelte-kit ./.svelte-kit +EXPOSE 3000 +CMD [ "yarn", "preview", "--host" ] diff --git a/frontend/README.md b/frontend_old/README.md similarity index 100% rename from frontend/README.md rename to frontend_old/README.md diff --git a/frontend/package.json b/frontend_old/package.json similarity index 96% rename from frontend/package.json rename to frontend_old/package.json index ac31581..ccb62ef 100644 --- a/frontend/package.json +++ b/frontend_old/package.json @@ -12,7 +12,7 @@ "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." }, "devDependencies": { - "@sveltejs/adapter-auto": "next", + "@sveltejs/adapter-auto": "^1.0.0-next.86", "@sveltejs/kit": "next", "@types/cookie": "^0.4.1", "@typescript-eslint/eslint-plugin": "^5.10.1", diff --git a/frontend/src/app.d.ts b/frontend_old/src/app.d.ts similarity index 100% rename from frontend/src/app.d.ts rename to frontend_old/src/app.d.ts diff --git a/frontend/src/app.html b/frontend_old/src/app.html similarity index 100% rename from frontend/src/app.html rename to frontend_old/src/app.html diff --git a/frontend/src/hooks.ts b/frontend_old/src/hooks.ts similarity index 77% rename from frontend/src/hooks.ts rename to frontend_old/src/hooks.ts index 9bacb5c..51d202d 100644 --- a/frontend/src/hooks.ts +++ b/frontend_old/src/hooks.ts @@ -1,4 +1,4 @@ -import type { RequestEvent, App, ResolveOpts } from "@sveltejs/kit/types"; +import type { RequestEvent, ResolveOpts } from "@sveltejs/kit"; import * as cookie from "cookie"; import * as cookieNames from "$lib/cookieNames"; @@ -8,7 +8,8 @@ export async function handle(input: { opts?: ResolveOpts; resolve(event: RequestEvent, opts?: ResolveOpts): Promise; }): Promise { - const cookies = cookie.parse(input.event.request.headers.get("cookie") || ""); + const header = input.event.request.headers.get("cookie"); + const cookies = header ? cookie.parse(header) : {}; const token: string | undefined = cookies[cookieNames.TOKEN]; input.event.locals = { diff --git a/frontend_old/src/lib/StudentHome.svelte b/frontend_old/src/lib/StudentHome.svelte new file mode 100644 index 0000000..d848742 --- /dev/null +++ b/frontend_old/src/lib/StudentHome.svelte @@ -0,0 +1 @@ +

STUDENT!

diff --git a/frontend_old/src/lib/TeacherHome.svelte b/frontend_old/src/lib/TeacherHome.svelte new file mode 100644 index 0000000..1ac1373 --- /dev/null +++ b/frontend_old/src/lib/TeacherHome.svelte @@ -0,0 +1 @@ +

TEACHER!

diff --git a/frontend/src/lib/cookieNames.ts b/frontend_old/src/lib/cookieNames.ts similarity index 100% rename from frontend/src/lib/cookieNames.ts rename to frontend_old/src/lib/cookieNames.ts diff --git a/frontend/src/lib/graphql.ts b/frontend_old/src/lib/graphql.ts similarity index 100% rename from frontend/src/lib/graphql.ts rename to frontend_old/src/lib/graphql.ts diff --git a/frontend/src/routes/__layout.svelte b/frontend_old/src/routes/__layout.svelte similarity index 100% rename from frontend/src/routes/__layout.svelte rename to frontend_old/src/routes/__layout.svelte diff --git a/frontend/src/routes/index.svelte b/frontend_old/src/routes/index.svelte similarity index 95% rename from frontend/src/routes/index.svelte rename to frontend_old/src/routes/index.svelte index 5481e63..d351ec6 100644 --- a/frontend/src/routes/index.svelte +++ b/frontend_old/src/routes/index.svelte @@ -17,11 +17,12 @@