# Deploy UVORA.NET on the existing VPS (alongside Growth OS)

## 1. Server preparation

```bash
mkdir -p /var/www/uvora-net
mkdir -p /var/www/uvora-net/logs
mkdir -p /var/www/uvora-net/storage/deliveries
mkdir -p /var/www/uvora-net/storage/tickets
mkdir -p /var/www/uvora-net/certs   # only if you enable MobilPay
```

## 2. Database

Create a separate PostgreSQL role and database, unrelated to Growth OS:

```bash
sudo -u postgres psql -c "CREATE USER uvora_net_user WITH PASSWORD 'strong_password';"
sudo -u postgres psql -c "CREATE DATABASE uvora_net OWNER uvora_net_user;"
psql -U uvora_net_user -d uvora_net -f database/schema.sql
```

The `uvora_net_user` role must have zero privileges on the Growth OS database.

## 3. Backend

```bash
cd /var/www/uvora-net
cp .env.example .env
# fill in every value in .env: DATABASE_URL, JWT secrets, Stripe/PayPal/etc keys
npm install --production
pm2 start ecosystem.config.js
pm2 save
```

Generate the JWT secrets with:
```bash
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"
```
Run it 3 times, for JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, JWT_ADMIN_SECRET.

## 4. Initial admin account

```bash
node -e "
require('dotenv').config();
const AdminUser = require('./backend/models/AdminUser');
AdminUser.create({ email: 'admin@uvora.net', password: 'ChangeThisPassword123!', full_name: 'Cosmin Gherase', role: 'super_admin' })
  .then(a => { console.log('Admin created:', a); process.exit(0); });
"
```

## 5. Frontend

```bash
cd /var/www/uvora-net/frontend
npm install
npm run build
```

The build output goes to `frontend/dist`, served directly by Nginx (see `nginx/uvora-net.conf`).

## 6. Nginx

```bash
cp nginx/uvora-net.conf /etc/nginx/sites-available/uvora-net.conf
ln -s /etc/nginx/sites-available/uvora-net.conf /etc/nginx/sites-enabled/
certbot --nginx -d uvora.net
nginx -t && systemctl reload nginx
```

## 7. Webhooks — provider-side configuration

- **Stripe**: Dashboard → Webhooks → endpoint `https://uvora.net/api/webhooks/stripe`, events `checkout.session.completed` and `checkout.session.expired`. Copy the signing secret into `STRIPE_WEBHOOK_SECRET`.
- **PayPal**: Developer Dashboard → Webhooks → endpoint `https://uvora.net/api/webhooks/paypal`, events `PAYMENT.CAPTURE.COMPLETED`, `PAYMENT.CAPTURE.DENIED`. Copy `PAYPAL_WEBHOOK_ID`.
- **MobilPay**: the callback is configured in the Netopia merchant panel when the account is activated, pointing to `https://uvora.net/api/webhooks/mobilpay`. Requires public/private certificates generated in their panel.
- **EuPlatesc**: the `silenturl` is sent directly in the payment form (see `EuPlatescProvider.js`), no separate panel configuration needed, but verify the hash field order against the documentation received at merchant activation.
- **Revolut**: Business Dashboard → API → Webhooks → endpoint `https://uvora.net/api/webhooks/revolut`, events `ORDER_COMPLETED`, `ORDER_PAYMENT_FAILED`.

## 8. Integration with Growth OS

Growth OS routes are mounted WITHOUT an `/api` prefix (e.g. `/auth`, `/leads`). The external
fulfillment module lives at `src/modules/external/` on the Growth OS side and exposes:

```
POST https://growth.uvora.cloud/external/generate-content   — receives order_id, service_key, brief, callback_url, failure_callback_url
GET  https://growth.uvora.cloud/external/jobs/:jobId/status  — best-effort status (not required, results arrive via callback)
```

UVORA.NET's own routes DO use `/api` (see `backend/server.js`), so the callback/failure URLs Growth OS
calls back to are, correctly:

```
POST https://uvora.net/api/external/job-complete
POST https://uvora.net/api/external/job-failed
```

Set `GROWTH_OS_API_URL=https://growth.uvora.cloud/external` (no `/api`) in `.env`.

Generate a dedicated API key for UVORA.NET → Growth OS (`GROWTH_OS_API_KEY`) and one for Growth OS → UVORA.NET (`GROWTH_OS_CALLBACK_KEY`). Don't reuse keys between the two directions. Both are already generated and stored on the Growth OS side — ask for the values to paste into `.env`.

### Supported `growth_os_service_key` values

Set one of these on each Service row in the admin panel — Growth OS routes generation based on this key:

| service_key             | Delivers | `brief` fields read |
|--------------------------|----------|----------------------|
| `article`                | HTML article | `topic`, `keywords`, `word_count`, `tone`, `language` |
| `website-copy`           | HTML website copy (hero, about, features, CTA) | `business_name`, `industry`, `tone`, `language` |
| `product-description`    | HTML product description | `product_name`, `features`, `tone`, `language` |
| `logo-design`            | Image file (logo) | `prompt` or `description`, `style`, `width`, `height` |
| `product-image`          | Image file (product photo) | `prompt` or `description`, `style`, `width`, `height` |
| `social-graphic`         | Image file (social post graphic) | `prompt` or `description`, `style`, `width`, `height` |
| `website-package`        | HTML bundle: website copy + generated logo + hero image | `business_name`, `industry`, `tone`, `language` |

Any other `service_key` value is rejected with `400` and the list of supported keys, so a typo in the
admin panel fails loudly at generation time rather than silently.

## 9. Final check

```bash
curl https://uvora.net/health
pm2 logs uvora-net --lines 50
```

## What NOT to do

- Don't connect UVORA.NET directly to the Growth OS PostgreSQL database.
- Don't reuse JWT_ACCESS_SECRET between the two applications.
- Don't put any payment secret in code, only in `.env`.
