added cors
This commit is contained in:
parent
f36ed55788
commit
22d0e0dfd0
133
NGINX_SETUP.md
Normal file
133
NGINX_SETUP.md
Normal file
@ -0,0 +1,133 @@
|
||||
# Xtream Player - Nginx Setup Guide
|
||||
|
||||
## Problem Fixed
|
||||
|
||||
Když jste provozovali aplikaci přes nginx, video přehrávač nefungoval. Problém byl v chybějících **CORS (Cross-Origin Resource Sharing)** headerech. Nyní jsme:
|
||||
|
||||
1. ✅ Přidali CORS headers do Java backendu
|
||||
2. ✅ Přidali support pro OPTIONS preflight requests
|
||||
3. ✅ Vytvořili optimalizovanou nginx konfiguraci pro streamování videa
|
||||
|
||||
## Spuštění s Docker Compose (Doporučeno)
|
||||
|
||||
Nejjednodušší způsob je spustit obě služby (backend + nginx) najednou:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Aplikace pak bude dostupná na:
|
||||
- **Frontend s nginx**: http://localhost/
|
||||
- **Backend přímo**: http://localhost:8080/
|
||||
|
||||
## Ruční Spuštění
|
||||
|
||||
### 1. Spusťte backend Java aplikaci:
|
||||
```bash
|
||||
./mvn clean package
|
||||
java -jar target/xtream-player-1.0.0.jar
|
||||
```
|
||||
|
||||
### 2. Spusťte nginx:
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 80:80 \
|
||||
-v $(pwd)/deploy/nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
|
||||
nginx:latest
|
||||
```
|
||||
|
||||
Nebo s native nginx (Linux):
|
||||
```bash
|
||||
cp deploy/nginx/nginx.conf /etc/nginx/sites-available/xtream-player
|
||||
sudo ln -s /etc/nginx/sites-available/xtream-player /etc/nginx/sites-enabled/
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## HTTPS/SSL Setup
|
||||
|
||||
Pro bezpečné HTTPS připojení:
|
||||
|
||||
1. Vytvořte SSL certifikáty:
|
||||
```bash
|
||||
mkdir -p deploy/nginx/ssl
|
||||
# Použijte Let's Encrypt, self-signed certs, nebo váš certifikát
|
||||
```
|
||||
|
||||
2. Upravte `deploy/nginx/nginx.conf`:
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||
|
||||
# ... rest of config
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
3. Aktualizujte docker-compose.yml pro SSL volume
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Video přehrávač stále nefunguje?
|
||||
|
||||
1. **Zkontrolujte browser console** (F12 → Console):
|
||||
- Hledejte CORS errory
|
||||
- Hledejte "stream-proxy" errory
|
||||
|
||||
2. **Zkontrolujte nginx logs**:
|
||||
```bash
|
||||
docker logs <nginx_container_id>
|
||||
# nebo
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
3. **Zkontrolujte backend logs**:
|
||||
```bash
|
||||
docker logs <xtream-player_container_id>
|
||||
# nebo koukejte na console kde jste spustili Java
|
||||
```
|
||||
|
||||
### Nginx vrací 502 Bad Gateway?
|
||||
|
||||
- Ujistěte se, že backend je spuštěný na portu 8080
|
||||
- Zkontrolujte, že nginx má přístup k backendu (network settings)
|
||||
- Zkontrolujte nginx logs pro konkrétní chybu
|
||||
|
||||
### Velké soubory se neloadují?
|
||||
|
||||
- V nginx.conf je `client_max_body_size 256M;` - zvyšte v případě potřeby
|
||||
- Ujistěte se, že `proxy_buffering off;` je nastaveno pro `/api/stream-proxy`
|
||||
|
||||
## Architektura
|
||||
|
||||
```
|
||||
Klient (browser)
|
||||
↓
|
||||
Nginx (reverse proxy, CORS headers)
|
||||
↓
|
||||
Java aplikace (8080)
|
||||
├─ /api/... (API endpoints s CORS)
|
||||
├─ /api/stream-proxy (HLS stream proxy)
|
||||
└─ / (static HTML/CSS/JS)
|
||||
```
|
||||
|
||||
## CORS Headers
|
||||
|
||||
Nyní jsou automaticky přidávány všem odpovědím:
|
||||
|
||||
```
|
||||
Access-Control-Allow-Origin: *
|
||||
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
|
||||
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
|
||||
Access-Control-Max-Age: 86400
|
||||
```
|
||||
|
||||
Pokud potřebujete omezit origin (bezpečnější), měňte `Access-Control-Allow-Origin` v kódu.
|
||||
95
deploy/nginx/nginx.conf
Normal file
95
deploy/nginx/nginx.conf
Normal file
@ -0,0 +1,95 @@
|
||||
upstream xtream_backend {
|
||||
server localhost:8080;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# Limit request sizes to prevent abuse
|
||||
client_max_body_size 256M;
|
||||
|
||||
# Root location - serve static files and proxy to backend
|
||||
location / {
|
||||
# Try to serve static files first, then proxy to backend
|
||||
proxy_pass http://xtream_backend;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Preserve original request information
|
||||
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;
|
||||
|
||||
# WebSocket support (if needed)
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# Timeouts for long-running requests (streaming)
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 3600s;
|
||||
|
||||
# Buffering for streaming
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
# API endpoints with special handling
|
||||
location /api/ {
|
||||
proxy_pass http://xtream_backend;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
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_set_header X-Forwarded-Host $server_name;
|
||||
|
||||
# For preflight requests
|
||||
proxy_set_header Access-Control-Allow-Origin *;
|
||||
|
||||
# Timeouts for streaming endpoints
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 3600s;
|
||||
|
||||
# Disable buffering for stream proxying
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
# Stream proxy endpoint - special handling for media streams
|
||||
location /api/stream-proxy {
|
||||
proxy_pass http://xtream_backend;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
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;
|
||||
|
||||
# Important: disable buffering for media streaming
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
# Allow range requests for seeking
|
||||
proxy_set_header Range $http_range;
|
||||
proxy_set_header If-Range $http_if_range;
|
||||
|
||||
# Long timeout for streaming
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_read_timeout 3600s;
|
||||
|
||||
# Pass through the 206 Partial Content status
|
||||
proxy_pass_request_headers on;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "OK";
|
||||
add_header Content-Type "text/plain";
|
||||
}
|
||||
}
|
||||
@ -80,23 +80,23 @@ public final class XtreamPlayerApplication {
|
||||
UserAuthenticator userAuthenticator = new UserAuthenticator(applicationDao);
|
||||
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||
server.createContext("/api/auth/login", new LoginHandler(userAuthenticator));
|
||||
server.createContext("/api/auth/logout", new LogoutHandler(userAuthenticator));
|
||||
server.createContext("/api/config", new ConfigHandler(configStore, userAuthenticator));
|
||||
server.createContext("/api/test-login", new TestLoginHandler(configStore, userAuthenticator));
|
||||
server.createContext("/api/xtream", new XtreamProxyHandler(configStore, userAuthenticator));
|
||||
server.createContext("/api/stream-url", new StreamUrlHandler(configStore, userAuthenticator));
|
||||
server.createContext("/api/stream-proxy", new StreamProxyHandler(userAuthenticator));
|
||||
server.createContext("/api/open-in-player", new OpenInPlayerHandler(configStore, userAuthenticator));
|
||||
server.createContext("/api/library/load", new LibraryLoadHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/api/library/status", new LibraryStatusHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/api/library/categories", new LibraryCategoriesHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/api/library/items", new LibraryItemsHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/api/library/search", new LibrarySearchHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/api/library/series-episodes", new LibrarySeriesEpisodesHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/api/library/epg", new LibraryEpgHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/api/favorites", new FavoritesHandler(libraryService, userAuthenticator));
|
||||
server.createContext("/user", new UserCrudHandler(applicationDao));
|
||||
server.createContext("/api/auth/login", corsWrapper(new LoginHandler(userAuthenticator)));
|
||||
server.createContext("/api/auth/logout", corsWrapper(new LogoutHandler(userAuthenticator)));
|
||||
server.createContext("/api/config", corsWrapper(new ConfigHandler(configStore, userAuthenticator)));
|
||||
server.createContext("/api/test-login", corsWrapper(new TestLoginHandler(configStore, userAuthenticator)));
|
||||
server.createContext("/api/xtream", corsWrapper(new XtreamProxyHandler(configStore, userAuthenticator)));
|
||||
server.createContext("/api/stream-url", corsWrapper(new StreamUrlHandler(configStore, userAuthenticator)));
|
||||
server.createContext("/api/stream-proxy", corsWrapper(new StreamProxyHandler(userAuthenticator)));
|
||||
server.createContext("/api/open-in-player", corsWrapper(new OpenInPlayerHandler(configStore, userAuthenticator)));
|
||||
server.createContext("/api/library/load", corsWrapper(new LibraryLoadHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/api/library/status", corsWrapper(new LibraryStatusHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/api/library/categories", corsWrapper(new LibraryCategoriesHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/api/library/items", corsWrapper(new LibraryItemsHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/api/library/search", corsWrapper(new LibrarySearchHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/api/library/series-episodes", corsWrapper(new LibrarySeriesEpisodesHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/api/library/epg", corsWrapper(new LibraryEpgHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/api/favorites", corsWrapper(new FavoritesHandler(libraryService, userAuthenticator)));
|
||||
server.createContext("/user", corsWrapper(new UserCrudHandler(applicationDao)));
|
||||
server.createContext("/", new StaticHandler(userAuthenticator));
|
||||
server.setExecutor(Executors.newFixedThreadPool(12));
|
||||
server.start();
|
||||
@ -104,6 +104,18 @@ public final class XtreamPlayerApplication {
|
||||
LOGGER.info("Xtream Player started on http://localhost:{}", port);
|
||||
}
|
||||
|
||||
private static HttpHandler corsWrapper(HttpHandler delegate) {
|
||||
return exchange -> {
|
||||
addCorsHeaders(exchange);
|
||||
if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||
exchange.sendResponseHeaders(200, -1);
|
||||
exchange.close();
|
||||
} else {
|
||||
delegate.handle(exchange);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static int resolvePort() {
|
||||
String raw = System.getenv("PORT");
|
||||
if (raw == null || raw.isBlank()) {
|
||||
@ -1506,12 +1518,20 @@ public final class XtreamPlayerApplication {
|
||||
}
|
||||
|
||||
private static void writeBytes(HttpExchange exchange, int statusCode, byte[] bytes, String contentType) throws IOException {
|
||||
addCorsHeaders(exchange);
|
||||
exchange.sendResponseHeaders(statusCode, bytes.length);
|
||||
exchange.getResponseBody().write(bytes);
|
||||
logApiResponse(exchange, statusCode, bytes.length, contentType);
|
||||
exchange.close();
|
||||
}
|
||||
|
||||
private static void addCorsHeaders(HttpExchange exchange) {
|
||||
exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");
|
||||
exchange.getResponseHeaders().set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
exchange.getResponseHeaders().set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
|
||||
exchange.getResponseHeaders().set("Access-Control-Max-Age", "86400");
|
||||
}
|
||||
|
||||
private static String contentType(String resourcePath) {
|
||||
if (resourcePath.endsWith(".html")) {
|
||||
return "text/html; charset=utf-8";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user