ComposerUpdate

This commit is contained in:
Anyon 2020-08-31 20:26:30 +08:00
parent 9704c64d13
commit 27dd871878
10 changed files with 184 additions and 235 deletions

View File

@ -16,7 +16,6 @@
namespace app\admin\controller\api;
use think\admin\Controller;
use think\admin\service\InstallService;
use think\admin\service\ModuleService;
/**
@ -50,7 +49,7 @@ class Update extends Controller
*/
public function node()
{
$this->success('获取文件列表成功!', InstallService::instance()->getList(
$this->success('获取文件列表成功!', ModuleService::instance()->getChanges(
json_decode($this->request->post('rules', '[]', ''), true),
json_decode($this->request->post('ignore', '[]', ''), true)
));

View File

@ -299,7 +299,6 @@ return array(
'think\\admin\\service\\AdminService' => $vendorDir . '/zoujingli/think-library/src/service/AdminService.php',
'think\\admin\\service\\CaptchaService' => $vendorDir . '/zoujingli/think-library/src/service/CaptchaService.php',
'think\\admin\\service\\ExpressService' => $vendorDir . '/zoujingli/think-library/src/service/ExpressService.php',
'think\\admin\\service\\InstallService' => $vendorDir . '/zoujingli/think-library/src/service/InstallService.php',
'think\\admin\\service\\InterfaceService' => $vendorDir . '/zoujingli/think-library/src/service/InterfaceService.php',
'think\\admin\\service\\MenuService' => $vendorDir . '/zoujingli/think-library/src/service/MenuService.php',
'think\\admin\\service\\MessageService' => $vendorDir . '/zoujingli/think-library/src/service/MessageService.php',

View File

@ -436,7 +436,6 @@ class ComposerStaticInitb55556af42203bcef68382c66bc39c70
'think\\admin\\service\\AdminService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/AdminService.php',
'think\\admin\\service\\CaptchaService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/CaptchaService.php',
'think\\admin\\service\\ExpressService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/ExpressService.php',
'think\\admin\\service\\InstallService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/InstallService.php',
'think\\admin\\service\\InterfaceService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/InterfaceService.php',
'think\\admin\\service\\MenuService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/MenuService.php',
'think\\admin\\service\\MessageService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/MessageService.php',

View File

@ -573,8 +573,8 @@
},
{
"name": "symfony/options-resolver",
"version": "v3.4.43",
"version_normalized": "3.4.43.0",
"version": "v3.4.44",
"version_normalized": "3.4.44.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
@ -958,17 +958,17 @@
},
{
"name": "zoujingli/think-library",
"version": "v6.0.1",
"version_normalized": "6.0.1.0",
"version": "v6.0.2",
"version_normalized": "6.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/zoujingli/ThinkLibrary.git",
"reference": "a5c1e18d0e57dd0a83174793f09cca5ba592b773"
"reference": "a498595d637568481601091985ae22d5cd05a057"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/a5c1e18d0e57dd0a83174793f09cca5ba592b773",
"reference": "a5c1e18d0e57dd0a83174793f09cca5ba592b773",
"url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/a498595d637568481601091985ae22d5cd05a057",
"reference": "a498595d637568481601091985ae22d5cd05a057",
"shasum": "",
"mirrors": [
{
@ -985,7 +985,7 @@
"ext-mbstring": "*",
"topthink/framework": "^6.0"
},
"time": "2020-08-28T11:32:28+00:00",
"time": "2020-08-31T11:59:58+00:00",
"type": "library",
"extra": {
"think": {

2
vendor/services.php vendored
View File

@ -1,5 +1,5 @@
<?php
// This file is automatically generated at:2020-08-28 19:49:26
// This file is automatically generated at:2020-08-31 20:25:48
declare (strict_types = 1);
return array (
0 => 'think\\admin\\Library',

View File

@ -38,6 +38,11 @@ use function Composer\Autoload\includeFile;
*/
class Library extends Service
{
/**
* 扩展库版本号
*/
const VERSION = '6.0.2';
/**
* 启动服务
*/

View File

@ -16,7 +16,7 @@
namespace think\admin\command;
use think\admin\Command;
use think\admin\service\InstallService;
use think\admin\service\ModuleService;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
@ -114,10 +114,10 @@ class Install extends Command
protected function installFile()
{
$data = InstallService::instance()->grenerateDifference($this->rules, $this->ignore);
$data = ModuleService::instance()->grenerateDifference($this->rules, $this->ignore);
if (empty($data)) $this->output->writeln('No need to update the file if the file comparison is consistent');
else foreach ($data as $file) {
[$state, $mode, $name] = InstallService::instance()->updateFileByDownload($file);
[$state, $mode, $name] = ModuleService::instance()->updateFileByDownload($file);
if ($state) {
if ($mode === 'add') $this->output->writeln("--- {$name} add successfully");
if ($mode === 'mod') $this->output->writeln("--- {$name} update successfully");

View File

@ -74,6 +74,7 @@ class AdminService extends Service
if ($this->isSuper()) return true;
$service = NodeService::instance();
[$real, $nodes] = [$service->fullnode($node), $service->getMethods()];
// 以下代码为兼容 win 控制器不区分大小写的验证问题
foreach ($nodes as $key => $rule) {
if (strpos($key, '_') !== false && strpos($key, '/') !== false) {
$attr = explode('/', $key);

View File

@ -1,196 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Library for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2020 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
// +----------------------------------------------------------------------
// | 官方网站: https://gitee.com/zoujingli/ThinkLibrary
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | gitee 仓库地址 https://gitee.com/zoujingli/ThinkLibrary
// | github 仓库地址 https://github.com/zoujingli/ThinkLibrary
// +----------------------------------------------------------------------
namespace think\admin\service;
use think\admin\extend\HttpExtend;
use think\admin\Service;
/**
* 模块安装服务管理
* Class InstallService
* @package think\admin\service
*/
class InstallService extends Service
{
/**
* 项目根目录
* @var string
*/
private $root;
/**
* 线上服务器地址
* @var string
*/
private $server;
/**
* 初始化服务
*/
protected function initialize()
{
$this->root = strtr($this->app->getRootPath(), '\\', '/');
$this->server = ModuleService::instance()->getServer();
}
/**
* 获取文件信息列表
* @param array $rules 文件规则
* @param array $ignore 忽略规则
* @param array $data 扫描结果列表
* @return array
*/
public function getList(array $rules, array $ignore = [], array $data = []): array
{
// 扫描规则文件
foreach ($rules as $key => $rule) {
$name = strtr(trim($rule, '\\/'), '\\', '/');
$data = array_merge($data, $this->_scanList($this->root . $name));
}
// 清除忽略文件
foreach ($data as $key => $item) foreach ($ignore as $ign) {
if (stripos($item['name'], $ign) === 0) unset($data[$key]);
}
// 返回文件数据
return ['rules' => $rules, 'ignore' => $ignore, 'list' => $data];
}
/**
* 获取文件差异数据
* @param array $rules 文件规则
* @param array $ignore 忽略规则
* @return array
*/
public function grenerateDifference(array $rules = [], array $ignore = []): array
{
[$rules1, $ignore1, $data] = [$rules, $ignore, []];
$result = json_decode(HttpExtend::post($this->server . '/admin/api.update/node', [
'rules' => json_encode($rules1), 'ignore' => json_encode($ignore1),
]), true);
if (!empty($result['code'])) {
$new = $this->getList($result['data']['rules'], $result['data']['ignore']);
foreach ($this->_grenerateDifferenceContrast($result['data']['list'], $new['list']) as $file) {
if (in_array($file['type'], ['add', 'del', 'mod'])) foreach ($rules1 as $rule) {
if (stripos($file['name'], $rule) === 0) $data[] = $file;
}
}
}
return $data;
}
/**
* 下载并更新文件
* @param array $file 文件信息
* @return array
*/
public function updateFileByDownload(array $file): array
{
if (in_array($file['type'], ['add', 'mod'])) {
if ($this->_downloadFile(encode($file['name']))) {
return [true, $file['type'], $file['name']];
} else {
return [false, $file['type'], $file['name']];
}
} elseif (in_array($file['type'], ['del'])) {
$real = $this->root . $file['name'];
if (is_file($real) && unlink($real)) {
$this->_removeEmptyDirectory(dirname($real));
return [true, $file['type'], $file['name']];
} else {
return [false, $file['type'], $file['name']];
}
}
}
/**
* 下载更新文件内容
* @param string $encode
* @return boolean|integer
*/
private function _downloadFile($encode)
{
$source = $this->server . '/admin/api.update/get?encode=' . $encode;
$result = json_decode(HttpExtend::get($source), true);
if (empty($result['code'])) return false;
$filename = $this->root . decode($encode);
file_exists(dirname($filename)) || mkdir(dirname($filename), 0755, true);
return file_put_contents($filename, base64_decode($result['data']['content']));
}
/**
* 清理空目录
* @param string $path
*/
private function _removeEmptyDirectory($path)
{
if (is_dir($path) && count(scandir($path)) === 2 && rmdir($path)) {
$this->_removeEmptyDirectory(dirname($path));
}
}
/**
* 两二维数组对比
* @param array $serve 线上文件列表信息
* @param array $local 本地文件列表信息
* @return array
*/
private function _grenerateDifferenceContrast(array $serve = [], array $local = []): array
{
// 数据扁平化
[$_serve, $_local, $_diffy] = [[], [], []];
foreach ($serve as $t) $_serve[$t['name']] = $t;
foreach ($local as $t) $_local[$t['name']] = $t;
unset($serve, $local);
// 线上数据差异计算
foreach ($_serve as $t) isset($_local[$t['name']]) ? array_push($_diffy, [
'type' => $t['hash'] === $_local[$t['name']]['hash'] ? null : 'mod', 'name' => $t['name'],
]) : array_push($_diffy, ['type' => 'add', 'name' => $t['name']]);
// 本地数据增量计算
foreach ($_local as $t) if (!isset($_serve[$t['name']])) array_push($_diffy, ['type' => 'del', 'name' => $t['name']]);
unset($_serve, $_local);
usort($_diffy, function ($a, $b) {
return $a['name'] !== $b['name'] ? ($a['name'] > $b['name'] ? 1 : -1) : 0;
});
return $_diffy;
}
/**
* 获取指定文件信息
* @param string $path 文件路径
* @return array
*/
private function _getInfo($path): array
{
return [
'name' => str_replace($this->root, '', $path),
'hash' => md5(preg_replace('/\s+/', '', file_get_contents($path))),
];
}
/**
* 获取目录文件列表
* @param string $path 待扫描目录
* @param array $data 扫描结果
* @return array
*/
private function _scanList($path, $data = []): array
{
foreach (NodeService::instance()->scanDirectory($path, [], null) as $file) {
$data[] = $this->_getInfo(strtr($file, '\\', '/'));
}
return $data;
}
}

View File

@ -17,6 +17,7 @@ namespace think\admin\service;
use think\admin\extend\HttpExtend;
use think\admin\extend\Parsedown;
use think\admin\Library;
use think\admin\Service;
/**
@ -26,6 +27,12 @@ use think\admin\Service;
*/
class ModuleService extends Service
{
/**
* 代码根目录
* @var string
*/
protected $root;
/**
* 官方应用地址
* @var string
@ -39,14 +46,14 @@ class ModuleService extends Service
protected $version;
/**
* 服务初始化
* 模块服务初始化
*/
public function initialize()
{
$full = $this->app->config->get('app.thinkadmin_ver', 'v4.0.0');
$this->version = trim($full, 'v');
$version = strstr($full . '.', '.', true);
$this->server = "https://{$version}.thinkadmin.top";
$this->root = $this->app->getRootPath();
$this->version = trim(Library::VERSION, 'v');
$maxver = strstr($this->version, '.', true);
$this->server = "https://v{$maxver}.thinkadmin.top";
}
/**
@ -115,13 +122,12 @@ class ModuleService extends Service
*/
public function install($name): array
{
$install = InstallService::instance();
$this->app->cache->set('moduleOnlineData', []);
$data = $install->grenerateDifference(['app' . '/' . $name]);
$data = $this->grenerateDifference(['app' . '/' . $name]);
if (empty($data)) return [0, '没有需要安装的文件', []];
$lines = [];
foreach ($data as $file) {
[$state, $mode, $name] = $install->updateFileByDownload($file);
[$state, $mode, $name] = $this->updateFileByDownload($file);
if ($state) {
if ($mode === 'add') $lines[] = "add {$name} successed";
if ($mode === 'mod') $lines[] = "modify {$name} successed";
@ -147,7 +153,7 @@ class ModuleService extends Service
$vars = $this->_getModuleVersion($name);
if (is_array($vars) && isset($vars['version']) && preg_match('|^\d{4}\.\d{2}\.\d{2}\.\d{2}$|', $vars['version'])) {
$data[$name] = array_merge($vars, ['change' => []]);
foreach ($service->scanDirectory($this->_getModulePath($name) . 'change', [], 'md') as $file) {
foreach ($service->scanDirectory($this->_getModuleInfoPath($name) . 'change', [], 'md') as $file) {
$data[$name]['change'][pathinfo($file, PATHINFO_FILENAME)] = Parsedown::instance()->parse(file_get_contents($file));
}
}
@ -156,17 +162,25 @@ class ModuleService extends Service
}
/**
* 获取允许下载的规则
* 获取文件信息列表
* @param array $rules 文件规则
* @param array $ignore 忽略规则
* @param array $data 扫描结果列表
* @return array
*/
public function getAllowDownloadRule(): array
public function getChanges(array $rules, array $ignore = [], array $data = []): array
{
$data = $this->app->cache->get('moduleAllowRule', []);
if (is_array($data) && count($data) > 0) return $data;
$data = ['config', 'public/static', 'public/router.php', 'public/index.php'];
foreach (array_keys($this->getModules()) as $name) $data[] = "app/{$name}";
$this->app->cache->set('moduleAllowRule', $data, 30);
return $data;
// 扫描规则文件
foreach ($rules as $key => $rule) {
$name = strtr(trim($rule, '\\/'), '\\', '/');
$data = array_merge($data, $this->_scanLocalFileHashList($this->root . $name));
}
// 清除忽略文件
foreach ($data as $key => $item) foreach ($ignore as $ign) {
if (stripos($item['name'], $ign) === 0) unset($data[$key]);
}
// 返回文件数据
return ['rules' => $rules, 'ignore' => $ignore, 'list' => $data];
}
/**
@ -174,28 +188,89 @@ class ModuleService extends Service
* @param string $name 文件名称
* @return boolean
*/
public function checkAllowDownload($name): bool
public function checkAllowDownload(string $name): bool
{
// 禁止下载数据库配置文件
if (stripos($name, 'database.php') !== false) {
return false;
}
// 检查允许下载的文件规则
foreach ($this->getAllowDownloadRule() as $rule) {
foreach ($this->_getAllowDownloadRule() as $rule) {
if (stripos($name, $rule) !== false) return true;
}
// 不在允许下载的文件规则
return false;
}
/**
* 获取文件差异数据
* @param array $rules 文件规则
* @param array $ignore 忽略规则
* @return array
*/
public function grenerateDifference(array $rules = [], array $ignore = []): array
{
[$rules1, $ignore1, $data] = [$rules, $ignore, []];
$result = json_decode(HttpExtend::post($this->server . '/admin/api.update/node', [
'rules' => json_encode($rules1), 'ignore' => json_encode($ignore1),
]), true);
if (!empty($result['code'])) {
$new = $this->getChanges($result['data']['rules'], $result['data']['ignore']);
foreach ($this->_grenerateDifferenceContrast($result['data']['list'], $new['list']) as $file) {
if (in_array($file['type'], ['add', 'del', 'mod'])) foreach ($rules1 as $rule) {
if (stripos($file['name'], $rule) === 0) $data[] = $file;
}
}
}
return $data;
}
/**
* 尝试下载并更新文件
* @param array $file 文件信息
* @return array
*/
public function updateFileByDownload(array $file): array
{
if (in_array($file['type'], ['add', 'mod'])) {
if ($this->_downloadUpdateFile(encode($file['name']))) {
return [true, $file['type'], $file['name']];
} else {
return [false, $file['type'], $file['name']];
}
} elseif (in_array($file['type'], ['del'])) {
$real = $this->root . $file['name'];
if (is_file($real) && unlink($real)) {
$this->_removeEmptyDirectory(dirname($real));
return [true, $file['type'], $file['name']];
} else {
return [false, $file['type'], $file['name']];
}
}
}
/**
* 获取允许下载的规则
* @return array
*/
private function _getAllowDownloadRule(): array
{
$data = $this->app->cache->get('moduleAllowRule', []);
if (is_array($data) && count($data) > 0) return $data;
$data = ['think', 'config', 'public/static', 'public/router.php', 'public/index.php'];
foreach (array_keys($this->getModules()) as $name) $data[] = 'app/' . $name;
$this->app->cache->set('moduleAllowRule', $data, 30);
return $data;
}
/**
* 获取模块版本信息
* @param string $name 模块名称
* @return bool|array|null
*/
private function _getModuleVersion($name)
private function _getModuleVersion(string $name)
{
$filename = $this->_getModulePath($name) . 'module.json';
$filename = $this->_getModuleInfoPath($name) . 'module.json';
if (file_exists($filename) && is_file($filename) && is_readable($filename)) {
$vars = json_decode(file_get_contents($filename), true);
return isset($vars['name']) && isset($vars['version']) ? $vars : null;
@ -204,14 +279,81 @@ class ModuleService extends Service
}
}
/**
* 下载更新文件内容
* @param string $encode
* @return boolean|integer
*/
private function _downloadUpdateFile(string $encode)
{
$source = $this->server . '/admin/api.update/get?encode=' . $encode;
$result = json_decode(HttpExtend::get($source), true);
if (empty($result['code'])) return false;
$filename = $this->root . decode($encode);
file_exists(dirname($filename)) || mkdir(dirname($filename), 0755, true);
return file_put_contents($filename, base64_decode($result['data']['content']));
}
/**
* 清理空目录
* @param string $path
*/
private function _removeEmptyDirectory(string $path)
{
if (is_dir($path) && count(scandir($path)) === 2 && rmdir($path)) {
$this->_removeEmptyDirectory(dirname($path));
}
}
/**
* 获取模块信息路径
* @param string $name
* @param string $name 模块名称
* @return string
*/
private function _getModulePath($name)
private function _getModuleInfoPath(string $name): string
{
$appdir = $this->app->getBasePath() . $name;
return $appdir . DIRECTORY_SEPARATOR . 'module' . DIRECTORY_SEPARATOR;
}
/**
* 根据线上线下生成操作数组
* @param array $serve 线上文件列表信息
* @param array $local 本地文件列表信息
* @return array
*/
private function _grenerateDifferenceContrast(array $serve = [], array $local = []): array
{
// 数据扁平化
[$_serve, $_local, $_diffy] = [[], [], []];
foreach ($serve as $t) $_serve[$t['name']] = $t;
foreach ($local as $t) $_local[$t['name']] = $t;
unset($serve, $local);
// 线上数据差异计算
foreach ($_serve as $t) isset($_local[$t['name']]) ? array_push($_diffy, [
'type' => $t['hash'] === $_local[$t['name']]['hash'] ? null : 'mod', 'name' => $t['name'],
]) : array_push($_diffy, ['type' => 'add', 'name' => $t['name']]);
// 本地数据增量计算
foreach ($_local as $t) if (!isset($_serve[$t['name']])) array_push($_diffy, ['type' => 'del', 'name' => $t['name']]);
unset($_serve, $_local);
usort($_diffy, function ($a, $b) {
return $a['name'] !== $b['name'] ? ($a['name'] > $b['name'] ? 1 : -1) : 0;
});
return $_diffy;
}
/**
* 获取目录文件列表
* @param mixed $path 扫描目录
* @param array $data 扫描结果
* @return array
*/
private function _scanLocalFileHashList(string $path, array $data = []): array
{
foreach (NodeService::instance()->scanDirectory($path, [], null) as $file) $data[] = [
'name' => str_replace(strtr($this->root, '\\', '/'), '', $file),
'hash' => md5(preg_replace('/\s+/', '', file_get_contents($file))),
];
return $data;
}
}