# 部署指南 ## 环境变量(.env 关键项) ```dotenv APP_URL=https://blog.example.com APP_LOCALE=zh_CN # MySQL 8 DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_DATABASE=laralog DB_USERNAME=laralog DB_PASSWORD=*** # S3 兼容附件(R2 / COS / OSS) S3_ACCESS_KEY=xxx S3_SECRET_KEY=xxx S3_REGION=auto # R2 用 auto;COS/OSS 填对应区域 S3_BUCKET=laralog S3_ENDPOINT=https://.r2.cloudflarestorage.com # COS: https://cos..myqcloud.com / OSS: https://oss-.aliyuncs.com S3_URL=https://media.example.com # 公开访问域名(CDN),留空则用 endpoint+bucket 拼 S3_VISIBILITY=public UPLOAD_DISK=uploads # LLM(OpenAI 兼容:DeepSeek / 通义 / 自建代理) LLM_BASE_URL=https://api.deepseek.com/v1 LLM_API_KEY=sk-xxx LLM_MODEL=deepseek-chat # Workerman WORKERMAN_QUEUE_WORKERS=2 WORKERMAN_WS_ENABLED=true WORKERMAN_WS_PORT=8787 # 插件/主题市场(留空仅支持本地 ZIP) MARKET_URL= MARKET_TOKEN= ``` ## 生产迁移顺序 1. `php artisan migrate --seed` 2. 导入老数据:`php artisan sablog:import --database=sablog ... --attachments-dir=... --sync-attachments --convert-markdown` 3. 配置 S3 后同步附件:`php artisan attachments:sync-s3 --legacy-dir=...` 4. 生成主题资产(可选):`php artisan theme:publish` 5. 启动 Workerman + 队列 + 调度器 ## Nginx(前端由 Laravel 接管,老 URL 由应用层 301) ```nginx server { listen 443 ssl http2; server_name blog.example.com; root /var/www/laralog/public; index index.php; ssl_certificate /etc/nginx/ssl/blog.crt; ssl_certificate_key /etc/nginx/ssl/blog.key; # 主题资产(生产已 theme:publish 时可交给 Nginx;未发布则走 Laravel 路由) location ~ ^/themes/.+\.(css|js|png|jpg|jpeg|gif|svg|woff2?|ico)$ { try_files $uri /index.php?$query_string; expires 7d; add_header Cache-Control "public"; } # 附件已全部走 S3,无需本地静态附件目录 location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } location ~ /\.(?!well-known).* { deny all; } } ``` > 兼容性说明:所有 sablog 老 URL(`/show-12.html`、`/?action=show&id=12`、`rss.php`、`attachment.php` 等)均由 `routes/web.php` 处理为 301,无需在 Nginx 额外配置重写。 ## systemd:Workerman 常驻服务 ```ini # /etc/systemd/system/laralog-workerman.service [Unit] Description=LaraLog Workerman (queue + websocket) After=network.target mysql.service [Service] User=www-data Group=www-data WorkingDirectory=/var/www/laralog ExecStart=/usr/bin/php artisan workerman:serve start ExecStop=/usr/bin/php artisan workerman:serve stop Restart=always RestartSec=3 [Install] WantedBy=multi-user.target ``` ```bash systemctl enable --now laralog-workerman ``` ## 调度器(cron) ```cron * * * * * cd /var/www/laralog && php artisan schedule:run >> /dev/null 2>&1 ``` 调度器内注册的内容:`spatie/laravel-backup` 备份、缓存清理等(按需在 `routes/console.php` 增删)。 ## 队列消费:workerman 与 queue:work 等价 队列任务(AI 审核/润色等)由**消费者**执行,两种方式消费同一个队列、功能完全等价: | | workerman:serve | queue:work | |---|---|---| | 执行任务 | ✅ | ✅ | | 性能 | 常驻进程,快 | 每任务启动框架,慢 | | WebSocket 进度推送 | ✅ :8787 | ❌(任务照常执行,仅后台无实时提示) | | 适用 | 生产推荐 | 降级 / 标准 Laravel 部署 | **两者不要同时跑同一个队列**(虽不会重复消费,但浪费资源)。改业务代码后两者都需要重启才生效(常驻内存);`queue:work --once` 每任务新进程,适合调试。 ### 降级方案(不用 Workerman) ```ini # /etc/systemd/system/laralog-queue.service [Service] User=www-data WorkingDirectory=/var/www/laralog ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3 --queue=default,ai Restart=always ``` ### 开发期热重载 ```bash php artisan dev:watch # 监听 app/plugins/routes/config/.env 变化,自动重启 workerman(fswatch 或 PHP 轮询兜底) ``` ## 备份 ```bash # spatie/laravel-backup(备份 DB + 附件;S3 附件无需本地备份) php artisan backup:run --only-db ``` ## 常见操作 ```bash php artisan theme:publish # 把主题 assets 拷贝到 public/themes php artisan workerman:serve status # 查看常驻进程 php artisan content:convert --all # 老 HTML 文章批量转 Markdown ```