diff --git a/README.md b/README.md
index 207b9da..5c69052 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,25 @@
# DevOps W07 In-Class Exercise Template
-This repository contains a full-stack application with a SvelteKit client and a Spring Boot server. It demonstrates modern web application architecture and DevOps practices.
+This repository contains a full-stack canteen application with a SvelteKit client, Spring Boot server, and LLM recommendation service. It demonstrates modern web application architecture and DevOps practices.
## Project Overview
This project includes:
-- **Client**: SvelteKit with TypeScript, TailwindCSS, and reusable UI components.
-- **Server**: Spring Boot Java application with RESTful APIs.
-- **DevOps**: Dockerized services, CI/CD pipelines, and production-ready deployment configurations.
+- **Client**: SvelteKit with TypeScript, TailwindCSS, and reusable UI components for browsing canteen meals.
+- **Server**: Spring Boot Java application with RESTful APIs, gRPC communication, and PostgreSQL integration.
+- **LLM Service**: Python FastAPI service for generating meal recommendations using AI.
+- **Database**: PostgreSQL for storing user preferences and application data.
+- **DevOps**: Dockerized services, CI/CD pipelines, Helm charts, and production-ready deployment configurations.
## Prerequisites
- Node.js (v22 or later)
- Java JDK 21+
+- Python 3.x
- Gradle
- Docker and Docker Compose
- Git
+- Kubernetes and Helm (for Kubernetes deployment)
## Setup Instructions
@@ -48,8 +52,25 @@ cd w07-solution
./gradlew build
```
+### LLM Service Setup
+
+1. Navigate to the `llm` directory:
+ ```bash
+ cd llm
+ ```
+2. Install dependencies:
+ ```bash
+ pip install -r requirements.txt
+ ```
+
## Running the Application
+### Start the Database
+
+```bash
+docker compose up database -d
+```
+
### Start the Client
```bash
@@ -66,6 +87,14 @@ cd server
```
The server API will be available at [http://localhost:8080](http://localhost:8080).
+### Start the LLM Service
+
+```bash
+cd llm
+python main.py
+```
+The LLM service will be available at [http://localhost:5000](http://localhost:5000).
+
## Development Workflow
### Client Development
@@ -73,14 +102,24 @@ The server API will be available at [http://localhost:8080](http://localhost:808
- Built with SvelteKit and TypeScript for a modern, reactive UI.
- TailwindCSS for styling.
- Components and routes are organized in the `src` directory.
+- Features meal browsing, favoriting, and user preferences.
### Server Development
- Built with Spring Boot for scalable and maintainable server services.
+- Includes gRPC communication with the LLM service.
+- PostgreSQL integration for user preferences storage.
+- RESTful APIs for canteen data and user management.
- Gradle is used for dependency management and building.
- Source code is in the `src/main/java` directory.
- Tests are in the `src/test/java` directory.
+### LLM Service Development
+
+- Built with FastAPI for AI-powered meal recommendations.
+- Integrates with external LLM APIs for generating personalized suggestions.
+- Source code is in the `llm` directory.
+
## Building for Production
### Client Build
@@ -103,13 +142,31 @@ The project includes Docker configurations for containerized deployment.
### Build and Run with Docker Compose
-1. Build and start the services:
+1. Build and start all services:
```bash
docker compose up --build
```
2. Access the application:
- Client: [http://localhost:3000](http://localhost:3000)
- Server: [http://localhost:8080](http://localhost:8080)
+ - LLM Service: [http://localhost:5000](http://localhost:5000)
+ - Database: PostgreSQL on port 5432
+
+## Kubernetes Deployment
+
+The project includes Helm charts for Kubernetes deployment.
+
+### Deploy with Helm
+
+1. Update the `tumid` value in [`helm/canteen-app/values.yaml`](helm/canteen-app/values.yaml):
+ ```yaml
+ tumid: your-tum-id
+ ```
+
+2. Install the Helm chart:
+ ```bash
+ helm install canteen ./helm/canteen-app
+ ```
## CI/CD Pipeline
@@ -122,18 +179,31 @@ The project includes GitHub Actions workflows for:
```
├── client/ # SvelteKit client
│ ├── src/ # Source code
-│ ├── public/ # Static assets
+│ ├── static/ # Static assets
│ └── package.json # Client dependencies
│
├── server/ # Spring Boot server
-│ ├── src/ # Source code
+│ ├── src/ # Source code including gRPC services
│ ├── build.gradle # Gradle build file
│ └── Dockerfile # Server Dockerfile
│
+├── llm/ # Python LLM service
+│ ├── main.py # FastAPI application
+│ ├── requirements.txt # Python dependencies
+│ └── Dockerfile # LLM service Dockerfile
+│
+├── helm/ # Kubernetes Helm charts
+│ └── canteen-app/ # Main application chart
+│
+├── docs/ # API documentation (Bruno collection)
├── compose.yml # Docker Compose for local development
└── .github/workflows/ # CI/CD workflows
```
+## API Documentation
+
+API documentation is available in the [`docs/CanteenApp Bruno`](docs/CanteenApp%20Bruno) directory as a Bruno collection for testing endpoints.
+
## License
This project is licensed under the MIT License.
\ No newline at end of file
diff --git a/client/src/lib/env.ts b/client/src/lib/env.ts
new file mode 100644
index 0000000..cda89cd
--- /dev/null
+++ b/client/src/lib/env.ts
@@ -0,0 +1,3 @@
+import { env } from '$env/dynamic/public';
+
+export let BaseURL = env.PUBLIC_API_URL || "http://localhost:8080/api";
\ No newline at end of file
diff --git a/client/src/lib/index.ts b/client/src/lib/index.ts
index 856f2b6..40800d8 100644
--- a/client/src/lib/index.ts
+++ b/client/src/lib/index.ts
@@ -1 +1,2 @@
// place files you want to import through the `$lib` alias in this folder.
+export { getCookie, setCookie } from './utils';
diff --git a/client/src/lib/types.ts b/client/src/lib/types.ts
index aef99a5..49ea78a 100644
--- a/client/src/lib/types.ts
+++ b/client/src/lib/types.ts
@@ -2,4 +2,10 @@ export type Meal = {
name: string;
dish_type: string;
labels: string[];
+ favorite: boolean;
};
+
+export type UserPreferences = {
+ username: string;
+ favoriteMeals: string[];
+};
\ No newline at end of file
diff --git a/client/src/lib/utils.ts b/client/src/lib/utils.ts
new file mode 100644
index 0000000..cb3ba37
--- /dev/null
+++ b/client/src/lib/utils.ts
@@ -0,0 +1,13 @@
+export function getCookie(name: string): string | null {
+ if (typeof document === 'undefined') return null;
+ return document.cookie
+ .split('; ')
+ .find(row => row.startsWith(name + '='))
+ ?.split('=')[1] || null;
+}
+
+export function setCookie(name: string, value: string, days: number) {
+ if (typeof document === 'undefined') return;
+ const expires = new Date(Date.now() + days * 864e5).toUTCString();
+ document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`;
+}
diff --git a/client/src/routes/+page.svelte b/client/src/routes/+page.svelte
index 35ca410..e248842 100644
--- a/client/src/routes/+page.svelte
+++ b/client/src/routes/+page.svelte
@@ -1,8 +1,6 @@
@@ -90,7 +25,7 @@
{:else}
{#each meals as meal}
-
+
{/each}
{/if}
diff --git a/client/src/routes/+page.ts b/client/src/routes/+page.ts
index 06493b6..56bb597 100644
--- a/client/src/routes/+page.ts
+++ b/client/src/routes/+page.ts
@@ -1,14 +1,40 @@
import type { PageLoad } from './$types';
-import { env } from '$env/dynamic/public';
+import { BaseURL} from '$lib/env';
+import type { Meal, UserPreferences } from '$lib/types';
+import { getCookie, setCookie } from '$lib';
export const ssr = false;
-let baseUrl = env.PUBLIC_API_URL || "http://localhost:8080/api";
-
+//get the username from the cookie
+let username: string | null = null;
export const load: PageLoad = async ({ fetch }) => {
- const res = await fetch(`${baseUrl}/mensa-garching/today`);
- const meals = await res.json();
+ username = getCookie('username');
- return { meals };
+ // Prompt for username if not found in cookie
+ if (!username) {
+ username = prompt('Please enter your username:');
+ if (username) {
+ setCookie('username', username, 30); // Store for 30 days
+ } else {
+ // Handle case where user cancels or enters empty string
+ username = 'anonymous';
+ }
+ }
+
+ const res = await fetch(`${BaseURL}/mensa-garching/today`);
+ const meals: Meal[] = await res.json();
+
+ const res2 = await fetch(`${BaseURL}/preferences/${username}`);
+ const preferences: UserPreferences = await res2.json();
+
+ console.log('Meals:', meals);
+ console.log('Preferences:', preferences);
+
+ // Set the boolean favorite property for each meal
+ meals.forEach((meal: any) => {
+ meal.favorite = preferences.favoriteMeals.includes(meal.name);
+ });
+
+ return { meals};
};
\ No newline at end of file
diff --git a/client/src/routes/FoodCard.svelte b/client/src/routes/FoodCard.svelte
index dd20dd8..c829411 100644
--- a/client/src/routes/FoodCard.svelte
+++ b/client/src/routes/FoodCard.svelte
@@ -1,55 +1,61 @@