Files
dnsmgr/app/service/CertDeployService.php
T

155 lines
5.9 KiB
PHP

<?php
namespace app\service;
use Exception;
use think\facade\Db;
use app\lib\DeployHelper;
/**
* SSL证书自动部署
*/
class CertDeployService
{
private static $retry_interval = [60, 300, 600, 1800, 3600];
private $client;
private $aid;
private $task;
private $info;
//任务状态:0:待处理 1:已完成 -1:处理失败
public function __construct($tid)
{
$task = Db::name('cert_deploy')->where('id', $tid)->find();
if (!$task) throw new Exception('该自动部署任务不存在', 102);
$this->task = $task;
$this->aid = $task['aid'];
$this->client = DeployHelper::getModel($this->aid);
if (!$this->client) throw new Exception('该自动部署任务类型不存在', 102);
$this->info = $task['info'] ? json_decode($task['info'], true) : [];
}
public function process($isManual = false)
{
if ($this->task['status'] >= 1) return;
if ($this->task['retry'] >= 6 && !$isManual) {
throw new Exception('已超出最大重试次数('.$this->task['error'].')', 103);
}
$order = Db::name('cert_order')->where('id', $this->task['oid'])->find();
if(!$order) throw new Exception('SSL证书订单不存在', 102);
if($order['status'] == 4) throw new Exception('SSL证书订单已吊销', 102);
if($order['status'] != 3) throw new Exception('SSL证书订单未完成签发', 102);
if(empty($order['fullchain']) || empty($order['privatekey'])) throw new Exception('SSL证书或私钥内容不存在', 102);
$this->lockTaskData();
try {
$this->deploy($order['fullchain'], $order['privatekey']);
} finally {
$this->unlockTaskData();
}
}
//部署证书
public function deploy($fullchain, $privatekey)
{
$this->client->setLogger(function ($txt) {
$this->saveLog($txt);
});
$this->saveLog(date('Y-m-d H:i:s'));
$config = json_decode($this->task['config'], true);
$config['domainList'] = Db::name('cert_domain')->where('oid', $this->task['oid'])->order('sort', 'asc')->column('domain');
try {
$this->client->deploy($fullchain, $privatekey, $config, $this->info);
$this->saveResult(1);
$this->saveLog('[Success] 证书部署成功');
} catch (Exception $e) {
$this->saveResult(-1, $e->getMessage(), date('Y-m-d H:i:s', time() + (array_key_exists($this->task['retry'], self::$retry_interval) ? self::$retry_interval[$this->task['retry']] : 3600)));
throw $e;
} finally {
if ($this->info && is_array($this->info)) {
if (isset($this->info['config']) && is_array($this->info['config'])) {
$config = array_merge(json_decode($this->task['config'], true), $this->info['config']);
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['config' => json_encode($config)]);
unset($this->info['config']);
}
if (!empty($this->info)) {
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['info' => json_encode($this->info)]);
}
}
}
}
//重置任务
public function reset()
{
Db::name('cert_deploy')->where('id', $this->task['id'])->data(['status' => 0, 'retry' => 0, 'retrytime' => null, 'issend' => 0, 'islock' => 0])->update();
//$file_name = app()->getRuntimePath().'log/'.$this->task['processid'].'.log';
//if (file_exists($file_name)) unlink($file_name);
$this->task['status'] = 0;
$this->task['retry'] = 0;
}
private function saveResult($status, $error = null, $retrytime = null)
{
$this->task['status'] = $status;
if (!empty($error) && strlen($error) > 300) {
$error = mb_strcut($error, 0, 300);
}
$update = ['status' => $status, 'error' => $error ? str_replace(["\r", "\n"], '', $error) : null, 'retrytime' => $retrytime];
if ($status == 1){
$update['retry'] = 0;
$update['lasttime'] = date('Y-m-d H:i:s');
}
$res = Db::name('cert_deploy')->where('id', $this->task['id'])->data($update);
if ($status < 0 || $retrytime) {
$this->task['retry']++;
$res->inc('retry');
}
$res->update();
if ($error) {
$this->saveLog('[Error] ' . $error);
}
}
private function lockTaskData()
{
Db::startTrans();
try {
$isLock = Db::name('cert_deploy')->where('id', $this->task['id'])->lock(true)->value('islock');
if ($isLock == 1 && time() - strtotime($this->task['locktime']) < 3600) {
throw new Exception('部署任务处理中,请稍后再试');
}
$update = ['islock' => 1, 'locktime' => date('Y-m-d H:i:s')];
if (empty($this->task['processid'])) $this->task['processid'] = $update['processid'] = getSid();
Db::name('cert_deploy')->where('id', $this->task['id'])->update($update);
Db::commit();
} catch (Exception $e) {
Db::rollback();
throw $e;
}
}
private function unlockTaskData()
{
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['islock' => 0]);
}
private function saveLog($txt)
{
if (empty($this->task['processid'])) return;
if (!is_dir(app()->getRuntimePath() . 'log')) mkdir(app()->getRuntimePath() . 'log');
$file_name = app()->getRuntimePath().'log/'.$this->task['processid'].'.log';
$file_exists = file_exists($file_name);
file_put_contents($file_name, $txt . PHP_EOL, FILE_APPEND);
if (!$file_exists) {
@chmod($file_name, 0777);
}
if(php_sapi_name() == 'cli'){
echo $txt . PHP_EOL;
}
}
}