ClientController.php 11 KB

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