diff --git a/README.md b/README.md index 79a34ed1a8c4555b5f71e1308e10f7e36294cfdf..d7cab01d932cad3358214b2c532602ac16ebe9e2 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ ## 安装 -使用composer安装 +1、使用composer安装 ~~~ composer install ~~~ -配置域名(下面以monitor.test.top为例) +2、配置域名(下面以monitor.test.top为例) ~~~ server { listen 80; @@ -36,7 +36,7 @@ server { } } ~~~ -Nginx伪静态 +3、Nginx伪静态 ~~~ location / { if (!-e $request_filename) { @@ -44,10 +44,39 @@ location / { } } ~~~ -+ 复制install/struct.db到data/db/manager.db 数据库基础文件 -(main.sql是结构文件,struct.db是数据库文件可以直接用) +4、复制sqlite数据库文件 +(1)自动复制 + - composer install 命令执行完成后,自动复制数据库文件 + +(2)如果发生未知错误,可以进行手动复制 + - 可以直接执行 `php think copy_db_file` 快速进行复制 + + - 复制install/struct.db到data/db/manager.db 数据库基础文件 + (main.sql是结构文件,struct.db是数据库文件可以直接用) + + +### 具体检测 + +### 方式一:workerman定时器 + +具体命令: +``` +# 守护进程方式启动,每15秒执行一次定时器,定时检查域名,以及发送提醒邮件 +php think timer start --d --i 15 + +# 停止 +php think timer stop + +# 重启 +php think timer restart --d --i 15 + +``` + +注:如果修改了代码,请务必重启定时器 + +### 方式二:curl请求或linux定时任务 ### 一、检测域名有效期 ##### 定时任务api接口地址(如果域名数量小于30建议每天一次) @@ -83,6 +112,6 @@ http://abc.manager.top/admin ### 四、其它说明 + config/admin.php 管理员的登录账号信息 + config/menus.php 后台管理菜单 - + config/notice.php 配置距离多少天过期可以发送邮件 - + data/db/manager.db 次乃sqlite数据库文件(不添加到版本控制) - + 确保runtime、data目录可写 \ No newline at end of file + + config/notice.php 配置距离多少天过期可以发送邮件,以及是否立即发送提醒邮件(默认开启) + + data/db/manager.db 以及sqlite数据库文件(不添加到版本控制) + + 确保runtime、data目录可写 diff --git a/application/admin/view/domain/form.html b/application/admin/view/domain/form.html index 38cffe99a85a823ffd3c55ea3fbbd48611311fbc..21c6ead8c4b6682c84d1a3dae79d4de04260a64a 100644 --- a/application/admin/view/domain/form.html +++ b/application/admin/view/domain/form.html @@ -16,7 +16,7 @@
- + http(s)://开头的完整地址,比如https://bs.top
@@ -40,12 +40,12 @@
- +
-   提交   +   提交   {if empty($Request.param.from)}   返回   {/if} @@ -57,6 +57,14 @@ {include file="public/footer"} - \ No newline at end of file + diff --git a/application/command/CopyDB.php b/application/command/CopyDB.php new file mode 100644 index 0000000000000000000000000000000000000000..5b5f1887cdb0de8c15f2e7e17e317bfac0270a2f --- /dev/null +++ b/application/command/CopyDB.php @@ -0,0 +1,48 @@ +setName('copy_db_file') + ->setDescription("复制sqlite数据库") + ->setHelp("php think copy_db_file"); + } + + + protected function execute(Input $input, Output $output) + { + $output->writeln('开始复制sqlite数据库... ' . date('Y-m-d H:i:s')); + + $rootDir = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR; + + $filePath = $rootDir . 'install' . DIRECTORY_SEPARATOR . 'struct.db'; + + $targetFile = $rootDir . 'data' . DIRECTORY_SEPARATOR . 'db' . DIRECTORY_SEPARATOR . 'manager.db'; + + if (!is_dir($rootDir . 'data' . DIRECTORY_SEPARATOR . 'db')) { + mkdir($rootDir . 'data' . DIRECTORY_SEPARATOR . 'db', '0777', true); + } + + if (!file_exists($filePath)) { + $output->writeln('源数据库文件不存在 ' . date('Y-m-d H:i:s')); + } else { + if (file_exists($targetFile)) { + $output->writeln('数据库文件已复制, 忽略本次操作 ' . date('Y-m-d H:i:s')); + } else { + if (!copy($filePath, $targetFile)) { + $output->writeln('复制数据库文件失败! ' . date('Y-m-d H:i:s')); + } else { + $output->writeln('复制数据库文件结束... ' . date('Y-m-d H:i:s')); + } + } + } + } +} diff --git a/application/command/Timer.php b/application/command/Timer.php new file mode 100644 index 0000000000000000000000000000000000000000..2964e5658997340c06954362cadf24e9e5c6e066 --- /dev/null +++ b/application/command/Timer.php @@ -0,0 +1,116 @@ +setName('timer') + ->addArgument('status', Argument::REQUIRED, 'start/stop/reload/status/connections') + ->addOption('d', null, Option::VALUE_NONE, 'daemon(守护进程)方式启动') + ->addOption('i', null, Option::VALUE_OPTIONAL, '多长时间执行一次') + ->setDescription('start/stop/reload/status/connections 定时任务。eg: php think timer start --d --i 3 表示守护进程方式启动,每隔3秒执行定时器'); + } + + /** + * 创建定时器 + * @param Input $input + * @param Output $output + * @return int|void|null + */ + protected function execute(Input $input, Output $output) + { + $this->init($input, $output); + // 创建定时器任务 + $worker = new Worker(); + $worker->count = 2; + $worker->onWorkerStart = [$this, 'start']; + $worker::runAll(); + } + + /** + * 定时器执行的内容 + * @return false|int + */ + public function start(Worker $worker) + { + // 第一个进程执行检查 + if ($worker->id === 0) { + $this->timer = \Workerman\Lib\Timer::add($this->interval, function () { + try { + trace('任务开始' . date('y-m-d H:i:s'), 'check_domain'); + + \app\common\service\Timer::check_domain(); + + trace('任务结束' . date('y-m-d H:i:s'), 'check_domain'); + + } catch (\Exception $e) { + echo 'ERROR: ' . $e->getMessage() . PHP_EOL; + $this->stop(); + } + }); + } + + // 第二个进程执行发送邮件 + if ($worker->id === 1) { + $this->timer = \Workerman\Lib\Timer::add($this->interval, function () { + try { + \app\common\service\Timer::send_mail(); + + } catch (\Exception $e) { + echo 'ERROR: ' . $e->getMessage() . PHP_EOL; + $this->stop(); + } + }); + } + + return $this->timer; + } + + /** + * 停止/删除定时器 + * @return bool + */ + public function stop() + { + return \Workerman\Lib\Timer::del($this->timer); + } + + protected function init(Input $input, Output $output) + { + global $argv; + + if ($input->hasOption('i')) { + $this->interval = (float)$input->getOption('i'); + } + + $argv[1] = $input->getArgument('status') ?: 'start'; + if ($input->hasOption('d')) { + $argv[2] = '-d'; + } else { + unset($argv[2]); + } + } +} diff --git a/application/common/service/Timer.php b/application/common/service/Timer.php index 49151844a14c6cc3413dedc3c644963dbba75dd2..82f1f275815cdd9057d1443fae5d16e158752259 100644 --- a/application/common/service/Timer.php +++ b/application/common/service/Timer.php @@ -17,9 +17,15 @@ class Timer //每天一次 $where[] = ['last_check_time', '<', strtotime('today')]; $data = $model->list_data_for_check($where, input('limit', 30, 'intval')); - if (empty($data)) return data_return_error('暂无需要检测的域名', -1, [], false); + if (empty($data)) { + return data_return_error('暂无需要检测的域名', -1, [], false); + } $update = []; $notices = []; + //提前x天提示 + $day = config('notice.https_expire'); + // 是否发送测试邮件 + $sendMailImmediately = config('notice.send_mail_immediately'); foreach ($data as $value) { $info = Bs::get_cert_info($value['domain']); $up = [ @@ -31,10 +37,8 @@ class Timer 'start_time' => $info['data']['validFrom_time_t'], 'end_time' => $info['data']['validTo_time_t'], ]); - //提前x天提示 - $day = config('notice.https_expire'); $noticeExpire = ($day > 0 ? $day : 15) * 86400; - if ($info['data']['validTo_time_t'] < time() + $noticeExpire) { + if ($sendMailImmediately || $info['data']['validTo_time_t'] < time() - $noticeExpire) { $notices[] = [ 'content' => '域名' . (Bs::parse_domain($value['domain'])) . '的https证书即将到期,到期时间为:' . date('Y-m-d H:i:s', $info['data']['validTo_time_t']) . ',请及时续约,以免到期造成访问异常问题~' ]; @@ -98,4 +102,4 @@ class Timer return data_return('ok', count($data), 0, false); } -} \ No newline at end of file +} diff --git a/composer.json b/composer.json index c5fc17b9ae9f2a83d9ecb1687521653302cb6613..1eab6a5852e0f1bf353ed65d3c7416f14d81065f 100644 --- a/composer.json +++ b/composer.json @@ -17,12 +17,13 @@ ], "require": { "php": ">=5.6.0", + "ext-iconv": "*", + "ext-openssl": "*", + "ext-json": "*", "topthink/framework": "5.1.*", "topthink/think-captcha": "2.*", - "ext-iconv": "*", - "ext-openssl": "*", "phpmailer/phpmailer": "^6.6", - "ext-json": "*" + "topthink/think-worker": "1.0.*" }, "autoload": { "psr-4": { @@ -37,5 +38,10 @@ "allow-plugins": { "topthink/think-installer": true } + }, + "scripts": { + "post-autoload-dump": [ + "@php think copy_db_file" + ] } } diff --git a/composer.lock b/composer.lock index 78b78e566564a19004a96968018bbe453d850b10..96d9b525d1dc80f1ca9c3bc50d0f332c428907a9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "96341c3837183afb974d0d3d5a580735", + "content-hash": "6ef9ac4866fa583854149fd549780699", "packages": [ { "name": "phpmailer/phpmailer", @@ -226,6 +226,110 @@ "source": "https://github.com/top-think/think-installer/tree/v2.0.5" }, "time": "2021-01-14T12:12:14+00:00" + }, + { + "name": "topthink/think-worker", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-worker.git", + "reference": "b609ff5e38dbb7194aab027d2b2c6b31a7ed1bd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-worker/zipball/b609ff5e38dbb7194aab027d2b2c6b31a7ed1bd1", + "reference": "b609ff5e38dbb7194aab027d2b2c6b31a7ed1bd1", + "shasum": "" + }, + "require": { + "workerman/workerman": "^3.3.0" + }, + "type": "library", + "autoload": { + "files": [], + "psr-4": { + "think\\worker\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "description": "workerman extend for thinkphp5", + "support": { + "issues": "https://github.com/top-think/think-worker/issues", + "source": "https://github.com/top-think/think-worker/tree/master" + }, + "time": "2016-10-08T06:07:03+00:00" + }, + { + "name": "workerman/workerman", + "version": "v3.5.32", + "source": { + "type": "git", + "url": "https://github.com/walkor/workerman.git", + "reference": "10b24f0aaed93e5a29f3fdd68f3334b5b142c4bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/walkor/workerman/zipball/10b24f0aaed93e5a29f3fdd68f3334b5b142c4bc", + "reference": "10b24f0aaed93e5a29f3fdd68f3334b5b142c4bc", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "type": "library", + "autoload": { + "psr-4": { + "Workerman\\": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "http://www.workerman.net", + "role": "Developer" + } + ], + "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", + "homepage": "http://www.workerman.net", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "email": "walkor@workerman.net", + "forum": "http://wenda.workerman.net/", + "issues": "https://github.com/walkor/workerman/issues", + "source": "https://github.com/walkor/workerman", + "wiki": "http://doc.workerman.net/" + }, + "funding": [ + { + "url": "https://opencollective.com/workerman", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/walkor", + "type": "patreon" + } + ], + "time": "2022-10-22T07:00:10+00:00" } ], "packages-dev": [], @@ -237,7 +341,8 @@ "platform": { "php": ">=5.6.0", "ext-iconv": "*", - "ext-openssl": "*" + "ext-openssl": "*", + "ext-json": "*" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/config/database.php b/config/database.php index 4558421b7e02ad558d4eb75cc9d27340a72addd9..943fa01876217092c9658a3e63b0dcb37206baf5 100644 --- a/config/database.php +++ b/config/database.php @@ -23,7 +23,7 @@ return [ // 端口 'hostport' => '', // 连接dsn - 'dsn' => str_replace("\\",'/','sqlite:'.__DIR__.'/../data/db/manager.db'), + 'dsn' => 'sqlite:'.__DIR__. DIRECTORY_SEPARATOR .'..'. DIRECTORY_SEPARATOR .'data'. DIRECTORY_SEPARATOR .'db'. DIRECTORY_SEPARATOR .'manager.db', // 数据库连接参数 'params' => [], // 数据库编码默认采用utf8 diff --git a/config/notice.php b/config/notice.php index 93d0be6fb76dde8a41ed181e497f2ebb2b55eb4b..086d94e3eccf292d995ed835a498b22566ce4ae4 100644 --- a/config/notice.php +++ b/config/notice.php @@ -2,4 +2,6 @@ return [ //https证书到期前x天提醒 'https_expire' => 15, -]; \ No newline at end of file + // 是否执行实时通知,开启后,每次检查域名都将发送邮件,不再到期前再提醒 + 'send_mail_immediately' => 1 +];