# Ticketsystem A ticket system with backend and frontend components. ## Components - **[Backend]** - The server-side API and business logic - **[Frontend]** - The client-side user interface ## Usage ### Prerequisite > [!IMPORTANT] > Before compiling the programm you have to install the rust toolchain. > For a guide to do this, visit: A instance of a postgresql has to be accessible to the backend. Place the connection details in a .env file into the variable `DATABASE_URL` To setup the tables you either can create them manually by following the sheme specified in `backend/migrations` or apply them with sqlx To install sqlx run `cargo install sqlx` and then in the `backend` directory run `sqlx migrate run` to create the tables ### Environment The .env file has to be in the root directory of the project or in the same directory as the executable Keys: `DATABASE_URL`: Specifying the url and connection details for the database `TOKEN_SECRET`: The JWT token secret, can theoretically be anything but is more secure when generated with a tool, e.g: `ORIGIN`: The origin of the frontend, used for CORS rules `BACKEND_PORT`: The port which the backend should use to run on ### Backend The backend can either be run via `cargo run --release` or `cargo build --release` using the correct target architecture, e.g. 'x86_64-unknown-linux-gnu', the executable will be placed in the `target/release` directory and can then be run via any method ### Frontend The HTML code for the frontend can be generated by using `trunk build`. The resulting files will end up in the `frontend/dist` directory and can be served over any webserver supporting wasm > [!NOTE] > To install trunk run `cargo install trunk` > [!IMPORTANT] > Requests from the frontend to /api/* have to be proxied to the Backend > Example with nginx and frontend running at localhost:8000 and backend at localhost:9000 : > ```nginx > location /api/ { > proxy_pass http://localhost:9000/api; > } > ``` ## Diagrams ### Class Diagramm ```mermaid classDiagram direction RL namespace Backend { class cookie_Error { +status: &'static str +message: String } class TicketResponse { +id: i32 +category: String +betreff: String +description: String +room: i16 +status: String +date: chrono::DateTime~chrono::Utc~ +user_id: i16 +user_first_name: String +user_last_name: String } class User { +id: i16 +last_name: String +first_name: String +username: String +is_admin: bool +pwd: String } class TicketCreateScheme { +category: String +betreff: String +description: String +room: i16 } class TicketUpdateScheme { +status: String } class UserUpdateScheme { +id: i16 +first_name: String +last_name: String +username: String +make_admin: bool +new_pwd: String } class UserCreateScheme { +first_name: String +last_name: String +username: String +is_admin: bool +pwd: String } class LoginScheme { +username: String +pwd: String } class FilteredUser { +id: i16 +first_name: String +last_name: String +username: String +is_admin: bool } class Claims { +sub: String +issued: usize +expires: usize } class AppState { -db: PgPool -env: Env } class Env { +db_url: String +token_secret: String +origin: String +backend_port: String +load() Env } } namespace Frontend { class TicketCreateScheme { +category: String +betreff: String +description: String +room: i16 } class TicketUpdateScheme { +status: String } class Ticket { +id: i32 +category: String +betreff: String +description: String +room: i16 +status: String +date: chrono::DateTime~chrono::Utc~ +user_id: i16 +user_first_name: String +user_last_name: String } class TicketProps { +id: i32 } class ActiveUser { +id: Option~i16~ +is_admin: bool } class ApiError { -message: String -_status: String } class SidebarExpandState { +ticket_open: bool +users_open: bool } class Default { +default() Self } class SidebarState { +expand: SidebarExpandState +set_tickets_open: Callback~bool~ +toggle_tickets: Callback~()~ +set_users_open: Callback~bool~ +toggle_users: Callback~()~ +new(expand:SidebarExpandState, set_tickets_open:Callback~bool~, toggle_tickets:Callback~()~, set_users_open:Callback~bool~, toggle_users:Callback~()~) Self } class SidebarProps { +children: Children } class TicketPartial { -date: DateTime~Utc~ -room: i16 -user_id: i16 } class UserPartial { -id: i16 -first_name: String -last_name: String } class RoomTotalsProps { -tickets: Vec~TicketPartial~ } class UserTotalProps { -users: Vec~UserPartial~ -tickets: Vec~TicketPartial~ } class AdminSetupScheme { +first_name: String +last_name: String +username: String +pwd: String } class UserCreateScheme { +first_name: String +last_name: String +username: String +is_admin: bool +pwd: String } class LoginScheme { +username: String +pwd: String } class UserUpdateScheme { +id: i16 +first_name: String +last_name: String +username: String +make_admin: bool +new_pwd: String } class FilteredUser { +id: i16 +first_name: String +last_name: String +username: String +is_admin: bool } class UserProps { +id: i16 } class ApiError { -message: String -_status: String } class AuthState { +is_authenticated: Option~bool~ +is_admin: Option~bool~ } class ProtectedRouteProps { +children: Children +admin_page: bool } class SidebarShellProps { +children: Children } class AdminCheckWrapperProps { +children: Children } } AppState --> Env Env ..> Env SidebarState --> SidebarExpandState RoomTotalsProps --> TicketPartial UserTotalProps --> UserPartial UserTotalProps --> TicketPartial ``` ### Sequence Diagrams #### 1. System Initialization & Administrator Setup ```mermaid sequenceDiagram autonumber actor Admin as Initial Administrator participant FE as Frontend (Yew) participant BE as Backend (Axum) participant DB@{"type": "database", "alias": "Database"} Note over Admin, DB: System Initialization Flow FE->>BE: GET /api/check-admin BE->>DB: SELECT COUNT(*) FROM users WHERE is_admin = true DB-->>BE: 0 (No admin found) BE-->>FE: HTTP 200 OK {"exists": false} FE-->>Admin: Render Admin Setup Page Admin->>FE: Input username, password, first/last name FE->>BE: POST /api/setup-admin {username, pwd, ...} Note over BE: Hash password using Argon2 BE->>DB: INSERT INTO users (username, pwd, is_admin, ...) DB-->>BE: Success BE-->>FE: HTTP 200 OK {"status": "success"} FE-->>Admin: Redirect to Login Page ``` #### 2. User Authentication Flow (Login) ```mermaid sequenceDiagram autonumber actor User participant FE as Frontend (Yew) participant BE as Backend (Axum) participant DB as Database@{"type": "database"} Note over User, DB: Authentication & Cookie Session Setup User->>FE: Enter username & password FE->>BE: POST /api/login {username, pwd} BE->>DB: SELECT * FROM users WHERE username = $1 DB-->>BE: Return user record with password hash Note over BE: Verify password using Argon2 alt Password Valid Note over BE: Generate JWT token containing claims (sub: user_id) Note over BE: Build HttpOnly, Secure, Lax cookie 'token' BE-->>FE: HTTP 200 OK {"status": "success", "token": "...", "user": {...}} Note over BE,FE: Header: Set-Cookie: token=...#59; Path=/#59; HttpOnly#59; SameSite=Lax Note over FE: Save auth state to global context FE-->>User: Redirect to Dashboard / Home else Password Invalid BE-->>FE: HTTP 400 Bad Request {"status": "error", "message": "Invalid password"} FE-->>User: Display error message end ``` #### 3. Ticket Lifecycle Flow ```mermaid sequenceDiagram autonumber actor User as Authenticated User actor Admin as Administrator participant FE as Frontend (Yew) participant BE as Backend (Axum) participant DB as Database@{"type": "database"} Note over User, DB: Ticket Creation Flow (Protected Route) User->>FE: Fill out ticket form & submit FE->>BE: POST /api/tickets/create {category, betreff, description, room} (Includes 'token' cookie) Note over BE: validate_token middleware decodes & verifies JWT BE->>DB: INSERT INTO tickets (category, description, betreff, room, user_id) DB-->>BE: Success BE-->>FE: HTTP 200 OK {"status": "success"} FE-->>User: Clear form & display success notification Note over Admin, DB: Ticket Review & Resolution (Admin Only Route) Admin->>FE: View Ticket Board FE->>BE: GET /api/tickets (Includes 'token' cookie) Note over BE: validate_token middleware checks JWT BE->>DB: SELECT tickets JOIN users ... DB-->>BE: Return list of tickets BE-->>FE: HTTP 200 OK [tickets] FE-->>Admin: Render Ticket List Admin->>FE: Click "Resolve" on ticket FE->>BE: PATCH /api/tickets/{id} {"status": "Resolved"} (Includes 'token' cookie) Note over BE: validate_admin middleware verifies token & checks is_admin = true BE->>DB: UPDATE tickets SET status = $1 WHERE id = $2 DB-->>BE: Success BE-->>FE: HTTP 200 OK {"status": "success"} FE-->>Admin: Update ticket status in UI ``` ## Usage of AI Github Copilot CLI was used with the model Claude Haiku 4.5 to generate most of the documentation Google Antigravity generated the Sequence Diagrams ### Prompt Generate comments for cargo doc describing the indivilual components and create links to relevant structs, functions etc. Generate a sequence diagramm in @[README.md] behind the class diagramms ### Output The comments with `///` or `//!` I've gone over most of it and modified it to my needs and opinions The sequence Diagrams above