Documentation
This commit is contained in:
@@ -4,5 +4,382 @@ A ticket system with backend and frontend components.
|
||||
|
||||
## Components
|
||||
|
||||
- **[Backend](../backend/index.html)** - The server-side API and business logic
|
||||
- **[Frontend](../frontend/index.html)** - The client-side user interface
|
||||
- **[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: <https://rust-lang.org/tools/install/>
|
||||
|
||||
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: <https://jwtsecrets.com/>
|
||||
`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
|
||||
#### Backend
|
||||
```mermaid
|
||||
classDiagram
|
||||
class 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
|
||||
}
|
||||
AppState --> Env
|
||||
Env ..> Env
|
||||
```
|
||||
|
||||
#### Frontend
|
||||
```mermaid
|
||||
classDiagram
|
||||
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 SidebarComponentProps {
|
||||
+is_open: bool
|
||||
+on_close: Callback~()~
|
||||
}
|
||||
class AdminCheckWrapperProps {
|
||||
+children: Children
|
||||
}
|
||||
SidebarState --> SidebarExpandState
|
||||
RoomTotalsProps --> TicketPartial
|
||||
UserTotalProps --> UserPartial
|
||||
UserTotalProps --> TicketPartial
|
||||
SidebarShellProps ..> SidebarComponentProps
|
||||
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
Note over Admin, DB: Ticket Archiving Flow (Admin Only Route)
|
||||
Admin->>FE: Open Archived Tickets page
|
||||
FE->>BE: GET /api/tickets/archive (Includes 'token' cookie)
|
||||
Note over BE: validate_admin middleware verifies token & admin role
|
||||
BE->>DB: SELECT * FROM tickets WHERE status = 'Archived'
|
||||
DB-->>BE: Return archived tickets
|
||||
BE-->>FE: HTTP 200 OK [Archived Tickets]
|
||||
FE-->>Admin: Render historical archive board
|
||||
```
|
||||
|
||||
## 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 it and modified it to my needs and opinions
|
||||
|
||||
The general structure sequence diagrams above. I've modified and fixed any errors and discrepancys
|
||||
|
||||
Reference in New Issue
Block a user