# Chordly — deployment package

A self-contained production build of the app (chord analyser + downloader).
It needs **Node.js 20+** and nothing else — no install step, no `node_modules`
on the target machine beyond what ships in this folder.

---

## 1. Run the app

```bash
node server.js          # or: ./start.sh
```

It listens on `http://0.0.0.0:3000` by default (`PORT` and `HOSTNAME` override
those).

---

## 2. Put a web server in front of it

> ### If your site shows **"Index of /"**
>
> That means the web server is serving **this folder as static files**. A
> Next.js app has no `index.html` — the HTML is generated by Node — so the
> web server has nothing to show and falls back to a directory listing.
>
> The fix is to **proxy the domain to the Node process**. The app must be
> started (`node server.js`) *and* the web server must forward requests to it.
> Never point the document root at this folder.

Ready-made configs are in the `deploy/` folder of this package:

| Server | File | Install |
| --- | --- | --- |
| Nginx | `deploy/nginx.conf` | `sudo cp deploy/nginx.conf /etc/nginx/sites-available/chordly && sudo ln -s /etc/nginx/sites-available/chordly /etc/nginx/sites-enabled/chordly && sudo nginx -t && sudo systemctl reload nginx` |
| Caddy | `deploy/Caddyfile` | `sudo cp deploy/Caddyfile /etc/caddy/Caddyfile && sudo systemctl reload caddy` |
| Apache | `deploy/apache.conf` | `sudo cp deploy/apache.conf /etc/apache2/sites-available/chordly.conf && sudo a2enmod proxy proxy_http headers && sudo a2ensite chordly && sudo systemctl reload apache2` |

All three configs are already set for **cloudpal.live**. Before starting the
server, make sure DNS points at it:

| Record | Type | Value |
| --- | --- | --- |
| `cloudpal.live` | A | your server IP |
| `www.cloudpal.live` | A | your server IP |

Then enable HTTPS once the site responds:

```bash
# Nginx
sudo certbot --nginx -d cloudpal.live -d www.cloudpal.live
# Caddy does this for you automatically.
```

> **Keep `proxy_buffering off` (or `flush_interval -1` for Caddy).** Without it
> the web server collects the whole download before sending anything to the
> browser, and downloads appear to hang for minutes.

**cPanel / shared hosting:** set *Application root* to this folder, *Startup
file* to `server.js`, and the *document root* to `public` — never the folder
itself.

---

## 3. Keep it running

```bash
sudo cp deploy/chordly.service /etc/systemd/system/chordly.service
sudo systemctl daemon-reload
sudo systemctl enable --now chordly
sudo systemctl status chordly
```

Adjust `WorkingDirectory` in the unit file to where you unpacked the app.

Or with pm2: `pm2 start server.js --name chordly && pm2 save`.

---

## 4. Before you go live

1. **HTTPS.** The app downloads files and talks to YouTube, so TLS matters.
   Let's Encrypt works with all three configs above.
2. **Forward the client IP** if you sit behind a proxy — set `X-Forwarded-For`
   so the app knows the real address. The configs already do this.
3. **ffmpeg** must be installed on the machine for the fast download path
   (merging 1080p/4K video with audio, and MP3 conversion). Without it the
   downloader still works, but each file is fetched completely before the
   browser receives it.
   - Debian/Ubuntu: `sudo apt install ffmpeg`
   - RHEL/Alma: `sudo dnf install ffmpeg`
   - macOS: `brew install ffmpeg`
   - Windows: `winget install Gyan.FFmpeg`

---

## 5. AdSense

The AdSense script (`ca-pub-6240641071037146`) is already in the page head and
ad slots are placed on the landing, downloader and library pages.

**The app is disabled for visitors using an ad blocker** — a cosmetic filter
that removes ad containers is detected on load and the app is replaced with a
notice explaining what to allow. If you would rather show a dismissible
banner instead of blocking the app, remove the `<AdBlockGuard>` wrapper in
`src/app/layout.tsx` and rebuild.

Two things still need your attention:

1. **Ad unit slot id** — open `src/components/ads/ad-slot.tsx` and replace
   `REPLACE_WITH_YOUR_AD_SLOT_ID` with the id from your AdSense dashboard
   (AdSense → Ads → Ad units → the id looks like `1234567890`), then rebuild.
2. **ads.txt** — `public/ads.txt` ships with the publisher line. Check it
   matches the one in your AdSense account, and make sure it is served at
   `https://cloudpal.live/ads.txt` (it is, from the `public` folder).

---

## What's inside

```
server.js            the production server
start.sh             convenience launcher
.next/               compiled application (server + client assets)
public/              static files — brand logo, landing background, ads.txt
node_modules/        runtime dependencies, including the bundled yt-dlp binary
deploy/              nginx / Caddy / Apache configs and the systemd unit
DEPLOY.md            this file
```

## Environment variables

| Variable | Purpose |
| --- | --- |
| `PORT` | Port to listen on (default `3000`) |
| `HOSTNAME` | Interface to bind (default `0.0.0.0`) |
| `YTDLP_PATH` | Path to a `yt-dlp` binary, if you want a different one |
| `YOUTUBE_DL_DIR` | Folder holding the `yt-dlp` binary (default: the bundled one) |
| `YOUTUBE_DL_FILENAME` | Binary filename (default `yt-dlp`) |

## Troubleshooting

| Symptom | Cause |
| --- | --- |
| **"Index of /"** | The web server is serving the folder as static files. Proxy the domain to the Node server — see step 2. |
| Blank page, no CSS | Assets not served. Make sure `.next/static` and `public/` are in place next to `server.js`. |
| Downloads hang for minutes | The proxy is buffering. Set `proxy_buffering off` (Nginx) or `flush_interval -1` (Caddy). |
| "yt-dlp could not be started" | ffmpeg/yt-dlp path issue. Check `YTDLP_PATH`, or confirm `node_modules/youtube-dl-exec/bin/yt-dlp.exe` exists. |
| App works but no ads show | The ad slot id is still the placeholder — see step 5. |
| Ad blocker notice for a visitor with no blocker | Something on their network filters ad containers (a router-level filter). Nothing to do on the server. |

## Notes

- Analysis runs entirely in the visitor's browser; nothing is uploaded.
- The downloader streams files straight through, so no large temporary files
  are written to disk.
- Download and YouTube features need outbound internet access.
- Swap the logo by replacing `public/brand/cloudpal_logo.png` (or any image in
  `public/brand/`) — the app serves whichever image it finds there.
