# Zoom S3 Pipeline — Local Testing Guide

## Prerequisites

- macOS with Homebrew
- Local Moodle running on custom domain (e.g. `http://vlearn`)
- Zoom account with cloud recording enabled
- AWS S3 access credentials

---

## 1. Ngrok Setup

```bash
# Install ngrok
brew install ngrok

# Sign up at https://dashboard.ngrok.com/signup
# Get authtoken from https://dashboard.ngrok.com/get-started/your-authtoken

# Add your authtoken
ngrok config add-authtoken <your-authtoken>

# Start ngrok (--host-header needed because Moodle runs on custom domain "vlearn")
ngrok http --host-header=vlearn 80

# Copy the https URL it gives you (e.g. https://xxxxx.ngrok-free.dev)
# Keep this terminal open — ngrok must stay running
```

## 2. Zoom Webhook App (for local testing)

1. Go to https://marketplace.zoom.us/develop/apps
2. Click **Develop** > **Build App** > **Webhook Only**
3. Name: "VLearn Local Test"
4. Under **Feature** > **Event Subscriptions**, add subscription:
   - Subscription name: Zoom recording complete
   - Endpoint URL: `https://xxxxx.ngrok-free.dev/local/zooms3/payload_zoom_updated.php`
   - Event: **Recording** > **All Recordings have completed**
5. Copy the **Secret Token** from the app
6. Update the webhook file with the new secret token:
   - `local/zooms3/payload_zoom_updated.php` line 28:
   - `$webhookSecretToken = '<new-secret-token>';`
7. Click **Validate** — should pass
8. If validation fails, check ngrok inspector at http://127.0.0.1:4040

**NOTE:** Use `payload_zoom_updated.php` (not `payload_zoom_temp.php`) for testing. This keeps the production endpoint untouched.

### Known Issue: Header Casing

nginx sends headers in title case (`X-Zm-Signature`) but code originally expected lowercase (`x-zm-signature`). Fix already applied using `array_change_key_case()`. If you're on a fresh codebase, make sure this line exists:

```php
$rawHeaders = getallheaders();
$headers = array_change_key_case($rawHeaders, CASE_LOWER);
```

## 3. Moodle DB Table Setup

```bash
# Make sure version.php is bumped to 2026051800
# Make sure db/install.xml and db/upgrade.php exist

# Run Moodle upgrade to create the table
php admin/cli/upgrade.php

# Verify
mysql -u sagar -p vlearn -e "DESCRIBE mdl_local_zooms3_recording_sync;"
```

## 4. Python Script Setup

```bash
cd server_scripts/zoom_s3_sync

# macOS (Homebrew Python) blocks global pip install.
# Must use virtual environment.
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

# Create .env from template
cp .env.example .env
```

Edit `.env` with **local** values:

```bash
# Database
DB_HOST=localhost
DB_PORT=3306
DB_USER=sagar
DB_PASSWORD=<your local mysql password>
DB_NAME=vlearn
DB_TABLE_PREFIX=mdl_

# AWS S3
S3_BUCKET=zoom-recodings
S3_REGION=ap-south-1
S3_PREFIX=test-recordings/
# Use test-recordings/ prefix to avoid mixing with production data
AWS_ACCESS_KEY_ID=<your AWS key>
AWS_SECRET_ACCESS_KEY=<your AWS secret>

# Paths (macOS specific)
MOODLE_DATA_DIR=/Users/sagarsingh/Web/data/vlearn_upgrade_data
MOODLE_DIR=/Users/sagarsingh/Web/http/vlearn_upgrade
PHP_BIN=/opt/homebrew/bin/php

# Processing
MAX_WORKERS=3
TOKEN_SAFETY_BUFFER_HOURS=20
LOCK_FILE=/tmp/zoom_s3_sync.lock
LOG_FILE=/tmp/zoom_s3_sync.log
WEBHOOK_TEMP_DIR=zoom_recordings_webhook
```

### Known Issues on macOS

- **LOG_FILE:** `/var/log/zoom_s3_sync.log` fails with `PermissionError`. Use `/tmp/zoom_s3_sync.log` instead.
- **PHP_BIN:** `/usr/bin/php` doesn't exist on macOS with Homebrew. Use `/opt/homebrew/bin/php` (check with `which php`).

## 5. Test End-to-End

```bash
# Make sure ngrok is running in another terminal

# Step 5a: Trigger recording
#   Start a Zoom meeting > Record (cloud) > End meeting
#   Wait 1-2 min for Zoom to process

# Step 5b: Verify webhook received
#   Check ngrok inspector: http://127.0.0.1:4040
#   Should see POST with event: recording.completed, status 200

# Step 5c: Verify DB row
mysql -u sagar -p vlearn -e \
  "SELECT id, meeting_id, topic, status, recording_type
   FROM mdl_local_zooms3_recording_sync ORDER BY id DESC LIMIT 5;"
# Expected: status = pending

# Step 5d: Verify webhook.json saved
ls /Users/sagarsingh/Web/data/vlearn_upgrade_data/temp/zoom_recordings_webhook/
# Should see folder with meeting ID

# Step 5e: Run Python script
cd server_scripts/zoom_s3_sync
source venv/bin/activate
python3 zoom_s3_sync.py
# Expected log:
#   [Meeting XXXXX] Processing started...
#   [Meeting XXXXX] Starting S3 upload: test-recordings/XXXXX/original.mp4
#   [Meeting XXXXX] S3 upload completed: test-recordings/XXXXX/original.mp4
#   [Meeting XXXXX] Uploaded webhook.json to S3: test-recordings/XXXXX/webhook.json
#   [Meeting XXXXX] Deleted temp folder...
#   [Meeting XXXXX] Completed successfully.
#   [Meeting XXXXX] Result: SUCCESS

# Step 5f: Verify S3 upload
aws s3 ls s3://zoom-recodings/test-recordings/<meeting_id>/ --region ap-south-1
# Expected: original.mp4 + webhook.json

# Step 5g: Verify DB updated
mysql -u sagar -p vlearn -e \
  "SELECT id, meeting_id, status, s3_path
   FROM mdl_local_zooms3_recording_sync ORDER BY id DESC LIMIT 5;"
# Expected: status = completed, s3_path = test-recordings/<meeting_id>/original.mp4

# Step 5h: Verify temp folder cleaned
ls /Users/sagarsingh/Web/data/vlearn_upgrade_data/temp/zoom_recordings_webhook/
# Meeting folder should be gone

# Step 5i: Check logs
cat /tmp/zoom_s3_sync.log
```

### Token Recovery Test

If a recording's download_token has expired (>24 hours old), the Python script automatically calls the PHP CLI recovery script. You should see:

```
[Meeting XXXXX] Token expired or nearing expiry. Attempting recovery.
[Meeting XXXXX] Recovering download URL via Moodle CLI...
[Meeting XXXXX] Recovery successful. Recording type: shared_screen_with_speaker_view, size: XXXXX
```

This only works if:
- The recording still exists on Zoom (not past retention period)
- Zoom OAuth credentials are configured in Moodle admin (Site admin > Plugins > Zoom)

## 6. Cleanup After Testing

```bash
# Stop ngrok (Ctrl+C in the ngrok terminal)

# Deactivate Python venv
deactivate

# Optionally delete test Zoom app from marketplace
# Optionally clean test rows from DB:
#   DELETE FROM mdl_local_zooms3_recording_sync WHERE meeting_id = 99999999;
```

## Isolation Summary

Both production and test flows run simultaneously without interference:

| Component | Current Prod | Local Test |
|-----------|-------------|------------|
| Webhook endpoint | `payload_zoom_temp.php` | `payload_zoom_updated.php` |
| Zoom app secret | `noETtCC0QuaF6iIQgY97mg` | `KHWlYouTQ7GRM-lRZ66IeQ` |
| Temp folder | `zoom_webhook/` | `zoom_recordings_webhook/` |
| S3 path | `s3://zoom-recodings/<id>/` | `s3://zoom-recodings/test-recordings/<id>/` |
| Sync script | `sync_and_delete.sh` | `zoom_s3_sync.py` |
