Initial commit: PodcastYes — AI podcast platform
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# Deploying PodcastYes on Plesk (Linux)
|
||||
|
||||
Single-VPS deployment: one Next.js web process + one worker process under PM2, an
|
||||
**external** Postgres, **local-disk** storage, and **no Redis**.
|
||||
|
||||
## 0. Prerequisites on the VPS
|
||||
|
||||
```bash
|
||||
# Node 20+ (Plesk → Tools & Settings → Node.js, or nvm)
|
||||
node -v # >= 20
|
||||
|
||||
# ffmpeg (the worker shells out to it for audio stitching)
|
||||
sudo apt update && sudo apt install -y ffmpeg
|
||||
ffmpeg -version
|
||||
|
||||
# PM2 (run outside Plesk's Passenger/Node extension)
|
||||
sudo npm install -g pm2
|
||||
```
|
||||
|
||||
## 1. Database
|
||||
|
||||
Create the database on your external Postgres host and put its URL in `.env` as
|
||||
`DATABASE_URL`. pg-boss auto-creates its own `pgboss` schema on first run.
|
||||
|
||||
## 2. Code + env
|
||||
|
||||
```bash
|
||||
cd /var/www/vhosts/<your-domain> # your vhost docroot
|
||||
git clone <repo> app && cd app # or Plesk Git deployment
|
||||
cp .env.example .env # then fill in real values
|
||||
npm ci
|
||||
```
|
||||
|
||||
Fill in `.env`: `DATABASE_URL`, `BETTER_AUTH_SECRET` (`openssl rand -base64 32`),
|
||||
`BETTER_AUTH_URL` / `NEXT_PUBLIC_APP_URL` (your https domain), `OPENAI_API_KEY`,
|
||||
`ELEVENLABS_API_KEY`, `STORAGE_DIR` (absolute, e.g.
|
||||
`/var/www/vhosts/<your-domain>/storage`), Stripe + PayPal keys, `RESEND_API_KEY`.
|
||||
|
||||
## 3. Migrate, seed, build
|
||||
|
||||
```bash
|
||||
npx prisma migrate deploy # create all tables
|
||||
npm run db:seed # populate the Plan catalog
|
||||
npm run build # next build (standalone) + postbuild asset copy
|
||||
mkdir -p "$STORAGE_DIR"/{mp3,art,exports}
|
||||
```
|
||||
|
||||
## 4. Run under PM2
|
||||
|
||||
```bash
|
||||
pm2 start ecosystem.config.js # starts podcastyes-web + podcastyes-worker
|
||||
pm2 save # remember the process list
|
||||
pm2 startup # print the systemd command to run once (resurrect on reboot)
|
||||
pm2 logs # tail logs
|
||||
```
|
||||
|
||||
The web process listens on `127.0.0.1:3000`; the worker consumes the pg-boss queue.
|
||||
|
||||
## 5. nginx (reverse proxy + public art)
|
||||
|
||||
Plesk → Domains → your domain → **Apache & nginx Settings** → *Additional nginx
|
||||
directives*. Paste `deploy/nginx-podcastyes.conf` (replace `PODCASTYES_DOMAIN` and
|
||||
the storage path). `proxy_buffering off` is required for the live progress stream (SSE).
|
||||
|
||||
## 6. SSL
|
||||
|
||||
Plesk → SSL/TLS Certificates → install **Let's Encrypt** for the domain. Make sure
|
||||
`BETTER_AUTH_URL` / `NEXT_PUBLIC_APP_URL` use `https://`.
|
||||
|
||||
## 7. Webhooks
|
||||
|
||||
- **Stripe**: Dashboard → Developers → Webhooks → add endpoint
|
||||
`https://<domain>/api/webhooks/stripe`, events `checkout.session.completed`,
|
||||
`customer.subscription.created/updated/deleted`. Put the signing secret in
|
||||
`STRIPE_WEBHOOK_SECRET`. Create the 3 paid Prices and set the `STRIPE_PRICE_*` env vars.
|
||||
- **PayPal**: create a subscription Product + Plans (Creator/Pro/Agency), set
|
||||
`PAYPAL_PLAN_*`. Add a webhook to `https://<domain>/api/webhooks/paypal` for the
|
||||
`BILLING.SUBSCRIPTION.*` events and set `PAYPAL_WEBHOOK_ID`.
|
||||
|
||||
## 8. First admin
|
||||
|
||||
```bash
|
||||
# After signing up in the app once:
|
||||
npm run make-admin you@email.com
|
||||
```
|
||||
|
||||
Then visit `/admin`.
|
||||
|
||||
## 9. Redeploys (zero-downtime)
|
||||
|
||||
```bash
|
||||
git pull
|
||||
npm ci
|
||||
npx prisma migrate deploy
|
||||
npm run build
|
||||
pm2 reload ecosystem.config.js
|
||||
```
|
||||
|
||||
## Backups
|
||||
|
||||
`STORAGE_DIR` (generated MP3s + art) is the only state not in Postgres — add it to a
|
||||
nightly offsite backup. The DB is your external Postgres provider's responsibility.
|
||||
|
||||
## Scaling later (optional)
|
||||
|
||||
- Move the worker to a second VPS pointing at the same `DATABASE_URL` — change nothing in code.
|
||||
- Swap local disk for S3/R2 by adding `lib/storage/s3.ts` and switching the registry in `lib/storage/index.ts`.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Plesk → Domains → <your-domain> → Apache & nginx Settings →
|
||||
# "Additional nginx directives". Paste the blocks below (adjust the storage path).
|
||||
#
|
||||
# Plesk already terminates SSL (Let's Encrypt) and proxies to Apache; these
|
||||
# directives make nginx proxy directly to the Next.js standalone server instead,
|
||||
# and serve public cover art straight from disk.
|
||||
|
||||
# Public cover art only (MP3s stay private, served via the authed /api/assets route).
|
||||
location /media/art/ {
|
||||
alias /var/www/vhosts/PODCASTYES_DOMAIN/storage/art/;
|
||||
expires 7d;
|
||||
add_header Cache-Control "public";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Proxy everything else to the Next.js web process (PM2 podcastyes-web on :3000).
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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;
|
||||
|
||||
# Critical for Server-Sent Events (episode generation progress stream):
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
Reference in New Issue
Block a user