| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- <?php
- // +----------------------------------------------------------------------
- // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
- // +----------------------------------------------------------------------
- // | Author: CRMEB Team <admin@crmeb.com>
- // +----------------------------------------------------------------------
- namespace app\services\system;
- use app\services\BaseServices;
- use crmeb\exceptions\AdminException;
- use think\facade\Log;
- /**
- * Node.js 环境管理服务类
- *
- * 功能概述:
- * 本服务类负责检测和管理小程序 CI 上传所需的运行环境,
- * 包括 Node.js、npm 和 miniprogram-ci 工具的检测与安装指导。
- *
- * 主要功能:
- * 1. 环境检测 - 检测 Node.js、npm、miniprogram-ci 的安装状态和版本
- * 2. 系统识别 - 识别操作系统类型 (CentOS/Ubuntu/macOS/Windows)
- * 3. 安装指南 - 根据操作系统提供对应的安装命令
- * 4. exec 函数检测 - 检查 PHP 的 exec() 函数是否可用
- *
- * 环境要求:
- * - Node.js >= 14.0.0 (推荐 18.x LTS)
- * - npm (随 Node.js 一起安装)
- * - miniprogram-ci (通过 npm install -g miniprogram-ci 全局安装)
- * - PHP exec() 函数未被禁用
- *
- * @package app\services\system
- */
- class NodeEnvironmentServices extends BaseServices
- {
- /**
- * Node.js 最低版本要求
- *
- * miniprogram-ci 工具需要 Node.js 14.0.0 或更高版本。
- */
- const MIN_NODE_VERSION = '14.0.0';
- /**
- * 推荐的 Node.js 版本
- *
- * 推荐使用 Node.js 18 LTS 版本,提供更好的性能和安全性。
- */
- const RECOMMENDED_NODE_VERSION = '18';
- /**
- * 获取完整的环境状态信息
- *
- * 检测并返回小程序 CI 上传所需的所有环境信息,
- * 前端根据这些信息展示环境就绪状态或引导用户完成环境配置。
- *
- * @return array 完整的环境状态信息,包含:
- * - os: 操作系统信息 {family, type, version}
- * - node: Node.js 状态 {installed, version, path, meets_requirement}
- * - npm: npm 状态 {installed, version}
- * - miniprogram_ci: CI工具状态 {installed, version}
- * - ready: 环境是否完全就绪
- * - can_install: 是否支持自动安装
- * - exec_enabled: exec 函数是否可用
- * - message: 提示信息
- */
- public function getEnvironmentStatus(): array
- {
- // 检测各项环境
- $nodeInfo = $this->checkNodeInstalled(); // Node.js 状态
- $npmInfo = $this->checkNpmInstalled(); // npm 状态
- $ciInfo = $this->checkMiniprogramCIInstalled(); // miniprogram-ci 状态
- $osInfo = $this->getOsInfo(); // 操作系统信息
- $execEnabled = $this->isExecEnabled(); // exec 函数可用性
- return [
- 'os' => $osInfo, // 操作系统信息
- 'node' => $nodeInfo, // Node.js 状态
- 'npm' => $npmInfo, // npm 状态
- 'miniprogram_ci' => $ciInfo, // miniprogram-ci 状态
- // 环境就绪条件: Node.js + npm + miniprogram-ci 均已安装且 exec 可用
- 'ready' => $nodeInfo['installed'] && $npmInfo['installed'] && $ciInfo['installed'] && $execEnabled,
- 'can_install' => $this->canAutoInstall(), // 是否支持自动安装
- 'exec_enabled' => $execEnabled, // exec 函数是否可用
- 'message' => $execEnabled ? '' : '服务器禁用了 exec 函数,无法使用小程序上传功能。请联系服务器管理员启用 exec 函数。',
- ];
- }
- /**
- * 检查 PHP exec() 函数是否可用
- *
- * 小程序 CI 上传功能依赖 PHP 的 exec() 函数来执行命令行工具。
- * 很多服务器出于安全考虑会禁用该函数,需要检测其可用性。
- *
- * 检测步骤:
- * 1. 检查 exec 函数是否存在
- * 2. 检查 disable_functions 配置中是否包含 exec
- * 3. 尝试执行简单命令验证实际可用性
- *
- * @return bool exec 函数可用返回 true,否则返回 false
- */
- public function isExecEnabled(): bool
- {
- // 检查 exec 函数是否存在
- if (!function_exists('exec')) {
- return false;
- }
- // 检查 disable_functions 配置中是否禁用了 exec
- $disabled = explode(',', ini_get('disable_functions'));
- $disabled = array_map('trim', $disabled);
- if (in_array('exec', $disabled)) {
- return false;
- }
- // 尝试执行一个简单命令验证实际可用性
- $output = [];
- $code = 0;
- @exec('echo test 2>&1', $output, $code);
- return $code === 0 && !empty($output);
- }
- /**
- * 检查 Node.js 是否已安装
- *
- * 通过执行 node -v 命令获取 Node.js 的安装状态和版本信息。
- *
- * @return array Node.js 状态信息,包含:
- * - installed: 是否已安装
- * - version: 版本号 (如 18.17.0)
- * - path: 可执行文件路径
- * - meets_requirement: 是否满足最低版本要求
- */
- public function checkNodeInstalled(): array
- {
- $result = [
- 'installed' => false,
- 'version' => '',
- 'path' => '',
- 'meets_requirement' => false,
- ];
- // 执行 node -v 获取版本信息
- $output = $this->execCommand('node -v 2>&1');
- if ($output && preg_match('/v?(\d+\.\d+\.\d+)/', $output, $matches)) {
- $result['installed'] = true;
- $result['version'] = $matches[1];
- // 检查版本是否满足最低要求 (>= 14.0.0)
- $result['meets_requirement'] = version_compare($matches[1], self::MIN_NODE_VERSION, '>=');
- // 获取 node 可执行文件的完整路径
- $path = $this->execCommand('which node 2>&1');
- $result['path'] = trim($path);
- }
- return $result;
- }
- /**
- * 检查 npm 是否已安装
- *
- * npm 是 Node.js 的包管理器,通常随 Node.js 一起安装。
- * 用于安装 miniprogram-ci 等 npm 包。
- *
- * @return array npm 状态信息,包含:
- * - installed: 是否已安装
- * - version: 版本号
- */
- public function checkNpmInstalled(): array
- {
- $result = [
- 'installed' => false,
- 'version' => '',
- ];
- // 执行 npm -v 获取版本信息
- $output = $this->execCommand('npm -v 2>&1');
- if ($output && preg_match('/(\d+\.\d+\.\d+)/', $output, $matches)) {
- $result['installed'] = true;
- $result['version'] = $matches[1];
- }
- return $result;
- }
- /**
- * 检查 miniprogram-ci 是否已全局安装
- *
- * miniprogram-ci 是微信官方提供的小程序代码上传工具。
- * 需要通过 npm install -g miniprogram-ci 全局安装。
- *
- * @return array miniprogram-ci 状态信息,包含:
- * - installed: 是否已安装
- * - version: 版本号
- */
- public function checkMiniprogramCIInstalled(): array
- {
- $result = [
- 'installed' => false,
- 'version' => '',
- ];
- // 通过 npm list -g 检查全局安装的包
- $output = $this->execCommand('npm list -g miniprogram-ci --depth=0 2>&1');
- if ($output && preg_match('/miniprogram-ci@(\d+\.\d+\.\d+)/', $output, $matches)) {
- $result['installed'] = true;
- $result['version'] = $matches[1];
- }
- return $result;
- }
- /**
- * 获取操作系统信息
- *
- * 识别服务器的操作系统类型,用于提供对应的安装指南。
- * 使用命令行方式检测,避免受 open_basedir 限制影响。
- *
- * 支持识别的系统:
- * - Linux: CentOS/RHEL/Rocky/Ubuntu/Debian/Alpine 等
- * - macOS: 通过 sw_vers 获取版本
- * - Windows: 通过 PHP_OS_FAMILY 识别
- *
- * @return array 操作系统信息,包含:
- * - family: 系统家族 (Linux/Darwin/Windows)
- * - type: 具体类型 (centos/ubuntu/debian/macos/windows)
- * - version: 系统版本号
- */
- public function getOsInfo(): array
- {
- $os = PHP_OS_FAMILY; // 获取 PHP 识别的操作系统家族
- $type = 'unknown';
- $version = '';
- if ($os === 'Linux') {
- // Linux 系统: 通过命令行读取 /etc/os-release 获取发行版信息
- $osRelease = $this->execCommand('cat /etc/os-release 2>/dev/null');
- if ($osRelease) {
- // 解析 os-release 文件内容
- $lines = explode("\n", $osRelease);
- $osInfo = [];
- foreach ($lines as $line) {
- if (strpos($line, '=') !== false) {
- list($key, $value) = explode('=', $line, 2);
- $osInfo[trim($key)] = trim($value, '"\' ');
- }
- }
- $id = strtolower($osInfo['ID'] ?? '');
- $version = $osInfo['VERSION_ID'] ?? '';
- // 映射操作系统类型
- if (in_array($id, ['centos', 'rhel', 'rocky', 'almalinux', 'fedora'])) {
- $type = 'centos'; // Red Hat 系列
- } elseif ($id === 'ubuntu') {
- $type = 'ubuntu';
- } elseif (in_array($id, ['debian', 'raspbian'])) {
- $type = 'debian';
- } elseif ($id === 'alpine') {
- $type = 'alpine';
- } else {
- $type = $id ?: 'linux';
- }
- } else {
- // 备用方案: 使用 uname 命令
- $uname = $this->execCommand('uname -a 2>/dev/null');
- $type = 'linux';
- $version = $uname ?: '';
- }
- } elseif ($os === 'Darwin') {
- // macOS 系统
- $type = 'macos';
- $version = $this->execCommand('sw_vers -productVersion 2>&1') ?: '';
- } elseif ($os === 'Windows') {
- // Windows 系统
- $type = 'windows';
- }
- return [
- 'family' => $os, // 系统家族
- 'type' => $type, // 具体类型
- 'version' => trim($version), // 版本号
- ];
- }
- /**
- * 检查是否支持自动安装
- *
- * 检查服务器环境是否支持自动安装 Node.js 和相关工具。
- * 自动安装功能依赖于操作系统类型和 PHP 函数可用性。
- *
- * 支持的操作系统: CentOS/RHEL、Ubuntu、Debian、Alpine、macOS
- *
- * @return bool 支持自动安装返回 true
- */
- public function canAutoInstall(): bool
- {
- $os = $this->getOsInfo();
- // 支持自动安装的操作系统列表
- $supportedOs = ['centos', 'rhel', 'ubuntu', 'debian', 'alpine', 'macos'];
- if (!in_array($os['type'], $supportedOs)) {
- return false;
- }
- // 检查是否有执行命令的权限 (exec 或 shell_exec)
- if (!function_exists('exec') && !function_exists('shell_exec')) {
- return false;
- }
- return true;
- }
- /**
- * 执行命令并返回输出
- *
- * 封装的命令执行方法,优先使用 exec,如果不可用则尝试 shell_exec。
- *
- * @param string $command 要执行的命令
- * @return string|null 命令输出,执行失败返回 null
- */
- protected function execCommand(string $command): ?string
- {
- // 优先使用 exec 函数
- if (function_exists('exec')) {
- $output = [];
- exec($command, $output);
- return implode("\n", $output);
- }
- // 备用: 使用 shell_exec 函数
- elseif (function_exists('shell_exec')) {
- return shell_exec($command);
- }
- return null;
- }
- /**
- * 获取一键安装脚本 URL
- *
- * 返回用于自动安装 Node.js 环境的 Shell 脚本地址。
- *
- * @return string 安装脚本的 URL 地址
- */
- public function getInstallScriptUrl(): string
- {
- // return sys_config('site_url', '') . '/statics/scripts/install_node_env.sh';
- return 'https://www.crmeb.com/static/upgrade/install_node_env.sh';
- }
- /**
- * 获取安装指南 (手动安装说明)
- *
- * 根据服务器操作系统类型返回对应的 Node.js 和 miniprogram-ci 安装步骤。
- * 每个操作系统都有专门优化的安装命令。
- *
- * 支持的操作系统:
- * - CentOS/RHEL: 使用 NodeSource 的 rpm 仓库
- * - Ubuntu/Debian: 使用 NodeSource 的 deb 仓库
- * - macOS: 使用 Homebrew 包管理器
- * - Windows: 从 Node.js 官网下载安装包
- *
- * @return array 安装指南信息,包含:
- * - title: 指南标题 (如 "CentOS/RHEL 安装指南")
- * - steps: 安装步骤数组,包含具体的命令行指令
- * - script_url: 一键安装脚本的 URL 地址
- */
- public function getInstallGuide(): array
- {
- // 获取当前操作系统信息
- $os = $this->getOsInfo();
- // 各操作系统的安装指南
- $guides = [
- // CentOS/RHEL 系列 - 使用 yum 包管理器
- 'centos' => [
- 'title' => 'CentOS/RHEL 安装指南',
- 'steps' => [
- '1. 添加 NodeSource 仓库:',
- ' curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -',
- '2. 安装 Node.js:',
- ' sudo yum install -y nodejs',
- '3. 验证安装:',
- ' node -v && npm -v',
- '4. 安装 miniprogram-ci:',
- ' sudo npm install miniprogram-ci -g',
- ],
- ],
- // Ubuntu - 使用 apt 包管理器
- 'ubuntu' => [
- 'title' => 'Ubuntu/Debian 安装指南',
- 'steps' => [
- '1. 添加 NodeSource 仓库:',
- ' curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -',
- '2. 安装 Node.js:',
- ' sudo apt-get install -y nodejs',
- '3. 验证安装:',
- ' node -v && npm -v',
- '4. 安装 miniprogram-ci:',
- ' sudo npm install miniprogram-ci -g',
- ],
- ],
- // Debian - 与 Ubuntu 相同
- 'debian' => [
- 'title' => 'Ubuntu/Debian 安装指南',
- 'steps' => [
- '1. 添加 NodeSource 仓库:',
- ' curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -',
- '2. 安装 Node.js:',
- ' sudo apt-get install -y nodejs',
- '3. 验证安装:',
- ' node -v && npm -v',
- '4. 安装 miniprogram-ci:',
- ' sudo npm install miniprogram-ci -g',
- ],
- ],
- // macOS - 使用 Homebrew
- 'macos' => [
- 'title' => 'macOS 安装指南',
- 'steps' => [
- '1. 安装 Homebrew (如果未安装):',
- ' /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
- '2. 安装 Node.js:',
- ' brew install node@18',
- '3. 验证安装:',
- ' node -v && npm -v',
- '4. 安装 miniprogram-ci:',
- ' npm install miniprogram-ci -g',
- ],
- ],
- // Windows - 从官网下载安装
- 'windows' => [
- 'title' => 'Windows 安装指南',
- 'steps' => [
- '1. 下载 Node.js 安装包:',
- ' 访问 https://nodejs.org/zh-cn/download/',
- '2. 运行安装程序,按提示完成安装',
- '3. 打开命令提示符,验证安装:',
- ' node -v && npm -v',
- '4. 安装 miniprogram-ci:',
- ' npm install miniprogram-ci -g',
- ],
- ],
- ];
- // 根据操作系统类型获取对应指南,未知系统使用通用指南
- $type = $os['type'] ?: 'unknown';
- $guide = isset($guides[$type]) ? $guides[$type] : [
- 'title' => '通用安装指南',
- 'steps' => [
- '1. 访问 Node.js 官网下载安装包:',
- ' https://nodejs.org/zh-cn/download/',
- '2. 按照官方文档完成安装',
- '3. 验证安装:',
- ' node -v && npm -v',
- '4. 安装 miniprogram-ci:',
- ' npm install miniprogram-ci -g',
- ],
- ];
- // 添加一键安装脚本 URL
- $guide['script_url'] = $this->getInstallScriptUrl();
- return $guide;
- }
- }
|