# Operations, Deployments & Troubleshooting

This document provides step-by-step instructions for deploying, configuring, monitoring, and troubleshooting the **TFB Engine** on shared cPanel hosting.

---

## 1. Shared Hosting (cPanel) Deployment

The project is designed to have zero runtime Composer dependencies and can run on any cheap shared Apache server with PHP 8.1+.

### Folder Layout & Web Root
1. **Security Rule:** Apache's `DocumentRoot` should preferably point to the `public/` directory (Mode A), never the project root.
2. **Layered .htaccess Protection:** If your cPanel hosting does not allow pointing DocumentRoot to `/public/` and instead points to the project root (Mode B), the project-root `.htaccess` automatically rewrites all requests into the `/public/` directory, while strictly blocking direct public access to sensitive system directories and files.
3. Directories like `config/`, `src/`, and `storage/` are protected with their own localized `.htaccess` files containing `Require all denied` as a defense-in-depth measure.
4. Upload all files so that:
   - `/path/to/project/public/` is mapped to your domain's web root (e.g., `public_html/` or similar).
   - `/path/to/project/src/`, `config/`, `storage/`, `templates/` reside one level above public root.

---

## 2. Installation Wizard & Post-Install Cleanup

1. Place your configuration defaults (optional) inside `config/install.defaults.php`.
2. Visit `https://YOUR_DOMAIN/install.php` (Persian UI).
3. The wizard validates preflight requirements, tests connection to MySQL, creates the schema, seeds default settings, and attempts to register the Telegram webhook.
4. **MANDATORY Post-Install Cleanup:** After a successful install, the installer automatically triggers `InstallSelfCleanup` which:
   - Deletes `config/install.defaults.php` (sensitive defaults).
   - Deletes `public/install.php` (installer entry point).
   - *Fallback:* If file deletion fails due to directory permissions, the installer overwrites `public/install.php` with a locked, blank 403 stub so it cannot be re-run, and shows a loud warning.
5. **Security Check:** `AdminHealthController` has an `installer_exposed` CRITICAL check. Verify the installer is deleted or blocked before launching.

---

## 3. Webhook Registration & Verification

To verify or manually set up the webhook:
* **Webhook URL:** `https://YOUR_DOMAIN/index.php` (or `/` if index is mapped default).
* **Get Webhook Info:** Direct your browser to `https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getWebhookInfo` to verify `url`, `has_custom_certificate`, and `pending_update_count`.
* **Webhook Secret:** A unique `X-Telegram-Bot-Api-Secret-Token` is constant-time checked on every POST webhook request to verify the request originated from Telegram itself.

---

## 4. Logical Backups & Defends-In-Depth Restore

* **Backup artifact:** ZIP files in `storage/backup/` contain streamed `database.sql` plus `backup-meta.json` (app/schema revision; no secrets).
* **Basename-Only Safety:** Filenames are sanitized via `basename()` inside `resolveBackupPath()`.
* **Restore:** ZIP and legacy SQL are accepted after the explicit phrase confirmation. A safety backup and maintenance lock are created first; SQL is streamed with FK checks disabled only during import, then the current Migrator runs until pending=0. Backups from a future schema revision are refused.

---

## 5. Queue/Reminder Cron Job (cPanel)

Run `php /absolute/path/to/bin/pump.php` every minute. The command drains bounded broadcast batches and up to 25 due incomplete reminders; it never performs mass `getChatMember` calls. Reminder delay is stored in seconds (10 seconds is valid for testing), but actual delivery waits for the next cron tick. Completed/blocked/banned sessions are skipped and each user/test reminder row is one-time.

Update/downgrade use local secret-free code safety backups; Telegram upload is optional. ZIP compressed/uncompressed caps, unique staging, temp+rename file writes, lock refresh, rollback and concrete `storage/logs/update.log` failures are enforced. Health only checks cached schema state and never migrates. The minute pump has a global lock and performs 14-day idempotency retention, orphan cleanup, reminder claims and bounded broadcast slices.


For large audiences, the broadcast engine requires a cron job to automatically continue and pump running jobs without relying on manual admin clicks.

Set up a cPanel Cron Job running **every minute**:
```bash
* * * * * /usr/local/bin/php /home/username/public_html/bin/pump.php > /dev/null 2>&1
```
*(Adjust the PHP CLI path and the script path `/home/username/public_html/...` based on your exact cPanel file paths).*

---

## 6. Logs & Monitoring

All log files reside in `storage/logs/`. Production stage tracing is off unless `TFB_DEBUG` or app debug is enabled. App/error/diagnostic logs rotate at 512KB with three retained generations; identical warnings are limited to one per 60 seconds. Real errors remain in app/error/problems logs, and `Redaction` removes tokens/passwords/secrets.

Performance tracing is OFF by default. Temporarily enable `TFB_PERF_TRACE=1` or `app.perf_trace=true`; compact request metrics go to `storage/logs/perf.log` (512KB + 3 rotations). Disable after measurement. `transition_effect_ms` accepts 0..5000, but the webhook never sleeps for that duration.

---

## 7. Troubleshooting Common Issues

### A. "403 Forbidden" or "installer_disabled_after_install"
* **Cause:** You are trying to visit `install.php` after the installation has successfully locked or stubbed.
* **Fix:** This is correct behavior! To reinstall, you must delete `storage/install.lock` and restore the original `public/install.php` from your zip package.

### B. "Installed is false" or Redirect loop
* **Cause:** The file `config/app.php` is missing or unreadable, causing `App::isInstalled()` to return false and redirecting everything back to the installer.
* **Fix:** Check if the installer successfully generated `config/app.php`. Verify file permissions of the `config/` directory.

### C. Silent Bot (No replies from Telegram)
1. **DO NOT delete or rename `.htaccess` to fix a silent bot.** Doing so will expose your sensitive code, config secrets, and database backups to public web access! The current layered `.htaccess` setup is engineered to keep your bot fully functional with secure hardening enabled.
2. Check the webhook status via `getWebhookInfo` (see Section 3 above).
3. Look at `storage/logs/problems-*.log` to see if there are any webhook-auth (`E_WEBHOOK_AUTH`) or database connection (`E_DB_CONNECT`) errors.
4. Verify that the correct `X-Telegram-Bot-Api-Secret-Token` header matches between `config/app.php` and Telegram.
5. If you are using a subdirectory deployment (such as `https://YOUR_DOMAIN/test-tgads/public`), check that your webhook URL points to `/public/index.php` (e.g. `https://YOUR_DOMAIN/test-tgads/public/index.php`) and that parent directories are not overriding rewrites.

### D. Missing Admin Panel or Access Denied
* **Cause:** The ادمین Telegram ID is not included inside `admins.ids` list in `config/app.php`.
* **Fix:** Verify your Telegram ID via `@userinfobot` and add it to `admins.ids` array in `config/app.php`.

### E. Fake or Inflated Starts/Completes Stats
* **Cause:** Stale data or direct increments from old webhook retries.
* **Fix:** Run the "بازسازی آمار" (Rebuild Stats) tool from the admin health or backup screen to trigger `backfillFromAnalyticsEvents()`. This recalculates metrics directly from `test_user_stats` and `analytics_events`.

### F. Webhook Blocking and 403 Forbidden with .htaccess
* **Cause:** Old `.htaccess` files used an unconditional `Require all denied` in the project root, blocking all incoming webhook traffic.
* **Fix:** Ensure you have deployed the correct, smart root `.htaccess` file from the v1.4.2-hotfix release. It blocks direct access to system directories (`/config/`, `/src/`, `/storage/`, etc.) but allows traffic to cleanly flow to `/public/index.php`. It also ensures `/public/.htaccess` has `Require all granted` to permit public traffic.
