Controller.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Models\EmailFilter;
  4. use App\Models\Node;
  5. use App\Models\NodeDailyDataFlow;
  6. use App\Models\NodeHourlyDataFlow;
  7. use App\Models\User;
  8. use App\Models\UserDailyDataFlow;
  9. use App\Models\UserDataFlowLog;
  10. use App\Models\UserHourlyDataFlow;
  11. use DB;
  12. use Exception;
  13. use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
  14. use Illuminate\Foundation\Bus\DispatchesJobs;
  15. use Illuminate\Foundation\Validation\ValidatesRequests;
  16. use Illuminate\Http\UploadedFile;
  17. use Illuminate\Routing\Controller as BaseController;
  18. use RuntimeException;
  19. use Str;
  20. class Controller extends BaseController {
  21. use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
  22. // 生成随机密码
  23. public function makePasswd() {
  24. return makeRandStr();
  25. }
  26. // 生成UUID
  27. public function makeUUID() {
  28. return Str::uuid();
  29. }
  30. // 生成网站安全码
  31. public function makeSecurityCode(): string {
  32. return strtolower(makeRandStr(8));
  33. }
  34. // 类似Linux中的tail命令
  35. public function tail($file, $n, $base = 5) {
  36. $fileLines = $this->countLine($file);
  37. if($fileLines < 15000){
  38. return false;
  39. }
  40. $fp = fopen($file, 'rb+');
  41. assert($n > 0);
  42. $pos = $n + 1;
  43. $lines = [];
  44. while(count($lines) <= $n){
  45. try{
  46. fseek($fp, -$pos, SEEK_END);
  47. }catch(Exception $e){
  48. break;
  49. }
  50. $pos *= $base;
  51. while(!feof($fp)){
  52. array_unshift($lines, fgets($fp));
  53. }
  54. }
  55. return array_slice($lines, 0, $n);
  56. }
  57. /**
  58. * 计算文件行数
  59. *
  60. * @param $file
  61. *
  62. * @return int
  63. */
  64. public function countLine($file): int {
  65. $fp = fopen($file, 'rb');
  66. $i = 0;
  67. while(!feof($fp)){
  68. //每次读取2M
  69. if($data = fread($fp, 1024 * 1024 * 2)){
  70. //计算读取到的行数
  71. $num = substr_count($data, "\n");
  72. $i += $num;
  73. }
  74. }
  75. fclose($fp);
  76. return $i;
  77. }
  78. // 获取邮箱后缀
  79. public function emailFilterList($type): array {
  80. return EmailFilter::query()->whereType($type)->pluck('words')->toArray();
  81. }
  82. // 将Base64图片转换为本地图片并保存
  83. public function base64ImageSaver($base64_image_content): ?string {
  84. // 匹配出图片的格式
  85. if(preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)){
  86. $type = $result[2];
  87. $directory = date('Ymd');
  88. $path = '/assets/images/qrcode/'.$directory.'/';
  89. // 检查是否有该文件夹,如果没有就创建,并给予最高权限
  90. if(!file_exists(public_path($path))
  91. && !mkdir($concurrentDirectory = public_path($path), 0755, true)
  92. && !is_dir($concurrentDirectory)){
  93. throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
  94. }
  95. $fileName = makeRandStr(18, true).".{$type}";
  96. if(file_put_contents(public_path($path.$fileName),
  97. base64_decode(str_replace($result[1], '', $base64_image_content)))){
  98. chmod(public_path($path.$fileName), 0744);
  99. return $path.$fileName;
  100. }
  101. }
  102. return '';
  103. }
  104. // 上传文件处理
  105. public function uploadFile(UploadedFile $file): string {
  106. $fileType = $file->getClientOriginalExtension();
  107. // 验证文件合法性
  108. if(!in_array($fileType, ['jpg', 'png', 'jpeg', 'bmp'])){
  109. return false;
  110. }
  111. $name = date('YmdHis').random_int(1000, 2000).'.'.$fileType;
  112. $move = $file->move(base_path().'/public/upload/image/', $name);
  113. return $move? '/upload/image/'.$name : '';
  114. }
  115. /**
  116. * 节点信息
  117. *
  118. * @param int $uid 用户ID
  119. * @param int $nodeId 节点ID
  120. * @param int $infoType 信息类型:0为链接,1为文字
  121. *
  122. * @return string
  123. */
  124. public function getUserNodeInfo($uid, $nodeId, $infoType): string {
  125. $user = User::whereId($uid)->firstOrFail();
  126. $node = Node::whereId($nodeId)->firstOrFail();
  127. $scheme = null;
  128. // 获取分组名称
  129. $group = $node->level_name;
  130. $host = $node->is_relay? $node->relay_server : ($node->server?: $node->ip);
  131. $data = null;
  132. switch($node->type){
  133. case 2:
  134. // 生成v2ray scheme
  135. if($infoType !== 1){
  136. // 生成v2ray scheme
  137. $data = $this->v2raySubUrl($node->name, $host, $node->v2_port, $user->vmess_id, $node->v2_alter_id,
  138. $node->v2_net, $node->v2_type, $node->v2_host, $node->v2_path, $node->v2_tls? "tls" : "");
  139. }else{
  140. $data = "服务器:".$host.PHP_EOL."IPv6:".($node->ipv6?: "").PHP_EOL."端口:".$node->v2_port.PHP_EOL."加密方式:".$node->v2_method.PHP_EOL."用户ID:".$user->vmess_id.PHP_EOL."额外ID:".$node->v2_alter_id.PHP_EOL."传输协议:".$node->v2_net.PHP_EOL."伪装类型:".$node->v2_type.PHP_EOL."伪装域名:".($node->v2_host?: "").PHP_EOL."路径:".($node->v2_path?: "").PHP_EOL."TLS:".($node->v2_tls? "tls" : "").PHP_EOL;
  141. }
  142. break;
  143. case 3:
  144. if($infoType !== 1){
  145. $data = $this->trojanSubUrl($user->passwd, $host, $node->port, $node->name);
  146. }else{
  147. $data = "备注:".$node->name.PHP_EOL."服务器:".$host.PHP_EOL."密码:".$user->passwd.PHP_EOL."端口:".$node->port.PHP_EOL;
  148. }
  149. break;
  150. case 1:
  151. case 4:
  152. $protocol = $node->protocol;
  153. $method = $node->method;
  154. $obfs = $node->obfs;
  155. if($node->single){
  156. //单端口使用中转的端口
  157. $port = $node->is_relay? $node->relay_port : $node->port;
  158. $passwd = $node->passwd;
  159. $protocol_param = $user->port.':'.$user->passwd;
  160. }else{
  161. $port = $user->port;
  162. $passwd = $user->passwd;
  163. $protocol_param = $node->protocol_param;
  164. if($node->type === 1){
  165. $protocol = $user->protocol;
  166. $method = $user->method;
  167. $obfs = $user->obfs;
  168. }
  169. }
  170. if($infoType !== 1){
  171. // 生成ss/ssr scheme
  172. $data = $node->compatible? $this->ssSubUrl($host, $port, $method, $passwd,
  173. $group) : $this->ssrSubUrl($host, $port, $protocol, $method, $obfs, $passwd, $node->obfs_param,
  174. $protocol_param, $node->name, $group, $node->is_udp);
  175. }else{
  176. // 生成文本配置信息
  177. $data = "服务器:".$host.PHP_EOL."IPv6:".$node->ipv6.PHP_EOL."服务器端口:".$port.PHP_EOL."密码:".$passwd.PHP_EOL."加密:".$method.PHP_EOL.($node->compatible? '' : "协议:".$protocol.PHP_EOL."协议参数:".$protocol_param.PHP_EOL."混淆:".$obfs.PHP_EOL."混淆参数:".$node->obfs_param.PHP_EOL);
  178. }
  179. break;
  180. default:
  181. }
  182. return $data;
  183. }
  184. public function v2raySubUrl($name, $host, $port, $uuid, $alter_id, $net, $type, $domain, $path, $tls): string {
  185. return 'vmess://'.base64url_encode(json_encode([
  186. "v" => "2",
  187. "ps" => $name,
  188. "add" => $host,
  189. "port" => $port,
  190. "id" => $uuid,
  191. "aid" => $alter_id,
  192. "net" => $net,
  193. "type" => $type,
  194. "host" => $domain,
  195. "path" => $path,
  196. "tls" => $tls? "tls" : ""
  197. ], JSON_PRETTY_PRINT));
  198. }
  199. public function trojanSubUrl($password, $domain, $port, $remark): string {
  200. return 'trojan://'.urlencode($password).'@'.$domain.':'.$port.'#'.urlencode($remark);
  201. }
  202. public function ssSubUrl($host, $port, $method, $passwd, $group): string {
  203. return 'ss://'.base64url_encode($method.':'.$passwd.'@'.$host.':'.$port).'#'.$group;
  204. }
  205. public function ssrSubUrl(
  206. $host, $port, $protocol, $method, $obfs, $passwd, $obfs_param, $protocol_param, $name, $group, $is_udp
  207. ): string {
  208. return 'ssr://'.base64url_encode($host.':'.$port.':'.$protocol.':'.$method.':'.$obfs.':'.base64url_encode($passwd).'/?obfsparam='.base64url_encode($obfs_param).'&protoparam='.base64url_encode($protocol_param).'&remarks='.base64url_encode($name).'&group='.base64url_encode($group).'&udpport='.$is_udp.'&uot=0');
  209. }
  210. // 流量使用图表
  211. public function dataFlowChart($id, $is_node = 0): array {
  212. if($is_node){
  213. $currentFlow = UserDataFlowLog::query()->whereNodeId($id);
  214. $hourlyFlow = NodeHourlyDataFlow::query()->whereNodeId($id);
  215. $dailyFlow = NodeDailyDataFlow::query()->whereNodeId($id);
  216. }else{
  217. $currentFlow = UserDataFlowLog::query()->whereUserId($id);
  218. $hourlyFlow = UserHourlyDataFlow::query()->userHourly($id);
  219. $dailyFlow = UserDailyDataFlow::query()->userDaily($id);
  220. }
  221. $currentFlow = $currentFlow->where('log_time', '>=', strtotime(date('Y-m-d H:00')))->sum(DB::raw('u + d'));
  222. $hourlyFlow = $hourlyFlow->whereDate('created_at', date('Y-m-d'))->pluck('total', 'created_at')->toArray();
  223. $dailyFlow = $dailyFlow->whereMonth('created_at', date('n'))->pluck('total', 'created_at')->toArray();
  224. // 节点一天内的流量
  225. $hourlyData = array_fill(0, date('G') + 1, 0);
  226. foreach($hourlyFlow as $date => $dataFlow){
  227. $hourlyData[date('G', strtotime($date))] = round($dataFlow / GB, 3);
  228. }
  229. $hourlyData[date('G') + 1] = round($currentFlow / GB, 3);
  230. // 节点一个月内的流量
  231. $dailyData = array_fill(0, date('j'), 0);
  232. foreach($dailyFlow as $date => $dataFlow){
  233. $dailyData[date('j', strtotime($date)) - 1] = round($dataFlow / GB, 3);
  234. }
  235. $dailyData[date('j', strtotime(now())) - 1] = round((array_sum($hourlyFlow) + $currentFlow) / GB, 3);
  236. return [
  237. 'trafficDaily' => json_encode($dailyData),
  238. 'trafficHourly' => json_encode($hourlyData),
  239. 'monthDays' => json_encode(range(1, date("j"), 1)),// 本月天数
  240. 'dayHours' => json_encode(range(0, date("G") + 1, 1))// 本日小时
  241. ];
  242. }
  243. }