diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c1a021e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,58 @@ +# ── Stage 1: Build frontend ─────────────────────────────────────────────────── +FROM node:20-slim AS frontend-builder + +WORKDIR /build/frontend +COPY frontend/package*.json ./ +RUN npm ci +COPY frontend/ ./ +RUN npm run build + +# ── Stage 2: Build backend ──────────────────────────────────────────────────── +FROM node:20-slim AS backend-builder + +RUN apt-get update && apt-get install -y \ + python3 make g++ \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build/backend +COPY backend/package*.json backend/tsconfig.json ./ +RUN npm ci +COPY backend/src ./src +RUN npm run build && npm prune --production + +# ── Stage 3: Runtime ───────────────────────────────────────────────────────── +FROM node:20-slim + +RUN apt-get update && apt-get install -y \ + nginx \ + chromium \ + fonts-freefont-ttf \ + supervisor \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* + +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium +ENV NODE_ENV=production +ENV PORT=3001 +ENV DATABASE_PATH=/app/data/tracker.db + +WORKDIR /app + +# Backend +COPY --from=backend-builder /build/backend/dist ./backend/dist +COPY --from=backend-builder /build/backend/node_modules ./backend/node_modules +COPY backend/package.json ./backend/ + +# Frontend → nginx web root +COPY --from=frontend-builder /build/frontend/dist /usr/share/nginx/html + +# Config files +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +# Persistent data volume +VOLUME ["/app/data"] + +EXPOSE 8080 +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/UNRAID.md b/UNRAID.md new file mode 100644 index 0000000..f77d44c --- /dev/null +++ b/UNRAID.md @@ -0,0 +1,223 @@ +# Unraid Installation Guide — UI Stock Tracker + +UI Stock Tracker runs as a **single Docker container** — nginx serves the frontend and proxies API calls to the Node.js backend, both managed inside the container by supervisord. + +Two installation methods are covered: **CLI via SSH** (recommended) and **GUI via Compose Manager**. + +--- + +## Prerequisites + +- Unraid 6.10 or later +- Docker enabled: **Settings → Docker → Enable Docker → Yes** +- A share to store the persistent SQLite database (e.g. `/mnt/user/appdata/ui-tracker/data`) + +--- + +## Method 1 — CLI via SSH (Recommended) + +### 1. SSH into your Unraid server + +```bash +ssh root@ +``` + +### 2. Copy the project files + +```bash +mkdir -p /mnt/user/appdata/ui-tracker +cd /mnt/user/appdata/ui-tracker +git clone https://github.com//ui-tracker.git . +``` + +> No git? Copy the folder via SMB (`\\\appdata`) or SFTP instead. + +### 3. Build and start + +```bash +docker compose up -d --build +``` + +The first build takes a few minutes — it compiles both the React frontend and the Node.js backend, then installs Chromium. Subsequent starts are instant. + +### 4. Verify it's running + +```bash +docker compose ps +``` + +Expected: +``` +NAME STATUS +ui-tracker running +``` + +### 5. Open the web UI + +``` +http://:8080 +``` + +### Useful commands + +```bash +# Live logs (nginx + backend combined) +docker compose logs -f + +# Restart the container +docker compose restart + +# Stop +docker compose down + +# Rebuild after updating the code +docker compose up -d --build +``` + +--- + +## Method 2 — GUI via Compose Manager Plugin + +### 1. Install Compose Manager + +1. Go to **Apps** (Community Applications) +2. Search for **Compose Manager** and install it +3. It appears under **Docker → Compose** + +### 2. Upload the project files + +Copy the `ui-tracker` folder to your Unraid server at: + +``` +/mnt/user/appdata/ui-tracker/ +``` + +Use any of: SMB share (`\\\appdata`), SFTP (FileZilla / WinSCP), or the Unraid **Tools → File Manager**. + +### 3. Add the stack + +1. Go to **Docker → Compose → Add New Stack** +2. Name: `ui-tracker` +3. Compose file path: `/mnt/user/appdata/ui-tracker/docker-compose.yml` +4. Click **Save** + +### 4. Build and start + +1. Expand the `ui-tracker` stack row +2. Click **Build** (takes a few minutes on first run) +3. Click **Start** + +### 5. Open the web UI + +``` +http://:8080 +``` + +--- + +## Manual Docker Run (No Compose) + +If you prefer to run it without docker-compose: + +```bash +docker build -t ui-tracker /mnt/user/appdata/ui-tracker + +docker run -d \ + --name ui-tracker \ + --restart unless-stopped \ + -p 8080:8080 \ + -v /mnt/user/appdata/ui-tracker/data:/app/data \ + ui-tracker +``` + +--- + +## Container Internals + +| Process | Role | +|-----------|------------------------------------------------| +| supervisord | Process manager — keeps both services alive | +| nginx | Serves the React frontend on port 8080, proxies `/api/` to Node.js | +| node | Express API + Puppeteer scraper + scheduler | + +--- + +## Port Reference + +| Host Port | Purpose | +|-----------|----------------------------------| +| **8080** | Web UI (only port you need open) | + +> To use a different port, change `"8080:8080"` to `":8080"` in `docker-compose.yml` before building. + +--- + +## Data Persistence + +The SQLite database lives at: + +``` +/mnt/user/appdata/ui-tracker/data/tracker.db +``` + +It is mounted into the container via the volume in `docker-compose.yml`. All tracked items and Telegram settings survive container restarts, rebuilds, and updates. + +**Backup:** +```bash +cp /mnt/user/appdata/ui-tracker/data/tracker.db ~/tracker-backup.db +``` + +--- + +## First-Time Setup + +Once the UI is open: + +1. Click **Settings** (top right) +2. Enter your **Bot Token**: `8769097441:AAFBqPlSTcTIi3I-F5ZIN9EEpwbNDzHg8hM` +3. Enter your **Chat ID**: `8435449432` +4. Click **Test Alert** — a Telegram message should arrive within seconds +5. Click **Save** +6. Click **Add Item**, paste a `store.ui.com` product URL, set your check interval, click **Start Tracking** + +--- + +## Troubleshooting + +**UI shows "Cannot reach backend"** +```bash +docker logs ui-tracker +``` +Look for Node.js startup errors. The backend starts on port 3001 internally — nginx proxies to it. + +**Telegram test fails** +- Verify the bot token and chat ID in Settings +- Send `/start` to your bot in Telegram at least once to open the conversation +- Confirm Unraid has outbound HTTPS access (port 443) + +**Items stuck on "Unknown" status** +```bash +docker logs ui-tracker | grep Scheduler +``` +Puppeteer errors here usually mean Chromium failed to launch. Try restarting: +```bash +docker compose restart +``` + +**Port 8080 already in use** +```bash +netstat -tulnp | grep 8080 +``` +Change the host port in `docker-compose.yml` and rebuild. + +--- + +## Updating + +```bash +cd /mnt/user/appdata/ui-tracker +git pull +docker compose up -d --build +``` + +The database and all your settings are preserved. diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index d528b68..0000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -# ── Build stage ────────────────────────────────────────────────────────────── -FROM node:20-slim AS builder - -RUN apt-get update && apt-get install -y \ - python3 make g++ \ - --no-install-recommends \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -COPY package*.json tsconfig.json ./ -RUN npm ci - -COPY src ./src -RUN npm run build && npm prune --production - -# ── Runtime stage ───────────────────────────────────────────────────────────── -FROM node:20-slim - -# Install Chromium and runtime dependencies -RUN apt-get update && apt-get install -y \ - chromium \ - fonts-freefont-ttf \ - --no-install-recommends \ - && rm -rf /var/lib/apt/lists/* - -ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium -ENV NODE_ENV=production - -WORKDIR /app - -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/node_modules ./node_modules -COPY package.json ./ - -EXPOSE 3001 -CMD ["node", "dist/index.js"] diff --git a/docker-compose.yml b/docker-compose.yml index b12a871..3412881 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,25 +1,11 @@ version: '3.8' services: - backend: - build: ./backend - container_name: ui-tracker-backend + ui-tracker: + build: . + container_name: ui-tracker restart: unless-stopped ports: - - "3001:3001" + - "8080:8080" volumes: - ./data:/app/data - environment: - - NODE_ENV=production - - PORT=3001 - - DATABASE_PATH=/app/data/tracker.db - - PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium - - frontend: - build: ./frontend - container_name: ui-tracker-frontend - restart: unless-stopped - ports: - - "8080:80" - depends_on: - - backend diff --git a/frontend/Dockerfile b/frontend/Dockerfile deleted file mode 100644 index 60a189c..0000000 --- a/frontend/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# ── Build stage ────────────────────────────────────────────────────────────── -FROM node:20-slim AS builder - -WORKDIR /app - -COPY package*.json ./ -RUN npm ci - -COPY . . -RUN npm run build - -# ── Runtime stage ───────────────────────────────────────────────────────────── -FROM nginx:stable-alpine - -COPY --from=builder /app/dist /usr/share/nginx/html -COPY nginx.conf /etc/nginx/conf.d/default.conf - -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/nginx.conf b/nginx.conf similarity index 81% rename from frontend/nginx.conf rename to nginx.conf index 0f3c143..8103720 100644 --- a/frontend/nginx.conf +++ b/nginx.conf @@ -1,5 +1,5 @@ server { - listen 80; + listen 8080; server_name _; root /usr/share/nginx/html; @@ -10,9 +10,9 @@ server { try_files $uri $uri/ /index.html; } - # Proxy API requests to backend + # Proxy API requests to the Node.js backend running in the same container location /api/ { - proxy_pass http://ui-tracker-backend:3001; + proxy_pass http://127.0.0.1:3001; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..35e3594 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,26 @@ +[supervisord] +nodaemon=true +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/tmp/supervisord.pid +user=root + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +autorestart=true +priority=10 + +[program:backend] +command=node /app/backend/dist/index.js +directory=/app/backend +environment=NODE_ENV="production",PORT="3001",DATABASE_PATH="/app/data/tracker.db",PUPPETEER_EXECUTABLE_PATH="/usr/bin/chromium" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +autorestart=true +priority=20