├── .fastRequest └── config │ └── fastRequestCurrentProjectConfig.json ├── .idea ├── .gitignore ├── JavaSceneConfigState.xml ├── compiler.xml ├── encodings.xml ├── fastRequest │ └── fastRequestCurrentProjectLocalConfig.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── files ├── home.png ├── ip.png ├── proxy-res.png ├── proxy.png └── text.png ├── pom.xml ├── proxy-vue ├── .gitignore ├── .vscode │ └── extensions.json ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public │ └── vite.svg ├── src │ ├── App.vue │ ├── assets │ │ ├── logo.png │ │ └── vue.svg │ ├── axios.js │ ├── components │ │ └── NavBox.vue │ ├── config.js │ ├── main.js │ ├── router │ │ └── index.js │ ├── style.css │ └── views │ │ ├── DetectionIp.vue │ │ ├── Donate.vue │ │ ├── Home.vue │ │ ├── Proxy-wb.vue │ │ ├── Proxy.vue │ │ └── Text.vue ├── styles.css └── vite.config.js ├── src └── main │ ├── java │ └── com │ │ └── easy │ │ ├── ProxyNodeApplication.java │ │ ├── config │ │ ├── GlobalCorsFilter.java │ │ └── WebSocketConfig.java │ │ ├── controller │ │ ├── EmailController.java │ │ ├── IpController.java │ │ ├── ProxyController.java │ │ └── ProxyWebSocket.java │ │ ├── exception │ │ ├── BusinessException.java │ │ ├── CommonConstants.java │ │ └── GlobalExceptionHandler.java │ │ ├── pojo │ │ ├── dto │ │ │ ├── IpDTO.java │ │ │ ├── KeyDTO.java │ │ │ └── ProxyDTO.java │ │ └── vo │ │ │ ├── IpResVO.java │ │ │ └── ProxyVO.java │ │ ├── resutils │ │ ├── Constants.java │ │ ├── R.java │ │ └── REnum.java │ │ └── utils │ │ ├── email │ │ └── EmailUtils.java │ │ ├── ip │ │ └── IpUtils.java │ │ ├── okhttp3 │ │ └── OkHttpUtils.java │ │ └── proxy │ │ ├── GetProxyUrlUtil.java │ │ └── GetXuiProxyUtil.java │ └── resources │ └── application.yml └── target └── classes ├── application.yml └── com └── easy ├── ProxyNodeApplication.class ├── config ├── GlobalCorsFilter.class └── WebSocketConfig.class ├── controller ├── EmailController.class ├── IpController.class ├── ProxyController.class └── ProxyWebSocket.class ├── exception ├── BusinessException.class ├── CommonConstants.class └── GlobalExceptionHandler.class ├── pojo ├── dto │ ├── IpDTO.class │ ├── KeyDTO.class │ └── ProxyDTO.class └── vo │ ├── IpResVO.class │ └── ProxyVO.class ├── resutils ├── Constants.class ├── R$RBuilder.class ├── R.class └── REnum.class └── utils ├── email └── EmailUtils.class ├── ip └── IpUtils.class ├── okhttp3 └── OkHttpUtils.class └── proxy ├── GetProxyUrlUtil.class ├── GetXuiProxyUtil$1.class └── GetXuiProxyUtil.class /.fastRequest/config/fastRequestCurrentProjectConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiDocTemplate":"#if (${namingPolicy}=='byDoc')\n$H1 ${methodDescription}\n#else\n$H1 $!{methodName}\n\n$H3 Method description\n\n```\n$!{methodDescription}\n```\n#end\n\n> URL: $!{url}\n>\n> Origin Url: $!{originUrl}\n>\n> Type: $!{methodType}\n\n\n$H3 Request headers\n\n|Header Name| Header Value|\n|---------|------|\n#foreach( $h in ${headerList})\n|$h.type|$h.value|\n#end\n\n$H3 Parameters\n\n$H5 Path parameters\n\n| Parameter | Type | Value | Description |\n|---------|------|------|------------|\n#foreach( $node in ${pathKeyValueList})\n|$node.key|$!{node.type}|$!{node.value}|$!{node.comment}|\n#end\n\n\n$H5 URL parameters\n\n|Required| Parameter | Type | Value | Description |\n|---------|---------|------|------|------------|\n#foreach( $node in ${urlParamsKeyValueList})\n|$!{node.enabled}|$!{node.key}|$!{node.type}|$!{node.value}|$!{node.comment}|\n#end\n\n\n$H5 Body parameters\n\n$H6 JSON\n\n```\n${jsonParam}\n```\n\n$H6 JSON document\n\n```\n${jsonParamDocument}\n```\n\n\n$H5 Form URL-Encoded\n|Required| Parameter | Type | Value | Description |\n|---------|---------|------|------|------------|\n#foreach( $node in ${urlEncodedKeyValueList})\n|$!{node.enabled}|$!{node.key}|$!{node.type}|$!{node.value}|$!{node.comment}|\n#end\n\n\n$H5 Multipart\n|Required | Parameter | Type | Value | Description |\n|---------|---------|------|------|------------|\n#foreach( $node in ${multipartKeyValueList})\n|$!{node.enabled}|$!{node.key}|$!{node.type}|$!{node.value}|$!{node.comment}|\n#end\n\n\n$H3 Response\n\n$H5 Response example\n\n```\n$!{responseExample}\n```\n\n$H5 Response document\n```\n$!{returnDocument}\n```\n\n\n", 3 | "apifoxSetting":{ 4 | "domain":"https://api.apifox.com", 5 | "syncAfterSave":false 6 | }, 7 | "dataList":[], 8 | "envList":[], 9 | "headerList":[], 10 | "ignoreParseFieldList":[], 11 | "maxDescriptionLength":-1, 12 | "pmCollectionId":"", 13 | "postScript":"", 14 | "preScript":"", 15 | "projectList":[], 16 | "syncModel":{ 17 | "branch":"master", 18 | "domain":"https://github.com", 19 | "enabled":false, 20 | "gitToken":"", 21 | "namingPolicy":"byDoc", 22 | "owner":"", 23 | "repo":"", 24 | "repoUrl":"", 25 | "syncAfterRun":false, 26 | "type":"github" 27 | }, 28 | "syncPmAfterSave":false, 29 | "urlEncodedKeyValueList":[], 30 | "urlParamsKeyValueList":[], 31 | "urlSuffix":"", 32 | "workspaceId":"" 33 | } -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/JavaSceneConfigState.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/fastRequest/fastRequestCurrentProjectLocalConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 71 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # proxy_node 2 | **获取3xUI的代理节点** 3 | **批量获取IP信息** 4 | ![img.png](files/home.png) 5 | ![img.png](files/ip.png) 6 | ![img.png](files/proxy.png) 7 | ![img_1.png](files/proxy-res.png) 8 | ![img.png](files/text.png) 9 | -------------------------------------------------------------------------------- /files/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/files/home.png -------------------------------------------------------------------------------- /files/ip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/files/ip.png -------------------------------------------------------------------------------- /files/proxy-res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/files/proxy-res.png -------------------------------------------------------------------------------- /files/proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/files/proxy.png -------------------------------------------------------------------------------- /files/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/files/text.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.4.0 9 | 10 | 11 | com.easy 12 | proxy_node 13 | 0.0.1-SNAPSHOT 14 | proxy_node 15 | proxy_node 16 | 17 | 18 | 19 | 21 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-websocket 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-mail 41 | 42 | 43 | 44 | org.projectlombok 45 | lombok 46 | true 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-test 51 | test 52 | 53 | 54 | 55 | io.projectreactor 56 | reactor-core 57 | 3.7.0 58 | 59 | 60 | 61 | com.squareup.okhttp3 62 | okhttp 63 | 4.12.0 64 | 65 | 66 | 67 | com.alibaba.fastjson2 68 | fastjson2 69 | 2.0.40 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-compiler-plugin 81 | 82 | 83 | 84 | org.projectlombok 85 | lombok 86 | 87 | 88 | 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-maven-plugin 93 | 94 | 95 | 96 | org.projectlombok 97 | lombok 98 | 99 | 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-maven-plugin 105 | 3.2.1 106 | 107 | 108 | package 109 | 110 | repackage 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /proxy-vue/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /proxy-vue/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /proxy-vue/README.md: -------------------------------------------------------------------------------- 1 | # Vue3 后台管理系统 2 | 3 | ## 项目介绍 4 | 这是一个基于 Vue3 + Element Plus 的后台管理系统,具有以下功能: 5 | - 用户认证(登录/注册) 6 | - 邮箱登录 7 | - 邮箱注册(包含图形验证码和邮箱验证码) 8 | - 完整的后台管理界面 9 | - 响应式侧边菜单 10 | - 用户信息展示 11 | - 主题定制 12 | 13 | ## 技术栈 14 | - Vue3 15 | - Vue Router 16 | - Pinia 17 | - Element Plus 18 | - Axios 19 | 20 | ## 项目结构 -------------------------------------------------------------------------------- /proxy-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ProxyNode 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /proxy-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-admin", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@element-plus/icons-vue": "^2.3.1", 13 | "@fortawesome/fontawesome-free": "^6.7.2", 14 | "axios": "^1.7.9", 15 | "element-plus": "^2.9.0", 16 | "sass": "^1.82.0", 17 | "vue": "^3.5.13", 18 | "vue-router": "^4.5.0" 19 | }, 20 | "devDependencies": { 21 | "@vitejs/plugin-vue": "^5.2.1", 22 | "vite": "^6.0.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /proxy-vue/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /proxy-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /proxy-vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/proxy-vue/src/assets/logo.png -------------------------------------------------------------------------------- /proxy-vue/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /proxy-vue/src/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { config } from './config' 3 | 4 | // 创建一个Axios实例 5 | const instance = axios.create({ 6 | baseURL: config.BASE_URL, 7 | timeout: config.TIMEOUT, // 请求超时时间(可选) 5分钟 8 | headers: { 9 | 'Content-Type': 'application/json', 10 | }, 11 | }); 12 | 13 | // 请求拦截器(可选) 14 | instance.interceptors.request.use( 15 | config => { 16 | // 在发送请求之前做些什么 17 | // 例如,添加认证Token 18 | // config.headers.Authorization = `Bearer ${token}`; 19 | return config; 20 | }, 21 | error => { 22 | // 对请求错误做些什么 23 | return Promise.reject(error); 24 | } 25 | ); 26 | 27 | // 响应拦截器(可选) 28 | instance.interceptors.response.use( 29 | response => { 30 | // 对响应数据做些什么 31 | return response.data; 32 | }, 33 | error => { 34 | // 对响应错误做些什么 35 | return Promise.reject(error); 36 | } 37 | ); 38 | 39 | export default instance; 40 | -------------------------------------------------------------------------------- /proxy-vue/src/components/NavBox.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | 31 | 76 | -------------------------------------------------------------------------------- /proxy-vue/src/config.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | BASE_URL: 'http://127.0.0.1:9988', 3 | WS_URL: 'ws://127.0.0.1:9988', 4 | TIMEOUT: 300000 5 | } -------------------------------------------------------------------------------- /proxy-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import ElementPlus from 'element-plus' 5 | import 'element-plus/dist/index.css' 6 | import * as ElementPlusIconsVue from '@element-plus/icons-vue' 7 | import '@fortawesome/fontawesome-free/css/all.min.css'; 8 | import axios from './axios'; 9 | 10 | const app = createApp(App) 11 | 12 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) { 13 | app.component(key, component) 14 | } 15 | 16 | app.config.globalProperties.$axios = axios; 17 | app.provide('axios', axios); 18 | app.use(router) 19 | app.use(ElementPlus) 20 | app.mount('#app') -------------------------------------------------------------------------------- /proxy-vue/src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | 3 | const routes = [ 4 | { 5 | path: '/', 6 | component: () => import('../views/Home.vue'), 7 | hidden: true 8 | }, 9 | { 10 | path: '/proxy', 11 | component: () => import('../views/Proxy.vue'), 12 | hidden: true 13 | }, 14 | { 15 | path: '/proxy-wb', 16 | component: () => import('../views/Proxy-wb.vue'), 17 | hidden: true 18 | }, 19 | { 20 | path: '/text', 21 | component: () => import('../views/Text.vue'), 22 | hidden: true 23 | }, 24 | { 25 | path: '/donate', 26 | component: () => import('../views/Donate.vue'), 27 | hidden: true 28 | }, 29 | { 30 | path: '/detectionIp', 31 | component: () => import('../views/DetectionIp.vue'), 32 | hidden: true 33 | } 34 | ] 35 | 36 | const router = createRouter({ 37 | history: createWebHistory(), 38 | routes 39 | }) 40 | 41 | export default router -------------------------------------------------------------------------------- /proxy-vue/src/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | a { 17 | font-weight: 500; 18 | color: #646cff; 19 | text-decoration: inherit; 20 | } 21 | a:hover { 22 | color: #535bf2; 23 | } 24 | 25 | body { 26 | margin: 0; 27 | display: flex; 28 | place-items: center; 29 | min-width: 320px; 30 | min-height: 100vh; 31 | } 32 | 33 | h1 { 34 | font-size: 3.2em; 35 | line-height: 1.1; 36 | } 37 | 38 | button { 39 | border-radius: 8px; 40 | border: 1px solid transparent; 41 | padding: 0.6em 1.2em; 42 | font-size: 1em; 43 | font-weight: 500; 44 | font-family: inherit; 45 | background-color: #1a1a1a; 46 | cursor: pointer; 47 | transition: border-color 0.25s; 48 | } 49 | button:hover { 50 | border-color: #646cff; 51 | } 52 | button:focus, 53 | button:focus-visible { 54 | outline: 4px auto -webkit-focus-ring-color; 55 | } 56 | 57 | .card { 58 | padding: 2em; 59 | } 60 | 61 | #app { 62 | max-width: 1280px; 63 | margin: 0 auto; 64 | padding: 2rem; 65 | text-align: center; 66 | } 67 | 68 | @media (prefers-color-scheme: light) { 69 | :root { 70 | color: #213547; 71 | background-color: #ffffff; 72 | } 73 | a:hover { 74 | color: #747bff; 75 | } 76 | button { 77 | background-color: #f9f9f9; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /proxy-vue/src/views/DetectionIp.vue: -------------------------------------------------------------------------------- 1 | 147 | 148 | 302 | 303 | 426 | -------------------------------------------------------------------------------- /proxy-vue/src/views/Donate.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 90 | 91 | 193 | -------------------------------------------------------------------------------- /proxy-vue/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 43 | 44 | 80 | -------------------------------------------------------------------------------- /proxy-vue/src/views/Proxy-wb.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 288 | 289 | 552 | -------------------------------------------------------------------------------- /proxy-vue/src/views/Proxy.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 156 | 157 | 245 | -------------------------------------------------------------------------------- /proxy-vue/src/views/Text.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 104 | 105 | 314 | -------------------------------------------------------------------------------- /proxy-vue/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /proxy-vue/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import path from 'path' 4 | 5 | // https://vite.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | resolve: { 9 | alias: { 10 | '@': path.resolve(__dirname, 'src') 11 | } 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /src/main/java/com/easy/ProxyNodeApplication.java: -------------------------------------------------------------------------------- 1 | package com.easy; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.ConfigurableApplicationContext; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.web.socket.config.annotation.EnableWebSocket; 10 | 11 | import java.util.Arrays; 12 | 13 | /** 14 | * @author Administrator 15 | */ 16 | @EnableWebSocket 17 | @SpringBootApplication 18 | public class ProxyNodeApplication { 19 | 20 | private final static Logger LOGGER = LoggerFactory.getLogger(ProxyNodeApplication.class); 21 | public static void main(String[] args) { 22 | SpringApplication app = new SpringApplication(ProxyNodeApplication.class); 23 | ConfigurableApplicationContext run = app.run(args); 24 | Environment env = run.getEnvironment(); 25 | String severPort = env.getProperty("server.port"); 26 | String logo = """ 27 | ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ ██████╗ ███████╗ 28 | ██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝████╗ ██║██╔═══██╗██╔══██╗██╔════╝ 29 | ██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝ ██╔██╗ ██║██║ ██║██║ ██║█████╗ \s 30 | ██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝ ██║╚██╗██║██║ ██║██║ ██║██╔══╝ \s 31 | ██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║ ██║ ╚████║╚██████╔╝██████╔╝███████╗ 32 | ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝\s 33 | PROFILE: %s 34 | SERVER PORT: %s"""; 35 | LOGGER.warn("\n" + String.format(logo, Arrays.toString(env.getActiveProfiles()), severPort)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/easy/config/GlobalCorsFilter.java: -------------------------------------------------------------------------------- 1 | package com.easy.config; 2 | import org.springframework.core.Ordered; 3 | import org.springframework.http.HttpHeaders; 4 | import org.springframework.http.HttpMethod; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.server.reactive.ServerHttpRequest; 7 | import org.springframework.http.server.reactive.ServerHttpResponse; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.cors.reactive.CorsUtils; 10 | import org.springframework.web.server.ServerWebExchange; 11 | import org.springframework.web.server.WebFilter; 12 | import org.springframework.web.server.WebFilterChain; 13 | import reactor.core.publisher.Mono; 14 | 15 | 16 | /** 17 | * 跨域配置 18 | * 19 | * @author xlike 20 | */ 21 | @Component 22 | public class GlobalCorsFilter implements WebFilter, Ordered { 23 | 24 | /** 25 | * 这里为支持的请求头,如果有自定义的header字段请自己添加 26 | */ 27 | private static final String ALLOWED_HEADERS = "X-Requested-With, Content-Language, Content-Type, Authorization, " + "credential, X-XSRF-TOKEN, isToken, token, AQ-Token"; 28 | private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD"; 29 | private static final String ALLOWED_ORIGIN = "*"; 30 | private static final String ALLOWED_EXPOSE = "*"; 31 | private static final String MAX_AGE = "18000L"; 32 | 33 | @Override 34 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 35 | ServerHttpRequest request = exchange.getRequest(); 36 | if (CorsUtils.isCorsRequest(request)) { 37 | ServerHttpResponse response = exchange.getResponse(); 38 | HttpHeaders headers = response.getHeaders(); 39 | headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS); 40 | headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS); 41 | headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN); 42 | headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE); 43 | headers.add("Access-Control-Max-Age", MAX_AGE); 44 | headers.add("Access-Control-Allow-Credentials", "true"); 45 | if (request.getMethod() == HttpMethod.OPTIONS) { 46 | response.setStatusCode(HttpStatus.OK); 47 | return Mono.empty(); 48 | } 49 | } 50 | return chain.filter(exchange); 51 | } 52 | 53 | @Override 54 | public int getOrder() { 55 | return Ordered.HIGHEST_PRECEDENCE; 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/com/easy/config/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.easy.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.socket.server.standard.ServerEndpointExporter; 6 | 7 | /** 8 | * @author xlike 9 | */ 10 | @Configuration 11 | public class WebSocketConfig { 12 | @Bean 13 | public ServerEndpointExporter serverEndpointExporter() { 14 | return new ServerEndpointExporter(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/easy/controller/EmailController.java: -------------------------------------------------------------------------------- 1 | package com.easy.controller; 2 | 3 | import com.easy.pojo.dto.KeyDTO; 4 | import com.easy.resutils.R; 5 | import com.easy.utils.email.EmailUtils; 6 | import jakarta.annotation.Resource; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | /** 10 | * @author xlike 11 | */ 12 | @RestController 13 | @CrossOrigin 14 | @RequestMapping("/email") 15 | public class EmailController { 16 | 17 | @Resource 18 | private EmailUtils emailUtils; 19 | 20 | @PostMapping("/sendKey") 21 | public R sendEmailCode(@RequestBody KeyDTO keyDTO) { 22 | try { 23 | if (keyDTO.getKey() == null || keyDTO.getKey().isEmpty()) { 24 | return R.failed("API-KEY是空的呢"); 25 | } 26 | if (keyDTO.getKey().length() < 30) { 27 | return R.failed("API-KEY不太对呢,但还是非常感谢您的支持"); 28 | } 29 | emailUtils.sendHtmlEmail("2190418744@qq.com", "API KEY 捐赠", keyDTO.getKey()); 30 | return R.ok("API-KEY已成功发送,非常感谢"); 31 | } catch (Exception e) { 32 | return R.failed("服务异常,API-KEY发送失败,非常感谢您的支持"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/easy/controller/IpController.java: -------------------------------------------------------------------------------- 1 | package com.easy.controller; 2 | 3 | import com.easy.pojo.dto.IpDTO; 4 | import com.easy.pojo.vo.IpResVO; 5 | import com.easy.resutils.R; 6 | import com.easy.utils.ip.IpUtils; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import java.io.IOException; 12 | import java.util.ArrayList; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | /** 17 | * @author xlike 18 | */ 19 | @RestController 20 | @CrossOrigin 21 | public class IpController { 22 | 23 | 24 | @PostMapping("/ip") 25 | public R> getIpRes(@RequestBody IpDTO ipDTO) { 26 | if (ipDTO.getQuery() == null || ipDTO.getQuery().isEmpty()) { 27 | return R.failed("IP不能为空"); 28 | } 29 | List ipList; 30 | try { 31 | ipList = IpUtils.getIpHttpRes(ipDTO.getQuery()); 32 | } catch (IOException e) { 33 | ipList = Collections.emptyList(); 34 | } 35 | return R.ok(ipList); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/easy/controller/ProxyController.java: -------------------------------------------------------------------------------- 1 | package com.easy.controller; 2 | 3 | import com.easy.exception.BusinessException; 4 | import com.easy.pojo.dto.ProxyDTO; 5 | import com.easy.resutils.R; 6 | import com.easy.utils.proxy.GetXuiProxyUtil; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.concurrent.CompletableFuture; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * @author Administrator 16 | */ 17 | @RestController 18 | @CrossOrigin 19 | public class ProxyController { 20 | 21 | @GetMapping("/api/test") 22 | public R test() { 23 | return R.ok("测试成功"); 24 | } 25 | 26 | @PostMapping("/api") 27 | public R> proxy(@RequestBody ProxyDTO proxyDTO) { 28 | if (proxyDTO.getUrl() == null || proxyDTO.getUrl().trim().isEmpty()) { 29 | throw new BusinessException("URL不能为空"); 30 | } 31 | String[] urlList = proxyDTO.getUrl().split("\\s*,\\s*"); 32 | List> futures = Arrays.stream(urlList) 33 | .map(url -> CompletableFuture.supplyAsync(() -> GetXuiProxyUtil.loginAndGetUrl(url, proxyDTO.getUsername(), proxyDTO.getPassword())) 34 | .handle((result, exception) -> exception != null ? "" : result)) // Handle exception here 35 | .toList(); 36 | CompletableFuture allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); 37 | CompletableFuture> resultFuture = allFutures.thenApply(v -> 38 | futures.stream() 39 | .map(CompletableFuture::join) 40 | .filter(str -> !str.isEmpty()) 41 | .map(res -> res.substring(1, res.length() - 1)) 42 | .flatMap(trimmed -> Arrays.stream(trimmed.split("\\s*,\\s*"))) 43 | .collect(Collectors.toList()) 44 | ); 45 | List resUrlList = resultFuture.join(); 46 | return R.ok(resUrlList); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/easy/controller/ProxyWebSocket.java: -------------------------------------------------------------------------------- 1 | package com.easy.controller; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.easy.pojo.dto.ProxyDTO; 5 | import com.easy.utils.proxy.GetXuiProxyUtil; 6 | import io.micrometer.common.util.StringUtils; 7 | import jakarta.websocket.OnClose; 8 | import jakarta.websocket.OnMessage; 9 | import jakarta.websocket.OnOpen; 10 | import jakarta.websocket.Session; 11 | import jakarta.websocket.server.ServerEndpoint; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.io.IOException; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.concurrent.CopyOnWriteArraySet; 18 | 19 | @ServerEndpoint("/ws/proxy") 20 | @Component 21 | @Slf4j 22 | public class ProxyWebSocket { 23 | private Session session; 24 | private static final CopyOnWriteArraySet webSockets = new CopyOnWriteArraySet<>(); 25 | 26 | @OnOpen 27 | public void onOpen(Session session) { 28 | this.session = session; 29 | webSockets.add(this); 30 | log.info("有新连接加入,当前连接数:{}", webSockets.size()); 31 | } 32 | 33 | @OnClose 34 | public void onClose() { 35 | webSockets.remove(this); 36 | log.info("有连接断开,当前连接数:{}", webSockets.size()); 37 | } 38 | 39 | @OnMessage 40 | public void onMessage(String message) { 41 | // 解析接收到的消息 42 | ProxyDTO proxyDTO = JSON.parseObject(message, ProxyDTO.class); 43 | handleProxy(proxyDTO); 44 | } 45 | 46 | private void handleProxy(ProxyDTO proxyDTO) { 47 | if (StringUtils.isBlank(proxyDTO.getUrl())) { 48 | sendMessage("错误:URL不能为空"); 49 | return; 50 | } 51 | 52 | String[] urlList = proxyDTO.getUrl().split("\\s*,\\s*"); 53 | 54 | // 为每个URL创建一个异步任务 55 | for (String url : urlList) { 56 | CompletableFuture.supplyAsync(() -> { 57 | try { 58 | String result = GetXuiProxyUtil.loginAndGetUrl(url, proxyDTO.getUsername(), proxyDTO.getPassword()); 59 | if (result != null) { 60 | String trimmed = result.substring(1, result.length() - 1); 61 | String[] urls = trimmed.split("\\s*,\\s*"); 62 | // 逐个发送结果 63 | for (String resultUrl : urls) { 64 | if (StringUtils.isBlank(resultUrl)) { 65 | continue; 66 | } 67 | sendMessage(resultUrl); 68 | } 69 | } 70 | return null; 71 | } catch (Exception e) { 72 | sendMessage("处理URL失败: " + url + " - " + e.getMessage()); 73 | return null; 74 | } 75 | }); 76 | } 77 | } 78 | 79 | private void sendMessage(String message) { 80 | try { 81 | this.session.getBasicRemote().sendText(message); 82 | } catch (IOException e) { 83 | log.error("发送消息失败:", e); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/easy/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.easy.exception; 2 | 3 | public class BusinessException extends RuntimeException { 4 | private Integer errorCode; 5 | 6 | public BusinessException() { 7 | super(); 8 | } 9 | 10 | public BusinessException(Throwable cause) { 11 | super(cause.getMessage()); 12 | this.errorCode = CommonConstants.FAIL; 13 | } 14 | 15 | public BusinessException(Integer errorCode, String message) { 16 | super(message); 17 | this.errorCode = errorCode; 18 | } 19 | 20 | public BusinessException(String message) { 21 | super(message); 22 | this.errorCode = CommonConstants.FAIL; 23 | } 24 | 25 | public Integer getErrorCode() { 26 | return errorCode; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/easy/exception/CommonConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2018-2025, jants All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * Neither the name of the pig4cloud.com developer nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * Author: jants 17 | * 18 | */ 19 | 20 | package com.easy.exception; 21 | 22 | /** 23 | * @author jants 24 | * @date 2017/10/29 25 | */ 26 | public interface CommonConstants { 27 | 28 | /** 29 | * header 中租户ID 30 | */ 31 | String TENANT_ID = "TENANT-ID"; 32 | 33 | /** 34 | * header 中版本信息 35 | */ 36 | String VERSION = "VERSION"; 37 | 38 | /** 39 | * 租户ID 40 | */ 41 | Long TENANT_ID_1 = 1L; 42 | 43 | /** 44 | * 删除 45 | */ 46 | String STATUS_DEL = "1"; 47 | 48 | /** 49 | * 正常 50 | */ 51 | String STATUS_NORMAL = "0"; 52 | 53 | /** 54 | * 锁定 55 | */ 56 | String STATUS_LOCK = "9"; 57 | 58 | /** 59 | * 菜单树根节点 60 | */ 61 | Long MENU_TREE_ROOT_ID = -1L; 62 | 63 | /** 64 | * 编码 65 | */ 66 | String UTF8 = "UTF-8"; 67 | 68 | /** 69 | * 前端工程名 70 | */ 71 | String FRONT_END_PROJECT = "jants-ui"; 72 | 73 | /** 74 | * 移动端工程名 75 | */ 76 | String UNI_END_PROJECT = "jants-app"; 77 | 78 | /** 79 | * 后端工程名 80 | */ 81 | String BACK_END_PROJECT = "jants"; 82 | 83 | /** 84 | * 公共参数 85 | */ 86 | String PIG_PUBLIC_PARAM_KEY = "PIG_PUBLIC_PARAM_KEY"; 87 | 88 | /** 89 | * 成功标记 90 | */ 91 | Integer SUCCESS = 0; 92 | 93 | /** 94 | * 失败标记 95 | */ 96 | Integer FAIL = 1; 97 | 98 | /** 99 | * 默认存储bucket 100 | */ 101 | String BUCKET_NAME = "jants"; 102 | 103 | /** 104 | * 滑块验证码 105 | */ 106 | String IMAGE_CODE_TYPE = "blockPuzzle"; 107 | 108 | /** 109 | * 验证码开关 110 | */ 111 | String CAPTCHA_FLAG = "captcha_flag"; 112 | 113 | /** 114 | * 密码传输是否加密 115 | */ 116 | String ENC_FLAG = "enc_flag"; 117 | 118 | /** 119 | * 客户端允许同时在线数量 120 | */ 121 | String ONLINE_QUANTITY = "online_quantity"; 122 | 123 | /** 124 | * 请求开始时间 125 | */ 126 | String REQUEST_START_TIME = "REQUEST-START-TIME"; 127 | 128 | /** 129 | * 当前页 130 | */ 131 | String CURRENT = "current"; 132 | 133 | /** 134 | * size 135 | */ 136 | String SIZE = "size"; 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/easy/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.easy.exception; 2 | 3 | import com.easy.resutils.R; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.http.converter.HttpMessageNotReadableException; 7 | import org.springframework.validation.BindingResult; 8 | import org.springframework.validation.FieldError; 9 | import org.springframework.web.bind.MethodArgumentNotValidException; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.RestControllerAdvice; 12 | 13 | @RestControllerAdvice 14 | @Slf4j 15 | public class GlobalExceptionHandler { 16 | 17 | /** 18 | * 业务异常处理 19 | * @param e 20 | * @return 21 | */ 22 | @ExceptionHandler(value =BusinessException.class) 23 | public R handleException(BusinessException e) { 24 | e.printStackTrace(); 25 | R resultValue = new R(); 26 | resultValue.setCode(e.getErrorCode()); 27 | resultValue.setMsg(e.getMessage()); 28 | return resultValue; 29 | } 30 | 31 | /** 32 | * 400 33 | * @param req 34 | * @param e 35 | * @return 36 | * @throws Exception 37 | */ 38 | @ExceptionHandler(value = HttpMessageNotReadableException.class) 39 | public Object handleException400(HttpServletRequest req, HttpMessageNotReadableException e) throws Exception { 40 | e.printStackTrace(); 41 | return R.failed("请求的参数中有数据类型或数据格式错误"); 42 | } 43 | /** 44 | * 处理其他异常 45 | * @param req 46 | * @param e 47 | * @return 48 | */ 49 | @ExceptionHandler(value =Exception.class) 50 | public R exceptionHandler(HttpServletRequest req, Exception e){ 51 | e.printStackTrace(); 52 | return R.failed(); 53 | } 54 | 55 | @ExceptionHandler 56 | public R getMethodArgumentNotValidExceptionException(MethodArgumentNotValidException exception) { 57 | log.error("Global MethodArgumentNotValidException:",exception); 58 | MethodArgumentNotValidException exs = exception; 59 | BindingResult bindingResult = exs.getBindingResult(); 60 | StringBuffer sb = new StringBuffer(); 61 | for (FieldError fieldError : bindingResult.getFieldErrors()) { 62 | sb.append(fieldError.getDefaultMessage() + ","); 63 | } 64 | return R.failed(sb.substring(0, sb.length()-1)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/easy/pojo/dto/IpDTO.java: -------------------------------------------------------------------------------- 1 | package com.easy.pojo.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author Administrator 7 | */ 8 | @Data 9 | public class IpDTO { 10 | 11 | private String query; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/easy/pojo/dto/KeyDTO.java: -------------------------------------------------------------------------------- 1 | package com.easy.pojo.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author xlike 7 | */ 8 | @Data 9 | public class KeyDTO { 10 | 11 | private String key; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/easy/pojo/dto/ProxyDTO.java: -------------------------------------------------------------------------------- 1 | package com.easy.pojo.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author Administrator 7 | */ 8 | @Data 9 | public class ProxyDTO { 10 | 11 | private String url; 12 | 13 | private String username; 14 | 15 | private String password; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/easy/pojo/vo/IpResVO.java: -------------------------------------------------------------------------------- 1 | package com.easy.pojo.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author xlike 9 | */ 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class IpResVO { 15 | 16 | private String ip; 17 | 18 | // ipv4 or ipv6 19 | private String type; 20 | 21 | // 所属企业 22 | private String companyName; 23 | 24 | // 国家 区域 地址 25 | private String countryAddress; 26 | 27 | // 是否已经滥用 28 | private Boolean isAbuser; 29 | 30 | // 是否托管 31 | private Boolean isCloudProvider; 32 | 33 | // 是否 是 已知代理 34 | private Boolean isProxy; 35 | 36 | // 是否 中继 37 | private Boolean isRelay; 38 | 39 | // 是否 vpn 40 | private Boolean isVpn; 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/easy/pojo/vo/ProxyVO.java: -------------------------------------------------------------------------------- 1 | package com.easy.pojo.vo; 2 | 3 | public class ProxyVO { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/easy/resutils/Constants.java: -------------------------------------------------------------------------------- 1 | package com.easy.resutils; 2 | 3 | /** 4 | * @author Administrator 5 | */ 6 | public class Constants { 7 | public static final int SUCCESS = 0; 8 | public static final int FAIL = 1; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/easy/resutils/R.java: -------------------------------------------------------------------------------- 1 | package com.easy.resutils; 2 | import com.easy.exception.CommonConstants; 3 | import lombok.*; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 响应信息主体 10 | * 11 | * @param 12 | * @author xlike 13 | */ 14 | @Builder 15 | @ToString 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @Accessors(chain = true) 19 | public class R implements Serializable { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | @Getter 24 | @Setter 25 | private int code; 26 | 27 | @Getter 28 | @Setter 29 | private String msg; 30 | 31 | @Getter 32 | @Setter 33 | private T data; 34 | 35 | public static R ok() { 36 | return restResult(null, CommonConstants.SUCCESS, null); 37 | } 38 | 39 | public static R ok(T data) { 40 | return restResult(data, CommonConstants.SUCCESS, null); 41 | } 42 | 43 | public static R ok(T data, String msg) { 44 | return restResult(data, CommonConstants.SUCCESS, msg); 45 | } 46 | 47 | public static R failed() { 48 | return restResult(null, CommonConstants.FAIL, null); 49 | } 50 | 51 | public static R failed(String msg) { 52 | return restResult(null, CommonConstants.FAIL, msg); 53 | } 54 | 55 | public static R failed(T data) { 56 | return restResult(data, CommonConstants.FAIL, null); 57 | } 58 | 59 | public static R failed(T data, String msg) { 60 | return restResult(data, CommonConstants.FAIL, msg); 61 | } 62 | 63 | static R restResult(T data, int code, String msg) { 64 | R apiResult = new R<>(); 65 | apiResult.setCode(code); 66 | apiResult.setData(data); 67 | apiResult.setMsg(msg); 68 | return apiResult; 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/easy/resutils/REnum.java: -------------------------------------------------------------------------------- 1 | package com.easy.resutils; 2 | 3 | /** 4 | * @author Administrator 5 | */ 6 | 7 | public enum REnum { 8 | SUCCESS(0, "操作成功"), 9 | RUNTIME_EXCEPTION(1, "系统异常"); 10 | 11 | private final int code; 12 | private final String introduction; 13 | 14 | REnum(int code, String introduction) { 15 | this.code = code; 16 | this.introduction = introduction; 17 | } 18 | 19 | public int getCode() { 20 | return code; 21 | } 22 | 23 | public String getIntroduction() { 24 | return introduction; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/easy/utils/email/EmailUtils.java: -------------------------------------------------------------------------------- 1 | package com.easy.utils.email; 2 | 3 | import jakarta.mail.MessagingException; 4 | import jakarta.mail.internet.MimeMessage; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.mail.SimpleMailMessage; 7 | import org.springframework.mail.javamail.JavaMailSender; 8 | import org.springframework.mail.javamail.MimeMessageHelper; 9 | import org.springframework.scheduling.annotation.Async; 10 | import org.springframework.stereotype.Component; 11 | import java.util.Date; 12 | 13 | /** 14 | * @author xlike 15 | */ 16 | @Component 17 | public class EmailUtils { 18 | //定义邮件发送器 19 | @Autowired 20 | private JavaMailSender mailSender; 21 | 22 | 23 | @Async 24 | public void send(String to, String title, String content) { 25 | //创建一个简单文本邮件的对象 26 | SimpleMailMessage message = new SimpleMailMessage(); 27 | //目标邮箱 28 | message.setTo(to); 29 | message.setSubject(title); 30 | message.setText(content); 31 | // 需要与配置文件发件人的值相同 32 | message.setFrom("linux@xlike.email"); 33 | message.setSentDate(new Date()); 34 | //将邮件对象赋予邮件发送器 35 | mailSender.send(message); 36 | } 37 | 38 | 39 | @Async 40 | public void sendHtmlEmail(String to, String title, String content) { 41 | // 创建一个MIME类型的邮件对象 42 | MimeMessage message = mailSender.createMimeMessage(); 43 | MimeMessageHelper helper = null; 44 | try { 45 | helper = new MimeMessageHelper(message, true); 46 | // 设置目标邮箱 47 | helper.setTo(to); 48 | // 设置邮件标题 49 | helper.setSubject(title); 50 | // 设置发件人 51 | helper.setFrom("linux@xlike.email"); 52 | // 设置发送日期 53 | helper.setSentDate(new Date()); 54 | } catch (MessagingException e) { 55 | return; 56 | } 57 | // 构建HTML内容 58 | String htmlContent = "" + 59 | "" + 60 | "
" + 61 | "
" + 62 | "

API-KEY捐赠

" + 63 | "
" + 64 | "
" + 65 | "
" + 66 | "

" + content + "

" + 67 | "
" + 68 | "
" + 69 | "
" + 70 | "

此邮件为系统自动发送,请勿回复。

" + 71 | "

? 2024

" + 72 | "
" + 73 | "
" + 74 | "" + 75 | ""; 76 | try { 77 | helper.setText(htmlContent, true); 78 | } catch (MessagingException e) { 79 | return; 80 | } 81 | mailSender.send(message); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/easy/utils/ip/IpUtils.java: -------------------------------------------------------------------------------- 1 | package com.easy.utils.ip; 2 | 3 | import com.alibaba.fastjson2.JSONArray; 4 | import com.alibaba.fastjson2.JSONObject; 5 | import com.easy.pojo.vo.IpResVO; 6 | import com.easy.utils.okhttp3.OkHttpUtils; 7 | import okhttp3.Response; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.LinkedHashSet; 12 | import java.util.List; 13 | import java.util.Set; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * @author xlike 19 | */ 20 | public class IpUtils { 21 | 22 | private static final String IP_REGISTRY_URL = "https://api.ipregistry.co/?key=ira_YgGPCzMLCfgWGZyEas0oqjUC6N6jWs329qSu"; 23 | 24 | 25 | public static List getIpHttpRes(String ip) throws IOException { 26 | // ip格式化 如果有,号,则根据,号或如果有空格 则空格分割 27 | List ipList = ipFormat(ip); 28 | // List转化为JSON字符串 29 | String json = JSONObject.toJSONString(ipList); 30 | Response response = OkHttpUtils.post(IP_REGISTRY_URL, OkHttpUtils.createJsonRequestBody(json)); 31 | String bodyAsString = OkHttpUtils.getResponseBodyAsString(response); 32 | if(bodyAsString == null){ 33 | return null; 34 | } 35 | JSONObject resObj = JSONObject.parseObject(bodyAsString); 36 | JSONArray resArray = resObj.getJSONArray("results"); 37 | List resVOList = new ArrayList<>(); 38 | resArray.forEach(item -> { 39 | IpResVO ipResVO = new IpResVO(); 40 | JSONObject resObjItem = (JSONObject) item; 41 | ipResVO.setIp(resObjItem.getString("ip")); 42 | if(resObjItem.containsKey("code")){ 43 | // 查询出错的 44 | ipResVO.setCountryAddress("INVALID_IP_ADDRESS"); 45 | }else { 46 | ipResVO.setType(resObjItem.getString("type")); 47 | JSONObject companyJson = resObjItem.getJSONObject("company"); 48 | String name = companyJson.getString("name"); 49 | // 获取公司名称 50 | ipResVO.setCompanyName(name); 51 | // 地址 52 | JSONObject locationJson = resObjItem.getJSONObject("location"); 53 | String city = locationJson.getString("city"); 54 | JSONObject countryJson = locationJson.getJSONObject("country"); 55 | String countryName = countryJson.getString("name"); 56 | ipResVO.setCountryAddress(countryName + " " + city); 57 | JSONObject securityJson = resObjItem.getJSONObject("security"); 58 | Boolean isProxy = securityJson.getBoolean("is_proxy"); 59 | ipResVO.setIsProxy(isProxy); 60 | Boolean isCloudProvider = securityJson.getBoolean("is_cloud_provider"); 61 | ipResVO.setIsCloudProvider(isCloudProvider); 62 | Boolean isAbuser = securityJson.getBoolean("is_abuser"); 63 | ipResVO.setIsAbuser(isAbuser); 64 | Boolean isRelay = securityJson.getBoolean("is_relay"); 65 | ipResVO.setIsRelay(isRelay); 66 | Boolean isVpn = securityJson.getBoolean("is_vpn"); 67 | ipResVO.setIsVpn(isVpn); 68 | } 69 | resVOList.add(ipResVO); 70 | }); 71 | OkHttpUtils.closeResponse(response); 72 | return resVOList; 73 | } 74 | 75 | 76 | /** 77 | * 提取输入字符串中的所有IPv4地址,并将它们格式化为JSON数组的字符串形式。 78 | * 79 | * @param input 输入的IP字符串,可能包含逗号、空格或URL等。 80 | * @return 格式化后的JSON数组字符串,例如 ["154.21.116.52"] 81 | */ 82 | public static List ipFormat(String input) { 83 | if (input == null || input.isEmpty()) { 84 | return new ArrayList<>(); 85 | } 86 | // 定义IPv4地址的正则表达式,确保IP地址前后不被数字干扰 87 | String ipv4Pattern = "(? ipSet = new LinkedHashSet<>(); 93 | // 查找所有匹配的IP地址 94 | while (matcher.find()) { 95 | String ip = matcher.group(); 96 | ipSet.add(ip); 97 | } 98 | return new ArrayList<>(ipSet); 99 | } 100 | 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/easy/utils/okhttp3/OkHttpUtils.java: -------------------------------------------------------------------------------- 1 | package com.easy.utils.okhttp3; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import okhttp3.*; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import java.io.IOException; 8 | import java.net.InetSocketAddress; 9 | import java.net.Proxy; 10 | import java.time.LocalDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * @author xlike 20 | */ 21 | public class OkHttpUtils { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(OkHttpUtils.class); 24 | 25 | // 设置超时时间 30秒 (默认的是10秒) 26 | private static final int DEFAULT_READ_TIMEOUT = 30; 27 | private static final int DEFAULT_CONNECT_TIMEOUT = 30; 28 | 29 | // 获取OkHttpClient实例(可自定义超时和代理) 30 | private static OkHttpClient getClient(Integer connectTimeout, Integer readTimeout, Proxy proxy) { 31 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 32 | 33 | builder.connectTimeout(Objects.requireNonNullElse(connectTimeout, DEFAULT_CONNECT_TIMEOUT), TimeUnit.SECONDS); 34 | 35 | builder.readTimeout(Objects.requireNonNullElse(readTimeout, DEFAULT_READ_TIMEOUT), TimeUnit.SECONDS); 36 | 37 | if (proxy != null) { 38 | builder.proxy(proxy); 39 | logger.info("使用代理服务器 {}:{}", proxy.address().toString(), ((InetSocketAddress) proxy.address()).getPort()); 40 | } 41 | 42 | return builder.build(); 43 | } 44 | 45 | // 通用请求方法 46 | private static Response sendRequest(String url, String method, Map headers, RequestBody body, 47 | Integer connectTimeout, Integer readTimeout, Proxy proxy) throws IOException { 48 | logger.info("准备发送 {} 请求到 URL: {}", method, url); 49 | 50 | Request.Builder requestBuilder = new Request.Builder().url(url).method(method, body); 51 | 52 | if (headers != null) { 53 | for (Map.Entry header : headers.entrySet()) { 54 | requestBuilder.addHeader(header.getKey(), header.getValue()); 55 | logger.debug("添加请求头:{} = {}", header.getKey(), header.getValue()); 56 | } 57 | } 58 | 59 | Request request = requestBuilder.build(); 60 | OkHttpClient client = getClient(connectTimeout, readTimeout, proxy); 61 | long startTimeMillis = System.currentTimeMillis(); 62 | LocalDateTime startTime = LocalDateTime.now(); 63 | DateTimeFormatter beginFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 64 | try { 65 | logger.info("请求开始,当前时间: {}", startTime.format(beginFormatter)); 66 | Response response = client.newCall(request).execute(); 67 | long endTimeMillis = System.currentTimeMillis(); 68 | LocalDateTime endTime = LocalDateTime.now(); 69 | DateTimeFormatter endFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 70 | logger.info("请求结束,当前时间: {} ,耗时 {} 毫秒,返回状态码: {}", endTime.format(endFormatter), (endTimeMillis - startTimeMillis), response.code()); 71 | 72 | return response; 73 | } catch (IOException e) { 74 | long errorTimeMillis = System.currentTimeMillis(); 75 | logger.error("请求失败,耗时 {} 毫秒,错误信息: {}", (errorTimeMillis - startTimeMillis), e.getMessage()); 76 | throw e; 77 | } 78 | } 79 | 80 | // GET请求(可自定义超时和代理) 81 | public static Response get(String url, Map headers, Integer connectTimeout, 82 | Integer readTimeout, Proxy proxy) throws IOException { 83 | return sendRequest(url, "GET", headers, null, connectTimeout, readTimeout, proxy); 84 | } 85 | 86 | // GET请求(使用默认超时和无代理) 87 | public static Response get(String url, Map headers) throws IOException { 88 | return get(url, headers, null, null, null); 89 | } 90 | 91 | // GET请求(无自定义请求头,使用默认超时和无代理) 92 | public static Response get(String url) throws IOException { 93 | Map headers = new HashMap<>(); 94 | headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"); 95 | headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"); 96 | headers.put("Accept-Language", "en-US,en;q=0.9"); 97 | headers.put("Accept-Encoding", "gzip, deflate, br"); 98 | headers.put("Connection", "keep-alive"); 99 | headers.put("Upgrade-Insecure-Requests", "1"); 100 | headers.put("Sec-Fetch-Site", "none"); 101 | headers.put("Sec-Fetch-Mode", "navigate"); 102 | headers.put("Sec-Fetch-User", "?1"); 103 | headers.put("Sec-Fetch-Dest", "document"); 104 | return get(url, headers); 105 | } 106 | 107 | 108 | // GET请求,使用代理 109 | public static Response get(String url, Map headers, Proxy proxy) throws IOException { 110 | return get(url, headers, null, null, proxy); 111 | } 112 | 113 | 114 | // POST请求(可自定义超时和代理) 115 | public static Response post(String url, Map headers, RequestBody body, Integer connectTimeout, 116 | Integer readTimeout, Proxy proxy) throws IOException { 117 | return sendRequest(url, "POST", headers, body, connectTimeout, readTimeout, proxy); 118 | } 119 | 120 | 121 | public static Response post(String url, Map bodyParams) throws IOException { 122 | // 将 Map 转换为 JSON 字符串 123 | String jsonString = JSON.toJSONString(bodyParams); 124 | // 创建 JSON 请求体 125 | RequestBody requestBody = RequestBody.create(jsonString, MediaType.get("application/json; charset=utf-8")); 126 | return post(url,requestBody); 127 | } 128 | 129 | // POST请求(使用默认超时和无代理) 130 | public static Response post(String url, Map headers, RequestBody body) throws IOException { 131 | return post(url, headers, body, null, null, null); 132 | } 133 | 134 | // POST请求(无自定义请求头,使用默认超时和无代理) 135 | public static Response post(String url, RequestBody body) throws IOException { 136 | return post(url, null, body); 137 | } 138 | 139 | // PUT请求(可自定义超时和代理) 140 | public static Response put(String url, Map headers, RequestBody body, Integer connectTimeout, 141 | Integer readTimeout, Proxy proxy) throws IOException { 142 | return sendRequest(url, "PUT", headers, body, connectTimeout, readTimeout, proxy); 143 | } 144 | 145 | // DELETE请求(可自定义超时和代理) 146 | public static Response delete(String url, Map headers, Integer connectTimeout, 147 | Integer readTimeout, Proxy proxy) throws IOException { 148 | return sendRequest(url, "DELETE", headers, null, connectTimeout, readTimeout, proxy); 149 | } 150 | 151 | // 使用代理 152 | public static Proxy createProxy(String hostname, int port) { 153 | logger.info("创建代理,主机名: {},端口: {}", hostname, port); 154 | return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(hostname, port)); 155 | } 156 | 157 | // 创建JSON请求体 158 | public static RequestBody createJsonRequestBody(String json) { 159 | logger.info("创建JSON请求体: {}", json); 160 | return RequestBody.create(json, MediaType.get("application/json; charset=utf-8")); 161 | } 162 | 163 | // 创建表单请求体 164 | public static RequestBody createFormRequestBody(Map formData) { 165 | FormBody.Builder formBuilder = new FormBody.Builder(); 166 | for (Map.Entry entry : formData.entrySet()) { 167 | formBuilder.add(entry.getKey(), entry.getValue()); 168 | logger.debug("添加表单字段:{} = {}", entry.getKey(), entry.getValue()); 169 | } 170 | return formBuilder.build(); 171 | } 172 | 173 | public static String getCookie(Response response, String cookieName) { 174 | List cookies = response.headers("Set-Cookie"); 175 | for (String cookie : cookies) { 176 | String[] cookieParts = cookie.split(";"); 177 | for (String part : cookieParts) { 178 | if (part.trim().startsWith(cookieName + "=")) { 179 | return part.trim().substring((cookieName + "=").length()); 180 | } 181 | } 182 | } 183 | return null; 184 | } 185 | 186 | // 获取响应体字符串 187 | public static String getResponseBodyAsString(Response response) throws IOException { 188 | if (response.body() != null) { 189 | String responseBody = response.body().string(); 190 | logger.debug("响应体内容: {}", responseBody); 191 | return responseBody; 192 | } 193 | return null; 194 | } 195 | 196 | // 关闭响应 197 | public static void closeResponse(Response response) { 198 | if (response != null && response.body() != null) { 199 | logger.info("关闭响应,URL: {}", response.request().url()); 200 | response.close(); 201 | } 202 | } 203 | } -------------------------------------------------------------------------------- /src/main/java/com/easy/utils/proxy/GetProxyUrlUtil.java: -------------------------------------------------------------------------------- 1 | package com.easy.utils.proxy; 2 | 3 | import com.alibaba.fastjson2.JSONArray; 4 | import com.alibaba.fastjson2.JSONObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Base64; 8 | import java.util.List; 9 | 10 | /** 11 | * @author Administrator 12 | */ 13 | public class GetProxyUrlUtil { 14 | 15 | public static List getLink(String resJson, String path) { 16 | JSONObject object = JSONObject.parseObject(resJson); 17 | // 所有的列表 - clientStats 里面包含了所有的用户 18 | JSONArray urlArray = new JSONArray(); 19 | if (object != null) { 20 | JSONArray jsonArray = object.getJSONArray("obj"); 21 | for (Object obj : jsonArray) { 22 | JSONObject prObj = (JSONObject) obj; 23 | String port = prObj.getString("port"); 24 | // 节点是否启用 25 | String enable = prObj.getString("enable"); 26 | if (!"true".equals(enable)) { 27 | continue; 28 | } 29 | // 节点类型 vless / vmess / shadowsocks 等等 30 | String protocol = prObj.getString("protocol"); 31 | // 获取所有没有禁用的用户 32 | JSONObject settings = prObj.getJSONObject("settings"); 33 | JSONArray clients = settings.getJSONArray("clients"); 34 | // 有可能为空 35 | if (clients == null || clients.isEmpty()) { 36 | continue; 37 | } 38 | // 所有的用户 39 | JSONArray allUser = getAllUser(clients); 40 | JSONObject streamSettings = prObj.getJSONObject("streamSettings"); 41 | // tcp 之类的 42 | String network = streamSettings.getString("network"); 43 | // 加密方式 44 | String security = streamSettings.getString("security"); 45 | // 伪装类型 46 | JSONObject tcpSettings = streamSettings.getJSONObject("tcpSettings"); 47 | JSONObject header = null; 48 | JSONArray host = null; 49 | String type = null; 50 | if (tcpSettings != null) { 51 | header = tcpSettings.getJSONObject("header"); 52 | if (header != null) { 53 | type = header.getString("type"); 54 | // 伪装类型 55 | if (type != null) { 56 | JSONObject request = header.getJSONObject("request"); 57 | JSONObject headers = null; 58 | if (request != null) { 59 | headers = request.getJSONObject("headers"); 60 | // 伪装域名 61 | host = headers.getJSONArray("Host"); 62 | } 63 | } 64 | } 65 | } 66 | String hostStr = null; 67 | if (host != null && !host.isEmpty()) { 68 | hostStr = host.getString(0); 69 | } 70 | for (Object objUser : allUser) { 71 | JSONObject user = (JSONObject) objUser; 72 | String remarks = user.getString("remarks"); 73 | String userId = user.getString("id"); 74 | urlArray.add(buildObj(remarks, path, port, userId, network, protocol, type, hostStr)); 75 | } 76 | } 77 | } 78 | return buildUrl(urlArray); 79 | } 80 | 81 | 82 | /** 83 | * 根据 protocol 这个字段 来区分不同的节点类型 84 | */ 85 | private static List buildUrl(JSONArray jsonArray) { 86 | if (jsonArray == null || jsonArray.isEmpty()) { 87 | return new ArrayList<>(); 88 | } 89 | List arrayList = new ArrayList<>(); 90 | for (Object obj : jsonArray) { 91 | JSONObject jsonObject = (JSONObject) obj; 92 | String protocol = jsonObject.getString("protocol"); 93 | String ps = jsonObject.getString("ps"); 94 | String address = jsonObject.getString("add"); 95 | int port = jsonObject.getIntValue("port"); 96 | String uuid = jsonObject.getString("id"); 97 | String net = jsonObject.getString("net"); 98 | String type = jsonObject.getString("type"); 99 | String host = jsonObject.getString("host"); 100 | String tls = jsonObject.getString("tls"); 101 | // 根据协议类型分支处理 102 | if ("vless".equalsIgnoreCase(protocol)) { 103 | // VLESS处理,与之前的示例相同,指定encryption=none, security=none/tls, headerType=http等 104 | StringBuilder params = new StringBuilder("encryption=none"); 105 | if (tls != null && !tls.isEmpty() && !tls.equalsIgnoreCase("none")) { 106 | params.append("&security=tls"); 107 | } else { 108 | params.append("&security=none"); 109 | } 110 | if ("http".equalsIgnoreCase(type)) { 111 | params.append("&headerType=http"); 112 | } 113 | if (host != null && !host.isEmpty()) { 114 | params.append("&host=").append(host); 115 | } 116 | String vlessLink = String.format("vless://%s@%s:%d?%s#%s", uuid, address, port, params.toString(), ps); 117 | arrayList.add(vlessLink); 118 | } else if ("vmess".equalsIgnoreCase(protocol)) { 119 | // VMess处理 120 | JSONObject vmessJson = new JSONObject(); 121 | vmessJson.put("v", "2"); 122 | vmessJson.put("ps", ps); 123 | vmessJson.put("add", address); 124 | vmessJson.put("port", port); 125 | vmessJson.put("id", uuid); 126 | vmessJson.put("aid", 0); 127 | vmessJson.put("net", net != null ? net : "tcp"); 128 | vmessJson.put("type", type != null ? type : "none"); 129 | vmessJson.put("host", host != null ? host : ""); 130 | vmessJson.put("path", ""); 131 | vmessJson.put("tls", tls != null ? tls : "none"); 132 | String vmessStr = vmessJson.toJSONString(); 133 | String base64Str = Base64.getEncoder().encodeToString(vmessStr.getBytes()); 134 | String vmessLink = "vmess://" + base64Str; 135 | arrayList.add(vmessLink); 136 | } else { 137 | // 其他协议类型,后续添加 138 | } 139 | } 140 | return arrayList; 141 | } 142 | 143 | 144 | private static JSONObject buildObj(String remark, String listen, String port, String id, String network, String protocol, 145 | String obfuscationType, String obfuscationDomain) { 146 | return new JSONObject() 147 | .fluentPut("v", "2") 148 | .fluentPut("protocol", protocol) 149 | .fluentPut("ps", remark) 150 | .fluentPut("add", listen) 151 | .fluentPut("port", Integer.valueOf(port)) 152 | .fluentPut("id", id) 153 | .fluentPut("net", network) 154 | .fluentPut("type", obfuscationType) 155 | .fluentPut("host", obfuscationDomain) 156 | // .fluentPut("tls", tls) 157 | .fluentPut("aid", 0) 158 | .fluentPut("path", null); 159 | } 160 | 161 | private static JSONArray getAllUser(JSONArray clients) { 162 | JSONArray jsonArray = new JSONArray(); 163 | for (Object client : clients) { 164 | JSONObject clientObj = (JSONObject) client; 165 | String email = clientObj.getString("email"); 166 | String userId = clientObj.getString("id"); 167 | jsonArray.add(new JSONObject() 168 | .fluentPut("remarks", email) 169 | .fluentPut("id", userId)); 170 | } 171 | return jsonArray; 172 | } 173 | 174 | 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/com/easy/utils/proxy/GetXuiProxyUtil.java: -------------------------------------------------------------------------------- 1 | package com.easy.utils.proxy; 2 | 3 | import com.alibaba.fastjson2.JSONObject; 4 | import com.easy.exception.BusinessException; 5 | import com.easy.pojo.dto.ProxyDTO; 6 | import com.easy.utils.okhttp3.OkHttpUtils; 7 | import okhttp3.MediaType; 8 | import okhttp3.RequestBody; 9 | import okhttp3.Response; 10 | import okio.BufferedSink; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.io.*; 15 | import java.net.MalformedURLException; 16 | import java.net.URL; 17 | import java.util.*; 18 | 19 | /** 20 | * @author Administrator 21 | */ 22 | public class GetXuiProxyUtil { 23 | 24 | 25 | 26 | /** 27 | * 调用用户提供的login方法。 28 | * @return Session Cookie,如果登录失败或未获取到则返回null 29 | */ 30 | public static String loginAndGetUrl(String url,String username, String password) { 31 | // 判断url是不是合法的http的url 32 | if(!isHttpUrl(url)){ 33 | throw new BusinessException("URL不合法"); 34 | } 35 | // 如果url最后面是 / 就去掉 36 | if(url.endsWith("/")){ 37 | url = url.substring(0, url.length()-1); 38 | } 39 | Map bodyMap = new HashMap<>(); 40 | bodyMap.put("username", Optional.ofNullable(username).filter(s ->!s.isEmpty()).orElse("admin")); 41 | bodyMap.put("password", Optional.ofNullable(password).filter(s ->!s.isEmpty()).orElse("admin")); 42 | bodyMap.put("LoginSecret", ""); 43 | RequestBody formRequestBody = OkHttpUtils.createFormRequestBody(bodyMap); 44 | try { 45 | String reUrl = url + "/login"; 46 | Response response = OkHttpUtils.post(reUrl, formRequestBody); 47 | String respBody = OkHttpUtils.getResponseBodyAsString(response); 48 | if (respBody != null) { 49 | JSONObject resObj = JSONObject.parseObject(respBody); 50 | if ("false".equals(resObj.get("success"))) { 51 | throw new BusinessException("登录失败"); 52 | } 53 | } 54 | String session = OkHttpUtils.getCookie(response, "session"); 55 | return getNodeList(url, session); 56 | } catch (IOException e) { 57 | throw new BusinessException("未知原因,登录请求失败"); 58 | } 59 | } 60 | 61 | public static boolean isHttpUrl(String urlStr) { 62 | try { 63 | URL url = new URL(urlStr); 64 | String protocol = url.getProtocol(); 65 | return "http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol); 66 | } catch (MalformedURLException e) { 67 | return false; 68 | } 69 | } 70 | 71 | private static String getNodeList(String url, String session) { 72 | Map headerMap = new HashMap<>(); 73 | headerMap.put("Cookie", "session=" + session); 74 | List link = null; 75 | try { 76 | url = url + "/panel/inbound/list"; 77 | Response response = OkHttpUtils.post(url, headerMap, new RequestBody() { 78 | @Nullable 79 | @Override 80 | public MediaType contentType() { 81 | return null; 82 | } 83 | 84 | @Override 85 | public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException { 86 | 87 | } 88 | }); 89 | String bodyAsString = OkHttpUtils.getResponseBodyAsString(response); 90 | if (response.code() == 200 && bodyAsString != null) { 91 | JSONObject jsonObject = JSONObject.parseObject(bodyAsString); 92 | if ("false".equals(jsonObject.getString("success"))) { 93 | throw new BusinessException("获取节点列表失败"); 94 | } 95 | } 96 | URL ur = new URL(url); 97 | String path = ur.getHost(); 98 | link = GetProxyUrlUtil.getLink(bodyAsString, path); 99 | } catch (IOException e) { 100 | throw new RuntimeException(e); 101 | } 102 | return link.toString(); 103 | } 104 | 105 | 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: proxy_node 4 | mail: 5 | # 指定邮件服务器地址 6 | host: smtp.qiye.aliyun.com 7 | # 登录账户 8 | username: linux@xlike.email 9 | # 登录密码 10 | password: "123456" 11 | # 端口 12 | port: 25 13 | # 默认编码 14 | default-encoding: UTF-8 15 | # 使用的协议 16 | protocol: smtp 17 | # 配置SSL 加密工厂 18 | properties: 19 | mail: 20 | smtp: 21 | host: smtp.qiye.aliyun.com #阿里云 22 | port: 465 23 | auth: true 24 | socketFactory: 25 | class: javax.net.ssl.SSLSocketFactory 26 | fallback: false 27 | port: 465 28 | debug: true 29 | server: 30 | port: 9988 -------------------------------------------------------------------------------- /target/classes/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: proxy_node 4 | mail: 5 | # 指定邮件服务器地址 6 | host: smtp.qiye.aliyun.com 7 | # 登录账户 8 | username: linux@xlike.email 9 | # 登录密码 10 | password: "123456" 11 | # 端口 12 | port: 25 13 | # 默认编码 14 | default-encoding: UTF-8 15 | # 使用的协议 16 | protocol: smtp 17 | # 配置SSL 加密工厂 18 | properties: 19 | mail: 20 | smtp: 21 | host: smtp.qiye.aliyun.com #阿里云 22 | port: 465 23 | auth: true 24 | socketFactory: 25 | class: javax.net.ssl.SSLSocketFactory 26 | fallback: false 27 | port: 465 28 | debug: true 29 | server: 30 | port: 9988 -------------------------------------------------------------------------------- /target/classes/com/easy/ProxyNodeApplication.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/ProxyNodeApplication.class -------------------------------------------------------------------------------- /target/classes/com/easy/config/GlobalCorsFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/config/GlobalCorsFilter.class -------------------------------------------------------------------------------- /target/classes/com/easy/config/WebSocketConfig.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/config/WebSocketConfig.class -------------------------------------------------------------------------------- /target/classes/com/easy/controller/EmailController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/controller/EmailController.class -------------------------------------------------------------------------------- /target/classes/com/easy/controller/IpController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/controller/IpController.class -------------------------------------------------------------------------------- /target/classes/com/easy/controller/ProxyController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/controller/ProxyController.class -------------------------------------------------------------------------------- /target/classes/com/easy/controller/ProxyWebSocket.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/controller/ProxyWebSocket.class -------------------------------------------------------------------------------- /target/classes/com/easy/exception/BusinessException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/exception/BusinessException.class -------------------------------------------------------------------------------- /target/classes/com/easy/exception/CommonConstants.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/exception/CommonConstants.class -------------------------------------------------------------------------------- /target/classes/com/easy/exception/GlobalExceptionHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/exception/GlobalExceptionHandler.class -------------------------------------------------------------------------------- /target/classes/com/easy/pojo/dto/IpDTO.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/pojo/dto/IpDTO.class -------------------------------------------------------------------------------- /target/classes/com/easy/pojo/dto/KeyDTO.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/pojo/dto/KeyDTO.class -------------------------------------------------------------------------------- /target/classes/com/easy/pojo/dto/ProxyDTO.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/pojo/dto/ProxyDTO.class -------------------------------------------------------------------------------- /target/classes/com/easy/pojo/vo/IpResVO.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/pojo/vo/IpResVO.class -------------------------------------------------------------------------------- /target/classes/com/easy/pojo/vo/ProxyVO.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/pojo/vo/ProxyVO.class -------------------------------------------------------------------------------- /target/classes/com/easy/resutils/Constants.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/resutils/Constants.class -------------------------------------------------------------------------------- /target/classes/com/easy/resutils/R$RBuilder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/resutils/R$RBuilder.class -------------------------------------------------------------------------------- /target/classes/com/easy/resutils/R.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/resutils/R.class -------------------------------------------------------------------------------- /target/classes/com/easy/resutils/REnum.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/resutils/REnum.class -------------------------------------------------------------------------------- /target/classes/com/easy/utils/email/EmailUtils.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/utils/email/EmailUtils.class -------------------------------------------------------------------------------- /target/classes/com/easy/utils/ip/IpUtils.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/utils/ip/IpUtils.class -------------------------------------------------------------------------------- /target/classes/com/easy/utils/okhttp3/OkHttpUtils.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/utils/okhttp3/OkHttpUtils.class -------------------------------------------------------------------------------- /target/classes/com/easy/utils/proxy/GetProxyUrlUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/utils/proxy/GetProxyUrlUtil.class -------------------------------------------------------------------------------- /target/classes/com/easy/utils/proxy/GetXuiProxyUtil$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/utils/proxy/GetXuiProxyUtil$1.class -------------------------------------------------------------------------------- /target/classes/com/easy/utils/proxy/GetXuiProxyUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xliking/proxy_node/e3f50b28a69002003dc22074c6d13210432c788e/target/classes/com/easy/utils/proxy/GetXuiProxyUtil.class --------------------------------------------------------------------------------