# Промпт: Развёртывание клона сервера X2Chat (Application Server)

> **Цель**: Развернуть полную копию Application Server X2Chat на чистом Ubuntu 24.04 сервере.
> БД (PostgreSQL, MongoDB, Redis, Cassandra/ScyllaDB, Kafka, Elasticsearch) находятся на **ОТДЕЛЬНОМ** сервере.

---

## Плейсхолдеры (заменить перед выполнением)

| Плейсхолдер | Описание | Пример |
|---|---|---|
| `<APP_SERVER_IP>` | Внутренний IP нового сервера приложений | `172.29.89.XXX` |
| `<APP_SERVER_PUBLIC_IP>` | Публичный IP нового сервера (или через GA) | `1.2.3.4` |
| `<APP_DOMAIN>` | Домен нового сервера (или тот же ix.x2chat.com) | `ix.x2chat.com` |
| `<DB_SERVER_IP>` | IP сервера баз данных | `172.29.89.214` |
| `<GITEA_IP>` | IP Gitea сервера (внутренний) | `172.29.89.220` |
| `<ORIGIN_SERVER_IP>` | IP исходного сервера (для scp файлов) | `172.29.89.216` |

---

## Часть 0: Креденшлы баз данных (НЕ менять!)

БД на отдельном сервере `<DB_SERVER_IP>` с теми же доступами:

### PostgreSQL
- **Host**: `<DB_SERVER_IP>`
- **Port**: `5433`
- **User**: `messenger_user`
- **Password**: `secure_password_123`
- **Database**: `messenger_users`
- **SSL**: `false`

### MongoDB
- **URI**: `mongodb://mongo_admin:mongo_password_123@<DB_SERVER_IP>:27017/messenger_media?authSource=admin`
- **User**: `mongo_admin`
- **Password**: `mongo_password_123`
- **Database**: `messenger_media`

### Redis
- **Host**: `<DB_SERVER_IP>`
- **Port**: `6379`
- **Password**: `redis_password_123`

### Cassandra (ScyllaDB)
- **Contact Points**: `<DB_SERVER_IP>`
- **Local DC**: `datacenter1`
- **Keyspace**: `messenger`

### Kafka
- **Brokers**: `<DB_SERVER_IP>:9092`
- **Client ID**: `messenger-backend`

### Elasticsearch
- **Node**: `http://<DB_SERVER_IP>:9200`

---

## Часть 1: Базовые пакеты и системные зависимости

```bash
apt update && apt upgrade -y

# Основные пакеты
apt install -y \
  build-essential \
  git \
  curl \
  wget \
  unzip \
  pkg-config \
  libssl-dev \
  cmake \
  nginx \
  certbot \
  python3-certbot-nginx \
  coturn \
  openjdk-17-jdk \
  clang \
  llvm \
  lld

# Node.js 18 (LTS) — через NodeSource
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
apt install -y nodejs
# Проверка:
node --version   # ожидаем v18.x
npm --version
```

---

## Часть 2: Flutter SDK + Android SDK

### Flutter 3.38.3 (stable)
```bash
mkdir -p /opt
cd /opt
git clone https://github.com/flutter/flutter.git -b 3.38.3 --depth 1
export PATH="/opt/flutter/bin:$PATH"
echo 'export PATH="/opt/flutter/bin:$PATH"' >> /root/.bashrc

flutter precache
flutter doctor
```

### Android SDK
```bash
mkdir -p /opt/android-sdk/cmdline-tools
cd /opt/android-sdk/cmdline-tools

# Скачать командные инструменты (актуальная ссылка с https://developer.android.com/studio#command-tools)
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O cmdline-tools.zip
unzip cmdline-tools.zip
mv cmdline-tools latest
rm cmdline-tools.zip

export ANDROID_HOME="/opt/android-sdk"
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"
echo 'export ANDROID_HOME="/opt/android-sdk"' >> /root/.bashrc
echo 'export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"' >> /root/.bashrc

# Принять лицензии
yes | sdkmanager --licenses

# Установить SDK компоненты
sdkmanager "platform-tools" \
  "platforms;android-35" \
  "platforms;android-34" \
  "platforms;android-33" \
  "build-tools;35.0.0" \
  "build-tools;34.0.0" \
  "ndk;28.2.13676358" \
  "cmake;3.22.1"

flutter doctor --android-licenses
flutter doctor
```

---

## Часть 3: Rust (для QUIC relay)

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
rustc --version   # ожидаем 1.94+
```

---

## Часть 4: Клонирование репозитория и SSH-ключи для Gitea

### SSH-ключ для Gitea
Скопировать SSH-ключ с исходного сервера:
```bash
mkdir -p ~/.ssh
chmod 700 ~/.ssh

# Скопировать ключи с исходного сервера
scp root@<ORIGIN_SERVER_IP>:~/.ssh/gitea_ed25519 ~/.ssh/gitea_ed25519
scp root@<ORIGIN_SERVER_IP>:~/.ssh/gitea_ed25519.pub ~/.ssh/gitea_ed25519.pub
chmod 600 ~/.ssh/gitea_ed25519
chmod 644 ~/.ssh/gitea_ed25519.pub

# Также скопировать ключ для доступа к БД серверу
scp root@<ORIGIN_SERVER_IP>:~/.ssh/ESC-test.pem ~/.ssh/ESC-test.pem
chmod 600 ~/.ssh/ESC-test.pem
```

### SSH Config
```bash
cat > ~/.ssh/config << 'EOF'
Host db
    HostName <DB_SERVER_IP>
    User root
    IdentityFile ~/.ssh/ESC-test.pem
    Port 22
    IdentitiesOnly yes

Host db.x2chat.com
    User root
    IdentityFile ~/.ssh/ESC-test.pem
    IdentitiesOnly yes

Host git.x2chat.com
    User git
    IdentityFile ~/.ssh/gitea_ed25519
    IdentitiesOnly yes

Host <GITEA_IP>
    User git
    IdentityFile ~/.ssh/gitea_ed25519
    IdentitiesOnly yes
    StrictHostKeyChecking no
EOF
chmod 600 ~/.ssh/config
```

### Клонирование репозитория
```bash
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/gitea_ed25519

# Проверить соединение
ssh -T git@<GITEA_IP>

# Клонировать
git clone git@<GITEA_IP>:administrator/x2chat.git /opt/x2chat
cd /opt/x2chat

# Настроить remote-ы
git remote rename origin gitea-internal
git remote add origin git@git.x2chat.com:administrator/x2chat.git

# Checkout нужной ветки
git checkout master
# или: git checkout wechat-go
```

### Обновление кода (синхронизация):
```bash
# Для обновления до последней версии:
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/gitea_ed25519
cd /opt/x2chat
git pull gitea-internal master
# Или из внешнего:
git pull origin master
```

---

## Часть 5: Backend (Node.js API)

### Установка зависимостей
```bash
cd /opt/x2chat/backend
npm install
```

### Файл .env
Создать `/opt/x2chat/backend/.env`:
```bash
cat > /opt/x2chat/backend/.env << 'EOF'
APP_PORT=8080
NODE_ENV=production

# Frontend URL (для CORS)
APP_BASE_URL=https://<APP_DOMAIN>
FRONTEND_URL=http://<APP_DOMAIN>:3100

# --- PostgreSQL ---
POSTGRES_HOST=<DB_SERVER_IP>
POSTGRES_PORT=5433
POSTGRES_USER=messenger_user
POSTGRES_PASSWORD=secure_password_123
POSTGRES_DB=messenger_users
POSTGRES_SSL=false

# --- MongoDB (Media) ---
MONGO_URI=mongodb://mongo_admin:mongo_password_123@<DB_SERVER_IP>:27017/messenger_media?authSource=admin

# --- Redis ---
REDIS_HOST=<DB_SERVER_IP>
REDIS_PORT=6379
REDIS_PASSWORD=redis_password_123

# --- Cassandra (Messages) ---
CASSANDRA_CONTACT_POINTS=<DB_SERVER_IP>
CASSANDRA_LOCAL_DC=datacenter1
CASSANDRA_KEYSPACE=messenger

# --- Kafka ---
KAFKA_BROKERS=<DB_SERVER_IP>:9092
KAFKA_CLIENT_ID=messenger-backend

# --- Elasticsearch ---
ELASTICSEARCH_NODE=http://<DB_SERVER_IP>:9200

# --- JWT ---
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d

# --- AI / System ---
AI_ASSISTANT_ENABLED=true

# --- TURN Server (WebRTC) ---
TURN_SERVERS=turn:<APP_DOMAIN>:3478?transport=udp,turn:<APP_DOMAIN>:3478?transport=tcp
TURN_SHARED_SECRET=347f15de9ba619701fcf5dc3b40cc2b570eaa7116292584702cd2ac663792460
TURN_REALM=<APP_DOMAIN>
AI_PROVIDER_SECRET=x2chat_ai_secret_key_1764276696

# --- OSS Updates (Alibaba OSS) ---
OSS_UPDATE_BUCKET=x2chat-update
OSS_UPDATE_ENDPOINT=oss-eu-central-1.aliyuncs.com
OSS_UPDATE_PREFIX=apk
OSS_UPDATE_SIGNED_EXPIRES_SEC=3600
OSS_UPDATE_ACCESS_KEY_ID=LTAI5tMarp72vaXzZdcay5A8
OSS_UPDATE_ACCESS_KEY_SECRET=o8NqisSBiQCu3AepX5kpsOzKGDSlLb

# --- APNS (iOS Push) ---
APNS_ENABLED=true
APNS_KEY_PATH=/opt/x2chat/backend/config/apns/AuthKey_3TPQP33HLQ.p8
APNS_KEY_ID=3TPQP33HLQ
APNS_TEAM_ID=9FN67TG98U
APNS_BUNDLE_ID=com.x2chat.app
APNS_PRODUCTION=false
EOF
chmod 600 /opt/x2chat/backend/.env
```

### Конфиг-файлы (скопировать с исходного сервера)
```bash
# APNS ключ
mkdir -p /opt/x2chat/backend/config/apns
scp root@<ORIGIN_SERVER_IP>:/opt/x2chat/backend/config/apns/AuthKey_3TPQP33HLQ.p8 \
    /opt/x2chat/backend/config/apns/AuthKey_3TPQP33HLQ.p8
chmod 600 /opt/x2chat/backend/config/apns/AuthKey_3TPQP33HLQ.p8

# Firebase Service Account
scp root@<ORIGIN_SERVER_IP>:/opt/x2chat/backend/config/firebaseServiceAccountKey.json \
    /opt/x2chat/backend/config/firebaseServiceAccountKey.json
```

### Systemd сервис для Backend
```bash
cat > /etc/systemd/system/x2chat-backend.service << 'EOF'
[Unit]
Description=X2Chat Backend API
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
User=root
WorkingDirectory=/opt/x2chat/backend
ExecStartPre=/usr/bin/bash -lc 'fuser -k 8080/tcp >/dev/null 2>&1 || true'
ExecStart=/usr/bin/node --import ./load-env.js src/index.js
Restart=always
RestartSec=2
TimeoutStopSec=30
KillMode=mixed
KillSignal=SIGTERM
ExecStartPost=/usr/bin/bash -lc 'for i in {1..45}; do curl --connect-timeout 1 --max-time 1 -fsS http://127.0.0.1:8080/health >/dev/null 2>&1 && exit 0; sleep 1; done; echo "x2chat-backend readiness check timeout" >&2; exit 1'
StandardOutput=append:/var/log/x2chat/backend.log
StandardError=append:/var/log/x2chat/backend.log
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
EOF

mkdir -p /var/log/x2chat/client-logs
systemctl daemon-reload
systemctl enable x2chat-backend
systemctl start x2chat-backend
```

### Проверка Backend
```bash
sleep 5
curl -s http://localhost:8080/health
# Ожидаем: {"status":"ok"...}
```

---

## Часть 6: Nginx + SSL (Let's Encrypt)

### Структура веб-директорий
```bash
mkdir -p /var/www/x2chat.com/{downloads,api,admin,privacy}
mkdir -p /opt/x2chat/uploads

# Скопировать лендинг и статику с исходного сервера
scp root@<ORIGIN_SERVER_IP>:/var/www/x2chat.com/index.html /var/www/x2chat.com/index.html
scp root@<ORIGIN_SERVER_IP>:/var/www/x2chat.com/version.json /var/www/x2chat.com/version.json
scp -r root@<ORIGIN_SERVER_IP>:/var/www/x2chat.com/api/ /var/www/x2chat.com/api/
scp -r root@<ORIGIN_SERVER_IP>:/var/www/x2chat.com/admin/ /var/www/x2chat.com/admin/
scp -r root@<ORIGIN_SERVER_IP>:/var/www/x2chat.com/privacy/ /var/www/x2chat.com/privacy/
scp root@<ORIGIN_SERVER_IP>:/var/www/x2chat.com/logs.html /var/www/x2chat.com/logs.html
```

### Nginx конфиг
Создать `/etc/nginx/sites-available/x2chat.com`:
```nginx
server {
    server_name <APP_DOMAIN> _;

    root /var/www/x2chat.com;
    index index.html;

    access_log /var/log/nginx/x2chat.access.log;
    error_log /var/log/nginx/x2chat.error.log;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    client_max_body_size 200M;

    # API PROXY -> Backend на 8080
    location /api/ {
        proxy_pass http://127.0.0.1:8080/api/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # SSE Notifications stream (Huawei/Honor)
    location ^~ /api/v1/notifications/stream {
        proxy_pass http://127.0.0.1:8080/api/v1/notifications/stream;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 3600s;
        proxy_read_timeout 3600s;
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding on;
    }

    # API v1 -> Backend
    location ^~ /api/v1/ {
        proxy_pass http://127.0.0.1:8080/api/v1/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # WebSocket для чатов - /ws
    location /ws {
        proxy_pass http://127.0.0.1:8080/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 60s;
        proxy_send_timeout 3600s;
        proxy_read_timeout 3600s;
    }

    # WebSocket Socket.IO (legacy)
    location /socket.io/ {
        proxy_pass http://127.0.0.1:8080/socket.io/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 60s;
        proxy_send_timeout 3600s;
        proxy_read_timeout 3600s;
    }

    # WebRTC Signaling WebSocket
    location /ws/webrtc {
        proxy_pass http://127.0.0.1:8080/ws/webrtc;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 60s;
        proxy_send_timeout 3600s;
        proxy_read_timeout 3600s;
    }

    # Admin Panel (ограничение по IP)
    location /admin/ {
        allow 172.29.89.0/24;
        allow 127.0.0.1;
        allow ::1;
        # Добавить свои IP:
        # allow X.X.X.X;
        deny all;
        try_files $uri $uri/ /admin/index.html;
    }
    location = /admin {
        allow 172.29.89.0/24;
        allow 127.0.0.1;
        allow ::1;
        deny all;
        return 301 /admin/;
    }

    # Admin API (ограничение по IP)
    location ^~ /api/v1/admin/ {
        allow 172.29.89.0/24;
        allow 127.0.0.1;
        allow ::1;
        deny all;
        proxy_pass http://127.0.0.1:8080/api/v1/admin/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Временные ссылки на файлы (/s/:token)
    location ^~ /s/ {
        proxy_pass http://127.0.0.1:8080/s/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Главная страница
    location / {
        try_files $uri $uri/ /index.html;
    }

    # APK файлы
    location /downloads/ {
        alias /var/www/x2chat.com/downloads/;
        autoindex on;
        autoindex_exact_size off;
        autoindex_localtime on;
        types {
            application/vnd.android.package-archive apk;
        }
        sendfile on;
        tcp_nopush on;
    }

    # Uploaded files (avatars, media)
    location /uploads/ {
        alias /opt/x2chat/uploads/;
        expires 30d;
        add_header Cache-Control "public, immutable";
        add_header Access-Control-Allow-Origin *;
    }

    # API version.json
    location /api/version.json {
        alias /var/www/x2chat.com/api/version.json;
        default_type application/json;
        add_header Access-Control-Allow-Origin *;
    }

    listen 80;
    listen [::]:80;
}
```

### Активация и SSL
```bash
ln -sf /etc/nginx/sites-available/x2chat.com /etc/nginx/sites-enabled/x2chat.com
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl restart nginx

# SSL через Let's Encrypt (домен должен резолвиться на этот сервер!)
certbot --nginx -d <APP_DOMAIN> --non-interactive --agree-tos -m admin@x2chat.com

# Автообновление сертификата (certbot timer уже включён при установке)
systemctl enable certbot.timer
```

---

## Часть 7: coturn (TURN Server для WebRTC)

### Конфигурация
```bash
cat > /etc/turnserver.conf << 'EOF'
# X2Chat TURN Server Configuration
fingerprint
lt-cred-mech
use-auth-secret
static-auth-secret=347f15de9ba619701fcf5dc3b40cc2b570eaa7116292584702cd2ac663792460
realm=<APP_DOMAIN>

listening-port=3478
alt-listening-port=3479

# NAT traversal: external-ip=<public>/<private>
external-ip=<APP_SERVER_PUBLIC_IP>/<APP_SERVER_IP>

listening-ip=<APP_SERVER_IP>
relay-ip=<APP_SERVER_IP>

min-port=49152
max-port=65535

total-quota=0
stale-nonce=0

allowed-peer-ip=10.0.0.0-10.255.255.255
allowed-peer-ip=172.16.0.0-172.31.255.255
allowed-peer-ip=192.168.0.0-192.168.255.255

log-file=/var/log/turn.log
verbose
EOF

systemctl enable coturn
systemctl restart coturn
```

---

## Часть 8: QUIC Relay (Rust)

### Сборка
```bash
cd /opt/x2chat/quic-relay
cargo build --release
```

### Systemd сервис
```bash
cat > /etc/systemd/system/x2chat-quic-relay.service << 'EOF'
[Unit]
Description=X2Chat QUIC Signaling Relay
After=network.target

[Service]
Type=simple
ExecStart=/opt/x2chat/quic-relay/target/release/x2chat-quic-relay
WorkingDirectory=/opt/x2chat/quic-relay
Restart=always
RestartSec=3
Environment=RUST_LOG=info

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable x2chat-quic-relay
systemctl start x2chat-quic-relay
```

---

## Часть 9: Flutter конфигурация

### Flutter .env
Файл `/opt/x2chat/apps/flutter/.env`:
```bash
cat > /opt/x2chat/apps/flutter/.env << 'EOF'
# X2Chat Mobile Configuration
API_BASE_URL=https://<APP_DOMAIN>
API_TIMEOUT=30000
WS_URL=wss://<APP_DOMAIN>
ALLOW_SELF_SIGNED_CERTS=false
ENABLE_LOGGING=true
LOG_LEVEL=debug
ENABLE_VOICE_MESSAGES=true
ENABLE_FILE_SHARING=true
ENABLE_NOTIFICATIONS=true
ENABLE_E2E_ENCRYPTION=true
ENVIRONMENT=production
EOF
```

### Обновить api_config.dart
В файле `/opt/x2chat/apps/flutter/lib/src/config/api_config.dart` заменить все хосты на `<APP_DOMAIN>`.

### Сборка APK
```bash
export PATH="/opt/flutter/bin:$PATH"
export ANDROID_HOME="/opt/android-sdk"
cd /opt/x2chat/apps/flutter
flutter clean && flutter pub get && flutter build apk --debug
```

---

## Часть 10: Синхронизация кода через Git

### Стратегия обновления
Оба сервера (исходный и клон) работают с одним репозиторием Gitea.

**На клоне — получение обновлений:**
```bash
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/gitea_ed25519
cd /opt/x2chat
git pull gitea-internal master

# После pull — перезапуск backend:
cd /opt/x2chat/backend && npm install
systemctl restart x2chat-backend

# Если менялся QUIC relay:
cd /opt/x2chat/quic-relay && cargo build --release
systemctl restart x2chat-quic-relay
```

**Push с клона в Gitea:**
```bash
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/gitea_ed25519
REMOTE="git@<GITEA_IP>:administrator/x2chat.git"
git push $REMOTE HEAD:master
git push $REMOTE HEAD:macos-dev
git push $REMOTE HEAD:ios-dev
```

### Основные ветки в Gitea
| Ветка | Назначение |
|---|---|
| `master` | Основная ветка |
| `macos-dev` | macOS разработка |
| `ios-dev` | iOS разработка |
| `windows-dev` | Windows разработка |
| `wechat-go` | Текущая рабочая ветка |

---

## Часть 11: Директории и права

```bash
# Создать все необходимые директории
mkdir -p /var/log/x2chat/client-logs
mkdir -p /opt/x2chat/uploads
mkdir -p /var/www/x2chat.com/{downloads,api,admin,privacy,installers}

# Права
chmod 600 /opt/x2chat/backend/.env
chmod 600 /opt/x2chat/backend/config/apns/AuthKey_3TPQP33HLQ.p8
```

---

## Часть 12: Финальная проверка

```bash
echo "=== 1. Backend ==="
curl -s http://localhost:8080/health
# {"status":"ok"...}

echo "=== 2. Nginx ==="
curl -s -o /dev/null -w "%{http_code}" https://<APP_DOMAIN>/
# 200

echo "=== 3. API через Nginx ==="
curl -s https://<APP_DOMAIN>/api/v1/chat
# 401 (ожидаемо — нет токена)

echo "=== 4. TURN server ==="
systemctl is-active coturn
# active

echo "=== 5. QUIC relay ==="
systemctl is-active x2chat-quic-relay
# active

echo "=== 6. Git ==="
cd /opt/x2chat && git status
# чистый worktree

echo "=== 7. SSL ==="
certbot certificates
# валидный сертификат для <APP_DOMAIN>
```

---

## Сводка портов

| Порт | Сервис | Доступ |
|---|---|---|
| 22 | SSH | Внешний |
| 80 | Nginx (→ redirect 443) | Внешний |
| 443 | Nginx (HTTPS + WSS) | Внешний |
| 3478 | coturn (TURN) | Внешний |
| 8080 | Node.js backend | Только localhost |
| 49152–65535 | coturn relay ports | Внешний (UDP) |

---

## Что НЕ включено (доп. сервисы исходного сервера)

Эти сервисы **не являются частью X2Chat** и устанавливаются отдельно при необходимости:
- **x-ui** (3x-ui VPN panel, порт 2053)
- **xray** (VLESS proxy, порты 2054/8388/8389)
- **v2ray** (порт 16823)
- **iperf3** (порт 5201)

---

## Быстрый чеклист

- [ ] Ubuntu 24.04 чистая установка
- [ ] Системные пакеты установлены
- [ ] Node.js 18 установлен
- [ ] Flutter 3.38.3 + Android SDK установлены
- [ ] Rust установлен
- [ ] SSH-ключи скопированы (gitea_ed25519, ESC-test.pem)
- [ ] SSH config настроен
- [ ] Репозиторий склонирован в /opt/x2chat
- [ ] Backend .env создан с правильным <DB_SERVER_IP>
- [ ] APNS ключ и Firebase config скопированы
- [ ] npm install выполнен
- [ ] x2chat-backend.service создан и запущен
- [ ] Nginx настроен с SSL (certbot)
- [ ] coturn настроен и запущен
- [ ] QUIC relay собран и запущен
- [ ] Веб-директории созданы, статика скопирована
- [ ] Все healthcheck-и проходят
- [ ] Git pull/push работает с Gitea
