Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/deploy-k8s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ jobs:
helm upgrade --install bytebite ./helm/bytebite \
--namespace team-bytebite \
--set client.image.tag=${{ steps.vars.outputs.image_tag }} \
--set server.image.tag=${{ steps.vars.outputs.image_tag }} \
--set apiGateway.image.tag=${{ steps.vars.outputs.image_tag }} \
--set userService.image.tag=${{ steps.vars.outputs.image_tag }} \
--set groceryService.image.tag=${{ steps.vars.outputs.image_tag }} \
--set genai.image.tag=${{ steps.vars.outputs.image_tag }} \
--set genai.openaiApiKey="${{ secrets.OPENAI_API_KEY }}" \
--atomic \
--timeout 5m
--atomic
29 changes: 23 additions & 6 deletions .github/workflows/test-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jobs:
test:
name: Run Java Tests
runs-on: ubuntu-latest
strategy:
matrix:
service: [api-gateway, user-service, grocery-service]
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -27,15 +30,29 @@ jobs:
cache: maven

- name: Run tests with Maven
run: cd server && ./mvnw -B test
run: cd server/${{ matrix.service }} && ./mvnw -B test

build:
name: Build ${{ matrix.service }}
name: Build ${{ matrix.image }}
needs: test
runs-on: ubuntu-latest
strategy:
matrix:
service: [client, server, gen-ai]
include:
- image: client
context: ./client
- image: api-gateway
context: ./server/api-gateway
- image: user-service
context: ./server/user-service
- image: grocery-service
context: ./server/grocery-service
- image: gen-ai
context: ./gen-ai
- image: user-db
context: ./databases/user-db
- image: grocery-db
context: ./databases/grocery-db
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -57,7 +74,7 @@ jobs:
uses: docker/metadata-action@v5
with:
# metadata-action lowercases the image name for ghcr.io automatically.
images: ghcr.io/${{ github.repository }}/${{ matrix.service }}
images: ghcr.io/${{ github.repository }}/${{ matrix.image }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
Expand All @@ -66,8 +83,8 @@ jobs:
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ./${{ matrix.service }}
file: ./${{ matrix.service }}/Dockerfile
context: ${{ matrix.context }}
file: ${{ matrix.context }}/Dockerfile
push: ${{ github.ref == 'refs/heads/main' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Expand Down
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ client/node_modules/
client/dist/
client/.vite/

# ── server (Java Spring Boot) ─────────────────────────────────
server/target/
server/.mvn/wrapper/maven-wrapper.jar
# ── server (Java microservices) ───────────────────────────────
server/*/target/
server/*/.mvn/wrapper/maven-wrapper.jar
*.class
*.jar
*.war
Expand Down
68 changes: 60 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,28 @@ Unlike traditional apps that rely on rigid "If/Then" logic or specific formattin

```
team-bytebite/
├── client/ # React + Vite frontend
├── server/ # Java Spring Boot backend API
└── gen-ai/ # Python FastAPI service for AI-based recipe and shopping list generation
├── client/ # React + Vite frontend
├── gen-ai/ # Python FastAPI AI generation service
├── server/ # Java Spring Boot microservices
│ ├── api-gateway/ # Public entrypoint — routes requests to backend services
│ ├── user-service/ # User domain service
│ └── grocery-service/ # Grocery and recipe domain service
└── databases/ # Database image definitions and init schemas
```

## Services

### `client` — React / Vite
The user-facing web application. Provides a dish name input and displays the generated shopping list. Communicates with the backend via REST.

### `server` — Java Spring Boot
The core backend API. Manages users, recipes, and shopping lists. Orchestrates requests between the client and the gen-ai service.
### `api-gateway` — Java Spring Boot
The public backend entrypoint. Receives frontend API requests and forwards them to the owning backend service.

### `user-service` — Java Spring Boot
Owns user-related data and connects to the user database.

### `grocery-service` — Java Spring Boot
Owns recipes, grocery lists, and grocery items. Connects to the grocery database and calls the gen-ai service when ingredient generation is needed.

### `gen-ai` — Python FastAPI
The AI generation service. Receives a dish name from the server and returns a shopping list with all required ingredients using LLM integrations.
Expand All @@ -71,13 +81,25 @@ pip install -r requirements.txt
uvicorn main:app --reload
```

**2. Server** (port 8080)
**2. User Service** (port 8083)
```bash
cd server
cd server/user-service
./mvnw spring-boot:run
```

**3. Client** (port 5173)
**3. Grocery Service** (port 8082)
```bash
cd server/grocery-service
./mvnw spring-boot:run
```

**4. API Gateway** (port 8080)
```bash
cd server/api-gateway
./mvnw spring-boot:run
```

**5. Client** (port 5173)
```bash
cd client
npm install
Expand All @@ -100,3 +122,33 @@ docker compose up --build
Open http://localhost:8081

Drop `--build` on subsequent starts if nothing has changed. To stop: `docker compose down`.

---

### Kubernetes

#### Local Kubernetes Deployment
...


#### Kubernetes Deployment to the AET cluster

Prerequisite: The `team-bytebite` namespace must exist in the cluster.

Deployment is automated via GitHub Actions:

- **Automatic:** every push to `main` triggers the build workflow, which triggers the deploy workflow on success.
- **Manual:** go to Actions → *Deploy to Kubernetes* → *Run workflow* to manually start the deploy workflow.


Alternatively, you can do manual deployment with Helm:
(Requires `helm` and a valid kubeconfig)

```bash
helm upgrade --install bytebite ./helm/bytebite \
--namespace team-bytebite \
--set genai.openaiApiKey="sk-..." \
--atomic
```

The app is available at https://team-bytebite.stud.k8s.aet.cit.tum.de
4 changes: 2 additions & 2 deletions client/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ server {
try_files $uri $uri/ /index.html;
}

location /generate {
proxy_pass http://server:8080;
location /api/ {
proxy_pass http://api-gateway:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Expand Down
21 changes: 12 additions & 9 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
services:
user-db:
image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/user-db:${IMAGE_TAG:-latest}
build:
context: ./docker/user-db
context: ./databases/user-db
container_name: bytebite-user-db
environment:
POSTGRES_DB: ${USER_DB_NAME:-bytebite_user}
Expand All @@ -16,8 +17,9 @@ services:
retries: 5

grocery-db:
image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/grocery-db:${IMAGE_TAG:-latest}
build:
context: ./docker/grocery-db
context: ./databases/grocery-db
container_name: bytebite-grocery-db
environment:
POSTGRES_DB: ${GROCERY_DB_NAME:-bytebite_grocery}
Expand All @@ -37,15 +39,16 @@ services:
build:
context: ./gen-ai
container_name: bytebite-gen-ai
ports:
- "8000:8000"
expose:
- "8000"
environment:
OPENAI_API_KEY: ${OPENAI_API_KEY}


user-service:
image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/user-service:${IMAGE_TAG:-latest}
build:
context: ./user-service
context: ./server/user-service
container_name: bytebite-user-service
expose:
- "8080"
Expand All @@ -59,8 +62,9 @@ services:
condition: service_healthy

grocery-service:
image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/grocery-service:${IMAGE_TAG:-latest}
build:
context: ./grocery-service
context: ./server/grocery-service
container_name: bytebite-grocery-service
expose:
- "8080"
Expand All @@ -77,8 +81,9 @@ services:
condition: service_started

api-gateway:
image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/api-gateway:${IMAGE_TAG:-latest}
build:
context: ./api-gateway
context: ./server/api-gateway
container_name: bytebite-api-gateway
ports:
- "8080:8080"
Expand All @@ -91,8 +96,6 @@ services:
condition: service_started
grocery-service:
condition: service_started
gen-ai:
condition: service_started

client:
image: ${REGISTRY:-ghcr.io/aet-devops26/team-bytebite}/client:${IMAGE_TAG:-latest}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 4 additions & 2 deletions helm/bytebite/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ByteBite Helm Chart

Deploys the ByteBite application (client, server, gen-ai) to Kubernetes.
Deploys the ByteBite application (client, api-gateway, user-service, grocery-service, gen-ai) to Kubernetes.

## Prerequisites

Expand Down Expand Up @@ -29,5 +29,7 @@ helm uninstall bytebite --namespace team-bytebite
| `ingress.tls` | Enable TLS via cert-manager | `true` |
| `genai.openaiApiKey` | OpenAI API key for the gen-ai service | `""` |
| `client.image.tag` | Client image tag | `latest` |
| `server.image.tag` | Server image tag | `latest` |
| `apiGateway.image.tag` | API gateway image tag | `latest` |
| `userService.image.tag` | User service image tag | `latest` |
| `groceryService.image.tag` | Grocery service image tag | `latest` |
| `genai.image.tag` | Gen-AI image tag | `latest` |
33 changes: 33 additions & 0 deletions helm/bytebite/templates/api-gateway-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: bytebite-api-gateway
namespace: {{ .Values.namespace }}
spec:
replicas: {{ .Values.apiGateway.replicaCount }}
selector:
matchLabels:
app: bytebite-api-gateway-selector
template:
metadata:
labels:
app: bytebite-api-gateway-selector
spec:
containers:
- name: api-gateway
image: "{{ .Values.apiGateway.image.repository }}:{{ .Values.apiGateway.image.tag }}"
imagePullPolicy: {{ .Values.apiGateway.image.pullPolicy }}
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "200m"
memory: "256Mi"
ports:
- containerPort: {{ .Values.apiGateway.service.targetPort }}
env:
- name: GROCERY_SERVICE_BASE_URL
value: "http://grocery-service:{{ .Values.groceryService.service.port }}"
- name: USER_SERVICE_BASE_URL
value: "http://user-service:{{ .Values.userService.service.port }}"
13 changes: 13 additions & 0 deletions helm/bytebite/templates/api-gateway-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: api-gateway-service
namespace: {{ .Values.namespace }}
spec:
selector:
app: bytebite-api-gateway-selector
ports:
- port: {{ .Values.apiGateway.service.port }}
targetPort: {{ .Values.apiGateway.service.targetPort }}
protocol: TCP
type: {{ .Values.apiGateway.service.type }}
6 changes: 6 additions & 0 deletions helm/bytebite/templates/client-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ data:
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api-gateway-service:{{ .Values.apiGateway.service.port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: bytebite-server
name: bytebite-grocery-service
namespace: {{ .Values.namespace }}
spec:
replicas: {{ .Values.server.replicaCount }}
replicas: {{ .Values.groceryService.replicaCount }}
selector:
matchLabels:
app: bytebite-server-selector
app: bytebite-grocery-service-selector
template:
metadata:
labels:
app: bytebite-server-selector
app: bytebite-grocery-service-selector
spec:
containers:
- name: server
image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag }}"
imagePullPolicy: {{ .Values.server.image.pullPolicy }}
- name: grocery-service
image: "{{ .Values.groceryService.image.repository }}:{{ .Values.groceryService.image.tag }}"
imagePullPolicy: {{ .Values.groceryService.image.pullPolicy }}
resources:
limits:
cpu: "500m"
Expand All @@ -25,7 +25,7 @@ spec:
cpu: "200m"
memory: "256Mi"
ports:
- containerPort: {{ .Values.server.service.targetPort }}
- containerPort: {{ .Values.groceryService.service.targetPort }}
env:
- name: GENAI_BASE_URL
value: "http://genai-service:{{ .Values.genai.service.port }}"
13 changes: 13 additions & 0 deletions helm/bytebite/templates/grocery-service-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: grocery-service
namespace: {{ .Values.namespace }}
spec:
selector:
app: bytebite-grocery-service-selector
ports:
- port: {{ .Values.groceryService.service.port }}
targetPort: {{ .Values.groceryService.service.targetPort }}
protocol: TCP
type: {{ .Values.groceryService.service.type }}
Loading
Loading