UserController.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controllers;
  4. use App\Models\Ann;
  5. use App\Models\Docs;
  6. use App\Models\EmailVerify;
  7. use App\Models\InviteCode;
  8. use App\Models\LoginIp;
  9. use App\Models\Node;
  10. use App\Models\Payback;
  11. use App\Models\Setting;
  12. use App\Models\StreamMedia;
  13. use App\Models\User;
  14. use App\Services\Auth;
  15. use App\Services\Captcha;
  16. use App\Services\Config;
  17. use App\Services\DB;
  18. use App\Services\MFA;
  19. use App\Utils\Cookie;
  20. use App\Utils\Hash;
  21. use App\Utils\ResponseHelper;
  22. use App\Utils\Telegram;
  23. use App\Utils\Tools;
  24. use Exception;
  25. use Psr\Http\Message\ResponseInterface;
  26. use Ramsey\Uuid\Uuid;
  27. use Slim\Http\Response;
  28. use Slim\Http\ServerRequest;
  29. use voku\helper\AntiXSS;
  30. use function in_array;
  31. use function json_decode;
  32. use function strlen;
  33. use function time;
  34. /**
  35. * HomeController
  36. */
  37. final class UserController extends BaseController
  38. {
  39. /**
  40. * @throws Exception
  41. */
  42. public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  43. {
  44. $captcha = [];
  45. if (Setting::obtain('enable_checkin_captcha') === true) {
  46. $captcha = Captcha::generate();
  47. }
  48. $data = [
  49. 'today_traffic_usage' => (int) $this->user->transfer_enable === 0 ? 0 : ($this->user->u + $this->user->d - $this->user->last_day_t) / $this->user->transfer_enable * 100,
  50. 'past_traffic_usage' => (int) $this->user->transfer_enable === 0 ? 0 : $this->user->last_day_t / $this->user->transfer_enable * 100,
  51. 'residual_flow' => (int) $this->user->transfer_enable === 0 ? 0 : ($this->user->transfer_enable - ($this->user->u + $this->user->d)) / $this->user->transfer_enable * 100,
  52. ];
  53. return $response->write(
  54. $this->view()
  55. ->assign('ann', Ann::orderBy('date', 'desc')->first())
  56. ->assign('UniversalSub', SubController::getUniversalSub($this->user))
  57. ->assign('TraditionalSub', LinkController::getTraditionalSub($this->user))
  58. ->assign('data', $data)
  59. ->assign('captcha', $captcha)
  60. ->fetch('user/index.tpl')
  61. );
  62. }
  63. /**
  64. * @throws Exception
  65. */
  66. public function profile(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  67. {
  68. // 登录IP
  69. $logins = LoginIp::where('userid', '=', $this->user->id)->where('type', '=', 0)->orderBy('datetime', 'desc')->take(10)->get();
  70. foreach ($logins as $login) {
  71. $login->datetime = Tools::toDateTime((int) $login->datetime);
  72. $login->location = Tools::getIpLocation($login->ip);
  73. }
  74. return $response->write(
  75. $this->view()
  76. ->assign('logins', $logins)
  77. ->fetch('user/profile.tpl')
  78. );
  79. }
  80. /**
  81. * @throws Exception
  82. */
  83. public function announcement(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  84. {
  85. $anns = Ann::orderBy('date', 'desc')->get();
  86. return $response->write(
  87. $this->view()
  88. ->assign('anns', $anns)
  89. ->fetch('user/announcement.tpl')
  90. );
  91. }
  92. /**
  93. * @throws Exception
  94. */
  95. public function docs(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  96. {
  97. $docs = Docs::orderBy('id', 'desc')->get();
  98. return $response->write(
  99. $this->view()
  100. ->assign('docs', $docs)
  101. ->fetch('user/docs.tpl')
  102. );
  103. }
  104. /**
  105. * @throws Exception
  106. */
  107. public function media(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  108. {
  109. $results = [];
  110. $pdo = DB::getPdo();
  111. $nodes = $pdo->query('SELECT DISTINCT node_id FROM stream_media');
  112. foreach ($nodes as $node_id) {
  113. $node = Node::where('id', $node_id)->first();
  114. $unlock = StreamMedia::where('node_id', $node_id)
  115. ->orderBy('id', 'desc')
  116. ->where('created_at', '>', time() - 86460) // 只获取最近一天零一分钟内上报的数据
  117. ->first();
  118. if ($unlock !== null && $node !== null) {
  119. $details = json_decode($unlock->result, true);
  120. $details = str_replace('Originals Only', '仅限自制', $details);
  121. $details = str_replace('Oversea Only', '仅限海外', $details);
  122. $info = [];
  123. foreach ($details as $key => $value) {
  124. $info = [
  125. 'node_name' => $node->name,
  126. 'created_at' => $unlock->created_at,
  127. 'unlock_item' => $details,
  128. ];
  129. }
  130. $results[] = $info;
  131. }
  132. }
  133. if ($_ENV['streaming_media_unlock_multiplexing'] !== null) {
  134. foreach ($_ENV['streaming_media_unlock_multiplexing'] as $key => $value) {
  135. $key_node = Node::where('id', $key)->first();
  136. $value_node = StreamMedia::where('node_id', $value)
  137. ->orderBy('id', 'desc')
  138. ->where('created_at', '>', time() - 86460) // 只获取最近一天零一分钟内上报的数据
  139. ->first();
  140. if ($value_node !== null) {
  141. $details = json_decode($value_node->result, true);
  142. $details = str_replace('Originals Only', '仅限自制', $details);
  143. $details = str_replace('Oversea Only', '仅限海外', $details);
  144. $info = [
  145. 'node_name' => $key_node->name,
  146. 'created_at' => $value_node->created_at,
  147. 'unlock_item' => $details,
  148. ];
  149. $results[] = $info;
  150. }
  151. }
  152. }
  153. $node_names = array_column($results, 'node_name');
  154. array_multisort($node_names, SORT_ASC, $results);
  155. return $response->write($this->view()
  156. ->assign('results', $results)
  157. ->fetch('user/media.tpl'));
  158. }
  159. /**
  160. * @throws Exception
  161. */
  162. public function edit(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  163. {
  164. $themes = Tools::getDir(BASE_PATH . '/resources/views');
  165. $bind_token = Telegram::addBindSession($this->user);
  166. $methods = Config::getSupportParam('method');
  167. $gaurl = MFA::getGAurl($this->user);
  168. return $response->write($this->view()
  169. ->assign('user', $this->user)
  170. ->assign('themes', $themes)
  171. ->assign('bind_token', $bind_token)
  172. ->assign('methods', $methods)
  173. ->assign('gaurl', $gaurl)
  174. ->assign('telegram_bot', $_ENV['telegram_bot'])
  175. ->registerClass('Config', Config::class)
  176. ->fetch('user/edit.tpl'));
  177. }
  178. /**
  179. * @throws Exception
  180. */
  181. public function invite(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  182. {
  183. $code = InviteCode::where('user_id', $this->user->id)->first();
  184. if ($code === null) {
  185. $this->user->addInviteCode();
  186. $code = InviteCode::where('user_id', $this->user->id)->first();
  187. }
  188. $paybacks = Payback::where('ref_by', $this->user->id)
  189. ->orderBy('id', 'desc')
  190. ->get();
  191. foreach ($paybacks as $payback) {
  192. $payback->datetime = Tools::toDateTime($payback->datetime);
  193. }
  194. $paybacks_sum = Payback::where('ref_by', $this->user->id)->sum('ref_get');
  195. if (! $paybacks_sum) {
  196. $paybacks_sum = 0;
  197. }
  198. $invite_url = $_ENV['baseUrl'] . '/auth/register?code=' . $code->code;
  199. return $response->write($this->view()
  200. ->assign('code', $code)
  201. ->assign('paybacks', $paybacks)
  202. ->assign('invite_url', $invite_url)
  203. ->assign('paybacks_sum', $paybacks_sum)
  204. ->fetch('user/invite.tpl'));
  205. }
  206. public function updatePassword(ServerRequest $request, Response $response, array $args): ResponseInterface
  207. {
  208. $oldpwd = $request->getParam('oldpwd');
  209. $pwd = $request->getParam('pwd');
  210. $repwd = $request->getParam('repwd');
  211. $user = $this->user;
  212. if (! Hash::checkPassword($user->pass, $oldpwd)) {
  213. return ResponseHelper::error($response, '旧密码错误');
  214. }
  215. if ($pwd !== $repwd) {
  216. return ResponseHelper::error($response, '两次输入不符合');
  217. }
  218. if (strlen($pwd) < 8) {
  219. return ResponseHelper::error($response, '密码太短啦');
  220. }
  221. $hashPwd = Hash::passwordHash($pwd);
  222. $user->pass = $hashPwd;
  223. $user->save();
  224. if (Setting::obtain('enable_forced_replacement')) {
  225. $user->cleanLink();
  226. }
  227. return ResponseHelper::successfully($response, '修改成功');
  228. }
  229. public function updateEmail(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  230. {
  231. $antiXss = new AntiXSS();
  232. $user = $this->user;
  233. $newemail = $antiXss->xss_clean($request->getParam('newemail'));
  234. $oldemail = $user->email;
  235. $otheruser = User::where('email', $newemail)->first();
  236. if (! $_ENV['enable_change_email']) {
  237. return ResponseHelper::error($response, '此项不允许自行修改,请联系管理员操作');
  238. }
  239. if (Setting::obtain('reg_email_verify')) {
  240. $emailcode = $request->getParam('emailcode');
  241. $mailcount = EmailVerify::where('email', '=', $newemail)
  242. ->where('code', '=', $emailcode)->where('expire_in', '>', time())->first();
  243. if ($mailcount === null) {
  244. return ResponseHelper::error($response, '您的邮箱验证码不正确');
  245. }
  246. }
  247. if ($newemail === '') {
  248. return ResponseHelper::error($response, '未填写邮箱');
  249. }
  250. $check_res = Tools::isEmailLegal($newemail);
  251. if ($check_res['ret'] === 0) {
  252. return $response->withJson($check_res);
  253. }
  254. if ($otheruser !== null) {
  255. return ResponseHelper::error($response, '邮箱已经被使用了');
  256. }
  257. if ($newemail === $oldemail) {
  258. return ResponseHelper::error($response, '新邮箱不能和旧邮箱一样');
  259. }
  260. $user->email = $newemail;
  261. $user->save();
  262. return ResponseHelper::successfully($response, '修改成功');
  263. }
  264. public function updateUsername(ServerRequest $request, Response $response, array $args): ResponseInterface
  265. {
  266. $antiXss = new AntiXSS();
  267. $newusername = $antiXss->xss_clean($request->getParam('newusername'));
  268. $user = $this->user;
  269. $user->user_name = $newusername;
  270. $user->save();
  271. return ResponseHelper::successfully($response, '修改成功');
  272. }
  273. public function updateContact(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  274. {
  275. $antiXss = new AntiXSS();
  276. $type = $antiXss->xss_clean($request->getParam('imtype'));
  277. $value = $antiXss->xss_clean($request->getParam('imvalue'));
  278. $user = $this->user;
  279. if ($user->telegram_id !== null) {
  280. return $response->withJson([
  281. 'ret' => 0,
  282. 'msg' => '你的账户绑定了 Telegram ,所以此项并不能被修改',
  283. ]);
  284. }
  285. if ($value === '' || $type === '') {
  286. return $response->withJson([
  287. 'ret' => 0,
  288. 'msg' => '联络方式不能为空',
  289. ]);
  290. }
  291. $user_exist = User::where('im_value', $value)->where('im_type', $type)->first();
  292. if ($user_exist !== null) {
  293. return $response->withJson([
  294. 'ret' => 0,
  295. 'msg' => '此联络方式已经被注册',
  296. ]);
  297. }
  298. $user->im_type = $type;
  299. $user->im_value = $value;
  300. $user->save();
  301. return $response->withJson([
  302. 'ret' => 1,
  303. 'msg' => '修改成功',
  304. ]);
  305. }
  306. public function updateTheme(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  307. {
  308. $antiXss = new AntiXSS();
  309. $theme = $antiXss->xss_clean($request->getParam('theme'));
  310. $user = $this->user;
  311. if ($theme === '') {
  312. return $response->withJson([
  313. 'ret' => 0,
  314. 'msg' => '主题不能为空',
  315. ]);
  316. }
  317. $user->theme = $theme;
  318. $user->save();
  319. return $response->withJson([
  320. 'ret' => 1,
  321. 'msg' => '修改成功',
  322. ]);
  323. }
  324. public function updateMail(ServerRequest $request, Response $response, array $args): ResponseInterface
  325. {
  326. $value = (int) $request->getParam('mail');
  327. if (in_array($value, [0, 1, 2])) {
  328. $user = $this->user;
  329. if ($value === 2 && $_ENV['enable_telegram'] === false) {
  330. return ResponseHelper::error(
  331. $response,
  332. '修改失败,当前无法使用 Telegram 接收每日报告'
  333. );
  334. }
  335. $user->sendDailyMail = $value;
  336. $user->save();
  337. return ResponseHelper::successfully($response, '修改成功');
  338. }
  339. return ResponseHelper::error($response, '非法输入');
  340. }
  341. public function resetPasswd(ServerRequest $request, Response $response, array $args): ResponseInterface
  342. {
  343. $user = $this->user;
  344. $user->uuid = Uuid::uuid4();
  345. $user->passwd = Tools::genRandomChar(16);
  346. if (! $user->save()) {
  347. return ResponseHelper::error($response, '目前出现一些问题,请稍后再试');
  348. }
  349. return ResponseHelper::successfully($response, '修改成功');
  350. }
  351. public function resetApiToken(ServerRequest $request, Response $response, array $args): ResponseInterface
  352. {
  353. $user = $this->user;
  354. $user->api_token = Uuid::uuid4();
  355. if (! $user->save()) {
  356. return ResponseHelper::error($response, '目前出现一些问题,请稍后再试');
  357. }
  358. return ResponseHelper::successfully($response, '修改成功');
  359. }
  360. public function updateMethod(ServerRequest $request, Response $response, array $args): ResponseInterface
  361. {
  362. $antiXss = new AntiXSS();
  363. $user = $this->user;
  364. $method = strtolower($antiXss->xss_clean($request->getParam('method')));
  365. if ($method === '') {
  366. ResponseHelper::error($response, '非法输入');
  367. }
  368. if (! Tools::isParamValidate('method', $method)) {
  369. ResponseHelper::error($response, '加密无效');
  370. }
  371. $user->method = $method;
  372. $user->save();
  373. return ResponseHelper::successfully($response, '修改成功');
  374. }
  375. public function logout(ServerRequest $request, Response $response, array $args): Response
  376. {
  377. Auth::logout();
  378. return $response->withStatus(302)->withHeader('Location', '/');
  379. }
  380. public function doCheckIn(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  381. {
  382. if ($_ENV['enable_checkin'] === false) {
  383. return ResponseHelper::error($response, '暂时还不能签到');
  384. }
  385. if (Setting::obtain('enable_checkin_captcha') === true) {
  386. $ret = Captcha::verify($request->getParams());
  387. if (! $ret) {
  388. return ResponseHelper::error($response, '系统无法接受您的验证结果,请刷新页面后重试');
  389. }
  390. }
  391. if (strtotime($this->user->expire_in) < time()) {
  392. return ResponseHelper::error($response, '没有过期的账户才可以签到');
  393. }
  394. $checkin = $this->user->checkin();
  395. if (! $checkin['ok']) {
  396. return ResponseHelper::error($response, (string) $checkin['msg']);
  397. }
  398. return $response->withJson([
  399. 'ret' => 1,
  400. 'trafficInfo' => [
  401. 'todayUsedTraffic' => $this->user->todayUsedTraffic(),
  402. 'lastUsedTraffic' => $this->user->lastUsedTraffic(),
  403. 'unUsedTraffic' => $this->user->unusedTraffic(),
  404. ],
  405. 'traffic' => Tools::flowAutoShow($this->user->transfer_enable),
  406. 'unflowtraffic' => $this->user->transfer_enable,
  407. 'msg' => $checkin['msg'],
  408. ]);
  409. }
  410. /**
  411. * @throws Exception
  412. */
  413. public function kill(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  414. {
  415. return $response->write($this->view()->fetch('user/kill.tpl'));
  416. }
  417. public function handleKill(ServerRequest $request, Response $response, array $args): ResponseInterface
  418. {
  419. $user = $this->user;
  420. $passwd = $request->getParam('passwd');
  421. if (! Hash::checkPassword($user->pass, $passwd)) {
  422. return ResponseHelper::error($response, '密码错误');
  423. }
  424. if ($_ENV['enable_kill'] === true) {
  425. Auth::logout();
  426. $user->killUser();
  427. return ResponseHelper::successfully($response, '您的帐号已经从我们的系统中删除。欢迎下次光临');
  428. }
  429. return ResponseHelper::error($response, '管理员不允许删除,如需删除请联系管理员。');
  430. }
  431. /**
  432. * @throws Exception
  433. */
  434. public function banned(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  435. {
  436. $user = $this->user;
  437. return $response->write($this->view()
  438. ->assign('banned_reason', $user->banned_reason)
  439. ->fetch('user/banned.tpl'));
  440. }
  441. public function resetTelegram(ServerRequest $request, Response $response, array $args): ResponseInterface
  442. {
  443. $user = $this->user;
  444. $user->telegramReset();
  445. return ResponseHelper::successfully($response, '重置成功');
  446. }
  447. public function resetURL(ServerRequest $request, Response $response, array $args): ResponseInterface
  448. {
  449. $user = $this->user;
  450. $user->cleanLink();
  451. return ResponseHelper::successfully($response, '重置成功');
  452. }
  453. public function resetInviteURL(ServerRequest $request, Response $response, array $args): ResponseInterface
  454. {
  455. $user = $this->user;
  456. $user->clearInviteCodes();
  457. return ResponseHelper::successfully($response, '重置成功');
  458. }
  459. public function backtoadmin(ServerRequest $request, Response $response, array $args): Response
  460. {
  461. $userid = Cookie::get('uid');
  462. $adminid = Cookie::get('old_uid');
  463. $user = User::find($userid);
  464. $admin = User::find($adminid);
  465. if (! $admin->is_admin || ! $user) {
  466. Cookie::set([
  467. 'uid' => null,
  468. 'email' => null,
  469. 'key' => null,
  470. 'ip' => null,
  471. 'expire_in' => null,
  472. 'old_uid' => null,
  473. 'old_email' => null,
  474. 'old_key' => null,
  475. 'old_ip' => null,
  476. 'old_expire_in' => null,
  477. 'old_local' => null,
  478. ], time() - 1000);
  479. }
  480. $expire_in = Cookie::get('old_expire_in');
  481. $local = Cookie::get('old_local');
  482. Cookie::set([
  483. 'uid' => Cookie::get('old_uid'),
  484. 'email' => Cookie::get('old_email'),
  485. 'key' => Cookie::get('old_key'),
  486. 'ip' => Cookie::get('old_ip'),
  487. 'expire_in' => $expire_in,
  488. 'old_uid' => null,
  489. 'old_email' => null,
  490. 'old_key' => null,
  491. 'old_ip' => null,
  492. 'old_expire_in' => null,
  493. 'old_local' => null,
  494. ], $expire_in);
  495. return $response->withStatus(302)->withHeader('Location', $local);
  496. }
  497. public function switchThemeMode(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
  498. {
  499. $user = $this->user;
  500. if ($user->is_dark_mode === 1) {
  501. $user->is_dark_mode = 0;
  502. } else {
  503. $user->is_dark_mode = 1;
  504. }
  505. $user->save();
  506. return $response->withJson([
  507. 'ret' => 1,
  508. 'msg' => '切换成功',
  509. ]);
  510. }
  511. }