# راهنمای نصب و استقرار (فارسی)

این سند نصب کامل روی **هاست اشتراکی** و **سرور مجازی Ubuntu** را توضیح می‌دهد.

---

## ۱. پیش‌نیازها

| مورد | مقدار لازم |
|---|---|
| PHP | ۸.۲ یا بالاتر |
| افزونه‌های PHP | `pdo` + (`pdo_sqlite` یا `pdo_mysql`)، `mbstring`، `json`، `curl`، `openssl`، `fileinfo` |
| وب‌سرور | Apache 2.4 (با `mod_rewrite` و AllowOverride) یا Nginx |
| خط فرمان | دستور `php` برای اجرای cron (`bin/worker.php`) |
| سرور ماینکرفت | Paper یا Purpur (برای تحویل خودکار) |

بررسی از خط فرمان:

```bash
php -v
php -m | grep -Ei 'pdo|sqlite|mysql|mbstring|curl|openssl|fileinfo|json'
```

---

## ۲. امن‌ترین چیدمان پوشه‌ها

**DocumentRoot باید دقیقاً پوشهٔ `public/` باشد.** بقیهٔ پوشه‌ها (`app/`, `config/`,
`storage/`, `schema/`, `views/`, `bin/`, `tests/`) باید **بیرون** از مسیر وب باشند.

```
/home/user/
└── aquacoin-marketplace/     ← ریشهٔ پروژه (غیرقابل دسترسی از وب)
    ├── app/  config/  storage/  schema/  views/  bin/  tests/  docs/
    └── public/               ← DocumentRoot
        ├── index.php
        ├── install.php
        └── assets/
```

### اگر هاست فقط `public_html` دارد (بدون امکان تغییر DocumentRoot)

```
/home/user/
├── aquacoin-marketplace/     ← app/, config/, storage/, schema/, views/, bin/
└── public_html/              ← محتوای public/ را اینجا کپی کنید
```

سپس در `public/index.php` مسیر ریشه را اصلاح کنید:

```php
require dirname(__DIR__) . '/aquacoin-marketplace/app/bootstrap.php';
```

در `public/install.php` هم همین یک خط را به‌روزرسانی کنید.
(اگر در این حالت از Apache استفاده می‌کنید، فایل `.htaccess` خودِ پروژه نیز هست؛
در Nginx معادل rewrite را در ادامه آورده‌ایم.)

> ⚠ در این حالت حتماً از کار نکردن دسترسی وب به `config/config.php` مطمئن شوید:
> `https://example.com/../aquacoin-marketplace/config/config.php` نباید پاسخ ۲۰۰ بدهد.
> فایل‌های `.htaccess` و `index.html` داخل پوشه‌ها برای کاهش ریسک گذاشته شده‌اند،
> اما **جایگزین** بیرون بودن از DocumentRoot نیستند.

---

## ۳. مجوزهای فایل‌ها

```bash
cd aquacoin-marketplace

# پوشه‌هایی که برنامه باید در آن‌ها بنویسد
chmod 775 storage storage/logs storage/sessions storage/database storage/cache
# فقط در زمان نصب:
chmod 775 config

chown -R $USER:www-data storage config     # Ubuntu با PHP-FPM
```

پس از پایان نصب، دسترسی نوشتن را از `config/` بگیرید:

```bash
chmod 640 config/config.php
chmod 555 config
```

فایل‌ها و پوشه‌های دیگر: `chmod 644` برای فایل‌ها و `755` برای پوشه‌ها کافی است.

---

## ۴. اجرای نصاب

مرورگر را باز کنید:

```
https://example.com/install.php
```

گام‌ها:

1. **بررسی پیش‌نیازها** — نسخهٔ PHP و افزونه‌ها
2. **بررسی مجوزها** — نوشتن در `storage/*` و `config/`
3. **پایگاه داده** — SQLite (پیش‌فرض، بدون نیاز به تنظیم) یا MySQL
   (در حالت MySQL ابتدا دیتابیس و کاربر را بسازید و در cPanel «Remote MySQL» نیاز نیست چون اتصال لوکال است)
4. **حساب مدیر WinixStore** — نام کاربری، نام نمایشی، ایمیل، رمز عبور (حداقل ۱۰ کاراکتر، شامل حرف بزرگ/کوچک و عدد)
   - هیچ حساب پیش‌فرضی در کد وجود ندارد؛ مدیر اولیه فقط همین‌جا ساخته می‌شود.
   - رمز عبور با `Argon2ID` (یا `bcrypt`) ذخیره می‌شود و هرگز در لاگ نمی‌آید.
5. **زرین‌پال** — `merchant_id`، واحد پول `IRT` (تومان)، حالت سندباکس/تولید
6. **اتصال ماینکرفت** — `none` / `rcon` / `bridge` (تست اتصال **بدون** اعطای سکه انجام می‌شود)
7. **بررسی نهایی** — خلاصهٔ تنظیمات
8. **پایان نصب** — ایجاد جداول، ساخت مدیر، نوشتن `config/config.php`، ایجاد `storage/installed.lock`

پس از گام ۸:

```bash
rm public/install.php          # حتماً انجام شود
chmod 640 config/config.php
```

نصاب از این پس پاسخ **۴۰۳** می‌دهد و حتی اگر فایلش دوباره آپلود شود،
به‌دلیل وجود `storage/installed.lock` اجازهٔ ساخت مدیر جدید نخواهد داشت.

---

## ۵. Apache (Ubuntu VPS)

```apache
<VirtualHost *:80>
    ServerName shop.example.com
    DocumentRoot /home/user/aquacoin-marketplace/public

    <Directory /home/user/aquacoin-marketplace/public>
        AllowOverride All
        Require all granted
        DirectoryIndex index.php
    </Directory>

    <Directory /home/user/aquacoin-marketplace>
        Require all denied
    </Directory>

    ErrorLog  ${APACHE_LOG_DIR}/aquacoin-error.log
    CustomLog ${APACHE_LOG_DIR}/aquacoin-access.log combined
</VirtualHost>
```

```bash
sudo a2enmod rewrite
sudo a2ensite aquacoin.conf
sudo systemctl reload apache2
sudo certbot --apache -d shop.example.com
```

هدر امنیتی `Strict-Transport-Security` را پس از فعال شدن HTTPS اضافه کنید.

---

## ۶. Nginx + PHP-FPM (Ubuntu VPS)

```nginx
server {
    listen 80;
    server_name shop.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name shop.example.com;

    ssl_certificate     /etc/letsencrypt/live/shop.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;

    root /home/user/aquacoin-marketplace/public;
    index index.php;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # مسدودسازی فایل‌های حساس و پوشه‌های خصوصی
    location ~ /\.(?!well-known) { deny all; }
    location ~* \.(sqlite|sql|env|log|lock|ini)$ { deny all; }
    location ~* /(config|storage|app|schema|tests|bin)/ { deny all; }

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;   # نسخهٔ PHP خود را بگذارید
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~* \.(css|js|png|jpg|jpeg|svg|woff2?)$ {
        expires 30d;
        access_log off;
    }
}
```

> نکته: `Content-Security-Policy` و بقیهٔ هدرهای امنیتی توسط خود برنامه
> (در `Response::send()` و `Security::headers()`) ارسال می‌شوند؛ در Nginx آن‌ها را تکرار نکنید
> تا تداخل ایجاد نشود.

---

## ۷. MySQL/MariaDB (اختیاری)

```sql
CREATE DATABASE aquacoin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'aquacoin'@'localhost' IDENTIFIED BY 'یک-رمز-عبور-قوی';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, ALTER ON aquacoin.* TO 'aquacoin'@'localhost';
FLUSH PRIVILEGES;
```

در گام ۳ نصاب: درایور `mysql`، میزبان `localhost`، نام دیتابیس/کاربر/رمز را وارد کنید.
ساختار جدول‌ها در `schema/mysql.sql` است و نصاب آن را اجرا می‌کند.

---

## ۸. کارهای زمان‌بندی‌شده (Cron)

```bash
crontab -e
```

```cron
* * * * *  /usr/bin/php /home/user/aquacoin-marketplace/bin/worker.php    >> /dev/null 2>&1
15 3 * * * /usr/bin/php /home/user/aquacoin-marketplace/bin/reconcile.php >> /dev/null 2>&1
```

روی هاست اشتراکی از بخش «Cron Jobs» در cPanel/DirectAdmin استفاده کنید و
مسیر کامل `php` را از هاست بپرسید (مثل `/usr/local/bin/php` یا `/opt/alt/php82/usr/bin/php`).

بدون cron هم برنامه کار می‌کند (تحویل در لحظهٔ تأیید پرداخت تلاش می‌شود)،
اما برای تلاش‌های مجدد و تطبیق، cron اکیداً توصیه می‌شود.

---

## ۹. تنظیم تحویل به ماینکرفت

دو روش پشتیبانی می‌شود (در پنل ادمین → تنظیمات):

### الف) Bridge (توصیه می‌شود — پشتیبانی از کلید یکتا)

1. فایل `minecraft-plugin/AquaCoinBridge-1.0.0.jar` را در پوشهٔ `plugins/` سرور قرار دهید.
2. سرور را یک بار اجرا و متوقف کنید تا `plugins/AquaCoinBridge/config.yml` ساخته شود.
3. مقدار `bridge.secret` را به یک رشتهٔ تصادفیِ طولانی تغییر دهید و
   `grant-command` را با افزونهٔ اقتصادی خود هماهنگ کنید.
4. اگر وب‌سرور و ماینکرفت **روی یک دستگاه** هستند: `host: 127.0.0.1` را نگه دارید.
   اگر روی دو دستگاه‌اند: از WireGuard/Tailscale استفاده کنید و `host` را آدرس VPN بگذارید؛
   پورت `8791` را هرگز روی اینترنت باز نکنید.
5. در پنل ادمین: درایور `bridge`، آدرس `http://127.0.0.1:8791/deliver`، همان `secret`.

### ب) RCON

در `server.properties`:

```properties
enable-rcon=true
rcon.password=یک-رمز-عبور-قوی-و-طولانی
rcon.port=25575
```

```bash
# فقط از IP وب‌سرور اجازه داده شود
sudo ufw allow from <IP-وب‌سرور> to any port 25575 proto tcp
```

در پنل ادمین: درایور `rcon`، میزبان/پورت/رمز RCON و قالب فرمان (پیش‌فرض
`giveaquacoin %s %d`). افزونهٔ همراه فرمان `/giveaquacoin` را ثبت می‌کند.

> **محدودیت مستند‌شده:** پروتکل RCON کلید یکتا (Idempotency) ندارد. اگر سرور در حین اجرا قطع شود،
> وضعیت کار تحویل «نامشخص» (Manual Review) ثبت می‌شود و هرگز به‌طور خودکار بازارسال نمی‌شود؛
> تأیید نهایی با ادمین است. برای تلاش مجددِ خودکارِ ایمن، از روش Bridge استفاده کنید.

---

## ۱۰. تنظیمات پس از نصب (پنل ادمین → تنظیمات)

| تنظیم | توضیح |
|---|---|
| قیمت واحد (تومان) | پیش‌فرض `۱۰۰۰` |
| حداقل تعداد | پیش‌فرض `۱۰` |
| حداکثر تعداد | پیش‌فرض `۵۰۰۰` (قابل تغییر توسط ادمین) |
| واحد پول درگاه | `IRT` (تومان) — زرین‌پال برای `IRR` مبلغ را ریال می‌گیرد |
| حالت سندباکس | برای تست؛ در تولید خاموش شود |
| `merchant_id` | UUID ۳۶ کاراکتری زرین‌پال |
| درایور تحویل | `bridge` / `rcon` / `none` |
| قالب فرمان | پیش‌فرض `giveaquacoin %s %d` |
| حداکثر تلاش مجدد | پیش‌فرض `۳` |
| محدودیت ورود | پیش‌فرض ۵ تلاش در ۱۵ دقیقه |
| محدودیت نرخ | پیش‌فرض ۱۰ درخواست در ۱۰ دقیقه برای فرم‌ها |
| حالت تعمیر و نگهداری | نمایش صفحهٔ `maintenance` به کاربران |
| آدرس پایه (`app.base_url`) | اگر در زمان نصب نشانی لوکال باشد، مقدار خالی ذخیره می‌شود و آدرس در هر درخواست تشخیص داده می‌شود؛ برای تثبیت مقدار از متغیر محیطی `WINIX_BASE_URL` استفاده کنید |

---

## ۱۱. بررسی سلامت و نگهداری

* پنل ادمین → **سلامت سیستم**: وضعیت دیتابیس، مجوز پوشه‌ها، درگاه، اتصال ماینکرفت (بدون نمایش رازها)
* پنل ادمین → **لاگ‌ها**: آخرین خطاها و رویدادها
* `php bin/reconcile.php` — گزارش تطبیق (اختلاف سفارش/تحویل)
* بکاپ روزانه از `storage/database/app.sqlite` (یا دیتابیس MySQL) + `config/config.php`
* پاک‌سازی نشست‌های قدیمی: فایل‌های `storage/sessions/*` قدیمی‌تر از ۲۴ ساعت را حذف کنید

---

## ۱۲. عیب‌یابی

| نشانه | علت احتمالی | راه‌حل |
|---|---|---|
| صفحهٔ سفید | خطای PHP پنهان | `storage/logs/app-*.log` را ببینید؛ در تولید خطاها هرگز نمایش داده نمی‌شوند |
| ۴۰۴ روی همهٔ صفحات | `mod_rewrite`/rewrite Nginx فعال نیست | `a2enmod rewrite` یا بررسی `try_files` |
| ۴۱۹ (نشست منقضی) | توکن CSRF قدیمی/نبود کوکی | صفحه را رفرش و دوباره تلاش کنید؛ دامنهٔ کوکی را بررسی کنید |
| پرداخت تأیید نمی‌شود | `merchant_id` اشتباه، حالت سندباکس، یا مبلغ کمتر از حداقل | لاگ `payment.*` و پاسخ درگاه را ببینید |
| تحویل ناموفق | RCON/Bridge در دسترس نیست | مسیر شبکه، فایروال و `secret` را بررسی کنید |
| خطای دسترسی به دیتابیس | مجوز نوشتن در `storage/database` | `chmod 775 storage/database` و مالکیت را اصلاح کنید |
