Rewrite frontend in rust with yew
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dominic Grimm 2022-11-04 21:23:36 +01:00
parent c56d359814
commit 860ae7ed5e
No known key found for this signature in database
GPG key ID: 6F294212DEAAC530
54 changed files with 3135 additions and 211 deletions

View file

@ -16,6 +16,7 @@
require "jwt" require "jwt"
require "json" require "json"
require "uuid"
module Backend module Backend
module Api module Api
@ -31,16 +32,18 @@ module Backend
include JSON::Serializable include JSON::Serializable
getter iss : String getter iss : String
getter vrs : String
getter iat : Int64 getter iat : Int64
getter exp : Int64 getter exp : Int64
getter jti : String getter jti : UUID
getter context : Context getter context : Context
def initialize( def initialize(
@iss : String, @iss : String,
@vrs : String,
@iat : Int64, @iat : Int64,
@exp : Int64, @exp : Int64,
@jti : String, @jti : UUID,
@context : Context @context : Context
) )
end end
@ -52,9 +55,10 @@ 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, 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: token["jti"].as_s, jti: UUID.new(token["jti"].as_s),
context: Context.from_hash(token["context"].as_h) context: Context.from_hash(token["context"].as_h)
) )
end end

View file

@ -62,6 +62,8 @@ module Backend
rescue rescue
@status = Status::JWTError @status = Status::JWTError
else else
pp! payload
if @user = Db::User.find(payload.context.user) if @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
@ -98,8 +100,10 @@ module Backend
# :ditto: # :ditto:
def authenticated! : Bool def authenticated! : Bool
raise "Session expired" if @status.session_expired? # raise "Session expired" if @status.session_expired?
raise "Not authenticated" unless authenticated? raise Errors::SessionExpired.new if @status.session_expired?
# raise "Not authenticated" unless authenticated?
raise Errors::NotAuthenticated.new unless authenticated?
true true
end end
@ -112,7 +116,8 @@ module Backend
# :ditto: # :ditto:
def admin! : Bool def admin! : Bool
authenticated! authenticated!
raise "Invalid permissions" unless admin? # raise "Invalid permissions" unless admin?
raise Errors::InvalidPermissions.new unless admin?
true true
end end
@ -142,7 +147,8 @@ module Backend
# :ditto: # :ditto:
def role!(roles : Array(Schema::UserRole), external_check = true) : Bool def role!(roles : Array(Schema::UserRole), external_check = true) : Bool
authenticated! 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 true
end end
@ -169,32 +175,31 @@ module Backend
# Custom error handler # Custom error handler
def handle_exception(ex : Exception) : String? def handle_exception(ex : Exception) : String?
pp! ex, ex.message # pp! ex, ex.message, ex.class, typeof(ex), ex.is_a? Errors::PublicError
# # ex.message
# ex.message
case ex case ex
when Errors::Error when Errors::PublicError
ex.api_message
when Errors::PrivateError
{% if !flag?(:release) %} {% if !flag?(:release) %}
if @development if @development
ex.message ex.api_message
else else
nil Errors::UNKNOWN_PRIVATE_ERROR
end end
{% else %} {% else %}
nil Errors::UNKNOWN_PRIVATE_ERROR
{% end %} {% end %}
when Errors::PublicError
ex.message
else else
{% if !flag?(:release) %} {% if !flag?(:release) %}
if @development if @development
ex.message ex.message || Errors::UNKNOWN_PRIVATE_ERROR
else else
nil Errors::UNKNOWN_PRIVATE_ERROR
end end
{% else %} {% else %}
nil Errors::UNKNOWN_PRIVATE_ERROR
{% end %} {% end %}
end end
end end

View file

@ -1,5 +1,9 @@
module Backend::Api::Errors module Backend::Api::Errors
UNKNOWN_PRIVATE_ERROR = "UNKNOWN_ERROR"
UNKNOWN_PUBLIC_ERROR = "UNKNOWN_PUBLIC_ERROR"
abstract class Error < Exception abstract class Error < Exception
abstract def api_message : String
end end
abstract class PrivateError < Error abstract class PrivateError < Error
@ -8,6 +12,57 @@ module Backend::Api::Errors
abstract class PublicError < Error abstract class PublicError < Error
end 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
end end

View file

@ -16,6 +16,8 @@
require "ldap" require "ldap"
require "uuid" require "uuid"
require "uuid/json"
require "random/secure"
module Backend module Backend
module Api module Api
@ -25,19 +27,20 @@ module Backend
@[GraphQL::Field] @[GraphQL::Field]
# Logs in as *username* with credential *password* # Logs in as *username* with credential *password*
def login(username : String, password : String) : LoginPayload 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 } 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( LoginPayload.new(
user: User.new(user), user: User.new(user),
token: Auth::Token.new( token: Auth::Token.new(
iss: "mentorenwahl", iss: "Mentorenwahl",
vrs: Backend::VERSION,
iat: Time.utc.to_unix, iat: Time.utc.to_unix,
exp: (Time.utc + Backend.config.api.jwt_expiration.minutes).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!) context: Auth::Context.new(user.id.not_nil!)
).encode ).encode
) )
@ -48,11 +51,11 @@ module Backend
def create_user(context : Context, input : UserCreateInput, check_ldap : Bool = true) : User def create_user(context : Context, input : UserCreateInput, check_ldap : Bool = true) : User
context.admin! context.admin!
raise "LDAP user does not exist" if check_ldap && begin raise Errors::LdapUserDoesNotExist.new if check_ldap && begin
!Ldap::User.from_username(input.username) !Ldap::User.from_username(input.username)
rescue LDAP::Client::AuthError rescue LDAP::Client::AuthError
true true
end end
user = Db::User.create!(username: input.username, role: input.role.to_db, admin: input.admin) 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 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 def create_vote(context : Context, input : VoteCreateInput) : Vote
context.student! context.student!
raise "Duplicate teachers" if input.teacher_ids.uniq.size != input.teacher_ids.size raise Errors::DuplicateTeachers.new 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::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 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| input.teacher_ids.each do |id|
teacher = Db::Teacher.find(id) teacher = Db::Teacher.find(id)
if teacher.nil? if teacher.nil?
raise "Teachers not found" raise Errors::TeachersNotFound.new
# elsif teacher.user.skif != context.user.not_nil!.skif # elsif teacher.user.skif != context.user.not_nil!.skif
# if teacher.user.skif # if teacher.user.skif
# raise "Teacher is SKIF, student is not" # raise "Teacher is SKIF, student is not"

View file

@ -125,6 +125,14 @@ module Backend
students > 0 && votes >= students students > 0 && votes >= students
end 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] @[GraphQL::Field]
# Teacher vote by ID # Teacher vote by ID
def teacher_vote(context : Context, id : Int32) : TeacherVote def teacher_vote(context : Context, id : Int32) : TeacherVote

View file

@ -56,7 +56,7 @@ module Backend
if ex if ex
raise ex raise ex
else 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 end
end end

View file

@ -33,7 +33,7 @@ module Backend
@[ARTA::Get("")] @[ARTA::Get("")]
def playground : ATH::Response 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) IO.copy(Public.get("index.html"), io)
end end
end end

View file

@ -20,7 +20,7 @@ events {
http { http {
server { server {
location / { location / {
proxy_pass http://frontend:3000/; proxy_pass http://frontend/;
} }
location /graphql { location /graphql {

View file

@ -104,8 +104,6 @@ services:
- default - default
depends_on: depends_on:
- backend - backend
environment:
NODE_ENV: production
networks: networks:
db: db:

2
frontend/.cargo/config Normal file
View file

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

View file

@ -1,14 +1,20 @@
.DS_Store # Generated by Cargo
node_modules # will have compiled files and executables
yarn-error.log debug/
/build target/
/.svelte-kit
/package # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
.env # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
.env.* Cargo.lock
!.env.example
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
dist/
Dockerfile Dockerfile
.dockerignore
.gitignore .gitignore
README.md .dockerignore
.nvmrc vendor/

26
frontend/.gitignore vendored
View file

@ -1,9 +1,17 @@
.DS_Store # Generated by Cargo
node_modules # will have compiled files and executables
/build debug/
/.svelte-kit target/
/package
.env # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
.env.* # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
!.env.example Cargo.lock
yarn-error.log
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
dist/
vendor/

View file

@ -1 +0,0 @@
v16.17.1

19
frontend/Cargo.toml Normal file
View file

@ -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"

View file

@ -1,8 +1,26 @@
FROM node:16-alpine FROM lukemathwalker/cargo-chef:latest-rust-1.65.0 as chef
WORKDIR /usr/src/frontend WORKDIR /usr/src/frontend
COPY ./package.json ./yarn.lock ./
RUN yarn install --frozen-lockfile FROM chef as planner
COPY . . WORKDIR /usr/src/frontend
RUN yarn build RUN mkdir src && touch src/main.rs
EXPOSE 3000 COPY ./Cargo.toml .
CMD ["yarn", "preview", "--host"] 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

View file

@ -0,0 +1,3 @@
query Ok {
ok
}

View file

@ -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!]!
}

13
frontend/index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
</body>
</html>

37
frontend/nginx.conf Normal file
View file

@ -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;
}
}
}

View file

@ -0,0 +1,4 @@
use const_format::concatcp;
pub const BASE: &str = "mentorenwahl_";
pub const TOKEN: &str = concatcp!(BASE, "token");

View file

@ -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();
}

View file

@ -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;

View file

@ -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::<graphql::queries::Ok, _>(
&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! {
<>
<nav>
<ul>
<li>
<Link<routes::Route> to={routes::Route::Home}>
<button>{ "Home" }</button>
</Link<routes::Route>>
</li>
<li>
<button onclick={loginout_onclick}>{ "Login/Logout" }</button>
</li>
</ul>
</nav>
<hr />
<main>
{ for props.children.iter() }
</main>
</>
}
}

View file

@ -0,0 +1 @@
pub mod main;

5
frontend/src/lib.rs Normal file
View file

@ -0,0 +1,5 @@
pub mod components;
pub mod cookie_names;
pub mod graphql;
pub mod layouts;
pub mod routes;

22
frontend/src/main.rs Normal file
View file

@ -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! {
<BrowserRouter>
<Switch<routes::Route> render={Switch::render(routes::switch)} />
</BrowserRouter>
}
}
fn main() {
wasm_logger::init(wasm_logger::Config::default());
yew::start_app::<App>();
}

View file

@ -0,0 +1,8 @@
use yew::prelude::*;
#[function_component(Home)]
pub fn home() -> Html {
html! {
<h1>{ "HOME!" }</h1>
}
}

View file

@ -0,0 +1,8 @@
use yew::prelude::*;
#[function_component(Login)]
pub fn login() -> Html {
html! {
<h1>{ "LOGIN!" }</h1>
}
}

View file

@ -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! { <layouts::main::Main><home::Home /></layouts::main::Main> },
Route::Login => html! { <layouts::main::Main><login::Login /></layouts::main::Main> },
Route::NotFound => {
html! { <layouts::main::Main><not_found::NotFound /></layouts::main::Main> }
}
}
}

View file

@ -0,0 +1,8 @@
use yew::prelude::*;
#[function_component(NotFound)]
pub fn not_found() -> Html {
html! {
<h1>{ "404" }</h1>
}
}

View file

@ -0,0 +1,12 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
yarn-error.log
Dockerfile
.gitignore
.dockerignore

9
frontend_old/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
yarn-error.log

22
frontend_old/Dockerfile Normal file
View file

@ -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" ]

View file

@ -12,7 +12,7 @@
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "next", "@sveltejs/adapter-auto": "^1.0.0-next.86",
"@sveltejs/kit": "next", "@sveltejs/kit": "next",
"@types/cookie": "^0.4.1", "@types/cookie": "^0.4.1",
"@typescript-eslint/eslint-plugin": "^5.10.1", "@typescript-eslint/eslint-plugin": "^5.10.1",

View file

@ -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 cookie from "cookie";
import * as cookieNames from "$lib/cookieNames"; import * as cookieNames from "$lib/cookieNames";
@ -8,7 +8,8 @@ export async function handle(input: {
opts?: ResolveOpts; opts?: ResolveOpts;
resolve(event: RequestEvent, opts?: ResolveOpts): Promise<Response>; resolve(event: RequestEvent, opts?: ResolveOpts): Promise<Response>;
}): Promise<Response> { }): Promise<Response> {
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]; const token: string | undefined = cookies[cookieNames.TOKEN];
input.event.locals = { input.event.locals = {

View file

@ -0,0 +1 @@
<h3>STUDENT!</h3>

View file

@ -0,0 +1 @@
<h3>TEACHER!</h3>

View file

@ -17,11 +17,12 @@
<script lang="ts"> <script lang="ts">
import { operationStore, query, gql, mutation } from "@urql/svelte"; import { operationStore, query, gql, mutation } from "@urql/svelte";
import * as svelteForms from "svelte-forms"; // import * as svelteForms from "svelte-forms";
import * as validators from "svelte-forms/validators"; // import * as validators from "svelte-forms/validators";
import type { User, Teacher, Student } from "$lib/graphql"; import type { User } from "$lib/graphql";
import { UserRole } from "$lib/graphql"; import { UserRole } from "$lib/graphql";
import StudentHome from "$lib/StudentHome.svelte";
interface Data { interface Data {
me: User; me: User;
@ -175,5 +176,6 @@
<p>Registrierung erfolgreich!</p> <p>Registrierung erfolgreich!</p>
{/if} {/if}
{/if} --> {/if} -->
<StudentHome />
{/if} {/if}
{/if} {/if}

View file

2085
frontend_old/test.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -27,5 +27,5 @@
"$lib/*": ["src/lib/*"] "$lib/*": ["src/lib/*"]
} }
}, },
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte", "src/lib/cookieNames.ts"]
} }

File diff suppressed because it is too large Load diff