Skip to content

Commit c264c73

Browse files
committed
适配FastAdmin1.3.0
优化路由规则判断 新增授权域名判断 优化插件管理列表加载 优化错误提示信息
1 parent 7b0bbb2 commit c264c73

File tree

4 files changed

+225
-31
lines changed

4 files changed

+225
-31
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "addons package for fastadmin",
44
"homepage": "https://github.com/karsonzhang/fastadmin-addons",
55
"license": "Apache-2.0",
6-
"version": "1.2.12",
6+
"version": "1.3.0",
77
"authors": [
88
{
99
"name": "Karson",

src/addons/Route.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
use think\Config;
66
use think\exception\HttpException;
7+
use think\exception\HttpResponseException;
78
use think\Hook;
89
use think\Loader;
910
use think\Request;
11+
use think\Response;
1012

1113
/**
1214
* 插件执行默认控制器
@@ -38,6 +40,9 @@ public function execute($addon = null, $controller = null, $action = null)
3840
if (!$info['state']) {
3941
throw new HttpException(500, __('addon %s is disabled', $addon));
4042
}
43+
if (!Service::checkAddonAuthorization($addon)) {
44+
throw new HttpResponseException(new Response('',sprintf("%d1", 40)));
45+
}
4146
$dispatch = $request->dispatch();
4247
if (isset($dispatch['var']) && $dispatch['var']) {
4348
$request->route(array_diff_key($dispatch['var'], array_flip(['addon', 'controller', 'action'])));

src/addons/Service.php

Lines changed: 194 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use fast\Http;
66
use GuzzleHttp\Client;
77
use GuzzleHttp\Exception\TransferException;
8+
use GuzzleHttp\TransferStats;
89
use PhpZip\Exception\ZipException;
910
use PhpZip\ZipFile;
1011
use RecursiveDirectoryIterator;
@@ -13,13 +14,75 @@
1314
use think\Cache;
1415
use think\Db;
1516
use think\Exception;
17+
use think\Log;
1618

1719
/**
1820
* 插件服务
1921
* @package think\addons
2022
*/
2123
class Service
2224
{
25+
/**
26+
* 插件列表
27+
*/
28+
public static function addons($params = [])
29+
{
30+
$params['domain'] = request()->host(true);
31+
return self::sendRequest('/addon/index', $params, 'GET');
32+
}
33+
34+
/**
35+
* 登录
36+
*/
37+
public static function login($params = [])
38+
{
39+
return self::sendRequest('/user/login', $params, 'POST');
40+
}
41+
42+
/**
43+
* 会员信息
44+
*/
45+
public static function userinfo($params = [])
46+
{
47+
return self::sendRequest('/user/index', $params, 'POST');
48+
}
49+
50+
/**
51+
* 退出
52+
*/
53+
public static function logout($params = [])
54+
{
55+
return self::sendRequest('/user/logout', $params, 'POST');
56+
}
57+
58+
/**
59+
* 检测插件是否购买授权
60+
*/
61+
public static function isBuy($name, $extend = [])
62+
{
63+
$params = array_merge(['name' => $name, 'domain' => request()->host(true)], $extend);
64+
return self::sendRequest('/addon/isbuy', $params, 'POST');
65+
}
66+
67+
/**
68+
* 检测插件是否授权
69+
*
70+
* @param string $name 插件名称
71+
* @param string $domain 验证域名
72+
*/
73+
public static function isAuthorization($name, $domain = '')
74+
{
75+
$config = self::config($name);
76+
$request = request();
77+
$domain = self::getRootDomain($domain ? $domain : $request->host(true));
78+
if (isset($config['domains']) && isset($config['domains']) && isset($config['validations']) && isset($config['licensecodes'])) {
79+
$index = array_search($domain, $config['domains']);
80+
if ((in_array($domain, $config['domains']) && in_array(md5(md5($domain) . ($config['licensecodes'][$index] ?? '')), $config['validations'])) || $request->isCli()) {
81+
return true;
82+
}
83+
}
84+
return false;
85+
}
2386

2487
/**
2588
* 远程下载插件
@@ -39,27 +102,28 @@ public static function download($name, $extend = [])
39102
$content = $body->getContents();
40103
if (substr($content, 0, 1) === '{') {
41104
$json = (array)json_decode($content, true);
42-
43105
//如果传回的是一个下载链接,则再次下载
44106
if ($json['data'] && isset($json['data']['url'])) {
45107
$response = $client->get($json['data']['url']);
46108
$body = $response->getBody();
47109
$content = $body->getContents();
48110
} else {
111+
Log::write("[addon]" . $content);
49112
//下载返回错误,抛出异常
50113
throw new AddonException($json['msg'], $json['code'], $json['data']);
51114
}
52115
}
53116
} catch (TransferException $e) {
54-
throw new Exception("Addon package download failed");
117+
Log::write("[addon]" . $e->getMessage());
118+
throw new Exception(config('app_debug') ? $e->getMessage() : "Addon package download failed");
55119
}
56120

57121
if ($write = fopen($tmpFile, 'w')) {
58122
fwrite($write, $content);
59123
fclose($write);
60124
return $tmpFile;
61125
}
62-
throw new Exception("No permission to write temporary files");
126+
throw new Exception(config('app_debug') && isset($content) ? $content : "No permission to write temporary files");
63127
}
64128

65129
/**
@@ -83,7 +147,7 @@ public static function unzip($name)
83147
$zip->openFile($file);
84148
} catch (ZipException $e) {
85149
$zip->close();
86-
throw new Exception('Unable to open the zip file');
150+
throw new Exception(config('app_debug') ? $e->getMessage() : 'Unable to open the zip file');
87151
}
88152

89153
$dir = self::getAddonDir($name);
@@ -95,7 +159,7 @@ public static function unzip($name)
95159
try {
96160
$zip->extractTo($dir);
97161
} catch (ZipException $e) {
98-
throw new Exception('Unable to extract the file');
162+
throw new Exception(config('app_debug') ? $e->getMessage() : 'Unable to extract the file');
99163
} finally {
100164
$zip->close();
101165
}
@@ -209,6 +273,7 @@ public static function local($file, $extend = [])
209273

210274
$info['config'] = get_addon_config($name) ? 1 : 0;
211275
$info['bootstrap'] = is_file(Service::getBootstrapFile($name));
276+
$info['testdata'] = is_file(Service::getTestdataFile($name));
212277
return $info;
213278
}
214279

@@ -220,18 +285,7 @@ public static function local($file, $extend = [])
220285
*/
221286
public static function valid($params = [])
222287
{
223-
$client = self::getClient();
224-
$multipart = [];
225-
foreach ($params as $name => $value) {
226-
$multipart[] = ['name' => $name, 'contents' => $value];
227-
}
228-
try {
229-
$response = $client->post('/addon/valid', ['multipart' => $multipart]);
230-
$content = $response->getBody()->getContents();
231-
} catch (TransferException $e) {
232-
throw new Exception("Network error");
233-
}
234-
$json = (array)json_decode($content, true);
288+
$json = self::sendRequest('/addon/valid', $params, 'POST');
235289
if ($json && isset($json['code'])) {
236290
if ($json['code']) {
237291
return true;
@@ -265,7 +319,6 @@ public static function backup($name)
265319
$zipFile->close();
266320
}
267321

268-
269322
return true;
270323
}
271324

@@ -313,12 +366,14 @@ public static function noconflict($name)
313366
/**
314367
* 导入SQL
315368
*
316-
* @param string $name 插件名称
369+
* @param string $name 插件名称
370+
* @param string $fileName SQL文件名称
317371
* @return boolean
318372
*/
319-
public static function importsql($name)
373+
public static function importsql($name, $fileName = null)
320374
{
321-
$sqlFile = self::getAddonDir($name) . 'install.sql';
375+
$fileName = is_null($fileName) ? 'install.sql' : $fileName;
376+
$sqlFile = self::getAddonDir($name) . $fileName;
322377
if (is_file($sqlFile)) {
323378
$lines = file($sqlFile);
324379
$templine = '';
@@ -356,7 +411,7 @@ public static function refresh()
356411
$bootstrapArr = [];
357412
foreach ($addons as $name => $addon) {
358413
$bootstrapFile = self::getBootstrapFile($name);
359-
if ($addon['state'] && is_file($bootstrapFile)) {
414+
if ($addon['state'] && is_file($bootstrapFile) && self::isAuthorization($name)) {
360415
$bootstrapArr[] = file_get_contents($bootstrapFile);
361416
}
362417
}
@@ -407,6 +462,8 @@ public static function install($name, $force = false, $extend = [])
407462
throw new Exception('Addon already exists');
408463
}
409464

465+
$extend['domain'] = request()->host(true);
466+
410467
// 远程下载插件
411468
$tmpFile = Service::download($name, $extend);
412469

@@ -464,6 +521,7 @@ public static function install($name, $force = false, $extend = [])
464521

465522
$info['config'] = get_addon_config($name) ? 1 : 0;
466523
$info['bootstrap'] = is_file(Service::getBootstrapFile($name));
524+
$info['testdata'] = is_file(Service::getTestdataFile($name));
467525
return $info;
468526
}
469527

@@ -879,6 +937,84 @@ public static function getGlobalFiles($name, $onlyconflict = false)
879937
return $list;
880938
}
881939

940+
/**
941+
* 更新本地应用插件授权
942+
*/
943+
public static function authorization($params = [])
944+
{
945+
$addonList = get_addon_list();
946+
$result = [];
947+
$domain = request()->host(true);
948+
$addons = [];
949+
foreach ($addonList as $name => $item) {
950+
$config = self::config($name);
951+
$addons[] = ['name' => $name, 'domains' => $config['domains'] ?? [], 'licensecodes' => $config['licensecodes'] ?? [], 'validations' => $config['validations'] ?? []];
952+
}
953+
$params = array_merge($params, [
954+
'faversion' => config('fastadmin.version'),
955+
'domain' => $domain,
956+
'addons' => $addons
957+
]);
958+
$result = self::sendRequest('/addon/authorization', $params, 'POST');
959+
if (isset($result['code']) && $result['code'] == 1) {
960+
$json = $result['data']['addons'] ?? [];
961+
foreach ($addonList as $name => $item) {
962+
self::config($name, ['domains' => $json[$name]['domains'] ?? [], 'licensecodes' => $json[$name]['licensecodes'] ?? [], 'validations' => $json[$name]['validations'] ?? []]);
963+
}
964+
return true;
965+
} else {
966+
throw new Exception($result['msg'] ?? __('Network error'));
967+
}
968+
}
969+
970+
/**
971+
* 验证插件授权,应用插件需要授权使用,移除或绕过授权验证,保留追究法律责任的权利
972+
* @param $name
973+
* @return bool
974+
*/
975+
public static function checkAddonAuthorization($name)
976+
{
977+
$request = request();
978+
$config = self::config($name);
979+
$domain = self::getRootDomain($request->host(true));
980+
//应用插件需要授权使用,移除或绕过授权验证,保留追究法律责任的权利
981+
if (isset($config['domains']) && isset($config['domains']) && isset($config['validations']) && isset($config['licensecodes'])) {
982+
$index = array_search($domain, $config['domains']);
983+
if ((in_array($domain, $config['domains']) && in_array(md5(md5($domain) . ($config['licensecodes'][$index] ?? '')), $config['validations'])) || $request->isCli()) {
984+
$request->bind('authorized', $domain ?: 'cli');
985+
return true;
986+
} elseif ($config['domains']) {
987+
foreach ($config['domains'] as $index => $item) {
988+
if (substr_compare($domain, "." . $item, -strlen("." . $item)) === 0 && in_array(md5(md5($item) . ($config['licensecodes'][$index] ?? '')), $config['validations'])) {
989+
$request->bind('authorized', $domain);
990+
return true;
991+
}
992+
}
993+
}
994+
}
995+
return false;
996+
}
997+
998+
/**
999+
* 获取顶级域名
1000+
* @param $domain
1001+
* @return string
1002+
*/
1003+
public static function getRootDomain($domain)
1004+
{
1005+
$host = strtolower(trim($domain));
1006+
$hostArr = explode('.', $host);
1007+
$hostCount = count($hostArr);
1008+
$cnRegex = '/\w+\.(gov|org|ac|mil|net|edu|com|bj|tj|sh|cq|he|sx|nm|ln|jl|hl|js|zj|ah|fj|jx|sd|ha|hb|hn|gd|gx|hi|sc|gz|yn|xz|sn|gs|qh|nx|xj|tw|hk|mo)\.cn$/i';
1009+
$countryRegex = '/\w+\.(\w{2}|com|net)\.\w{2}$/i';
1010+
if ($hostCount > 2 && (preg_match($cnRegex, $host) || preg_match($countryRegex, $host))) {
1011+
$host = implode('.', array_slice($hostArr, -3, 3, true));
1012+
} else {
1013+
$host = implode('.', array_slice($hostArr, -2, 2, true));
1014+
}
1015+
return $host;
1016+
}
1017+
8821018
/**
8831019
* 获取插件行为、路由配置文件
8841020
* @return string
@@ -897,6 +1033,15 @@ public static function getBootstrapFile($name)
8971033
return ADDON_PATH . $name . DS . 'bootstrap.js';
8981034
}
8991035

1036+
/**
1037+
* 获取testdata.sql路径
1038+
* @return string
1039+
*/
1040+
public static function getTestdataFile($name)
1041+
{
1042+
return ADDON_PATH . $name . DS . 'testdata.sql';
1043+
}
1044+
9001045
/**
9011046
* 获取指定插件的目录
9021047
*/
@@ -964,7 +1109,7 @@ protected static function getCheckDirs()
9641109
* 获取请求对象
9651110
* @return Client
9661111
*/
967-
protected static function getClient()
1112+
public static function getClient()
9681113
{
9691114
$options = [
9701115
'base_uri' => self::getServerUrl(),
@@ -985,6 +1130,32 @@ protected static function getClient()
9851130
return $client;
9861131
}
9871132

1133+
/**
1134+
* 发送请求
1135+
* @return array
1136+
* @throws Exception
1137+
* @throws \GuzzleHttp\Exception\GuzzleException
1138+
*/
1139+
public static function sendRequest($url, $params = [], $method = 'POST')
1140+
{
1141+
$json = [];
1142+
try {
1143+
$client = self::getClient();
1144+
$options = strtoupper($method) == 'POST' ? ['form_params' => $params] : ['query' => $params];
1145+
$response = $client->request($method, $url, $options);
1146+
$body = $response->getBody();
1147+
$content = $body->getContents();
1148+
$json = (array)json_decode($content, true);
1149+
} catch (TransferException $e) {
1150+
Log::write("[addon]" . $e->getMessage());
1151+
throw new Exception(config('app_debug') ? $e->getMessage() : __('Network error'));
1152+
} catch (\Exception $e) {
1153+
Log::write("[addon]" . $e->getMessage());
1154+
throw new Exception(config('app_debug') ? $e->getMessage() : __('Unknown data format'));
1155+
}
1156+
return $json;
1157+
}
1158+
9881159
/**
9891160
* 匹配配置文件中info信息
9901161
* @param ZipFile $zip

0 commit comments

Comments
 (0)