docs(skills): record 54.169 coruna-lab cutover playbook
Capture git pull, WAF 30MB, supervisor including shell, APP_API_DOMAIN/ldid, and php-fpm 502 fixes from the last server move. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,290 +1,257 @@
|
|||||||
---
|
---
|
||||||
name: coruna-lab-migrate
|
name: coruna-lab-migrate
|
||||||
description: >-
|
description: >-
|
||||||
Migrate the coruna-lab (Coruna Lab) Laravel project from one server to another
|
Migrate or cut over the coruna-lab (Coruna Lab) Laravel project to a new
|
||||||
on BT Panel (宝塔面板). Covers code deployment, database dump/restore, file
|
BT Panel (宝塔) server, or update an already-provisioned remote box with
|
||||||
storage transfer (photos/ds-results/inbox), supervisor queue worker setup,
|
latest code and required config (git pull, migrate, supervisor, WAF,
|
||||||
DNS cutover, and post-migration verification. Use when the user asks to
|
APP_API_DOMAIN, ldid, php-fpm reload). Use when the user asks to migrate
|
||||||
migrate coruna-lab to a new server, move coruna-lab to another machine,
|
coruna-lab, 换服务器, 迁移机器, 搬家, 远程更新最新代码, 按更新完成相关配置,
|
||||||
换服务器, 迁移机器, 搬家, or set up a fresh coruna-lab deployment.
|
or set up a fresh coruna-lab deployment. Trigger keywords: coruna-lab 迁移,
|
||||||
Trigger keywords: coruna-lab 迁移, 迁移服务器, 换机器, migrate coruna-lab,
|
迁移服务器, 换机器, migrate coruna-lab, server migration, 远程更新,
|
||||||
server migration, 搬家, new server setup.
|
更新远程代码, 54.169, Lightsail pem.
|
||||||
---
|
---
|
||||||
|
|
||||||
# coruna-lab Server Migration
|
# coruna-lab Server Migration
|
||||||
|
|
||||||
Migrate the **coruna-lab** Laravel project between BT Panel (宝塔面板) servers.
|
Two workflows. Pick one from the user request:
|
||||||
|
|
||||||
## Architecture overview
|
| Mode | When | Start at |
|
||||||
|
|------|------|----------|
|
||||||
|
| **A. Remote update** | Target already has coruna-lab; user wants latest code + config (migrations, queues, WAF, APP API, ldid) | [Workflow A](#workflow-a--remote-update) |
|
||||||
|
| **B. Full migrate** | New empty BT box; move site + DB + storage from an old server | [Workflow B](#workflow-b--full-migrate) |
|
||||||
|
|
||||||
|
Do **not** deploy or cut DNS unless the user explicitly asked. Confirm the target IP and SSH key before any write.
|
||||||
|
|
||||||
|
## Constants
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|------|--------|
|
||||||
|
| Project | `/www/wwwroot/coruna-lab` |
|
||||||
|
| PHP | `/www/server/php/82/bin/php` (FPM pool `www`) |
|
||||||
|
| artisan | `sudo -u www /www/server/php/82/bin/php artisan …` |
|
||||||
|
| Git | `ssh://git@gitlab.fcpays.cc:2222/root/coruna-lab.git` |
|
||||||
|
| Supervisor | `/www/server/panel/pyenv/bin/supervisorctl` |
|
||||||
|
| Profiles | `/www/server/panel/plugin/supervisor/profile/*.ini` |
|
||||||
|
| WAF | `/www/server/btwaf/config.json` |
|
||||||
|
| PHP-FPM | `/etc/init.d/php-fpm-82` |
|
||||||
|
| open_basedir | `public/.user.ini` → `/www/wwwroot/coruna-lab/:/tmp/` |
|
||||||
|
|
||||||
|
Last successful cutover: **54.169.236.75** (Lightsail, `ubuntu` + sudo). PEM was in the operator workspace (`LightsailDefaultKey-ap-southeast-1.pem`). SSH: `IdentitiesOnly=yes`. BT MCP for **27.124** is a different box — never use it to change 54.169.
|
||||||
|
|
||||||
|
C2 and admin share one vhost/root (`public/`). Typical `server_name`: admin host + DS/xxbb/App API hosts (e.g. `admin.capssk888.com`, `zydrlfynoptx9aw.icu`, `kfsdljlows.com`, `mnskal.cc`).
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
|
||||||
|
1. Backup `.env`, WAF `config.json`, and nginx vhost before editing.
|
||||||
|
2. Never add a custom nginx `^~ location /api/ap` — it bypasses Laravel and breaks App C2.
|
||||||
|
3. After `git pull`, always reload **php-fpm-82**. Live FPM has `opcache.validate_timestamps=Off`.
|
||||||
|
4. `git pull` as `www` needs `chown -R www:www .git` (and the tree).
|
||||||
|
5. `APP_API_DOMAIN` is **host only** (`mnskal.cc`), not `https://…`.
|
||||||
|
6. Do not print `.env` secrets, APP_KEY, Tokenview keys, or mnemonics.
|
||||||
|
7. Do not spam the product Telegram 通知群. Agent chat is a separate cc-connect window.
|
||||||
|
8. Do not modify 宝塔 panel source or `bt_agent_mcp` credentials.
|
||||||
|
|
||||||
|
## Workflow A — remote update
|
||||||
|
|
||||||
|
Copy this checklist and tick as you go:
|
||||||
|
|
||||||
```
|
```
|
||||||
Old Server New Server
|
- [ ] Confirm target IP + SSH key (not the old BT MCP host)
|
||||||
┌─────────────────────┐ ┌─────────────────────┐
|
- [ ] Pull latest code as www; composer install
|
||||||
│ BT Panel + Nginx │ │ BT Panel + Nginx │
|
- [ ] APP_API_DOMAIN + LDID_PATH; bin/ldid present
|
||||||
│ PHP 8.2 │ rsync │ PHP 8.2 │
|
- [ ] artisan migrate --force
|
||||||
│ MySQL (coruna DB) │ ────────> │ MySQL (coruna DB) │
|
- [ ] Supervisor: queue ocr extract keystore telegram transfer shell
|
||||||
│ Redis │ │ Redis │
|
- [ ] crontab schedule:run every minute
|
||||||
│ Supervisor (workers)│ │ Supervisor (workers)│
|
- [ ] WAF url_white + body_size 30MB; nginx client_body_buffer_size
|
||||||
│ storage/app/private │ tar+ssh │ storage/app/private │
|
- [ ] open_basedir + proc_open; no shell_exec required
|
||||||
│ c2/photos/ (155G+) │ ────────> │ c2/photos/ │
|
- [ ] config/route/view:clear; reload php-fpm-82
|
||||||
└─────────────────────┘ └─────────────────────┘
|
- [ ] Local curl: /admin 302, /beacon 200
|
||||||
```
|
```
|
||||||
|
|
||||||
## Key paths & services
|
### A1. Recon
|
||||||
|
|
||||||
| Item | Path / Command |
|
SSH as the provisioned user (`ubuntu` on Lightsail), then `sudo`. Confirm `hostname`, `df`, nginx/php-fpm/mysql/redis processes, and that the site root is `/www/wwwroot/coruna-lab/public`.
|
||||||
|------|----------------|
|
|
||||||
| Project root | `/www/wwwroot/coruna-lab` |
|
|
||||||
| PHP | `/www/server/php/82/bin/php` |
|
|
||||||
| artisan | `sudo -u www /www/server/php/82/bin/php artisan` |
|
|
||||||
| Supervisor | `sudo /www/server/panel/pyenv/bin/supervisorctl` |
|
|
||||||
| Storage | `storage/app/private/c2/{photos,ds-results,ds-chunks,ds-notes,inbox,photo-previews,plugin-sessions}` |
|
|
||||||
| Logs | `public/log/{xxbb,ds,transfer,c2}/` |
|
|
||||||
| Supervisor profiles | `/www/server/panel/plugin/supervisor/profile/*.ini` |
|
|
||||||
|
|
||||||
## Migration phases
|
### A2. Pull + deps
|
||||||
|
|
||||||
### Phase 1: Prepare new server
|
|
||||||
|
|
||||||
1. Install BT Panel, PHP 8.2, MySQL, Redis, Nginx on the new server.
|
|
||||||
2. Create MySQL database and user:
|
|
||||||
```sql
|
|
||||||
CREATE DATABASE coruna CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
CREATE USER 'coruna'@'localhost' IDENTIFIED BY '<password>';
|
|
||||||
GRANT ALL ON coruna.* TO 'coruna'@'localhost';
|
|
||||||
FLUSH PRIVILEGES;
|
|
||||||
```
|
|
||||||
3. Create the site in BT Panel (point domain to `/www/wwwroot/coruna-lab`).
|
|
||||||
|
|
||||||
### Phase 2: Deploy code
|
|
||||||
|
|
||||||
The new server has **no git** — deploy via tarball from a machine that has the repo:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# On the machine with git access:
|
|
||||||
cd /www/wwwroot/coruna-lab
|
cd /www/wwwroot/coruna-lab
|
||||||
git archive --format=tar HEAD | gzip > /tmp/coruna-lab-code.tar.gz
|
chown -R www:www .git .
|
||||||
scp /tmp/coruna-lab-code.tar.gz ubuntu@<new-server>:/tmp/
|
sudo -u www git pull --ff-only
|
||||||
|
sudo -u www /www/server/php/82/bin/php /usr/bin/composer install --no-dev --optimize-autoloader
|
||||||
# On the new server:
|
|
||||||
sudo mkdir -p /www/wwwroot/coruna-lab
|
|
||||||
sudo tar -xzf /tmp/coruna-lab-code.tar.gz -C /www/wwwroot/coruna-lab
|
|
||||||
sudo chown -R www:www /www/wwwroot/coruna-lab
|
|
||||||
cd /www/wwwroot/coruna-lab
|
|
||||||
composer install --no-dev --optimize-autoloader
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 3: Database migration
|
If `www` cannot read the deploy key, pull as root then `chown -R www:www .`.
|
||||||
|
|
||||||
|
### A3. .env (update only what the user named)
|
||||||
|
|
||||||
|
| Key | Notes |
|
||||||
|
|-----|--------|
|
||||||
|
| `APP_API_DOMAIN` | Host only. Used in IPA builds (`config/coruna.php` `app_api_domain`). |
|
||||||
|
| `LDID_PATH` | Default `base_path('bin/ldid')`. Must be inside open_basedir. Do **not** `file_exists()` it from FPM (open_basedir). |
|
||||||
|
| `APP_URL` | Admin / Laravel URL. |
|
||||||
|
| `QUEUE_CONNECTION` | `redis` |
|
||||||
|
| `DS_DOMAIN` | DarkSword C2 host if used by builders. |
|
||||||
|
|
||||||
|
Keep `APP_KEY`. Never `key:generate` on a live migrate unless the user asks.
|
||||||
|
|
||||||
|
### A4. Migrate + caches
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# On old server: dump
|
|
||||||
mysqldump -ucoruna -p<old-pass> coruna --single-transaction --routines > /tmp/coruna.sql
|
|
||||||
scp /tmp/coruna.sql ubuntu@<new-server>:/tmp/
|
|
||||||
|
|
||||||
# On new server: import
|
|
||||||
mysql -ucoruna -p<new-pass> coruna < /tmp/coruna.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
Run migrations on the new server:
|
|
||||||
```bash
|
|
||||||
cd /www/wwwroot/coruna-lab
|
|
||||||
sudo -u www /www/server/php/82/bin/php artisan migrate --force
|
sudo -u www /www/server/php/82/bin/php artisan migrate --force
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 4: Configure .env
|
|
||||||
|
|
||||||
Copy `.env` from old server, update for new server:
|
|
||||||
```bash
|
|
||||||
# Key settings to verify/update:
|
|
||||||
APP_URL=<new-domain>
|
|
||||||
DB_PASSWORD=<new-db-pass>
|
|
||||||
REDIS_HOST=127.0.0.1
|
|
||||||
QUEUE_CONNECTION=redis
|
|
||||||
# Keep these from old .env:
|
|
||||||
CORUNA_OFFICIAL_ALBUM_STORAGE=1
|
|
||||||
INTERCEPT_DEVICE_KEYS=...
|
|
||||||
INTERCEPT_BOT_TOKEN=...
|
|
||||||
INTERCEPT_CHAT_ID=...
|
|
||||||
```
|
|
||||||
|
|
||||||
Generate app key if needed (usually keep the old one):
|
|
||||||
```bash
|
|
||||||
sudo -u www /www/server/php/82/bin/php artisan key:generate
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 5: Transfer file storage
|
|
||||||
|
|
||||||
The `c2/` directory can be 200G+. Use `tar + ssh` for reliability with large file counts:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
# transfer_locked.sh — run on OLD server
|
|
||||||
NEW_HOST="ubuntu@<new-server>"
|
|
||||||
SSH_KEY="/path/to/key.pem"
|
|
||||||
SRC="/www/wwwroot/coruna-lab/storage/app/private/c2"
|
|
||||||
DST="/www/wwwroot/coruna-lab/storage/app/private/c2"
|
|
||||||
|
|
||||||
for dir in photos photo-previews ds-chunks ds-notes ds-results plugin-sessions inbox; do
|
|
||||||
echo "Transferring $dir..."
|
|
||||||
tar -C "$SRC" -cf - "$dir" | ssh -i "$SSH_KEY" $NEW_HOST "sudo tar -C '$DST' -xf -"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important**: Transfer `photos/` last (largest, 155G+). Use `flock` to prevent
|
|
||||||
duplicate runs. Monitor progress with `find ... | wc -l` on both servers.
|
|
||||||
|
|
||||||
### Phase 6: Set up supervisor workers
|
|
||||||
|
|
||||||
**This is the most critical step** — missing workers cause silent data loss.
|
|
||||||
|
|
||||||
Create one `.ini` file per queue in `/www/server/panel/plugin/supervisor/profile/`:
|
|
||||||
|
|
||||||
| File | Queue | Command |
|
|
||||||
|------|-------|---------|
|
|
||||||
| `queue.ini` | default | `artisan queue:work redis --sleep=1 --tries=3 --timeout=90 --max-time=3600` |
|
|
||||||
| `ocr.ini` | ocr | `artisan queue:work redis --queue=ocr --sleep=1 --tries=1 --timeout=90 --max-jobs=100` |
|
|
||||||
| `keystore.ini` | keystore | `artisan queue:work keystore --sleep=1 --tries=1 --timeout=320 --max-time=3600` |
|
|
||||||
| `telegram.ini` | telegram | `artisan queue:work telegram --sleep=1 --tries=3 --timeout=30 --max-time=3600` |
|
|
||||||
| `transfer.ini` | transfer | `artisan queue:work transfer --sleep=1 --tries=1 --timeout=200 --max-time=3600` |
|
|
||||||
| **`extract.ini`** | **extract** | `artisan queue:work redis --queue=extract --sleep=1 --tries=1 --timeout=200 --max-jobs=100` |
|
|
||||||
|
|
||||||
> **⚠️ CRITICAL: `extract.ini` is easily missed.** Without it, photo archives
|
|
||||||
> pile up in `inbox/` and the `extract` Redis queue backs up indefinitely.
|
|
||||||
> Photos appear to stop storing even though `/t` requests keep arriving.
|
|
||||||
|
|
||||||
Template for `extract.ini` (others follow the same pattern):
|
|
||||||
```ini
|
|
||||||
[program:extract]
|
|
||||||
command=/www/server/php/82/bin/php -d memory_limit=256M artisan queue:work redis --queue=extract --sleep=1 --tries=1 --timeout=200 --max-jobs=100
|
|
||||||
directory=/www/wwwroot/coruna-lab/
|
|
||||||
autorestart=true
|
|
||||||
startsecs=3
|
|
||||||
startretries=3
|
|
||||||
stdout_logfile=/www/server/panel/plugin/supervisor/log/extract.out.log
|
|
||||||
stderr_logfile=/www/server/panel/plugin/supervisor/log/extract.err.log
|
|
||||||
stdout_logfile_maxbytes=2MB
|
|
||||||
stderr_logfile_maxbytes=2MB
|
|
||||||
user=www
|
|
||||||
priority=999
|
|
||||||
numprocs=2
|
|
||||||
process_name=%(program_name)s_%(process_num)02d
|
|
||||||
```
|
|
||||||
|
|
||||||
Load and start all workers:
|
|
||||||
```bash
|
|
||||||
sudo /www/server/panel/pyenv/bin/supervisorctl reread
|
|
||||||
sudo /www/server/panel/pyenv/bin/supervisorctl update
|
|
||||||
sudo /www/server/panel/pyenv/bin/supervisorctl start all
|
|
||||||
sudo /www/server/panel/pyenv/bin/supervisorctl status
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 7: Clear caches & restart PHP-FPM
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /www/wwwroot/coruna-lab
|
|
||||||
sudo -u www /www/server/php/82/bin/php artisan config:clear
|
sudo -u www /www/server/php/82/bin/php artisan config:clear
|
||||||
sudo -u www /www/server/php/82/bin/php artisan route:clear
|
sudo -u www /www/server/php/82/bin/php artisan route:clear
|
||||||
sudo -u www /www/server/php/82/bin/php artisan view:clear
|
sudo -u www /www/server/php/82/bin/php artisan view:clear
|
||||||
sudo -u www /www/server/php/82/bin/php artisan cache:clear
|
|
||||||
# Restart PHP-FPM
|
|
||||||
sudo /etc/init.d/php-fpm-82 restart
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> **⚠️ `view:clear` is critical after code updates.** Stale compiled Blade
|
`view:clear` is required — stale Blade caused admin 500/404 after deploys.
|
||||||
> templates in `storage/framework/views/` cause 500 errors when new routes
|
|
||||||
> are referenced but old compiled cache doesn't have them.
|
|
||||||
|
|
||||||
### Phase 8: DNS cutover
|
### A5. Supervisor (7 programs, 2 procs each)
|
||||||
|
|
||||||
1. Update Cloudflare DNS A record to new server IP.
|
Missing **`shell`** or **`extract`** is silent data loss. Commands:
|
||||||
2. Wait for DNS propagation (or use Cloudflare proxy for instant cutover).
|
|
||||||
3. Verify the site loads on the new server.
|
|
||||||
|
|
||||||
### Phase 9: Verify
|
| ini | command |
|
||||||
|
|-----|---------|
|
||||||
|
| `queue.ini` | `artisan queue:work redis --sleep=1 --tries=3 --timeout=90 --max-time=3600` |
|
||||||
|
| `ocr.ini` | `artisan queue:work redis --queue=ocr --sleep=1 --tries=1 --timeout=90 --max-jobs=100` + `-d memory_limit=256M` |
|
||||||
|
| `extract.ini` | `artisan queue:work redis --queue=extract --sleep=1 --tries=1 --timeout=200 --max-jobs=100` + `-d memory_limit=256M` |
|
||||||
|
| `keystore.ini` | `artisan queue:work keystore --sleep=1 --tries=1 --timeout=320 --max-time=3600` |
|
||||||
|
| `telegram.ini` | `artisan queue:work telegram --sleep=1 --tries=3 --timeout=30 --max-time=3600` |
|
||||||
|
| `transfer.ini` | `artisan queue:work transfer --sleep=1 --tries=1 --timeout=200 --max-time=3600` |
|
||||||
|
| `shell.ini` | `artisan queue:work shell --queue=shell --sleep=1 --tries=2 --timeout=120 --memory=256 --max-time=3600` |
|
||||||
|
|
||||||
|
All: `directory=/www/wwwroot/coruna-lab/`, `user=www`, `numprocs=2`, `process_name=%(program_name)s_%(process_num)02d`, `autorestart=true`. Full template: [supervisor.md](supervisor.md).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check site responds
|
/www/server/panel/pyenv/bin/supervisorctl reread
|
||||||
curl -sI https://<domain>/ | head -5
|
/www/server/panel/pyenv/bin/supervisorctl update
|
||||||
|
/www/server/panel/pyenv/bin/supervisorctl status
|
||||||
|
```
|
||||||
|
|
||||||
# Check supervisor workers all RUNNING
|
### A6. Scheduler
|
||||||
sudo /www/server/panel/pyenv/bin/supervisorctl status
|
|
||||||
|
|
||||||
# Check Redis queue backlog (should be 0 or low for all queues)
|
```
|
||||||
cd /www/wwwroot/coruna-lab
|
* * * * * sudo -u www bash -c 'cd /www/wwwroot/coruna-lab && /www/server/php/82/bin/php artisan schedule:run >> /dev/null 2>&1'
|
||||||
sudo -u www /www/server/php/82/bin/php artisan tinker --execute='
|
```
|
||||||
$r = \Illuminate\Support\Facades\Redis::connection();
|
|
||||||
foreach (["default","extract","ocr","keystore","telegram","transfer"] as $q) {
|
### A7. WAF + nginx body size
|
||||||
echo "queues:$q = ".$r->llen("queues:$q")."\n";
|
|
||||||
|
Backup `/www/server/btwaf/config.json` first.
|
||||||
|
|
||||||
|
- `http_config.body_size` (or equivalent) → **30MB** (chunked App C2 / SignalShell uploads).
|
||||||
|
- `url_white`: at least `^/api/ap/*` plus existing C2 chunk paths. Do not whitelist everything.
|
||||||
|
- Nginx `client_body_buffer_size` **4096k** (1MiB chunks were blocked at 1MB).
|
||||||
|
- Reload WAF/nginx via panel-safe reload. `nginx -t` before reload.
|
||||||
|
|
||||||
|
### A8. ldid / IPA
|
||||||
|
|
||||||
|
Copy or build `ldid` to `/www/wwwroot/coruna-lab/bin/ldid`, `chmod +x`, owned by `www`. Set `LDID_PATH`. FPM must allow `proc_open`. Signing must not call `file_exists($ldidPath)` outside open_basedir.
|
||||||
|
|
||||||
|
### A9. PHP-FPM reload + probe
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/etc/init.d/php-fpm-82 reload # restart if listen backlog / pm.* changed
|
||||||
|
```
|
||||||
|
|
||||||
|
Local `--resolve` probes (delete probe files after):
|
||||||
|
|
||||||
|
- `/.probe_static.html` → 200
|
||||||
|
- `/.probe_php.php` → PHP version
|
||||||
|
- `/admin` → 302
|
||||||
|
- `/beacon` → 200
|
||||||
|
|
||||||
|
If admin/C2 return **502** with `connect() to unix:/tmp/php-cgi-82.sock failed (11: Resource temporarily unavailable)`: FPM pool is saturated. Raise `pm.max_children` (48 on a 36-core box), `net.core.somaxconn=4096`, recreate the listen socket with a **restart** (reload keeps backlog=1024). Do not dump full `/result` bodies to `error_log` — Nginx then 502s on the response header.
|
||||||
|
|
||||||
|
Admin and C2 share the same FPM pool.
|
||||||
|
|
||||||
|
## Workflow B — full migrate
|
||||||
|
|
||||||
|
Use when the new box does not yet have the site.
|
||||||
|
|
||||||
|
### B1. Prepare new server
|
||||||
|
|
||||||
|
BT Panel + Nginx + PHP 8.2 + MySQL + Redis. Create DB `coruna` utf8mb4. Create the site with root `/www/wwwroot/coruna-lab/public`. Bind all C2 + admin hostnames on that vhost.
|
||||||
|
|
||||||
|
### B2. Code
|
||||||
|
|
||||||
|
Prefer **git clone** of `gitlab.fcpays.cc:2222/root/coruna-lab.git` as `www` if the box has deploy key. Otherwise:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git archive --format=tar HEAD | gzip > /tmp/coruna-lab-code.tar.gz
|
||||||
|
# scp + tar -xzf into /www/wwwroot/coruna-lab
|
||||||
|
chown -R www:www /www/wwwroot/coruna-lab
|
||||||
|
sudo -u www composer install --no-dev --optimize-autoloader
|
||||||
|
```
|
||||||
|
|
||||||
|
### B3. Database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# old
|
||||||
|
mysqldump -ucoruna -p coruna --single-transaction --routines > /tmp/coruna.sql
|
||||||
|
# new
|
||||||
|
mysql -ucoruna -p coruna < /tmp/coruna.sql
|
||||||
|
sudo -u www php artisan migrate --force
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy `.env` from old; change `APP_URL`, `DB_*`, confirm Redis. Keep `APP_KEY`. Then Workflow A3–A9.
|
||||||
|
|
||||||
|
### B4. Storage
|
||||||
|
|
||||||
|
`storage/app/private/c2/` can be 200G+. Stream per directory (photos last):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SRC=/www/wwwroot/coruna-lab/storage/app/private/c2
|
||||||
|
DST=/www/wwwroot/coruna-lab/storage/app/private/c2
|
||||||
|
for dir in photo-previews ds-chunks ds-notes ds-results plugin-sessions inbox photos; do
|
||||||
|
tar -C "$SRC" -cf - "$dir" | ssh -i KEY ubuntu@NEW "sudo tar -C '$DST' -xf -"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Photos live at `c2/photos/{device}/…`, not `storage/app/private/photos/`.
|
||||||
|
|
||||||
|
### B5. DNS
|
||||||
|
|
||||||
|
Update A/AAAA (Cloudflare proxy = instant). Stop old supervisor only after the new box has taken traffic. Decommission old after 24–48h.
|
||||||
|
|
||||||
|
## Verify (both modes)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/www/server/panel/pyenv/bin/supervisorctl status
|
||||||
|
# all seven programs RUNNING (14 processes)
|
||||||
|
|
||||||
|
sudo -u www php artisan tinker --execute='
|
||||||
|
foreach (["default","extract","ocr","keystore","telegram","transfer","shell"] as $q) {
|
||||||
|
echo $q, "=", Illuminate\Support\Facades\Redis::llen("queues:".$q), "\n";
|
||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
# Check photos are being stored (should see recent timestamps)
|
|
||||||
mysql -ucoruna -p<pass> coruna -e '
|
|
||||||
SELECT COUNT(*) as cnt, MAX(created_at) as last
|
|
||||||
FROM photos WHERE created_at > DATE_SUB(NOW(), INTERVAL 10 MINUTE);
|
|
||||||
'
|
|
||||||
|
|
||||||
# Check inbox not backing up
|
|
||||||
ls /www/wwwroot/coruna-lab/storage/app/private/c2/inbox/ | wc -l
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Common pitfalls
|
- `/admin` 302, `/beacon` 200
|
||||||
|
- `inbox/` not growing unbounded (extract worker)
|
||||||
|
- Tokenview monitor cap is **5000**. After a migrate, reconcile list vs `wallet_addresses` (see Tokenview list API `GET …/monitor/address/list/{coin}?page=&apikey=`).
|
||||||
|
|
||||||
### 1. Missing `extract` queue worker (MOST COMMON)
|
## Pitfalls
|
||||||
|
|
||||||
**Symptom**: Users report photos stopped storing. `/t` requests arrive,
|
| Symptom | Cause | Fix |
|
||||||
DB has few/no new photos, `inbox/` directory grows, `queues:extract` in
|
|---------|--------|-----|
|
||||||
Redis has 100K+ backlog.
|
| Pull “insufficient permission” | `.git` owned by root | `chown -R www:www .git` |
|
||||||
|
| Code pulled, web still old | opcache, no FPM reload | reload php-fpm-82 |
|
||||||
|
| IPA build open_basedir / ldid | `file_exists('/usr/bin/ldid')` | project `bin/ldid` + `LDID_PATH`; no file_exists |
|
||||||
|
| App C2 404 after “优化” | extra nginx `location ^~ /api/ap` | remove it; Laravel routes only |
|
||||||
|
| Photos stop, inbox grows | no `extract` worker | add extract.ini |
|
||||||
|
| Shell zip never ingested | no `shell` worker | add shell.ini |
|
||||||
|
| Admin 502, load “only” ~20 | FPM 24 children + sock backlog 1024 | max_children 48, somaxconn 4096, FPM **restart** |
|
||||||
|
| Admin 500 after deploy | stale views | `view:clear` |
|
||||||
|
| Login locked | `login_attempts` / `locked_at` | reset those columns |
|
||||||
|
| WAF 403 on chunks | body 1MB / no url_white | 30MB + `^/api/ap/*` |
|
||||||
|
| BT MCP edits wrong host | MCP is bound to one panel | SSH the IP the user named |
|
||||||
|
|
||||||
**Fix**: Create `extract.ini` (see Phase 6), reload supervisor.
|
## After cutover
|
||||||
|
|
||||||
### 2. Stale Blade view cache causing 500 errors
|
1. Old supervisor `stop all` once DNS/traffic is on the new box.
|
||||||
|
2. Run `coruna-lab-cleanup` if disk is tight.
|
||||||
|
3. Watch php-fpm listen queue (`ss -lxnp` on `php-cgi-82.sock`) under C2 load.
|
||||||
|
|
||||||
**Symptom**: Pages return 500 after code update. Error log mentions
|
## Extra templates
|
||||||
route names not found in compiled view.
|
|
||||||
|
|
||||||
**Fix**: `php artisan view:clear` + restart PHP-FPM.
|
- Supervisor ini: [supervisor.md](supervisor.md)
|
||||||
|
|
||||||
### 3. OCR workers restarting frequently
|
|
||||||
|
|
||||||
**Symptom**: `ocr:ocr_00` and `ocr:ocr_01` show very short uptimes
|
|
||||||
(seconds). Error log shows PHP module warnings ("Module already loaded").
|
|
||||||
|
|
||||||
**Cause**: PHP modules loaded twice (fileinfo, redis, zip, gmp). Usually
|
|
||||||
harmless warnings but indicates PHP config issue. Workers still process
|
|
||||||
jobs but may be slower.
|
|
||||||
|
|
||||||
### 4. Photo files not found after migration
|
|
||||||
|
|
||||||
**Symptom**: DB has photo records but files missing on disk.
|
|
||||||
|
|
||||||
**Cause**: Transfer script didn't complete, or path mismatch. Photos
|
|
||||||
are stored at `storage/app/private/c2/photos/{device_uuid}/{sha256}_...`,
|
|
||||||
NOT `storage/app/private/photos/`.
|
|
||||||
|
|
||||||
**Fix**: Re-run transfer for `c2/photos/` directory. Verify with:
|
|
||||||
```bash
|
|
||||||
sudo find /www/wwwroot/coruna-lab/storage/app/private/c2/photos -type f | wc -l
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Admin login locked after migration
|
|
||||||
|
|
||||||
**Symptom**: Can't log in to admin panel. `login_attempts` column shows
|
|
||||||
high value, `locked_at` is not NULL.
|
|
||||||
|
|
||||||
**Fix**:
|
|
||||||
```sql
|
|
||||||
UPDATE admins SET login_attempts=0, locked_at=NULL WHERE username='<user>';
|
|
||||||
```
|
|
||||||
|
|
||||||
## Post-migration cleanup
|
|
||||||
|
|
||||||
After confirming the new server is stable:
|
|
||||||
|
|
||||||
1. Stop old server supervisor workers:
|
|
||||||
```bash
|
|
||||||
/www/server/panel/pyenv/bin/supervisorctl stop all
|
|
||||||
```
|
|
||||||
2. Verify no traffic to old server (check nginx access logs).
|
|
||||||
3. Decommission old server after 24-48 hours of stable operation.
|
|
||||||
4. Run disk cleanup on new server (see `coruna-lab-cleanup` skill).
|
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Supervisor profiles
|
||||||
|
|
||||||
|
Write each file under `/www/server/panel/plugin/supervisor/profile/`. Logs under `/www/server/panel/plugin/supervisor/log/`.
|
||||||
|
|
||||||
|
Shared footer for every program:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
directory=/www/wwwroot/coruna-lab/
|
||||||
|
autorestart=true
|
||||||
|
startsecs=3
|
||||||
|
startretries=3
|
||||||
|
user=www
|
||||||
|
priority=999
|
||||||
|
numprocs=2
|
||||||
|
process_name=%(program_name)s_%(process_num)02d
|
||||||
|
stdout_logfile_maxbytes=2MB
|
||||||
|
stderr_logfile_maxbytes=2MB
|
||||||
|
```
|
||||||
|
|
||||||
|
## extract.ini (easy to miss)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[program:extract]
|
||||||
|
command=/www/server/php/82/bin/php -d memory_limit=256M artisan queue:work redis --queue=extract --sleep=1 --tries=1 --timeout=200 --max-jobs=100
|
||||||
|
stdout_logfile=/www/server/panel/plugin/supervisor/log/extract.out.log
|
||||||
|
stderr_logfile=/www/server/panel/plugin/supervisor/log/extract.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## shell.ini (SignalShell zip ingest)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[program:shell]
|
||||||
|
command=/www/server/php/82/bin/php artisan queue:work shell --queue=shell --sleep=1 --tries=2 --timeout=120 --memory=256 --max-time=3600
|
||||||
|
stdout_logfile=/www/server/panel/plugin/supervisor/log/shell.out.log
|
||||||
|
stderr_logfile=/www/server/panel/plugin/supervisor/log/shell.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## queue.ini
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[program:queue]
|
||||||
|
command=/www/server/php/82/bin/php artisan queue:work redis --sleep=1 --tries=3 --timeout=90 --max-time=3600
|
||||||
|
stdout_logfile=/www/server/panel/plugin/supervisor/log/queue.out.log
|
||||||
|
stderr_logfile=/www/server/panel/plugin/supervisor/log/queue.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## ocr.ini
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[program:ocr]
|
||||||
|
command=/www/server/php/82/bin/php -d memory_limit=256M artisan queue:work redis --queue=ocr --sleep=1 --tries=1 --timeout=90 --max-jobs=100
|
||||||
|
stdout_logfile=/www/server/panel/plugin/supervisor/log/ocr.out.log
|
||||||
|
stderr_logfile=/www/server/panel/plugin/supervisor/log/ocr.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## keystore.ini
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[program:keystore]
|
||||||
|
command=/www/server/php/82/bin/php artisan queue:work keystore --sleep=1 --tries=1 --timeout=320 --max-time=3600
|
||||||
|
stdout_logfile=/www/server/panel/plugin/supervisor/log/keystore.out.log
|
||||||
|
stderr_logfile=/www/server/panel/plugin/supervisor/log/keystore.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## telegram.ini
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[program:telegram]
|
||||||
|
command=/www/server/php/82/bin/php artisan queue:work telegram --sleep=1 --tries=3 --timeout=30 --max-time=3600
|
||||||
|
stdout_logfile=/www/server/panel/plugin/supervisor/log/telegram.out.log
|
||||||
|
stderr_logfile=/www/server/panel/plugin/supervisor/log/telegram.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## transfer.ini
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[program:transfer]
|
||||||
|
command=/www/server/php/82/bin/php artisan queue:work transfer --sleep=1 --tries=1 --timeout=200 --max-time=3600
|
||||||
|
stdout_logfile=/www/server/panel/plugin/supervisor/log/transfer.out.log
|
||||||
|
stderr_logfile=/www/server/panel/plugin/supervisor/log/transfer.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
After writing: `supervisorctl reread && supervisorctl update && supervisorctl status`.
|
||||||
Reference in New Issue
Block a user