You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

465 lines
19 KiB

2 years ago
2 months ago
2 years ago
13 hours ago
2 years ago
13 hours ago
2 years ago
13 hours ago
2 years ago
13 hours ago
2 years ago
11 months ago
2 years ago
11 months ago
2 years ago
2 years ago
  1. <?php
  2. namespace app\controller;
  3. use app\BaseController;
  4. use think\facade\Db;
  5. use think\facade\View;
  6. use think\facade\Request;
  7. use think\facade\Cache;
  8. use app\lib\Btapi;
  9. use app\lib\Plugins;
  10. class Admin extends BaseController
  11. {
  12. public function verifycode()
  13. {
  14. return captcha();
  15. }
  16. public function login(){
  17. if(request()->islogin){
  18. return redirect('/admin');
  19. }
  20. if(request()->isAjax()){
  21. $username = input('post.username',null,'trim');
  22. $password = input('post.password',null,'trim');
  23. $code = input('post.code',null,'trim');
  24. if(empty($username) || empty($password)){
  25. return json(['code'=>-1, 'msg'=>'用户名或密码不能为空']);
  26. }
  27. if(!captcha_check($code)){
  28. return json(['code'=>-1, 'msg'=>'验证码错误']);
  29. }
  30. if($username == config_get('admin_username') && $password == config_get('admin_password')){
  31. Db::name('log')->insert(['uid' => 0, 'action' => '登录后台', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s")]);
  32. $session = md5($username.config_get('admin_password'));
  33. $expiretime = time()+2562000;
  34. $token = authcode("{$username}\t{$session}\t{$expiretime}", 'ENCODE', config_get('syskey'));
  35. cookie('admin_token', $token, ['expire' => $expiretime, 'httponly' => true]);
  36. config_set('admin_lastlogin', date('Y-m-d H:i:s'));
  37. return json(['code'=>0]);
  38. }else{
  39. return json(['code'=>-1, 'msg'=>'用户名或密码错误']);
  40. }
  41. }
  42. return view();
  43. }
  44. public function logout()
  45. {
  46. cookie('admin_token', null);
  47. return redirect('/admin/login');
  48. }
  49. public function index()
  50. {
  51. $stat = ['total'=>0, 'free'=>0, 'pro'=>0, 'ltd'=>0, 'third'=>0];
  52. $json_arr = Plugins::get_plugin_list();
  53. if($json_arr){
  54. foreach($json_arr['list'] as $plugin){
  55. $stat['total']++;
  56. if($plugin['type']==10) $stat['third']++;
  57. elseif($plugin['type']==12) $stat['ltd']++;
  58. elseif($plugin['type']==8) $stat['pro']++;
  59. elseif($plugin['type']==5 || $plugin['type']==6 || $plugin['type']==7) $stat['free']++;
  60. }
  61. }
  62. $stat['runtime'] = Db::name('config')->where('key','runtime')->value('value') ?? '<font color="red">未运行</font>';
  63. $stat['record_total'] = Db::name('record')->count();
  64. $stat['record_isuse'] = Db::name('record')->whereTime('usetime', '>=', strtotime('-7 days'))->count();
  65. View::assign('stat', $stat);
  66. $tmp = 'version()';
  67. $mysqlVersion = Db::query("select version()")[0][$tmp];
  68. $info = [
  69. 'framework_version' => app()::VERSION,
  70. 'php_version' => PHP_VERSION,
  71. 'mysql_version' => $mysqlVersion,
  72. 'software' => $_SERVER['SERVER_SOFTWARE'],
  73. 'os' => php_uname(),
  74. 'date' => date("Y-m-d H:i:s"),
  75. ];
  76. View::assign('info', $info);
  77. return view();
  78. }
  79. public function set(){
  80. if(request()->isAjax()){
  81. $params = Request::param();
  82. foreach ($params as $key => $value) {
  83. config_set($key, $value);
  84. }
  85. cache('configs', NULL);
  86. return json(['code'=>0]);
  87. }
  88. $mod = input('param.mod', 'sys');
  89. View::assign('mod', $mod);
  90. View::assign('conf', config('sys'));
  91. $runtime = Db::name('config')->where('key','runtime')->value('value') ?? '<font color="red">未运行</font>';
  92. View::assign('runtime', $runtime);
  93. View::assign('is_user_www', isset($_SERVER['USER']) && $_SERVER['USER'] == 'www');
  94. return view();
  95. }
  96. public function setaccount(){
  97. $params = Request::param();
  98. if(isset($params['username']))$params['username']=trim($params['username']);
  99. if(isset($params['oldpwd']))$params['oldpwd']=trim($params['oldpwd']);
  100. if(isset($params['newpwd']))$params['newpwd']=trim($params['newpwd']);
  101. if(isset($params['newpwd2']))$params['newpwd2']=trim($params['newpwd2']);
  102. if(empty($params['username'])) return json(['code'=>-1, 'msg'=>'用户名不能为空']);
  103. config_set('admin_username', $params['username']);
  104. if(!empty($params['oldpwd']) && !empty($params['newpwd']) && !empty($params['newpwd2'])){
  105. if(config_get('admin_password') != $params['oldpwd']){
  106. return json(['code'=>-1, 'msg'=>'旧密码不正确']);
  107. }
  108. if($params['newpwd'] != $params['newpwd2']){
  109. return json(['code'=>-1, 'msg'=>'两次新密码输入不一致']);
  110. }
  111. config_set('admin_password', $params['newpwd']);
  112. }
  113. cache('configs', NULL);
  114. cookie('admin_token', null);
  115. return json(['code'=>0]);
  116. }
  117. public function testbturl(){
  118. $bt_type = input('post.bt_type/d');
  119. if($bt_type == 1){
  120. $bt_surl = input('post.bt_surl');
  121. if(!$bt_surl)return json(['code'=>-1, 'msg'=>'参数不能为空']);
  122. $res = get_curl($bt_surl . 'api/SetupCount');
  123. if(strpos($res, 'ok')!==false){
  124. return json(['code'=>0, 'msg'=>'第三方云端连接测试成功!']);
  125. }else{
  126. return json(['code'=>-1, 'msg'=>'第三方云端连接测试失败']);
  127. }
  128. }else{
  129. $bt_url = input('post.bt_url');
  130. $bt_key = input('post.bt_key');
  131. $os = input('post.os');
  132. if(!$bt_url || !$bt_key)return json(['code'=>-1, 'msg'=>'参数不能为空']);
  133. $btapi = new Btapi($bt_url, $bt_key);
  134. if ($os == 'win') {
  135. $result = $btapi->get_config_go();
  136. if($result && isset($result['config'])){
  137. $result = $btapi->get_user_info();
  138. if($result && isset($result['username'])){
  139. return json(['code'=>0, 'msg'=>'面板连接测试成功!']);
  140. }else{
  141. return json(['code'=>-1, 'msg'=>'面板连接测试成功,但未安装专用插件/未登录账号']);
  142. }
  143. }else{
  144. return json(['code'=>-1, 'msg'=>isset($result['msg'])?$result['msg']:'面板地址无法连接']);
  145. }
  146. } else {
  147. $result = $btapi->get_config();
  148. if($result && isset($result['status']) && ($result['status']==1 || isset($result['sites_path']))){
  149. $result = $btapi->get_user_info();
  150. if($result && isset($result['username'])){
  151. return json(['code'=>0, 'msg'=>'面板连接测试成功!']);
  152. }else{
  153. return json(['code'=>-1, 'msg'=>'面板连接测试成功,但未安装专用插件/未登录账号']);
  154. }
  155. }else{
  156. return json(['code'=>-1, 'msg'=>isset($result['msg'])?$result['msg']:'面板地址无法连接']);
  157. }
  158. }
  159. }
  160. }
  161. public function plugins(){
  162. $typelist = [];
  163. $json_arr = Plugins::get_plugin_list();
  164. if($json_arr){
  165. foreach($json_arr['type'] as $type){
  166. if($type['title'] == '一键部署') continue;
  167. $typelist[$type['id']] = $type['title'];
  168. }
  169. }
  170. View::assign('typelist', $typelist);
  171. View::assign('skip_plugins', \app\lib\BtPlugins::$skip_plugins);
  172. return view();
  173. }
  174. public function pluginswin(){
  175. $typelist = [];
  176. $json_arr = Plugins::get_plugin_list('Windows');
  177. if($json_arr){
  178. foreach($json_arr['type'] as $type){
  179. if($type['title'] == '一键部署') continue;
  180. $typelist[$type['id']] = $type['title'];
  181. }
  182. }
  183. View::assign('typelist', $typelist);
  184. return view();
  185. }
  186. public function pluginsen(){
  187. $typelist = [];
  188. $json_arr = Plugins::get_plugin_list('en');
  189. if($json_arr){
  190. foreach($json_arr['type'] as $type){
  191. if($type['title'] == '一键部署') continue;
  192. $typelist[$type['id']] = $type['title'];
  193. }
  194. }
  195. View::assign('typelist', $typelist);
  196. return view();
  197. }
  198. public function plugins_data(){
  199. $type = input('post.type/d');
  200. $keyword = input('post.keyword', null, 'trim');
  201. $os = input('get.os');
  202. if(!$os) $os = 'Linux';
  203. $json_arr = Plugins::get_plugin_list($os);
  204. if(!$json_arr) return json([]);
  205. $typelist = [];
  206. foreach($json_arr['type'] as $row){
  207. $typelist[$row['id']] = $row['title'];
  208. }
  209. $list = [];
  210. foreach($json_arr['list'] as $plugin){
  211. if($type > 0 && $plugin['type']!=$type) continue;
  212. if(!empty($keyword) && $keyword != $plugin['name'] && stripos($plugin['title'], $keyword)===false) continue;
  213. $versions = [];
  214. foreach($plugin['versions'] as $version){
  215. $ver = $version['m_version'].'.'.$version['version'];
  216. if(isset($version['download'])){
  217. $status = false;
  218. if(file_exists(get_data_dir().'plugins/other/'.$version['download'])){
  219. $status = true;
  220. }
  221. $versions[] = ['status'=>$status, 'type'=>1, 'version'=>$ver, 'download'=>$version['download'], 'md5'=>$version['md5']];
  222. }else{
  223. $status = false;
  224. if(file_exists(get_data_dir($os).'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){
  225. $status = true;
  226. }
  227. $versions[] = ['status'=>$status, 'type'=>0, 'version'=>$ver];
  228. }
  229. }
  230. if($plugin['name'] == 'obs') $plugin['ps'] = substr($plugin['ps'],0,strpos($plugin['ps'],'<a '));
  231. $list[] = [
  232. 'id' => $plugin['id'],
  233. 'name' => $plugin['name'],
  234. 'title' => $plugin['title'],
  235. 'type' => $plugin['type'],
  236. 'typename' => isset($typelist[$plugin['type']]) ? $typelist[$plugin['type']] : '未知',
  237. 'desc' => str_replace('target="_blank"','target="_blank" rel="noopener noreferrer"',$plugin['ps']),
  238. 'price' => $plugin['price'],
  239. 'author' => isset($plugin['author']) ? $plugin['author'] : '官方',
  240. 'versions' => $versions
  241. ];
  242. }
  243. return json($list);
  244. }
  245. public function download_plugin(){
  246. $name = input('post.name', null, 'trim');
  247. $version = input('post.version', null, 'trim');
  248. $os = input('post.os');
  249. if(!$os) $os = 'Linux';
  250. if(!$name || !$version) return json(['code'=>-1, 'msg'=>'参数不能为空']);
  251. try{
  252. Plugins::download_plugin($name, $version, $os);
  253. Db::name('log')->insert(['uid' => 0, 'action' => '下载插件', 'data' => $name.'-'.$version.' os:'.$os, 'addtime' => date("Y-m-d H:i:s")]);
  254. return json(['code'=>0,'msg'=>'下载成功']);
  255. }catch(\Exception $e){
  256. return json(['code'=>-1, 'msg'=>$e->getMessage()]);
  257. }
  258. }
  259. public function refresh_plugins(){
  260. $os = input('get.os');
  261. if(!$os) $os = 'Linux';
  262. try{
  263. Plugins::refresh_plugin_list($os);
  264. Db::name('log')->insert(['uid' => 0, 'action' => '刷新插件列表', 'data' => '刷新'.$os.'插件列表成功', 'addtime' => date("Y-m-d H:i:s")]);
  265. return json(['code'=>0,'msg'=>'获取最新插件列表成功!']);
  266. }catch(\Exception $e){
  267. return json(['code'=>-1, 'msg'=>$e->getMessage()]);
  268. }
  269. }
  270. public function record(){
  271. return view();
  272. }
  273. public function record_data(){
  274. $ip = input('post.ip', null, 'trim');
  275. $offset = input('post.offset/d');
  276. $limit = input('post.limit/d');
  277. $select = Db::name('record');
  278. if(!empty($ip)){
  279. $select->where('ip', $ip);
  280. }
  281. $total = $select->count();
  282. $rows = $select->order('id','desc')->limit($offset, $limit)->select();
  283. return json(['total'=>$total, 'rows'=>$rows]);
  284. }
  285. public function log(){
  286. return view();
  287. }
  288. public function log_data(){
  289. $action = input('post.action', null, 'trim');
  290. $offset = input('post.offset/d');
  291. $limit = input('post.limit/d');
  292. $select = Db::name('log');
  293. if(!empty($action)){
  294. $select->where('action', $action);
  295. }
  296. $total = $select->count();
  297. $rows = $select->order('id','desc')->limit($offset, $limit)->select();
  298. return json(['total'=>$total, 'rows'=>$rows]);
  299. }
  300. public function list(){
  301. $type = input('param.type', 'black');
  302. View::assign('type', $type);
  303. View::assign('typename', $type=='white'?'白名单':'黑名单');
  304. return view();
  305. }
  306. public function list_data(){
  307. $type = input('param.type', 'black');
  308. $ip = input('post.ip', null, 'trim');
  309. $offset = input('post.offset/d');
  310. $limit = input('post.limit/d');
  311. $tablename = $type == 'black' ? 'black' : 'white';
  312. $select = Db::name($tablename);
  313. if(!empty($ip)){
  314. $select->where('ip', $ip);
  315. }
  316. $total = $select->count();
  317. $rows = $select->order('id','desc')->limit($offset, $limit)->select();
  318. return json(['total'=>$total, 'rows'=>$rows]);
  319. }
  320. public function list_op(){
  321. $type = input('param.type', 'black');
  322. $tablename = $type == 'black' ? 'black' : 'white';
  323. $act = input('post.act', null);
  324. if($act == 'get'){
  325. $id = input('post.id/d');
  326. if(!$id) return json(['code'=>-1, 'msg'=>'no id']);
  327. $data = Db::name($tablename)->where('id', $id)->find();
  328. return json(['code'=>0, 'data'=>$data]);
  329. }elseif($act == 'add'){
  330. $ip = input('post.ip', null, 'trim');
  331. if(!$ip) return json(['code'=>-1, 'msg'=>'IP不能为空']);
  332. if(Db::name($tablename)->where('ip', $ip)->find()){
  333. return json(['code'=>-1, 'msg'=>'该IP已存在']);
  334. }
  335. Db::name($tablename)->insert([
  336. 'ip' => $ip,
  337. 'enable' => 1,
  338. 'addtime' => date("Y-m-d H:i:s")
  339. ]);
  340. return json(['code'=>0, 'msg'=>'succ']);
  341. }elseif($act == 'edit'){
  342. $id = input('post.id/d');
  343. $ip = input('post.ip', null, 'trim');
  344. if(!$id || !$ip) return json(['code'=>-1, 'msg'=>'IP不能为空']);
  345. if(Db::name($tablename)->where('ip', $ip)->where('id', '<>', $id)->find()){
  346. return json(['code'=>-1, 'msg'=>'该IP已存在']);
  347. }
  348. Db::name($tablename)->where('id', $id)->update([
  349. 'ip' => $ip
  350. ]);
  351. return json(['code'=>0, 'msg'=>'succ']);
  352. }elseif($act == 'enable'){
  353. $id = input('post.id/d');
  354. $enable = input('post.enable/d');
  355. if(!$id) return json(['code'=>-1, 'msg'=>'no id']);
  356. Db::name($tablename)->where('id', $id)->update([
  357. 'enable' => $enable
  358. ]);
  359. return json(['code'=>0, 'msg'=>'succ']);
  360. }elseif($act == 'del'){
  361. $id = input('post.id/d');
  362. if(!$id) return json(['code'=>-1, 'msg'=>'no id']);
  363. Db::name($tablename)->where('id', $id)->delete();
  364. return json(['code'=>0, 'msg'=>'succ']);
  365. }
  366. return json(['code'=>-1, 'msg'=>'no act']);
  367. }
  368. public function deplist(){
  369. $deplist_linux = get_data_dir().'config/deployment_list.json';
  370. $deplist_win = get_data_dir('Windows').'config/deployment_list.json';
  371. $deplist_linux_time = file_exists($deplist_linux) ? date("Y-m-d H:i:s", filemtime($deplist_linux)) : '不存在';
  372. $deplist_win_time = file_exists($deplist_win) ? date("Y-m-d H:i:s", filemtime($deplist_win)) : '不存在';
  373. View::assign('deplist_linux_time', $deplist_linux_time);
  374. View::assign('deplist_win_time', $deplist_win_time);
  375. return view();
  376. }
  377. public function refresh_deplist(){
  378. $os = input('get.os');
  379. if(!$os) $os = 'Linux';
  380. try{
  381. Plugins::refresh_deplist($os);
  382. Db::name('log')->insert(['uid' => 0, 'action' => '刷新一键部署列表', 'data' => '刷新'.$os.'一键部署列表成功', 'addtime' => date("Y-m-d H:i:s")]);
  383. return json(['code'=>0,'msg'=>'获取最新一键部署列表成功!']);
  384. }catch(\Exception $e){
  385. return json(['code'=>-1, 'msg'=>$e->getMessage()]);
  386. }
  387. }
  388. public function cleancache(){
  389. Cache::clear();
  390. return json(['code'=>0,'msg'=>'succ']);
  391. }
  392. public function ssl(){
  393. if(request()->isAjax()){
  394. $domain_list = input('post.domain_list', null, 'trim');
  395. $common_name = input('post.common_name', null, 'trim');
  396. $validity = input('post.validity/d');
  397. if(empty($domain_list) || empty($validity)){
  398. return json(['code'=>-1, 'msg'=>'参数不能为空']);
  399. }
  400. $array = explode("\n", $domain_list);
  401. $domain_list = [];
  402. foreach($array as $domain){
  403. $domain = trim($domain);
  404. if(empty($domain)) continue;
  405. if(!checkDomain($domain)) return json(['code'=>-1, 'msg'=>'域名或IP格式不正确:'.$domain]);
  406. $domain_list[] = $domain;
  407. }
  408. if(empty($domain_list)) return json(['code'=>-1, 'msg'=>'域名列表不能为空']);
  409. if(empty($common_name)) $common_name = $domain_list[0];
  410. $result = makeSelfSignSSL($common_name, $domain_list, $validity);
  411. if(!$result){
  412. return json(['code'=>-1, 'msg'=>'生成证书失败']);
  413. }
  414. return json(['code'=>0, 'msg'=>'生成证书成功', 'cert'=>$result['cert'], 'key'=>$result['key']]);
  415. }
  416. $dir = app()->getBasePath().'script/';
  417. $ssl_path = app()->getRootPath().'public/ssl/baota_root.pfx';
  418. $ssl_path_mac = app()->getRootPath().'public/ssl/baota_root.crt';
  419. $isca = file_exists($dir.'ca.crt') && file_exists($dir.'ca.key') && file_exists($ssl_path) && file_exists($ssl_path_mac);
  420. View::assign('isca', $isca);
  421. return view();
  422. }
  423. }