# Borrar proyectos BewPro — guía operativa

> **Cuándo usar**: rollback de un test, limpieza de proyectos cancelados, recovery de un provision fallido, baja de un cliente que se da de baja del servicio.
>
> **Qué se borra**: cuenta cPanel + DB + archivos + cron tenant + DNS A-record + Stripe subscription + Airtable Project + Airtable Subscription.
>
> **Qué NO se borra** (manual): Stripe Customer (queda en Stripe sin sub activa), backups Backblaze (se conservan según retention).

---

## Inventario previo (antes de borrar nada)

Antes de cualquier borrado, listar todo lo que existe asociado al criterio (email, slug, cpanel_user). Correr **desde la Mac local con el repo cd-system** o desde VPS1.

### Listar Projects + Subscriptions por email

```bash
# Desde repo local cd-system
cd /Applications/XAMPP/xamppfiles/htdocs/cd-system
set -a && source .env && set +a

EMAIL="cliente@ejemplo.com"

echo "=== Projects ==="
curl -s -G -H "Authorization: Bearer $AIRTABLE_TOKEN" \
  --data-urlencode "filterByFormula={Email}='${EMAIL}'" \
  "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/$AIRTABLE_TABLE_ID" \
  | python3 -c "
import sys, json
d = json.load(sys.stdin)
for r in d.get('records', []):
    f = r.get('fields', {})
    print(f\"  {r['id']:<22} | {f.get('Name','?'):<30} | {f.get('Pipeline_Status','?'):<15} | {f.get('Cpanel_User','-'):<14} | {f.get('Domain','-')}\")
"

echo
echo "=== Subscriptions (por Cpanel_User listado arriba) ==="
# Reemplazar la lista USERS con los Cpanel_User encontrados arriba
USERS=(yastineditaatn cokegrw portofoliovqjr)
for u in "${USERS[@]}"; do
  curl -s -G -H "Authorization: Bearer $AIRTABLE_TOKEN" \
    --data-urlencode "filterByFormula={Cpanel_User}='${u}'" \
    "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/tblnpr52JhFBBi2Mg" \
    | python3 -c "
import sys, json
d = json.load(sys.stdin)
for r in d.get('records', []):
    f = r.get('fields', {})
    print(f\"  user=${u:<16} {r['id']:<22} | Status={f.get('Status','-'):<10} | Customer={f.get('Stripe_Customer_ID','-'):<22} | Sub={f.get('Stripe_Subscription_ID','-')}\")
"
done
```

### Listar cPanel accounts en ambos VPS

```bash
USERS=(yastineditaatn cokegrw portofoliovqjr)

echo "=== VPS1 ==="
ssh vps1-claude "for u in ${USERS[*]}; do id \$u >/dev/null 2>&1 && echo \"  EXISTS: \$u\" || echo \"  -      \$u\"; done"

echo "=== VPS2 ==="
ssh vps2-claude "for u in ${USERS[*]}; do id \$u >/dev/null 2>&1 && echo \"  EXISTS: \$u\" || echo \"  -      \$u\"; done"
```

### Listar DNS A-records en Hostinger

```bash
SUBDOMAINS=(yastin-edita coke portofolio-videos)

ssh vps1-claude 'source /root/scripts/.airtable.env && curl -s -H "Authorization: Bearer $HOSTINGER_TOKEN" \
  "https://developers.hostinger.com/api/dns/v1/zones/bewpro.com"' \
  | python3 -c "
import sys, json
SUBS = '${SUBDOMAINS[*]}'.split()
d = json.load(sys.stdin)
zone = d if isinstance(d, list) else d.get('zone', [])
for r in zone:
    if r.get('type') == 'A' and r.get('name') in SUBS:
        ips = [c.get('content','') for c in r.get('records', [])]
        print(f\"  {r['name']:<25} → {ips}\")
"
```

---

## Borrar UN proyecto

Datos requeridos:
- `CPANEL_USER` — usuario cPanel del tenant (ej. `cokegrw`)
- `SUBDOMAIN` — slug DNS (ej. `coke` para `coke.bewpro.com`)
- `STRIPE_SUB_ID` — ID Stripe de la subscription activa (ej. `sub_1TSITdLFhmxhEqlT...`)
- `AIRTABLE_PROJECT_ID` — record ID en Projects (ej. `recKAIViH8N85dGrW`)
- `AIRTABLE_SUB_ID` — record ID en Subscriptions (ej. `recPX1Zn6o78Ndz6w`)

> **Atajo**: si los completaste todos arriba, podés correr el flujo de un saque. Si no, corré los pasos uno a uno.

```bash
# Variables del proyecto a borrar
CPANEL_USER="cokegrw"
SUBDOMAIN="coke"
STRIPE_SUB_ID="sub_1TSITdLFhmxhEqlTJ9S8Ckun"
AIRTABLE_PROJECT_ID="recKAIViH8N85dGrW"
AIRTABLE_SUB_ID="recPX1Zn6o78Ndz6w"

cd /Applications/XAMPP/xamppfiles/htdocs/cd-system
set -a && source .env && set +a
```

### 1. Cancelar Stripe subscription (immediate)

```bash
curl -s -X DELETE -u "${STRIPE_SECRET}:" \
  "https://api.stripe.com/v1/subscriptions/${STRIPE_SUB_ID}" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print('  Stripe:', d.get('status','?'))"
```

Salida esperada: `Stripe: canceled`

### 2. Borrar cuenta cPanel (con backup automático)

```bash
# VPS1
ssh vps1-claude "/root/scripts/delete-project.sh ${CPANEL_USER} --yes"

# VPS2 — solo si la cuenta vive ahí
# ssh vps2-claude "/root/scripts/delete-project.sh ${CPANEL_USER} --yes"
```

> El script `delete-project.sh` ya hace pkgacct → Backblaze antes de borrar. También borra los records de Airtable Project + Subscription, **pero NO toca Stripe ni DNS Hostinger** (por eso los pasos 1, 3 y 4 son separados).
>
> Flags útiles:
> - `--dry-run` — muestra qué se haría sin ejecutar
> - `--keep-airtable` — solo borra cPanel, mantiene Airtable
> - `--skip-backup` — omite el backup (NO recomendado)
> - `--yes` — no pide confirmación

### 3. Borrar DNS A-record en Hostinger

```bash
ssh vps1-claude "source /root/scripts/.airtable.env && curl -s -X DELETE \
  -H \"Authorization: Bearer \$HOSTINGER_TOKEN\" \
  -H \"Content-Type: application/json\" \
  -d '{\"filters\":[{\"name\":\"${SUBDOMAIN}\",\"type\":\"A\"}]}' \
  https://developers.hostinger.com/api/dns/v1/zones/bewpro.com"
```

Salida esperada: `{"message":"Request accepted"}`

### 4. Borrar Airtable records (si `delete-project.sh` no los borró)

> Solo necesario si usaste `--keep-airtable`. Por defecto el script ya los borra.

```bash
# Subscription
curl -s -X DELETE -H "Authorization: Bearer $AIRTABLE_TOKEN" \
  "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/tblnpr52JhFBBi2Mg/${AIRTABLE_SUB_ID}"

# Project
curl -s -X DELETE -H "Authorization: Bearer $AIRTABLE_TOKEN" \
  "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/$AIRTABLE_TABLE_ID/${AIRTABLE_PROJECT_ID}"
```

### 5. Verificación final

```bash
echo "Stripe: $(curl -s -u \"${STRIPE_SECRET}:\" https://api.stripe.com/v1/subscriptions/${STRIPE_SUB_ID} | python3 -c \"import sys,json; print(json.load(sys.stdin).get('status','?'))\")"
echo "cPanel VPS1: $(ssh vps1-claude \"id ${CPANEL_USER} >/dev/null 2>&1 && echo STILL || echo GONE\")"
echo "DNS: $(dig +short A ${SUBDOMAIN}.bewpro.com @1.1.1.1 || echo GONE)"
echo "Airtable Project: $(curl -s -H \"Authorization: Bearer $AIRTABLE_TOKEN\" https://api.airtable.com/v0/$AIRTABLE_BASE_ID/$AIRTABLE_TABLE_ID/${AIRTABLE_PROJECT_ID} | grep -o '\"error\"' && echo GONE || echo STILL)"
```

Esperado:
- `Stripe: canceled`
- `cPanel VPS1: GONE`
- `DNS:` (vacío) o `GONE`
- `Airtable Project: GONE`

---

## Borrar MÚLTIPLES proyectos

Caso típico: limpiar todos los proyectos de un cliente (mismo email), o un set de tests.

### Opción A — script bulk existente (cubre cPanel + Airtable)

```bash
USERS="user1,user2,user3"

# VPS1
ssh vps1-claude "/root/scripts/delete-multiple-projects.sh ${USERS} --yes"

# VPS2 — si alguno vive ahí
# ssh vps2-claude "/root/scripts/delete-multiple-projects.sh ${USERS} --yes"
```

> Hace lo mismo que `delete-project.sh` por cada usuario en serie. **NO toca Stripe ni DNS** — eso lo cubre el bloque B.

### Opción B — flujo completo manual (Stripe + cPanel + DNS + Airtable)

```bash
cd /Applications/XAMPP/xamppfiles/htdocs/cd-system
set -a && source .env && set +a

# 1) Definir los 3 arrays alineados (mismo orden, misma longitud)
USERS=(yastineditaatn cokegrw portofoliovqjr productoartsmc ogyastimcno)
SUBDOMAINS=(yastin-edita coke portofolio-videos producto-art og-yastim)
STRIPE_SUBS=(sub_1TS47ALFhmxhEqlTv8eKQOi2 sub_1TSITdLFhmxhEqlTJ9S8Ckun sub_1TSHrDLFhmxhEqlTqmcTRB0G sub_1TRxVsLFhmxhEqlTWTWAOXwS sub_1TS1JTLFhmxhEqlTSw97vXFn)
AIRTABLE_PROJECTS=(rec3mmPMVorSLvatN recKAIViH8N85dGrW recLHzMNotdKBlQjR receqQVSXOYArssmc recoFGURq6TjQLCnO)
AIRTABLE_SUBS=(recGC3JMKJZmM9dvi recPX1Zn6o78Ndz6w recEaybzbebNUFZI2 recsVHIqnASjPKrkE recUYUWP9FY4mbIsS)

# 2) Stripe — cancelar todas
echo "=== Cancelando Stripe ==="
for sub in "${STRIPE_SUBS[@]}"; do
  RESP=$(curl -s -X DELETE -u "${STRIPE_SECRET}:" "https://api.stripe.com/v1/subscriptions/${sub}")
  STATUS=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('status','?'))" 2>/dev/null)
  echo "  ${sub:0:30}... → ${STATUS}"
done

# 3) cPanel — borrar todas (en VPS1, ajustar a vps2-claude si viven ahí)
echo "=== Borrando cPanel ==="
USERS_CSV=$(IFS=,; echo "${USERS[*]}")
ssh vps1-claude "/root/scripts/delete-multiple-projects.sh ${USERS_CSV} --yes"

# 4) DNS Hostinger — borrar todos los A-records
echo "=== Borrando DNS ==="
for sub in "${SUBDOMAINS[@]}"; do
  R=$(ssh vps1-claude "source /root/scripts/.airtable.env && curl -s -X DELETE \
    -H \"Authorization: Bearer \$HOSTINGER_TOKEN\" \
    -H \"Content-Type: application/json\" \
    -d '{\"filters\":[{\"name\":\"${sub}\",\"type\":\"A\"}]}' \
    https://developers.hostinger.com/api/dns/v1/zones/bewpro.com")
  echo "  ${sub} → ${R}"
done

# 5) Airtable Subscriptions (si --keep-airtable se usó arriba; sino skipear)
echo "=== Borrando Airtable Subs ==="
for rec in "${AIRTABLE_SUBS[@]}"; do
  R=$(curl -s -X DELETE -H "Authorization: Bearer $AIRTABLE_TOKEN" \
    "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/tblnpr52JhFBBi2Mg/${rec}")
  echo "  ${rec} → ${R}"
done

# 6) Airtable Projects
echo "=== Borrando Airtable Projects ==="
for rec in "${AIRTABLE_PROJECTS[@]}"; do
  R=$(curl -s -X DELETE -H "Authorization: Bearer $AIRTABLE_TOKEN" \
    "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/$AIRTABLE_TABLE_ID/${rec}")
  echo "  ${rec} → ${R}"
done
```

---

## Tablas Airtable de referencia

| Tabla | ID | Variable env |
|-------|----|--------------|
| Projects | `tblzCgJZCbbt5j13Q` | `$AIRTABLE_TABLE_ID` |
| Subscriptions | `tblnpr52JhFBBi2Mg` | (hardcoded en scripts) |
| Shop Products | `tblczbF5tQnq7SjZy` | — |
| Shop Products Copy | `tbldxXvyo1f7vIQp5` | — |

Base: `appRxvpzqCmNsw2JN` (BewPro 2.0)

---

## Gotchas

1. **Stripe Customer NO se borra** — queda en Stripe sin sub activa. Si el mismo email vuelve a comprar, Stripe puede reusar el Customer ID. Eso es OK; el upsert idempotente del orquestador lo maneja por `Stripe_Subscription_ID` (no Customer ID).

2. **DNS cache público (Google 8.8.8.8)** — TTL hasta 1h. Si después de borrar el record alguien con DNS Google sigue viendo el sitio viejo, es cache. Cloudflare 1.1.1.1 y Quad9 9.9.9.9 refrescan más rápido.

3. **Backups Backblaze** — el script `delete-project.sh` sube el `.tar.gz` antes de borrar. Si necesitás restaurar, los archivos quedan en el bucket configurado en WHM Backup Restoration. Retention según política de WHM.

4. **VPS donde vive el proyecto** — usar `whmapi1 listaccts | grep <user>` en cada VPS para confirmar antes. El script `delete-project.sh` hace fallback automático: prueba primero el VPS indicado en Airtable Server, luego el otro.

5. **`--dry-run` siempre primero** — antes de un borrado masivo, correr con `--dry-run` y revisar el plan.

6. **Webhook duplicate** — si después de borrar el record Airtable, el webhook Stripe sigue activo (sub aún viva en Stripe), volverá a crear el Subscription record. Por eso el orden Stripe → cPanel → DNS → Airtable.

---

## Comandos rápidos de auditoría

```bash
# ¿Cuántos cPanel users hay en cada VPS?
ssh vps1-claude '/usr/local/cpanel/bin/whmapi1 listaccts 2>/dev/null | grep -c "^      user:"'
ssh vps2-claude '/usr/local/cpanel/bin/whmapi1 listaccts 2>/dev/null | grep -c "^      user:"'

# ¿Cuántos Airtable Projects hay y por estado?
cd /Applications/XAMPP/xamppfiles/htdocs/cd-system && set -a && source .env && set +a
curl -s -H "Authorization: Bearer $AIRTABLE_TOKEN" \
  "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/$AIRTABLE_TABLE_ID?fields[]=Pipeline_Status&pageSize=100" \
  | python3 -c "
import sys, json, collections
d = json.load(sys.stdin)
c = collections.Counter(r['fields'].get('Pipeline_Status','?') for r in d.get('records', []))
for k, v in c.most_common(): print(f'  {k:<20} {v}')
"

# ¿Cuántas Subscriptions activas en Stripe?
curl -s -u "${STRIPE_SECRET}:" "https://api.stripe.com/v1/subscriptions?status=active&limit=100" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'  Active subs: {len(d.get(\"data\",[]))}')"
```
