ClientController.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <?php
  2. namespace App\Http\Controllers\Api\Client;
  3. use App\Helpers\ClientApiResponse;
  4. use App\Helpers\ResponseEnum;
  5. use App\Models\Article;
  6. use App\Models\GoodsCategory;
  7. use App\Models\Level;
  8. use App\Models\ReferralLog;
  9. use App\Models\Ticket;
  10. use App\Services\ProxyService;
  11. use App\Services\UserService;
  12. use App\Utils\Helpers;
  13. use Arr;
  14. use Artisan;
  15. use Illuminate\Database\Eloquent\Builder;
  16. use Illuminate\Http\JsonResponse;
  17. use Illuminate\Http\Request;
  18. use Illuminate\Routing\Controller;
  19. use Illuminate\Support\Facades\Cache;
  20. use function config;
  21. class ClientController extends Controller
  22. {
  23. use ClientApiResponse;
  24. public function __construct(Request $request)
  25. {
  26. if (str_contains($request->userAgent(), 'bob_vpn')) {
  27. $this->setClient('bob');
  28. }
  29. }
  30. public function getUserInfo(): false|JsonResponse
  31. {
  32. $user = auth()->user();
  33. if (! $user) {
  34. return false;
  35. }
  36. $userInfo = (new UserService)->getProfile();
  37. $userInfo['user_name'] = $user->nickname;
  38. $userInfo['email'] = $user->username;
  39. $userInfo['class_expire'] = $user->expiration_date;
  40. $userInfo['money'] = $user->credit;
  41. $userInfo['plan']['name'] = $user->orders()->activePlan()->latest()->first()->goods->name ?? '无';
  42. $ann = Article::type(2)->latest()->first();
  43. $user_expire = now()->diffInDays($user->expired_at, false) < 0;
  44. $total = $user->u + $user->d;
  45. $transfer_enable = $user->transfer_enable;
  46. $expired_days = now()->diffInDays($user->expired_at, false);
  47. $userInfo['class_expire_notice'] = '';
  48. if ($expired_days < 0) {
  49. $userInfo['class_expire_notice'] = '账号会员已过期,请先续费再使用~';
  50. } elseif ($expired_days > 0 && $expired_days <= config('client.class_expire_notice.days')) {
  51. $userInfo['class_expire_notice'] = sprintf(config('client.class_expire_notice.msg'), $expired_days);
  52. }
  53. $data['info'] = [
  54. 'user' => $userInfo,
  55. 'ssrSubToken' => $user->subscribe->code,
  56. 'user_expire' => $user_expire,
  57. 'subUrl' => $user->subUrl(),
  58. 'baseUrl' => sysConfig('subscribe_domain') ?? sysConfig('website_url'),
  59. 'ann' => $ann,
  60. 'avatar' => $user->avatar,
  61. 'usedTraffic' => formatBytes($total),
  62. 'enableTraffic' => formatBytes($transfer_enable),
  63. 'unUsedTraffic' => formatBytes($transfer_enable - $total),
  64. 'reset_time' => now()->diffInDays($user->reset_time, false),
  65. 'android_index_button' => config('client.android_index_button'),
  66. ];
  67. return $this->succeed(null, $data);
  68. }
  69. public function getOrders(Request $request): JsonResponse
  70. {
  71. $orders = $request->user()->orders()->orderByDesc('id')->limit(8)->get();
  72. $data = [];
  73. foreach ($orders as $order) {
  74. $data[] = [
  75. 'id' => $order->id,
  76. 'total_amount' => $order->amount * 100,
  77. 'plan' => ['name' => $order->goods()->value('name') ?? trans('user.recharge_credit')],
  78. 'status' => [-1 => 2, 0 => 0, 1 => 1, 2 => 3, 3 => 4][$order->status],
  79. 'created_at' => strtotime($order->created_at),
  80. ];
  81. }
  82. return $this->succeed($data);
  83. }
  84. public function getUserTransfer(): JsonResponse
  85. {
  86. $user = auth()->user();
  87. return $this->succeed(null, [
  88. 'arr' => [
  89. 'todayUsedTraffic' => formatBytes($user->d),
  90. 'lastUsedTraffic' => formatBytes($user->u),
  91. 'unUsedTraffic' => formatBytes($user->transfer_enable - $user->d - $user->u),
  92. ],
  93. ]);
  94. }
  95. public function shop(): JsonResponse
  96. {
  97. $shops = [
  98. 'keys' => [],
  99. 'data' => [],
  100. ];
  101. foreach (GoodsCategory::query()->whereStatus(1)->with('goods')->has('goods')->get() as $category) {
  102. $shops['keys'][] = $category['name'];
  103. $shops['data'][$category['name']] = $category->goods()->get(['name', 'price', 'traffic'])->append('traffic_label')->toArray();
  104. }
  105. return $this->succeed($shops);
  106. }
  107. public function getInvite(): JsonResponse
  108. {
  109. $user = auth()->user();
  110. $userService = new UserService;
  111. $referral_traffic = formatBytes(sysConfig('referral_traffic'), 'MiB');
  112. $referral_percent = sysConfig('referral_percent');
  113. // 邀请码
  114. $code = $user->invites()->whereStatus(0)->value('code');
  115. $data['invite_gift'] = trans('user.invite.promotion', [
  116. 'traffic' => $referral_traffic,
  117. 'referral_percent' => $referral_percent * 100,
  118. ]);
  119. $data['invite_code'] = $code ?? $userService->inviteURI(true);
  120. $data['invite_url'] = $userService->inviteURI();
  121. $data['invite_text'] = $data['invite_url'].'&(复制整段文字到浏览器打开即可访问),找梯子最重要的就是稳定,这个已经上线三年了,一直稳定没有被封过,赶紧下载备用吧!'.($code ? '安装后打开填写我的邀请码【'.$code.'】,你还能多得3天会员.' : '');
  122. // 累计数据
  123. $data['back_sum'] = ReferralLog::uid()->sum('commission') / 100;
  124. $data['user_num'] = $user->invitees()->count();
  125. $data['list'] = $user->invitees()->with('orders')->whereHas('orders', function (Builder $query) {
  126. $query->where('status', '>=', 2)->where('amount', '>', 0);
  127. })->selectRaw('username as user_name, UNIX_TIMESTAMP(created_at) as datetime, id')->orderByDesc('created_at')->limit(10)->get()->toArray();
  128. foreach ($data['list'] as &$item) {
  129. $item['ref_get'] = ReferralLog::uid()->where('invitee_id', $item['id'])->sum('commission') / 100;
  130. }
  131. return $this->succeed(null, $data);
  132. }
  133. public function checkIn(Request $request): JsonResponse
  134. {
  135. $user = $request->user();
  136. // 系统开启登录加积分功能才可以签到
  137. if (! sysConfig('is_checkin')) {
  138. return response()->json(['ret' => 0, 'title' => trans('common.failed'), 'msg' => trans('user.home.attendance.disable')]);
  139. }
  140. // 已签到过,验证是否有效
  141. if (Cache::has('userCheckIn_'.$user->id)) {
  142. return response()->json(['ret' => 0, 'title' => trans('common.success'), 'msg' => trans('user.home.attendance.done')]);
  143. }
  144. $traffic = random_int((int) sysConfig('min_rand_traffic'), (int) sysConfig('max_rand_traffic')) * MiB;
  145. if (! $user->incrementData($traffic)) {
  146. return response()->json(['ret' => 0, 'title' => trans('common.failed'), 'msg' => trans('user.home.attendance.failed')]);
  147. }
  148. Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, $user->transfer_enable + $traffic, trans('user.home.attendance.attribute'));
  149. // 多久后可以再签到
  150. $ttl = sysConfig('traffic_limit_time') ? sysConfig('traffic_limit_time') * Minute : Day;
  151. Cache::put('userCheckIn_'.$user->id, '1', $ttl);
  152. return $this->succeed(null, null, [200, trans('user.home.attendance.success', ['data' => formatBytes($traffic)])]);
  153. }
  154. public function proxyCheck(Request $request): JsonResponse
  155. {
  156. $md5 = $request->get('md5', '');
  157. $proxy = (new ProxyService)->getProxyCode('clash');
  158. if (strtolower(md5(json_encode($proxy))) === strtolower($md5)) {
  159. return $this->succeed(false);
  160. }
  161. return $this->succeed(true, ['md5' => strtolower(md5(json_encode($proxy)))]);
  162. }
  163. public function downloadProxies(Request $request)
  164. {
  165. $flag = strtolower($request->input('flag') ?? ($request->userAgent() ?? ''));
  166. return (new ProxyService)->getProxyText($flag === 'v2rayng' ? 'v2rayng' : 'clash', $request->input('type'));
  167. }
  168. public function getProxyList(): JsonResponse
  169. {
  170. $proxyService = new ProxyService;
  171. $servers = [];
  172. foreach ($proxyService->getNodeList(null, false) as $node) {
  173. $server = $proxyService->getProxyConfig($node);
  174. if ($server['type'] === '`shadowsocks`' || $server['type'] === 'shadowsocksr') {
  175. $server['type'] = 1;
  176. }
  177. $online_log = $node->onlineLogs->where('log_time', '>=', strtotime('-5 minutes'))->sortBy('log_time')->first(); // 在线人数
  178. $server['node_ip'] = filter_var($server['host'], FILTER_VALIDATE_IP) ? $server['host'] : gethostbyname($server['host']);
  179. $server['online'] = $online_log->online_user ?? 0;
  180. $this->getOnlineCount($server, $server['online']);
  181. $servers[] = $server;
  182. }
  183. return $this->succeed($servers);
  184. }
  185. private function getOnlineCount(&$node, int $online): void
  186. {
  187. $node['flag'] = $node['area'];
  188. if ($online < 15) {
  189. $node['text'] = '⭐ 畅 通';
  190. $node['color'] = '#28a745';
  191. } elseif ($online < 30) {
  192. $node['text'] = '💫 拥 挤';
  193. $node['color'] = '#ffc107';
  194. } else {
  195. $node['text'] = '🔥 爆 满';
  196. $node['color'] = '#dc3545';
  197. }
  198. }
  199. public function getConfig(): JsonResponse
  200. {
  201. $config = $this->clientConfig();
  202. Arr::forget($config, ['read', 'configured']);
  203. return $this->succeed(null, ['config' => $config]);
  204. }
  205. private function clientConfig(?string $key = null): array|bool|string|int
  206. {
  207. if (! config('client')) {
  208. Artisan::call('config:cache');
  209. }
  210. if (config('client.configured') !== true && config('client.read')) {
  211. $this->setClientConfig();
  212. }
  213. return $key ? config('client.'.$key) : config('client');
  214. }
  215. private function setClientConfig(): void
  216. {
  217. $ann = Article::type(2)->latest()->first();
  218. if ($ann) {
  219. config(['client.notice.title' => $ann->title, 'client.notice.content' => $ann->content]);
  220. }
  221. config([
  222. 'client.configured' => true,
  223. 'client.name' => sysConfig('website_name'),
  224. 'client.node_class_name' => Level::all()->pluck('name', 'level')->toArray(),
  225. 'client.baseUrl' => sysConfig('website_url'),
  226. 'client.subscribe_url' => sysConfig('subscribe_domain') ?: sysConfig('website_url'),
  227. 'client.checkinMin' => sysConfig('min_rand_traffic'),
  228. 'client.checkinMax' => sysConfig('max_rand_traffic'),
  229. 'client.invite_gift' => sysConfig('default_traffic') / 1024,
  230. ]);
  231. }
  232. public function checkClientVersion(Request $request): JsonResponse
  233. {
  234. $version = $request->input('version');
  235. $type = $request->input('type');
  236. $config = $this->clientConfig('vpn_update');
  237. if (! isset($version, $type)) {
  238. return $this->failed(ResponseEnum::CLIENT_PARAMETER_ERROR);
  239. }
  240. $vpn = $config[$type];
  241. if (! $config['enable'] || $vpn['version'] === $version) {
  242. return $this->succeed(null, ['enable' => false]);
  243. }
  244. return $this->succeed(null, ['enable' => true, 'download_url' => $vpn['download_url'], 'must' => $vpn['must'], 'message' => $vpn['message']], [200, $vpn['message']]);
  245. }
  246. public function ticketList(): JsonResponse
  247. {
  248. $ticket = Ticket::where('user_id', auth()->user()->id)->selectRaw('id, title, UNIX_TIMESTAMP(created_at) as datetime, status')->orderBy('created_at', 'DESC')->get();
  249. return $this->succeed($ticket);
  250. }
  251. }