added docker setup
This commit is contained in:
parent
57ae9c3320
commit
be5ce63add
12 changed files with 509 additions and 20 deletions
185
README-Docker.md
Normal file
185
README-Docker.md
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
# Tschunk Order - Docker Deployment
|
||||||
|
|
||||||
|
Diese Anleitung beschreibt, wie Sie die Tschunk Order Anwendung mit Docker Compose deployen können.
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- Docker
|
||||||
|
- Docker Compose
|
||||||
|
|
||||||
|
## Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
tschunkorder/
|
||||||
|
├── docker-compose.yml # Haupt-Konfiguration
|
||||||
|
├── frontend/
|
||||||
|
│ ├── Dockerfile # Frontend Container
|
||||||
|
│ ├── nginx.conf # Frontend Nginx-Konfiguration
|
||||||
|
│ └── .dockerignore
|
||||||
|
├── backend/
|
||||||
|
│ ├── Dockerfile # Backend Container
|
||||||
|
│ └── .dockerignore
|
||||||
|
└── nginx/
|
||||||
|
├── nginx.conf # Haupt-Nginx-Konfiguration
|
||||||
|
└── conf.d/
|
||||||
|
└── default.conf # Reverse Proxy-Konfiguration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
### 1. Frontend (Vue.js + Vite)
|
||||||
|
- **Port**: Intern 80, extern über Nginx
|
||||||
|
- **Technologie**: Vue.js 3 mit Vite
|
||||||
|
- **Container**: `tschunk-frontend`
|
||||||
|
|
||||||
|
### 2. Backend (FastAPI)
|
||||||
|
- **Port**: Intern 8000, extern 8000 (optional)
|
||||||
|
- **Technologie**: FastAPI mit Uvicorn
|
||||||
|
- **Container**: `tschunk-backend`
|
||||||
|
|
||||||
|
### 3. Nginx Reverse Proxy
|
||||||
|
- **Port**: 80 (HTTP), 443 (HTTPS, optional)
|
||||||
|
- **Funktion**: Reverse Proxy für Frontend und Backend
|
||||||
|
- **Container**: `tschunk-nginx`
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### 1. Anwendung starten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle Services bauen und starten
|
||||||
|
docker-compose up --build
|
||||||
|
|
||||||
|
# Im Hintergrund starten
|
||||||
|
docker-compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Anwendung stoppen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Services stoppen
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Services und Volumes löschen
|
||||||
|
docker-compose down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Logs anzeigen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle Logs
|
||||||
|
docker-compose logs
|
||||||
|
|
||||||
|
# Spezifischer Service
|
||||||
|
docker-compose logs frontend
|
||||||
|
docker-compose logs backend
|
||||||
|
docker-compose logs nginx
|
||||||
|
|
||||||
|
# Logs folgen
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zugriff auf die Anwendung
|
||||||
|
|
||||||
|
- **Frontend**: http://localhost/ (oder IP-Adresse)
|
||||||
|
- **Backend API**: http://localhost/api/ (oder IP-Adresse)
|
||||||
|
- **WebSocket**: ws://localhost/api/ws (oder IP-Adresse)
|
||||||
|
- **Health Check**: http://localhost/health (oder IP-Adresse)
|
||||||
|
|
||||||
|
## API-Endpunkte
|
||||||
|
|
||||||
|
- `GET /api/orders` - Alle Bestellungen abrufen
|
||||||
|
- `POST /api/orders` - Neue Bestellung erstellen
|
||||||
|
- `DELETE /api/orders/{id}` - Bestellung löschen
|
||||||
|
- `GET /api/drinks` - Verfügbare Getränke
|
||||||
|
|
||||||
|
## WebSocket
|
||||||
|
|
||||||
|
Der WebSocket-Endpunkt ist unter `ws://localhost/api/ws` verfügbar und unterstützt:
|
||||||
|
- Echtzeit-Updates bei neuen/gelöschten Bestellungen
|
||||||
|
- Ping/Pong für Verbindungsüberwachung
|
||||||
|
- Bestellungen erstellen/löschen über WebSocket
|
||||||
|
|
||||||
|
## Konfiguration
|
||||||
|
|
||||||
|
### Umgebungsvariablen
|
||||||
|
|
||||||
|
#### Frontend
|
||||||
|
- `VITE_API_BASE_URL`: Backend API URL (Standard: http://backend:8000)
|
||||||
|
|
||||||
|
#### Backend
|
||||||
|
- `PYTHONUNBUFFERED`: Python Output nicht puffern (Standard: 1)
|
||||||
|
|
||||||
|
### Nginx-Konfiguration
|
||||||
|
|
||||||
|
Die Nginx-Konfiguration befindet sich in:
|
||||||
|
- `nginx/nginx.conf` - Haupt-Konfiguration
|
||||||
|
- `nginx/conf.d/default.conf` - Server-Konfiguration
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
- Rate Limiting für API und WebSocket
|
||||||
|
- Gzip-Kompression
|
||||||
|
- Security Headers
|
||||||
|
- WebSocket-Support
|
||||||
|
- SPA-Routing für Frontend
|
||||||
|
- Vereinfachte Proxy-Logik (alle API-Endpunkte unter /api)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Container startet nicht
|
||||||
|
```bash
|
||||||
|
# Logs prüfen
|
||||||
|
docker-compose logs [service-name]
|
||||||
|
|
||||||
|
# Container neu bauen
|
||||||
|
docker-compose build --no-cache [service-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Netzwerk-Probleme
|
||||||
|
```bash
|
||||||
|
# Netzwerk-Status prüfen
|
||||||
|
docker network ls
|
||||||
|
docker network inspect tschunkorder_tschunk-network
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port-Konflikte
|
||||||
|
Falls Port 80 bereits belegt ist, ändern Sie in `docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
nginx:
|
||||||
|
ports:
|
||||||
|
- "8080:80" # Statt "80:80"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Entwicklung
|
||||||
|
|
||||||
|
### Hot Reload (Entwicklung)
|
||||||
|
Für die Entwicklung können Sie die Services einzeln starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend mit Hot Reload
|
||||||
|
cd backend
|
||||||
|
uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
||||||
|
|
||||||
|
# Frontend mit Hot Reload
|
||||||
|
cd frontend
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Datenbank-Persistenz
|
||||||
|
Die aktuelle Implementierung verwendet eine In-Memory-Datenbank. Für Produktionsumgebungen sollten Sie eine persistente Datenbank hinzufügen.
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
- Alle Services laufen in isolierten Containern
|
||||||
|
- Nginx bietet Rate Limiting und Security Headers
|
||||||
|
- Backend läuft als non-root User
|
||||||
|
- WebSocket-Verbindungen sind begrenzt
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Health Checks
|
||||||
|
- Backend: Automatischer Health Check alle 30 Sekunden
|
||||||
|
- Nginx: `/health` Endpunkt
|
||||||
|
|
||||||
|
### Logs
|
||||||
|
Alle Services loggen in stdout/stderr und können über `docker-compose logs` abgerufen werden.
|
30
backend/.dockerignore
Normal file
30
backend/.dockerignore
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
env
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
.tox
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.log
|
||||||
|
.git
|
||||||
|
.mypy_cache
|
||||||
|
.pytest_cache
|
||||||
|
.hypothesis
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env/
|
||||||
|
.venv/
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
32
backend/Dockerfile
Normal file
32
backend/Dockerfile
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install system dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy requirements first for better caching
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN useradd --create-home --shell /bin/bash app && chown -R app:app /app
|
||||||
|
USER app
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
||||||
|
CMD python -c "import requests; requests.get('http://localhost:8000/orders')" || exit 1
|
||||||
|
|
||||||
|
# Start the application
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
@ -1,4 +1,4 @@
|
||||||
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
|
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, APIRouter
|
||||||
from typing import List
|
from typing import List
|
||||||
from models import CreateOrderRequest, Order, DrinkType, MateType
|
from models import CreateOrderRequest, Order, DrinkType, MateType
|
||||||
from database import db
|
from database import db
|
||||||
|
@ -11,11 +11,14 @@ app = FastAPI(
|
||||||
version="1.0.0"
|
version="1.0.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# API Router mit /api Prefix
|
||||||
|
api_router = APIRouter(prefix="/api")
|
||||||
|
|
||||||
# Datenbank-Referenz im WebSocketManager setzen
|
# Datenbank-Referenz im WebSocketManager setzen
|
||||||
websocket_manager.set_database(db)
|
websocket_manager.set_database(db)
|
||||||
|
|
||||||
|
|
||||||
@app.websocket("/ws")
|
@app.websocket("/api/ws")
|
||||||
async def websocket_endpoint(websocket: WebSocket):
|
async def websocket_endpoint(websocket: WebSocket):
|
||||||
"""
|
"""
|
||||||
WebSocket-Endpunkt für Echtzeit-Updates der Bestellungen.
|
WebSocket-Endpunkt für Echtzeit-Updates der Bestellungen.
|
||||||
|
@ -74,7 +77,7 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||||
websocket_manager.disconnect(websocket)
|
websocket_manager.disconnect(websocket)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/orders", response_model=Order, status_code=201)
|
@api_router.post("/orders", response_model=Order, status_code=201)
|
||||||
async def create_order(order_request: CreateOrderRequest):
|
async def create_order(order_request: CreateOrderRequest):
|
||||||
"""
|
"""
|
||||||
Erstellt eine neue Bestellung.
|
Erstellt eine neue Bestellung.
|
||||||
|
@ -90,7 +93,7 @@ async def create_order(order_request: CreateOrderRequest):
|
||||||
return order
|
return order
|
||||||
|
|
||||||
|
|
||||||
@app.get("/orders", response_model=List[Order])
|
@api_router.get("/orders", response_model=List[Order])
|
||||||
async def get_all_orders():
|
async def get_all_orders():
|
||||||
"""
|
"""
|
||||||
Gibt alle Bestellungen zurück.
|
Gibt alle Bestellungen zurück.
|
||||||
|
@ -99,7 +102,7 @@ async def get_all_orders():
|
||||||
return orders
|
return orders
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/orders/{order_id}")
|
@api_router.delete("/orders/{order_id}")
|
||||||
async def delete_order(order_id: str):
|
async def delete_order(order_id: str):
|
||||||
"""
|
"""
|
||||||
Löscht eine spezifische Bestellung.
|
Löscht eine spezifische Bestellung.
|
||||||
|
@ -115,7 +118,7 @@ async def delete_order(order_id: str):
|
||||||
return {"message": f"Bestellung {order_id} wurde erfolgreich gelöscht"}
|
return {"message": f"Bestellung {order_id} wurde erfolgreich gelöscht"}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/drinks")
|
@api_router.get("/drinks")
|
||||||
async def get_available_drinks():
|
async def get_available_drinks():
|
||||||
"""
|
"""
|
||||||
Gibt alle verfügbaren Getränketypen zurück.
|
Gibt alle verfügbaren Getränketypen zurück.
|
||||||
|
@ -126,6 +129,10 @@ async def get_available_drinks():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# API Router zur App hinzufügen
|
||||||
|
app.include_router(api_router)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
43
docker-compose.yml
Normal file
43
docker-compose.yml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: tschunk-frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- tschunk-network
|
||||||
|
environment:
|
||||||
|
- VITE_API_BASE_URL=http://backend:8000
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: ./backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: tschunk-backend
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- tschunk-network
|
||||||
|
environment:
|
||||||
|
- PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: tschunk-nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
||||||
|
networks:
|
||||||
|
- tschunk-network
|
||||||
|
depends_on:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
|
||||||
|
networks:
|
||||||
|
tschunk-network:
|
||||||
|
driver: bridge
|
12
frontend/.dockerignore
Normal file
12
frontend/.dockerignore
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
.env
|
||||||
|
.nyc_output
|
||||||
|
coverage
|
||||||
|
.DS_Store
|
||||||
|
.vscode
|
||||||
|
*.log
|
||||||
|
dist
|
31
frontend/Dockerfile
Normal file
31
frontend/Dockerfile
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Build stage
|
||||||
|
FROM node:20-alpine as build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install all dependencies (including devDependencies for build)
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Copy built files from build stage
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Copy custom nginx configuration for SPA
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Start nginx
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
31
frontend/nginx.conf
Normal file
31
frontend/nginx.conf
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_proxied expired no-cache no-store private auth;
|
||||||
|
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript;
|
||||||
|
|
||||||
|
# Handle SPA routing
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache static assets
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||||
|
}
|
|
@ -25,8 +25,10 @@ export const useOrderStore = defineStore('orders', () => {
|
||||||
const connectWebSocket = () => {
|
const connectWebSocket = () => {
|
||||||
try {
|
try {
|
||||||
connectionStatus.value = 'connecting'
|
connectionStatus.value = 'connecting'
|
||||||
// WebSocket-Verbindung zum Backend
|
// WebSocket-Verbindung zum Backend über relative URL
|
||||||
wsConnection.value = new WebSocket('ws://localhost:8000/ws')
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||||
|
const wsUrl = `${protocol}//${window.location.host}/api/ws`
|
||||||
|
wsConnection.value = new WebSocket(wsUrl)
|
||||||
|
|
||||||
wsConnection.value.onopen = () => {
|
wsConnection.value.onopen = () => {
|
||||||
console.log('WebSocket verbunden')
|
console.log('WebSocket verbunden')
|
||||||
|
@ -168,7 +170,7 @@ export const useOrderStore = defineStore('orders', () => {
|
||||||
|
|
||||||
const createOrderHttp = async (orderRequest: CreateOrderRequest) => {
|
const createOrderHttp = async (orderRequest: CreateOrderRequest) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('http://localhost:8000/orders', {
|
const response = await fetch('/api/orders', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
@ -233,7 +235,7 @@ export const useOrderStore = defineStore('orders', () => {
|
||||||
|
|
||||||
const deleteOrderHttp = async (orderId: string) => {
|
const deleteOrderHttp = async (orderId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`http://localhost:8000/orders/${orderId}`, {
|
const response = await fetch(`/api/orders/${orderId}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ import { useRouter } from 'vue-router'
|
||||||
import { useOrderStore } from '@/stores/orderStore'
|
import { useOrderStore } from '@/stores/orderStore'
|
||||||
import { DrinkType, MateType, type Drink } from '@/types/order'
|
import { DrinkType, MateType, type Drink } from '@/types/order'
|
||||||
import DrinkCard from '@/components/NewDrinkCard.vue'
|
import DrinkCard from '@/components/NewDrinkCard.vue'
|
||||||
|
import { type Order } from '@/types/order'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const orderStore = useOrderStore()
|
const orderStore = useOrderStore()
|
||||||
|
@ -149,7 +150,7 @@ const submitOrder = async () => {
|
||||||
try {
|
try {
|
||||||
const newOrder = await orderStore.createOrder({
|
const newOrder = await orderStore.createOrder({
|
||||||
drinks: validDrinks.value
|
drinks: validDrinks.value
|
||||||
})
|
}) as Order
|
||||||
toast.value = {
|
toast.value = {
|
||||||
show: true,
|
show: true,
|
||||||
message: `Bestellung erfolgreich! ID: ${newOrder.id}`
|
message: `Bestellung erfolgreich! ID: ${newOrder.id}`
|
||||||
|
@ -167,15 +168,6 @@ const submitOrder = async () => {
|
||||||
|
|
||||||
// Modal- und createNewOrder-Logik entfernt
|
// Modal- und createNewOrder-Logik entfernt
|
||||||
|
|
||||||
const closeSuccessModal = () => {
|
|
||||||
showSuccessModal.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const createNewOrder = () => {
|
|
||||||
showSuccessModal.value = false
|
|
||||||
resetForm()
|
|
||||||
}
|
|
||||||
|
|
||||||
const retryConnection = () => {
|
const retryConnection = () => {
|
||||||
orderStore.connectWebSocket()
|
orderStore.connectWebSocket()
|
||||||
}
|
}
|
||||||
|
|
70
nginx/conf.d/default.conf
Normal file
70
nginx/conf.d/default.conf
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# Upstream definitions
|
||||||
|
upstream frontend {
|
||||||
|
server frontend:80;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream backend {
|
||||||
|
server backend:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main server block
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
|
||||||
|
# API routes - proxy to backend
|
||||||
|
location /api/ {
|
||||||
|
limit_req zone=api burst=20 nodelay;
|
||||||
|
proxy_pass http://backend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebSocket endpoint - proxy to backend
|
||||||
|
location /api/ws {
|
||||||
|
limit_req zone=ws burst=50 nodelay;
|
||||||
|
proxy_pass http://backend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_connect_timeout 7d;
|
||||||
|
proxy_send_timeout 7d;
|
||||||
|
proxy_read_timeout 7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Frontend routes - proxy to frontend
|
||||||
|
location / {
|
||||||
|
proxy_pass http://frontend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check endpoint
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "healthy\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
54
nginx/nginx.conf
Normal file
54
nginx/nginx.conf
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
use epoll;
|
||||||
|
multi_accept on;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
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;
|
||||||
|
|
||||||
|
# Basic settings
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
client_max_body_size 16M;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
text/xml
|
||||||
|
text/javascript
|
||||||
|
application/json
|
||||||
|
application/javascript
|
||||||
|
application/xml+rss
|
||||||
|
application/atom+xml
|
||||||
|
image/svg+xml;
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
|
||||||
|
limit_req_zone $binary_remote_addr zone=ws:10m rate=30r/s;
|
||||||
|
|
||||||
|
# Include server configurations
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue