mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
docs(docker): restore self-hosted PWA compose flow
This commit is contained in:
1 parent
3096115e64
commit
23a1330a14
7 files changed
+377
-51
No files matched your search
+25
-17
@@ -1,32 +1,40 @@
|
||||
# Stage 1 - build environment
|
||||
FROM node:22-alpine AS build
|
||||
|
||||
RUN apk add --no-cache python3 make g++ git
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Swtich to node user
|
||||
#RUN chown node:node ./
|
||||
#USER node
|
||||
|
||||
COPY .npmrc ./
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
COPY patches ./patches
|
||||
|
||||
# Install app dependencies
|
||||
RUN corepack enable && pnpm install --frozen-lockfile
|
||||
RUN corepack enable && pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
# Copy all required files
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN pnpm run build:web
|
||||
RUN pnpm nx build web --configuration=pwa
|
||||
RUN pnpm nx build web-backend
|
||||
|
||||
# Stage 2 - the production environment
|
||||
FROM nginx:stable-alpine
|
||||
FROM node:22-alpine
|
||||
|
||||
# Copy artifacts and nignx.conf
|
||||
COPY --from=build /usr/src/app/dist/browser /usr/share/nginx/html
|
||||
COPY --from=build /usr/src/app/docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
RUN apk add --no-cache gettext nginx
|
||||
|
||||
CMD sed -i "s#http://localhost:3333#$BACKEND_URL#g" /usr/share/nginx/html/main.js && nginx -g 'daemon off;'
|
||||
WORKDIR /opt/iptvnator
|
||||
|
||||
ENV PORT=3000
|
||||
ENV BACKEND_URL=/api
|
||||
ENV CLIENT_URL=http://localhost:4333
|
||||
|
||||
COPY --from=build /usr/src/app/dist/apps/web /usr/share/nginx/html
|
||||
COPY --from=build /usr/src/app/dist/apps/web-backend ./web-backend
|
||||
COPY docker/nginx.conf /etc/nginx/http.d/default.conf.template
|
||||
COPY docker/docker-entrypoint.sh /usr/local/bin/iptvnator-entrypoint
|
||||
|
||||
RUN chmod +x /usr/local/bin/iptvnator-entrypoint
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget -qO- http://127.0.0.1/api/health >/dev/null || exit 1
|
||||
|
||||
CMD ["iptvnator-entrypoint"]
|
||||
+79
-11
@@ -1,20 +1,88 @@
|
||||
# Self-hosted version of IPTVnator
|
||||
# Self-hosted IPTVnator
|
||||
|
||||
You can deploy and run the PWA version of IPTVnator on your own machine with `docker-compose` using the following command:
|
||||
The self-hosted image contains both pieces required for the browser PWA:
|
||||
|
||||
$ cd docker
|
||||
$ docker-compose up -d
|
||||
- Angular PWA static files served by nginx
|
||||
- The monorepo `web-backend` Express app proxied under `/api`
|
||||
|
||||
This command will launch the frontend and backend applications. By default, the application will be available at: http://localhost:4333/. The ports can be configured in the `docker-compose.yml` file.
|
||||
The historical standalone `4gray/iptvnator-backend` image is no longer needed
|
||||
for the default Docker deployment.
|
||||
|
||||
The web backend proxy accepts only `http` and `https` provider URLs. The PWA first registers provider URLs through `/provider-targets`, then uses the returned `targetId` for playlist, Xtream, and Stalker proxy calls. The backend blocks loopback, private, link-local, and reserved network targets by default to avoid exposing the self-hosted server as a generic internal-network fetcher. If you intentionally need to test against local mock servers or LAN-only IPTV sources, set `IPTVNATOR_PROXY_ALLOW_PRIVATE_NETWORKS=1` on the backend container and avoid exposing that instance to untrusted users.
|
||||
## Run With Docker Compose
|
||||
|
||||
For providers that use private certificate authorities, keep TLS validation enabled and pass the CA bundle to Node with `NODE_EXTRA_CA_CERTS=/path/to/ca.pem`.
|
||||
From the repository root:
|
||||
|
||||
## Build frontend
|
||||
```bash
|
||||
docker compose -f docker/docker-compose.yml up --build -d
|
||||
```
|
||||
|
||||
$ docker build -t 4gray/iptvnator -f docker/Dockerfile .
|
||||
The ready-to-run compose file is [`docker-compose.yml`](./docker-compose.yml).
|
||||
By default the app is available at <http://localhost:4333>. No additional
|
||||
environment variables, backend repository checkout, or separate backend
|
||||
container are required for the default local deployment.
|
||||
|
||||
## Build backend
|
||||
## Build The Image
|
||||
|
||||
You can find the backend app with all instructions in a separate GitHub repository - https://github.com/4gray/iptvnator-backend
|
||||
```bash
|
||||
docker build -t 4gray/iptvnator -f docker/Dockerfile .
|
||||
```
|
||||
|
||||
The image build runs:
|
||||
|
||||
```bash
|
||||
pnpm nx build web --configuration=pwa
|
||||
pnpm nx build web-backend
|
||||
```
|
||||
|
||||
## Runtime Configuration
|
||||
|
||||
The container writes `/usr/share/nginx/html/assets/app-config.js` on startup.
|
||||
That file sets `window.__IPTVNATOR_CONFIG__.BACKEND_URL`, which the PWA reads
|
||||
before it creates `PwaService`.
|
||||
|
||||
These variables are supported by the Docker image. The compose file sets the
|
||||
safe local defaults shown below.
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
| ---------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| `BACKEND_URL` | `/api` | Browser-facing backend URL used by the PWA. Keep `/api` for the bundled nginx proxy. |
|
||||
| `CLIENT_URL` | `http://localhost:4333` | Allowed browser origin for backend CORS. Use the public URL when hosting behind a reverse proxy. Multiple origins can be comma-separated. |
|
||||
| `PORT` | `3000` | Internal Express backend port. nginx proxy config is rendered from the template to match it at startup. |
|
||||
| `IPTVNATOR_PROXY_ALLOW_PRIVATE_NETWORKS` | `0` | Set to `1` or `true` only for trusted local/LAN deployments that intentionally proxy private network IPTV or mock endpoints. |
|
||||
| `NODE_EXTRA_CA_CERTS` | unset | Optional Node.js CA bundle path for providers using private certificate authorities. Mount the CA file into the container and set this path. |
|
||||
|
||||
The web backend proxy accepts only `http` and `https` provider URLs. The PWA
|
||||
first registers provider URLs through `/provider-targets`, then uses the
|
||||
returned `targetId` for playlist, Xtream, and Stalker proxy calls. The backend
|
||||
blocks loopback, private, link-local, and reserved network targets by default so
|
||||
a publicly exposed instance cannot be used as a generic internal-network
|
||||
fetcher. If you enable `IPTVNATOR_PROXY_ALLOW_PRIVATE_NETWORKS=1`, keep the
|
||||
instance restricted to trusted users.
|
||||
|
||||
For providers that use private certificate authorities, keep TLS validation
|
||||
enabled and pass the CA bundle to Node:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
iptvnator:
|
||||
volumes:
|
||||
- ./ca.pem:/etc/ssl/private/provider-ca.pem:ro
|
||||
environment:
|
||||
NODE_EXTRA_CA_CERTS: /etc/ssl/private/provider-ca.pem
|
||||
```
|
||||
|
||||
The entrypoint renders the nginx config from `docker/nginx.conf`, starts the
|
||||
backend, waits for `/health`, and only then starts nginx. The nginx config
|
||||
serves the PWA with SPA fallback, avoids caching `assets/app-config.js`, and
|
||||
proxies `/api/*` to the internal backend. The Dockerfile and compose file both
|
||||
define a health check against `/api/health`.
|
||||
|
||||
## Local Validation
|
||||
|
||||
```bash
|
||||
pnpm nx test web-backend
|
||||
pnpm nx build web --configuration=pwa --skip-nx-cache
|
||||
pnpm nx build web-backend
|
||||
pnpm nx run web-e2e:e2e -- --project=chromium --grep @self-hosted
|
||||
docker compose -f docker/docker-compose.yml config
|
||||
```
|
||||
+14
-12
@@ -1,18 +1,20 @@
|
||||
---
|
||||
services:
|
||||
backend:
|
||||
image: 4gray/iptvnator-backend:latest
|
||||
ports:
|
||||
- "7333:3000"
|
||||
environment:
|
||||
- CLIENT_URL=http://localhost:4333
|
||||
# this one should match with the address and port in frontend CLIENT_URL env
|
||||
|
||||
frontend:
|
||||
iptvnator:
|
||||
image: 4gray/iptvnator:latest
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
ports:
|
||||
- "4333:80"
|
||||
environment:
|
||||
- BACKEND_URL=http://localhost:7333
|
||||
# this one should match with the address of the backend service
|
||||
|
||||
BACKEND_URL: /api
|
||||
CLIENT_URL: http://localhost:4333
|
||||
PORT: "3000"
|
||||
IPTVNATOR_PROXY_ALLOW_PRIVATE_NETWORKS: "0"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/api/health >/dev/null || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
export PORT="${PORT:-3000}"
|
||||
export BACKEND_URL="${BACKEND_URL:-/api}"
|
||||
|
||||
envsubst '${PORT}' < /etc/nginx/http.d/default.conf.template > /etc/nginx/http.d/default.conf
|
||||
|
||||
node <<'NODE'
|
||||
const fs = require('node:fs');
|
||||
|
||||
fs.writeFileSync(
|
||||
'/usr/share/nginx/html/assets/app-config.js',
|
||||
`window.__IPTVNATOR_CONFIG__ = ${JSON.stringify(
|
||||
{ BACKEND_URL: process.env.BACKEND_URL || '/api' },
|
||||
null,
|
||||
2
|
||||
)};\n`
|
||||
);
|
||||
NODE
|
||||
|
||||
node /opt/iptvnator/web-backend/main.cjs &
|
||||
BACKEND_PID=$!
|
||||
|
||||
cleanup_done=0
|
||||
cleanup() {
|
||||
if [ "$cleanup_done" -eq 1 ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
cleanup_done=1
|
||||
trap - INT TERM EXIT
|
||||
|
||||
if [ -n "${NGINX_PID:-}" ]; then
|
||||
kill "$NGINX_PID" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [ -n "${BACKEND_PID:-}" ]; then
|
||||
kill "$BACKEND_PID" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
backend_ready=0
|
||||
|
||||
for _ in $(seq 1 30); do
|
||||
if wget -qO- "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then
|
||||
backend_ready=1
|
||||
break
|
||||
fi
|
||||
|
||||
if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||
wait "$BACKEND_PID"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$backend_ready" -ne 1 ]; then
|
||||
echo "IPTVnator web backend did not become healthy on port ${PORT}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
nginx -g 'daemon off;' &
|
||||
NGINX_PID=$!
|
||||
|
||||
wait "$NGINX_PID"
|
||||
+25
-10
@@ -1,11 +1,26 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
|
||||
include /etc/nginx/extra-conf.d/*.conf;
|
||||
}
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:${PORT}/;
|
||||
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;
|
||||
}
|
||||
|
||||
location /assets/app-config.js {
|
||||
add_header Cache-Control "no-store";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user