ExpireNoticeService.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <?php
  2. namespace backend\modules\admin\services;
  3. use common\models\common\Addons;
  4. use Yii;
  5. /**
  6. * 过期提醒
  7. * @package backend\modules\admin\services
  8. */
  9. class ExpireNoticeService extends BaseService
  10. {
  11. /**
  12. * 存储key
  13. */
  14. protected const KEY = 'expire-notice';
  15. const FILE_NAME = 'change_ssl.sh';
  16. /**
  17. * 剩余不足提示时间
  18. */
  19. protected const LIMIT = 7;
  20. /**
  21. * 保存配置
  22. *
  23. * @param array $config
  24. * @return boolean
  25. */
  26. public function setConfig(array $config)
  27. {
  28. // print_r($config);exit;
  29. //如果上传了ssl证书压缩包
  30. $admin_url = substr(Yii::$app->services->setting->platform->get('system.backend_url'), 8);
  31. $api_url = substr(Yii::$app->services->setting->platform->get('system.api_url'), 8);
  32. $h5_url = substr(Yii::$app->services->setting->platform->get('system.h5_url'), 8);
  33. $static_url = substr(Yii::$app->services->setting->platform->get('system.static_url'), 8);
  34. $salary_h5_url = parse_url(Yii::$app->services->setting->platform->get('system.cat_h5_url') ?: '')['host'] ?? '';
  35. if (!empty($config['certificate']['ssl_path'])) {
  36. $is_docker_deploy = Yii::$app->params['is_docker_deploy']; //获取配置
  37. $path = $config['certificate']['ssl_path'];
  38. $pwd = Yii::$app->params['oskad'];
  39. $is_docker_deploy = Yii::$app->params['is_docker_deploy'];
  40. $host = Yii::$app->params['host'];
  41. $replaces = [
  42. '{SSL_PATH}' => $path,
  43. '{PWD}' => $pwd,
  44. '{FILE_NAME}' => static::FILE_NAME,
  45. ];
  46. if ($is_docker_deploy) { //是否为docker搭建系统
  47. $file_path = '/www/wwwroot/qimall/';
  48. $file_content = static::shDockerTemplate(); //获取sh模板
  49. } else {
  50. //prod
  51. $file_path = '/data/wwwroot/qimall/';
  52. $file_content = static::shTemplate();
  53. }
  54. //组装sh脚本
  55. $content = str_replace(array_keys($replaces), array_values($replaces), $file_content);
  56. file_put_contents($file_path . static::FILE_NAME, $content);
  57. //执行脚本
  58. if ($is_docker_deploy) {
  59. // exec('sshpass -p "'. $pwd.'" ssh root@'. $host .' "sh '. $file_path . static::FILE_NAME.'"');
  60. exec("sshpass -p '".$pwd."' ssh -o StrictHostKeyChecking=no root@". $host." 'sh ". $file_path . static::FILE_NAME . "'");
  61. } else {
  62. exec('sh '. $file_path . static::FILE_NAME);
  63. }
  64. }
  65. $url_arr = [
  66. 'admin_expire_date' => $admin_url,
  67. 'h5_expire_date' => $h5_url,
  68. 'api_expire_date' => $api_url,
  69. 'static_expire_date' => $static_url,
  70. ];
  71. // 有发薪猫插件才获取过期日期
  72. if ($this->isInstallSalary($salary_h5_url)) {
  73. $url_arr['salary_h5_expire_date'] = $salary_h5_url;
  74. }
  75. //通过域名获取ssl证书过期日期
  76. foreach ($url_arr as $key => $value) {
  77. $info = $this->getCertificateExpireTime($value);
  78. if ($info['code'] == 0) $config['certificate'][$key] = date('Y-m-d', $info['cert_info']['validTo_time_t']);
  79. }
  80. //保存配置
  81. Yii::$app->services->setting->platform->set([static::KEY => $config]);
  82. return true;
  83. }
  84. /**
  85. * 获取配置
  86. *
  87. * @param string $key
  88. * @return mixed
  89. */
  90. public function getConfig(string $key = '')
  91. {
  92. $config = Yii::$app->services->setting->platform->get(static::KEY);
  93. foreach ($config as &$item) {
  94. $item = json_decode($item, true);
  95. }
  96. $info = $key ? ($config[$key] ?? '') : $config;
  97. if (!empty($info['certificate'])) $info['certificate']['ssl_path'] = '';
  98. return $info;
  99. }
  100. /**
  101. * 计算证书到期提醒
  102. *
  103. * @return array
  104. */
  105. public function certificateNotice()
  106. {
  107. // 全部证书
  108. $cers = ['admin', 'api', 'h5', 'static'];
  109. if ($this->isInstallSalary()) {
  110. $cers[] = 'salary_h5';
  111. }
  112. $notices = [];
  113. foreach ($cers as $key) {
  114. $this->keyShowNotice('certificate', sprintf('%s_expire_date', $key)) && ($notices[] = $key);
  115. }
  116. return $notices;
  117. }
  118. /**
  119. * 计算服务器到期提醒
  120. *
  121. * @return boolean
  122. */
  123. public function serverNotice()
  124. {
  125. return $this->keyShowNotice('server');
  126. }
  127. /**
  128. * 是否即将到期显示提示
  129. *
  130. * @param string $key
  131. * @return boolean
  132. */
  133. protected function keyShowNotice(string $key, string $expire_key = 'expire_date')
  134. {
  135. $config = $this->getConfig($key);
  136. if (!(isset($config['open_notice']) && $config['open_notice'])) return false;
  137. // 到期时间
  138. $expire_date = $config[$expire_key];
  139. return bcsub(strtotime($expire_date), time()) < bcmul(static::LIMIT, 86400);
  140. }
  141. //通过域名获取ssl证书过期时间
  142. public function getCertificateExpireTime($url)
  143. {
  144. if (!extension_loaded('openssl') || !is_callable('openssl_x509_parse')){
  145. return ['code' => -1, 'msg' => '请开启openssl扩展'];
  146. }
  147. //不规范入参处理
  148. $parse = parse_url($url);
  149. if (!empty($parse['host'])) {
  150. $domain = $parse['host'];
  151. } elseif (empty($parse['path'])) {
  152. return ['code' => -1, 'msg' => $url . '请输入合法的域名'];
  153. } else {
  154. //abc.top/x/xx
  155. $arr = explode('/', $parse['path']);
  156. $domain = $arr[0];
  157. }
  158. $context = stream_context_create([
  159. 'ssl' => [
  160. 'capture_peer_cert' => true,
  161. 'capture_peer_cert_chain' => true,
  162. ],
  163. ]);
  164. $client = @stream_socket_client("ssl://" . $domain . ":443", $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context);
  165. if ($client == false) {
  166. return ['code' => -1, 'msg' => $domain . '未查到可靠信息','err'=>[
  167. 'errno'=>$errno,
  168. 'errstr'=>iconv('gbk', 'utf-8', $errstr),
  169. ]];
  170. }
  171. $params = stream_context_get_params($client);
  172. if (empty($params['options']['ssl']['peer_certificate'])) {
  173. return ['code' => -1, 'msg' => $domain . '获取信息失败,请确保可以正常访问'];
  174. }
  175. $cert = $params['options']['ssl']['peer_certificate'];
  176. $cert_info = @openssl_x509_parse($cert);
  177. return ['code' => 0, 'cert_info' => $cert_info];
  178. }
  179. /**
  180. * prod部署-sh模板
  181. *
  182. * @return string
  183. */
  184. protected static function shTemplate()
  185. {
  186. return <<<'EOC'
  187. #!/bin/bash
  188. # 检查是否有sudo权限
  189. if [ "$(id -u)" != "0" ]; then
  190. # 设置root密码,这里是示例密码,请替换为您的实际密码
  191. root_password='{PWD}'
  192. # 使用echo和管道将密码传递给su命令
  193. echo $root_password | su -c "sh /data/wwwroot/qimall/{FILE_NAME}"
  194. fi
  195. # 检查是否有sudo权限
  196. if [ "$(id -u)" == "0" ]; then
  197. # 证书
  198. # 证书路径
  199. ssl_path='{SSL_PATH}'
  200. # 移动证书到目录
  201. dir=/application/nginx/conf/ssl
  202. # 证书目录
  203. ssl=/data/wwwroot/qimall/${ssl_path}
  204. # 复制
  205. cp ${ssl} ${dir}/ssl.zip
  206. cd ${dir}
  207. # 解压
  208. unzip -o ssl.zip
  209. # 重启nginx
  210. # systemctl restart nginx
  211. /application/nginx/sbin/nginx -s reload
  212. # 重启消费者cd
  213. # systemctl restart swoole_consume_run
  214. fi
  215. EOC;
  216. }
  217. /**
  218. * docker部署-sh模板
  219. *
  220. * @return string
  221. */
  222. protected static function shDockerTemplate()
  223. {
  224. return <<<'EOC'
  225. #!/bin/bash
  226. # 检查是否有sudo权限
  227. if [ "$(id -u)" != "0" ]; then
  228. # 设置root密码,这里是示例密码,请替换为您的实际密码
  229. root_password='{PWD}'
  230. # 使用echo和管道将密码传递给su命令
  231. echo $root_password | su -c "sh /www/wwwroot/qimall/{FILE_NAME}"
  232. fi
  233. # 检查是否有sudo权限
  234. if [ "$(id -u)" == "0" ]; then
  235. # 证书
  236. # 证书路径
  237. ssl_path='{SSL_PATH}'
  238. # 移动证书到目录
  239. dir=/data/confs/nginx/ssl
  240. # 证书目录
  241. ssl=/www/wwwroot/qimall/${ssl_path}
  242. # 复制
  243. cp ${ssl} ${dir}/ssl.zip
  244. cd ${dir}
  245. # 解压
  246. unzip -o ssl.zip
  247. # 重启nginx
  248. # systemctl restart nginx
  249. sudo docker restart nginx
  250. # 重启消费者cd
  251. # systemctl restart swoole_consume_run
  252. fi
  253. EOC;
  254. }
  255. /**
  256. * 是否安装发薪猫插件
  257. *
  258. * @param null $url
  259. *
  260. * @return bool
  261. */
  262. public function isInstallSalary($url = null): bool
  263. {
  264. if (is_null($url)) {
  265. $url = Yii::$app->services->setting->platform->get('system.cat_h5_url');
  266. }
  267. return Addons::isInstall('Salary') && $url;
  268. }
  269. }