Api.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <?php
  2. namespace app\controller;
  3. use app\BaseController;
  4. use app\model\ConfigModel;
  5. use app\model\LinkModel;
  6. use app\model\SettingModel;
  7. use GuzzleHttp\Client;
  8. use GuzzleHttp\Exception\RequestException;
  9. use PHPHtmlParser\Dom;
  10. use think\facade\Cache;
  11. use think\facade\Filesystem;
  12. use think\facade\View;
  13. use think\helper\Str;
  14. class Api extends BaseController
  15. {
  16. public function site(): \think\response\Json
  17. {
  18. return $this->success("ok", [
  19. 'email' => $this->Setting('email', ''),
  20. 'qqGroup' => $this->Setting("qqGroup", ''),
  21. 'beianMps' => $this->Setting("beianMps", ''),
  22. 'copyright' => $this->Setting("copyright", ''),
  23. "recordNumber" => $this->Setting("recordNumber", ''),
  24. "auth" => $this->auth,
  25. "logo" => $this->Setting('logo', ''),
  26. "qq_login" => $this->Setting('qq_login', '0')
  27. ]);
  28. }
  29. public function background(): \think\response\File
  30. {
  31. return download('static/background.jpeg', 'background.jpeg')->mimeType(\PluginStaticSystem::mimeType('static/background.jpeg'))->force(false)->expire(60 * 60 * 24 * 3);
  32. }
  33. //获取默认壁纸
  34. function DefBg()
  35. {
  36. $config = $this->Setting('defaultTab', 'static/defaultTab.json', true);
  37. if ($config) {
  38. $fp = public_path() . $config;
  39. if (file_exists($fp)) {
  40. $file = file_get_contents($fp);
  41. $json = json_decode($file, true);
  42. if (isset($json['config']['theme']['backgroundImage'])) {
  43. $bg = $json['config']['theme']['backgroundImage'];
  44. $bgMime = $json['config']['theme']['backgroundMime'] ?? 0;
  45. return $this->success("ok", ['background' => $bg, "mime" => $bgMime]);
  46. }
  47. }
  48. }
  49. return $this->success("ok", ['background' => "static/background.jpeg", "mime" => 0]);
  50. }
  51. function globalNotify()
  52. {
  53. $info = SettingModel::Config("globalNotify", false);
  54. if ($info) {
  55. return $this->success('ok', $info);
  56. }
  57. return $this->error('empty');
  58. }
  59. //获取邮件验证码
  60. function getMailCode(): \think\response\Json
  61. {
  62. $mail = $this->request->post("mail", false);
  63. $code = rand(100000, 999999);
  64. if ($mail) {
  65. if (Cache::get('code' . $mail)) {
  66. return $this->success("请勿频繁获取验证码");
  67. }
  68. $k = SettingModel::Config('smtp_code_template', false);
  69. if ($k === false || mb_strlen(trim($k)) == 0) {
  70. $k = '
  71. <div style="border:1px #DEDEDE solid;border-top:3px #009944 solid;padding:25px;background-color:#FFF;">
  72. <div style="font-size:17px;font-weight:bold;">邮箱验证码</div>
  73. <div style="font-size:14px;line-height:36px;padding-top:15px;padding-bottom:15px;">
  74. 尊敬的用户,您好!<br>
  75. 您的验证码是:<b style="color: #1e9fff">{$code}</b>。5分钟内有效,请尽快验证。
  76. </div>
  77. <div style="line-height:15px;">
  78. 此致
  79. </div>
  80. </div>
  81. ';
  82. }
  83. $html = View::display($k, ['time' => date('Y-m-d H:i:s'), 'code' => $code]);
  84. try {
  85. $status = \Mail::send($mail, $html);
  86. if ($status) {
  87. Cache::set('code' . $mail, $code, 300);
  88. return $this->success('发送成功');
  89. }
  90. }catch (\Exception $e){
  91. return $this->error($e->getMessage());
  92. }
  93. }
  94. return $this->error('发送失败');
  95. }
  96. private function addHttpProtocolRemovePath($url): string
  97. {
  98. // 解析URL
  99. $parsedUrl = parse_url($url);
  100. // 检查是否已经有协议,如果没有则添加http://
  101. if (!isset($parsedUrl['scheme'])) {
  102. // 检查是否以 // 开头,如果是,则转换为相对协议
  103. if (isset($parsedUrl['host']) && strpos($url, '//') === 0) {
  104. $url = 'http:' . $url;
  105. } else {
  106. $url = 'http://' . $url;
  107. }
  108. } else {
  109. // 如果有协议但没有路径,保留原样
  110. $url = $parsedUrl['scheme'] . '://';
  111. // 如果有主机,则添加主机部分
  112. if (isset($parsedUrl['host'])) {
  113. $url .= $parsedUrl['host'];
  114. // 如果有端口号,则添加端口号
  115. if (isset($parsedUrl['port'])) {
  116. $url .= ':' . $parsedUrl['port'];
  117. }
  118. }
  119. }
  120. return $url;
  121. }
  122. private function addHttpProtocol($url)
  123. {
  124. // 检查是否已经有协议,如果没有则添加http://
  125. if (!parse_url($url, PHP_URL_SCHEME)) {
  126. // 检查是否以 // 开头,如果是,则转换为相对协议
  127. if (strpos($url, '//') === 0) {
  128. $url = 'https:' . $url;
  129. } else {
  130. $url = 'http://' . $url;
  131. }
  132. }
  133. return $url;
  134. }
  135. private function hasOnlyPath($url): bool
  136. {
  137. $parsedUrl = parse_url($url);
  138. // 检查是否存在路径但不存在域名和协议
  139. if (isset($parsedUrl['path']) && !isset($parsedUrl['host']) && !isset($parsedUrl['scheme'])) {
  140. return true;
  141. }
  142. return false;
  143. }
  144. function getIcon(): \think\response\Json
  145. {
  146. $avatar = $this->request->post('avatar');
  147. if ($avatar) {
  148. $remote_avatar = $this->Setting("remote_avatar", "https://avatar.mtab.cc/6.x/bottts/png?seed=", true);
  149. $str = $this->downloadFile($remote_avatar . $avatar, md5($avatar) . '.png');
  150. return $this->success(['src' => $str]);
  151. }
  152. $url = $this->request->post('url', false);
  153. $icon = "";
  154. $cdn = $this->Setting('assets_host', '');
  155. if ($url) {
  156. $realUrl = $this->addHttpProtocolRemovePath($url);
  157. $client = \Axios::http();
  158. try {
  159. $response = $client->get($realUrl, [
  160. 'headers' => [
  161. "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
  162. ]
  163. ]);
  164. $status = $response->getStatusCode();
  165. } catch (\Exception $e) {
  166. return $this->error('无法连接远程目标服务器');
  167. }
  168. if ($status == 200) {
  169. $body = $response->getBody()->getContents();
  170. $dom = new Dom();
  171. $dom->loadStr($body);
  172. $title = $dom->find('title');
  173. if (count($title) > 0) {
  174. $title = $title->innerText;
  175. }
  176. try {
  177. $list = $dom->find('[rel="icon"]');
  178. if (count($list) == 0) {
  179. $list = $dom->find('[rel="shortcut icon"]');
  180. }
  181. if (count($list) == 0) {
  182. $list = $dom->find('[rel="Shortcut Icon"]');
  183. }
  184. if (count($list) > 0) {
  185. $href = $list->href;
  186. if ($this->hasOnlyPath($href)) {
  187. if ($href[0] != '/') {
  188. $href = "/" . $href;
  189. }
  190. $href = $realUrl . $href;
  191. }
  192. $href = $this->addHttpProtocol($href);
  193. $icon = $href;
  194. $response = \Axios::http()->get($icon, [
  195. 'headers' => [
  196. 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
  197. ]
  198. ]);
  199. $contentType = $response->getHeader('content-type');
  200. $contentType = $contentType[0];
  201. if (preg_match('/(png|jpg|jpeg|x-icon|svg\+xml)$/', $contentType, $matches)) {
  202. $contentType = array(
  203. 'png' => 'png',
  204. 'jpg' => 'jpg',
  205. 'jpeg' => 'jpeg',
  206. 'x-icon' => 'ico',
  207. 'svg+xml' => 'svg',
  208. );
  209. $fileFormat = $matches[1];
  210. $icon = $this->downloadFile($icon, md5($realUrl) . '.' . $contentType[$fileFormat]);
  211. if ($icon) {
  212. $icon = $cdn . $icon;
  213. }
  214. } else {
  215. $icon = '';
  216. }
  217. }
  218. } catch (\ErrorException $e) {
  219. }
  220. }
  221. if (strlen($icon) == 0) {
  222. try {
  223. $client = \Axios::http();
  224. $response = $client->get($realUrl . '/favicon.ico');
  225. $status = $response->getStatusCode();
  226. if ($status == 200) {
  227. $icon = $realUrl . '/favicon.ico';
  228. $icon = $this->downloadFile($icon, md5($realUrl) . '.ico');
  229. if ($icon) {
  230. $icon = $cdn . $icon;
  231. }
  232. }
  233. } catch (\Exception $e) {
  234. }
  235. }
  236. if (strlen($icon) > 0) {
  237. return $this->success(['src' => $icon, 'name' => $title]);
  238. }
  239. }
  240. return $this->error('没有抓取到图标');
  241. }
  242. private function downloadFile($url, $name)
  243. {
  244. $client = \Axios::http();
  245. $path = '/images/' . date('Y/m/d/');
  246. $remotePath = public_path() . $path;
  247. $downloadPath = $remotePath . $name;
  248. if (!is_dir($remotePath)) {
  249. mkdir($remotePath, 0755, true);
  250. }
  251. try {
  252. $response = $client->request('GET', $url, [
  253. 'sink' => $downloadPath,
  254. 'headers' => [
  255. 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
  256. ]
  257. ]);
  258. return $path . $name;
  259. } catch (RequestException $e) {
  260. }
  261. return false;
  262. }
  263. function renderIco(): \think\Response
  264. {
  265. $send = $this->request->get('seed');
  266. $client = new Client();
  267. $remote_avatar = $this->Setting('remote_avatar', 'https://avatar.mtab.cc/6.x/bottts/png?seed=', true);
  268. $response = $client->get($remote_avatar . urlencode($send), [
  269. 'stream' => true,
  270. 'timeout' => 10,
  271. ]);
  272. return response($response->getBody(), 200, ['Content-Type' => 'image/png']);
  273. }
  274. function upload(): \think\response\Json
  275. {
  276. $user = $this->getUser();
  277. if (!$user) {
  278. if ($this->Setting('touristUpload') !== '1') {
  279. //如果没有开启游客上传
  280. return $this->error('管理员已关闭游客上传!请登录后使用');
  281. }
  282. }
  283. $file = $this->request->file('file');
  284. if (empty($file)) {
  285. return $this->error('not File');
  286. }
  287. if ($file->getSize() > 1024 * 1024 * 2) {
  288. return $this->error('文件最大2MB,请压缩后再试');
  289. }
  290. if (in_array(strtolower($file->getOriginalExtension()), ['png', 'jpg', 'jpeg', 'webp', 'ico', 'svg'])) {
  291. // 验证文件并保存
  292. try {
  293. // 构建保存路径
  294. $savePath = '/images/' . date('Y/m/d');
  295. $hash = Str::random(32);
  296. $fileName = $hash . '.' . $file->getOriginalExtension();
  297. $filePath = Filesystem::disk('images')->putFileAs($savePath, $file, $fileName);
  298. $cdn = $this->Setting('assets_host', '/', true);
  299. return $this->success(['url' => $cdn . $filePath]);
  300. } catch (\think\exception\ValidateException $e) {
  301. // 验证失败,给出错误提示
  302. // ...
  303. }
  304. }
  305. return $this->error('上传失败');
  306. }
  307. function AdminUpload(): \think\response\Json
  308. {
  309. $this->getAdmin();
  310. $file = $this->request->file('file');
  311. if (empty($file)) {
  312. return $this->error('not File');
  313. }
  314. if ($file->getSize() > 1024 * 1024 * 5) {
  315. return $this->error('max fileSize is 5M');
  316. }
  317. // 验证文件并保存
  318. try {
  319. // 构建保存路径
  320. $savePath = '/images/' . date('Y/m/d');
  321. $hash = Str::random(32);
  322. $fileName = $hash . '.' . $file->getOriginalExtension();
  323. $filePath = Filesystem::disk('images')->putFileAs($savePath, $file, $fileName);
  324. $cdn = $this->Setting('assets_host', '/', true);
  325. return $this->success(['url' => $cdn . $filePath]);
  326. } catch (\think\exception\ValidateException $e) {
  327. // 验证失败,给出错误提示
  328. // ...
  329. }
  330. return $this->error('上传失败');
  331. }
  332. function refresh(): \think\response\Json
  333. {
  334. $user = $this->getUser();
  335. if ($user) {
  336. $data = [];
  337. $data['link_update_time'] = LinkModel::where("user_id", $user['user_id'])->value("update_time");
  338. return $this->success("ok", $data);
  339. }
  340. return $this->error("not login");
  341. }
  342. }