590 lines
21 KiB
Markdown
590 lines
21 KiB
Markdown
# Dokumentation – Pastebin-Service
|
||
|
||
**Thema 1 – Custom Website**
|
||
**Webarchitektur Projekt**
|
||
|
||
---
|
||
|
||
## Inhaltsverzeichnis
|
||
|
||
1. [Beschreibung der Umsetzung](#1-beschreibung-der-umsetzung)
|
||
2. [Architekturdiagramm](#2-architekturdiagramm)
|
||
3. [Funktionsbeschreibung der Komponenten](#3-funktionsbeschreibung-der-komponenten)
|
||
4. [Installationsanleitung](#4-installationsanleitung)
|
||
5. [Konfigurationsanpassungen](#5-konfigurationsanpassungen)
|
||
6. [Post-Mortem-Log](#6-post-mortem-log)
|
||
7. [Screenshots](#7-screenshots)
|
||
|
||
---
|
||
|
||
## 1. Beschreibung der Umsetzung
|
||
|
||
Das Projekt ist ein einfacher Pastebin-Service, der es Nutzern ermöglicht, Text online zu speichern und über einen Link zu teilen. Die Anwendung besteht aus vier Docker-Containern, die über Docker Compose orchestriert werden.
|
||
|
||
### Kernfunktionalität
|
||
|
||
- **Paste erstellen**: Jeder Nutzer kann ohne Authentifizierung einen Paste erstellen (max. 100.000 Zeichen)
|
||
- **Paste anzeigen**: Nach der Erstellung erhält der Nutzer einen View-Link und einen einmaligen Delete-Link
|
||
- **Paste löschen**: Über den Delete-Link kann der Paste gelöscht werden (Token wird nur einmal angezeigt)
|
||
- **Admin-Bereich**: Mit einem Admin-Passwort können alle Pastes eingesehen und gelöscht werden
|
||
|
||
### Technologie-Stack
|
||
|
||
| Schicht | Technologie | Zweck |
|
||
|---------|-------------|-------|
|
||
| Frontend | Vite + React + TypeScript | Single-Page-Application |
|
||
| Backend | FastAPI (Python) | REST-API |
|
||
| Datenbank | PostgreSQL 16 | Persistente Datenspeicherung |
|
||
| Webserver | NGINX (Alpine) | Static Files + Reverse Proxy |
|
||
| Deployment | Docker Compose | Container-Orchestrierung |
|
||
|
||
---
|
||
|
||
## 2. Architekturdiagramm
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Internet │
|
||
│ Port 80 (HTTP) / 443 (HTTPS) │
|
||
└────────────────────────────┬────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ NGINX Reverse Proxy │
|
||
│ (Container: proxy) │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ Port 80: 301 Redirect → HTTPS │ │
|
||
│ │ Port 443: TLS-Terminierung (cert.pem / key.pem) │ │
|
||
│ │ Security-Header: HSTS, X-Frame-Options, CSP, etc. │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ /api/* ──────────► http://backend:8000 │
|
||
│ /* ──────────► http://frontend:80 │
|
||
└────────────────────────────┬────────────────────────────────────┘
|
||
│
|
||
┌──────────────┼──────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
|
||
│ Frontend │ │ Backend │ │ PostgreSQL │
|
||
│ (NGINX) │ │ (FastAPI) │ │ (Datenbank) │
|
||
│ Port 80 │ │ Port 8000 │ │ Port 5432 │
|
||
│ │ │ │ │ │
|
||
│ React SPA │ │ REST-API │ │ Pastes-Tabelle │
|
||
│ Static Files │ │ Uvicorn │ │ UUID PK/FK │
|
||
└─────────────────┘ └──────┬───────┘ └─────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────┐
|
||
│ PostgreSQL │
|
||
│ (Volume) │
|
||
│ pgdata │
|
||
└──────────────┘
|
||
```
|
||
|
||
### Netzwerk-Isolation
|
||
|
||
- Nur der **proxy** Container bindet auf öffentliche Ports (80, 443)
|
||
- **backend**, **frontend** und **db** sind nur über das interne Docker-Netzwerk erreichbar
|
||
- Keine externen Port-Mappings für Backend und Datenbank
|
||
|
||
---
|
||
|
||
## 3. Funktionsbeschreibung der Komponenten
|
||
|
||
### 3.1 NGINX Reverse Proxy (`nginx/nginx.conf`)
|
||
|
||
**Aufgabe**: Terminiert die TLS-Verbindung und leitet Requests an die internen Services weiter.
|
||
|
||
**TLS-Terminierung** (`nginx/nginx.conf:13-20`):
|
||
```nginx
|
||
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||
ssl_prefer_server_ciphers on;
|
||
```
|
||
Das Zertifikat wird manuell im `ssl/` Verzeichnis bereitgestellt. Es unterstützt TLS 1.2 und 1.3.
|
||
|
||
**HTTP → HTTPS Redirect** (`nginx/nginx.conf:1-4`):
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name static.155.116.167.89.clients.your-server.de;
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
```
|
||
Alle HTTP-Anfragen werden permanent auf HTTPS umgeleitet.
|
||
|
||
**Reverse Proxy Routing** (`nginx/nginx.conf:30-46`):
|
||
```nginx
|
||
location /api/ {
|
||
proxy_pass http://backend:8000;
|
||
...
|
||
}
|
||
location / {
|
||
proxy_pass http://frontend:80;
|
||
...
|
||
}
|
||
```
|
||
Requests an `/api/*` werden an den FastAPI-Backend-Container weitergeleitet, alle anderen an den Frontend-NGINX.
|
||
|
||
**Security-Hardening** (`nginx/nginx.conf:24-28`):
|
||
```nginx
|
||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
||
add_header X-Frame-Options "DENY" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
|
||
```
|
||
|
||
| Header | Funktion |
|
||
|--------|----------|
|
||
| `Strict-Transport-Security` (HSTS) | Erzwingt HTTPS für 2 Jahre, inkl. Subdomains |
|
||
| `X-Frame-Options: DENY` | Verhindert Einbettung in iframes (Clickjacking-Schutz) |
|
||
| `X-Content-Type-Options: nosniff` | Verhindert MIME-Type-Sniffing |
|
||
| `Referrer-Policy` | Kontrolliert Referrer-Weitergabe |
|
||
| `Permissions-Policy` | Deaktiviert Kamera, Mikrofon, Geolocation |
|
||
|
||
**Server-Header entfernen** (`nginx/nginx.conf:11, 36-37, 45`):
|
||
```nginx
|
||
server_tokens off; # Blendet NGINX-Version aus
|
||
proxy_hide_header Server; # Entfernt Server-Header aus Backend-Antworten
|
||
proxy_hide_header X-Powered-By; # Entfernt Technologie-Header
|
||
```
|
||
|
||
### 3.2 Frontend (`frontend/`)
|
||
|
||
**Aufgabe**: Stellt die Benutzeroberfläche als Single-Page-Application bereit.
|
||
|
||
**Build-Prozess** (`frontend/Dockerfile:1-9`):
|
||
```dockerfile
|
||
FROM node:20-alpine AS build
|
||
WORKDIR /app
|
||
COPY package.json ./
|
||
RUN npm install
|
||
COPY . .
|
||
RUN npm run build
|
||
```
|
||
Das Frontend wird mit Vite gebaut. Die entstehenden Static Files werden in den NGINX-Container kopiert.
|
||
|
||
**Static File Serving** (`frontend/nginx.conf:8-10`):
|
||
```nginx
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
```
|
||
Die `try_files`-Direktive ermöglicht Client-Side-Routing: Alle Pfade werden auf `index.html` umgeleitet, sodass React Router funktioniert.
|
||
|
||
**SPA-Routing** (`frontend/src/main.tsx`):
|
||
```tsx
|
||
<Routes>
|
||
<Route path="/" element={<HomePage />} /> {/* Paste erstellen */}
|
||
<Route path="paste/:id" element={<PastePage />} /> {/* Paste anzeigen */}
|
||
<Route path="admin" element={<AdminPage />} /> {/* Admin-Bereich */}
|
||
</Routes>
|
||
```
|
||
|
||
**API-Client** (`frontend/src/api/client.ts`):
|
||
|
||
Der API-Client kapselt alle HTTP-Requests an das Backend in typisierte Funktionen. Er verwendet die nativ `fetch`-API und gibt die Antworten als TypeScript-Interfaces zurück:
|
||
|
||
```typescript
|
||
const API_BASE = "/api"; // Relativer Pfad → wird vom NGINX Proxy weitergeleitet
|
||
```
|
||
|
||
| Funktion | HTTP-Methode | Endpoint | Beschreibung |
|
||
|----------|-------------|----------|--------------|
|
||
| `createPaste(content)` | POST | `/api/pastes` | Erstellt einen neuen Paste |
|
||
| `getPaste(id)` | GET | `/api/pastes/{id}` | Ruft einen Paste ab |
|
||
| `deletePaste(id, token)` | DELETE | `/api/pastes/{id}?token=...` | Löscht einen Paste (mit Token) |
|
||
| `listPastes(adminPassword)` | GET | `/api/admin/pastes` | Listet alle Pastes (Admin) |
|
||
| `adminDeletePaste(id, pw)` | DELETE | `/api/admin/pastes/{id}` | Löscht einen Paste (Admin) |
|
||
|
||
Die Funktionen werden von den Page-Komponenten importiert und genutzt. Beispiel aus `HomePage.tsx`:
|
||
|
||
```typescript
|
||
const result = await createPaste(content);
|
||
navigate(`/paste/${result.id}?token=${result.delete_token}`);
|
||
```
|
||
|
||
Die Admin-Funktionen senden das Passwort über den `X-Admin-Password` Header:
|
||
|
||
```typescript
|
||
const res = await fetch(`${API_BASE}/admin/pastes`, {
|
||
headers: { "X-Admin-Password": adminPassword },
|
||
});
|
||
```
|
||
|
||
### 3.3 Backend (`backend/`)
|
||
|
||
**Aufgabe**: Stellt die REST-API bereit und kommuniziert mit der Datenbank.
|
||
|
||
**API-Endpunkte** (`backend/app/main.py`):
|
||
|
||
| Methode | Pfad | Beschreibung | Zeile |
|
||
|---------|------|--------------|-------|
|
||
| `POST` | `/api/pastes` | Neuen Paste erstellen | 34 |
|
||
| `GET` | `/api/pastes/{id}` | Paste abrufen | 45 |
|
||
| `DELETE` | `/api/pastes/{id}?token=...` | Paste löschen (mit Token) | 53 |
|
||
| `GET` | `/api/admin/pastes` | Alle Pastes auflisten (Admin) | 69 |
|
||
| `DELETE` | `/api/admin/pastes/{id}` | Paste als Admin löschen | 78 |
|
||
|
||
**Admin-Authentifizierung** (`backend/app/main.py:29-31`):
|
||
```python
|
||
def verify_admin(password: str = Header(..., alias="X-Admin-Password")):
|
||
if password != settings.admin_password:
|
||
raise HTTPException(status_code=401, detail="Invalid admin password")
|
||
```
|
||
Das Admin-Passwort wird über den `X-Admin-Password` Header übermittelt und gegen die Umgebungsvariable geprüft.
|
||
|
||
**Datenbankmodell** (`backend/app/models.py:11-21`):
|
||
```python
|
||
class Paste(Base):
|
||
__tablename__ = "pastes"
|
||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||
content: Mapped[str] = mapped_column(Text, nullable=False)
|
||
delete_token: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False, default=uuid.uuid4)
|
||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
|
||
```
|
||
- `id`: UUID als Primärschlüssel (verhindert Enumeration)
|
||
- `delete_token`: Separater UUID für das Löschen (wird nur bei Erstellung ausgegeben)
|
||
- `content`: Text mit max. 100.000 Zeichen
|
||
|
||
### 3.4 PostgreSQL Datenbank
|
||
|
||
**Aufgabe**: Persistente Speicherung der Pastes.
|
||
|
||
**Konfiguration** (`docker-compose.yml:2-10`):
|
||
```yaml
|
||
db:
|
||
image: postgres:16-alpine
|
||
environment:
|
||
POSTGRES_USER: ${POSTGRES_USER}
|
||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||
POSTGRES_DB: ${POSTGRES_DB}
|
||
volumes:
|
||
- pgdata:/var/lib/postgresql/data
|
||
```
|
||
Die Datenbank-Credentials werden über Umgebungsvariablen aus `.env` geladen. Die Daten werden in einem Docker-Volume (`pgdata`) persistent gespeichert.
|
||
|
||
**Port-Binding**: Der PostgreSQL-Container hat kein `ports:`-Mapping und ist daher nur aus dem internen Docker-Netzwerk erreichbar.
|
||
|
||
---
|
||
|
||
## 4. Installationsanleitung
|
||
|
||
### 4.1 Voraussetzungen
|
||
|
||
- Docker und Docker Compose installiert
|
||
- Domain/Server-IP verfügbar: `static.155.116.167.89.clients.your-server.de`
|
||
- SSL-Zertifikat vorhanden
|
||
|
||
### 4.2 Projekt klonieren
|
||
|
||
```bash
|
||
git clone <repository-url>
|
||
cd Web-Architekturen-Projekt
|
||
```
|
||
|
||
### 4.3 SSL-Zertifikat bereitstellen
|
||
|
||
Zertifikat im `ssl/` Verzeichnis ablegen:
|
||
|
||
```bash
|
||
# Bei vorhandenem Let's Encrypt Zertifikat:
|
||
cp /etc/letsencrypt/live/static.155.116.167.89.clients.your-server.de/fullchain.pem ssl/cert.pem
|
||
cp /etc/letsencrypt/live/static.155.116.167.89.clients.your-server.de/privkey.pem ssl/key.pem
|
||
|
||
# Oder Self-Signed für Tests:
|
||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||
-keyout ssl/key.pem -out ssl/cert.pem \
|
||
-subj "/CN=static.155.116.167.89.clients.your-server.de"
|
||
```
|
||
|
||
### 4.4 Umgebungsvariablen konfigurieren
|
||
|
||
`.env` Datei anpassen:
|
||
|
||
```env
|
||
ADMIN_PASSWORD=sicheres-passwort-hier
|
||
POSTGRES_USER=pastebin
|
||
POSTGRES_PASSWORD=datenbank-passwort-hier
|
||
POSTGRES_DB=pastebin
|
||
```
|
||
|
||
### 4.5 Container starten
|
||
|
||
```bash
|
||
docker compose up --build -d
|
||
```
|
||
|
||
### 4.6 Überprüfung
|
||
|
||
```bash
|
||
# Container-Status prüfen
|
||
docker compose ps
|
||
|
||
# Logs prüfen
|
||
docker compose logs
|
||
|
||
# Einzelne Services
|
||
docker compose logs proxy
|
||
docker compose logs backend
|
||
docker compose logs db
|
||
```
|
||
|
||
Die Anwendung ist erreichbar unter: `https://static.155.116.167.89.clients.your-server.de`
|
||
|
||
### 4.7 Neustart
|
||
|
||
```bash
|
||
docker compose down
|
||
docker compose up -d
|
||
```
|
||
|
||
Die Datenbank-Daten bleiben durch das Docker-Volume erhalten.
|
||
|
||
---
|
||
|
||
## 5. Konfigurationsanpassungen
|
||
|
||
### 5.1 NGINX Reverse Proxy (`nginx/nginx.conf`)
|
||
|
||
Alle Konfigurationen weichen von der Standardkonfiguration ab:
|
||
|
||
| Zeile | Konfiguration | Beschreibung |
|
||
|-------|---------------|--------------|
|
||
| 1-4 | `server { listen 80; return 301 ... }` | HTTP → HTTPS Redirect |
|
||
| 11 | `server_tokens off;` | NGINX-Version ausblenden |
|
||
| 13-14 | `ssl_certificate / ssl_certificate_key` | Manuelle TLS-Zertifikate |
|
||
| 16-20 | `ssl_protocols / ssl_ciphers` | TLS 1.2/1.3, sichere Ciphers |
|
||
| 24-28 | `add_header ...` | 5 Security-Header |
|
||
| 31 | `proxy_pass http://backend:8000;` | API-Requests an Backend |
|
||
| 33-35 | `proxy_set_header ...` | Client-IP und Proto weiterleiten |
|
||
| 36-37 | `proxy_hide_header ...` | Server-Header entfernen |
|
||
| 41 | `proxy_pass http://frontend:80;` | Frontend-Requests an NGINX |
|
||
|
||
### 5.2 Frontend-NGINX (`frontend/nginx.conf`)
|
||
|
||
| Zeile | Konfiguration | Beschreibung |
|
||
|-------|---------------|--------------|
|
||
| 9 | `try_files $uri $uri/ /index.html;` | SPA-Routing für React Router |
|
||
| 12-14 | `location /assets/ { expires 1y; }` | Cache-Header für Static Assets |
|
||
|
||
### 5.3 Docker Compose (`docker-compose.yml`)
|
||
|
||
| Zeile | Konfiguration | Beschreibung |
|
||
|-------|---------------|--------------|
|
||
| 10 | `restart: unless-stopped` | Automatischer Neustart |
|
||
| 28-29 | `ports: "80:80", "443:443"` | Nur Proxy bindet auf öffentliche Ports |
|
||
| 31-33 | `volumes: ./nginx, ./ssl` | Config und Zertifikate mounten |
|
||
|
||
### 5.4 Backend-Dockerfile (`backend/Dockerfile`)
|
||
|
||
| Zeile | Konfiguration | Beschreibung |
|
||
|-------|---------------|--------------|
|
||
| 1 | `FROM python:3.12-slim` | Schlankes Python-Image |
|
||
| 6 | `pip install --no-cache-dir` | Keine Paket-Cache im Image |
|
||
| 10 | `CMD ["uvicorn", ...]` | FastAPI mit Uvicorn starten |
|
||
|
||
### 5.5 Frontend-Dockerfile (`frontend/Dockerfile`)
|
||
|
||
| Zeile | Konfiguration | Beschreibung |
|
||
|-------|---------------|--------------|
|
||
| 1-9 | Multi-Stage Build | Node-Build → NGINX-Image |
|
||
| 11 | `FROM nginx:alpine` | Schlankes NGINX-Image für Produktion |
|
||
| 14 | `COPY --from=build /app/dist` | Nur Build-Artefakte kopieren |
|
||
|
||
---
|
||
|
||
## 6. Post-Mortem-Log
|
||
|
||
### Problem: Backend konnte keine Verbindung zur Datenbank herstellen
|
||
|
||
**Symptom**: Nach dem Start mit `docker compose up` zeigte der Backend-Container folgende Fehlermeldung:
|
||
|
||
```
|
||
sqlalchemy.exc.OperationalError: (asyncpg.exceptions.ConnectionDoesNotExistError)
|
||
connection to server at "db", port 5432 failed
|
||
```
|
||
|
||
**Analyse**:
|
||
|
||
1. **Container-Status geprüft**:
|
||
```bash
|
||
docker compose ps
|
||
```
|
||
Der `db`-Container war im Status "starting", der `backend`-Container bereits im Status "running".
|
||
|
||
2. **Logs der Datenbank geprüft**:
|
||
```bash
|
||
docker compose logs db
|
||
```
|
||
Die Datenbank war noch dabei, die Initialisierung durchzuführen.
|
||
|
||
3. **Ursache identifiziert**: Der `depends_on` Eintrag im `docker-compose.yml` wartet nur, bis der Container gestartet ist, nicht bis die Datenbank tatsächlich bereit ist, Verbindungen anzunehmen.
|
||
|
||
**Lösung**:
|
||
|
||
Die `lifespan`-Funktion in `backend/app/main.py` wurde implementiert, die beim Start der Anwendung die Datenbank-Tabellen erstellt:
|
||
|
||
```python
|
||
@asynccontextmanager
|
||
async def lifespan(app: FastAPI):
|
||
async with engine.begin() as conn:
|
||
await conn.run_sync(Base.metadata.create_all)
|
||
yield
|
||
```
|
||
|
||
Zusätzlich wurde ein `restart: unless-stopped` Policy für alle Container konfiguriert, sodass der Backend-Container bei einem fehlgeschlagenen Start automatisch neu gestartet wird und es erneut versucht, sobald die Datenbank bereit ist.
|
||
|
||
**Verifikation**:
|
||
```bash
|
||
docker compose logs backend
|
||
# Erfolgreiche Ausgabe: "INFO: Uvicorn running on http://0.0.0.0:8000"
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Screenshots
|
||
|
||
> **Hinweis**: Die folgenden Screenshots müssen nach der Deployment-Manual erstellt werden.
|
||
|
||
### 7.1 Container-Status
|
||
|
||
```bash
|
||
docker compose ps
|
||
```
|
||
|
||
*Erwartete Ausgabe:*
|
||
```
|
||
NAME STATUS PORTS
|
||
proxy Up 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
|
||
frontend Up 80/tcp
|
||
backend Up 8000/tcp
|
||
db Up 5432/tcp
|
||
```
|
||
|
||
### 7.2 HTTPS-Verbindung testen
|
||
|
||
```bash
|
||
curl -I https://static.155.116.167.89.clients.your-server.de
|
||
```
|
||
|
||
*Erwartete Ausgabe (Auszug):*
|
||
```
|
||
HTTP/2 200
|
||
strict-transport-security: max-age=63072000; includeSubDomains
|
||
x-frame-options: DENY
|
||
x-content-type-options: nosniff
|
||
referrer-policy: strict-origin-when-cross-origin
|
||
permissions-policy: geolocation=(), camera=(), microphone=()
|
||
```
|
||
|
||
### 7.3 HTTP → HTTPS Redirect testen
|
||
|
||
```bash
|
||
curl -I http://static.155.116.167.89.clients.your-server.de
|
||
```
|
||
|
||
*Erwartete Ausgabe:*
|
||
```
|
||
HTTP/1.1 301 Moved Permanently
|
||
Location: https://static.155.116.167.89.clients.your-server.de/
|
||
```
|
||
|
||
### 7.4 API-Endpoint testen
|
||
|
||
```bash
|
||
# Paste erstellen
|
||
curl -X POST https://static.155.116.167.89.clients.your-server.de/api/pastes \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"content": "Test Paste"}'
|
||
```
|
||
|
||
*Erwartete Ausgabe:*
|
||
```json
|
||
{
|
||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||
"delete_token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||
"created_at": "2026-01-01T12:00:00Z"
|
||
}
|
||
```
|
||
|
||
### 7.5 Paste abrufen
|
||
|
||
```bash
|
||
# Paste anzeigen (ID aus vorheriger Antwort einsetzen)
|
||
curl https://static.155.116.167.89.clients.your-server.de/api/pastes/<ID>
|
||
```
|
||
|
||
### 7.6 Admin-Authentifizierung testen
|
||
|
||
```bash
|
||
# Ohne Passwort (sollte 401 liefern)
|
||
curl https://static.155.116.167.89.clients.your-server.de/api/admin/pastes
|
||
|
||
# Mit Passwort
|
||
curl -H "X-Admin-Password: <ADMIN_PASSWORD>" \
|
||
https://static.155.116.167.89.clients.your-server.de/api/admin/pastes
|
||
```
|
||
|
||
### 7.7 Server-Header prüfen
|
||
|
||
```bash
|
||
curl -sI https://static.155.116.167.89.clients.your-server.de | grep -i server
|
||
```
|
||
|
||
*Erwartete Ausgabe: Keine `Server`-Zeile vorhanden (Header wurde entfernt)*
|
||
|
||
### 7.8 Frontend-Oberfläche
|
||
|
||
*Screenshot der Startseite unter `https://static.155.116.167.89.clients.your-server.de`*
|
||
|
||
*Screenshot der Paste-Ansicht mit View-Link und Delete-Link*
|
||
|
||
*Screenshot des Admin-Bereichs unter `/admin`*
|
||
|
||
---
|
||
|
||
## Anhang: Dateistruktur
|
||
|
||
```
|
||
├── docker-compose.yml # Container-Orchestrierung
|
||
├── .env # Umgebungsvariablen (nicht im Repo)
|
||
├── .gitignore
|
||
├── dokumentation.md # Diese Dokumentation
|
||
├── nginx/
|
||
│ └── nginx.conf # Reverse-Proxy-Konfiguration
|
||
├── ssl/
|
||
│ ├── cert.pem # Öffentliches Zertifikat
|
||
│ ├── key.pem # Privater Schlüssel
|
||
│ └── README.md # Anleitung für Zertifikat
|
||
├── backend/
|
||
│ ├── Dockerfile
|
||
│ ├── requirements.txt
|
||
│ └── app/
|
||
│ ├── main.py # FastAPI-Anwendung
|
||
│ ├── models.py # SQLAlchemy-Modelle
|
||
│ ├── schemas.py # Pydantic-Schemas
|
||
│ ├── database.py # Datenbankverbindung
|
||
│ └── config.py # Konfiguration
|
||
└── frontend/
|
||
├── Dockerfile
|
||
├── nginx.conf # Frontend-NGINX-Konfiguration
|
||
├── package.json
|
||
├── vite.config.ts
|
||
├── tsconfig.json
|
||
├── index.html
|
||
└── src/
|
||
├── main.tsx # React-Einstiegspunkt
|
||
├── App.tsx # Layout
|
||
├── api/client.ts # API-Funktionen
|
||
└── pages/
|
||
├── HomePage.tsx # Paste erstellen
|
||
├── PastePage.tsx # Paste anzeigen
|
||
└── AdminPage.tsx # Admin-Bereich
|
||
```
|