├── .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 |
5 |
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 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.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 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.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 |
--------------------------------------------------------------------------------
/.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 | 
5 | 
6 | 
7 | 
8 | 
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 |
2 |
3 |
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 |
2 |
3 |
4 |
5 |
6 |
{{ title }}
7 |
8 |
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 |
2 |
3 |
4 |
5 |
批量IP检测
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
响应数据
21 |
29 |
33 |
34 |
38 |
39 |
43 |
44 |
48 |
49 |
53 |
54 |
55 |
61 |
62 |
63 |
64 | {{ scope.row.isAbuser ? '是' : '否' }}
65 |
66 |
67 |
68 |
69 |
70 |
76 |
77 |
80 |
81 | {{ scope.row.isCloudProvider ? '是' : '否' }}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
94 |
95 |
98 |
99 | {{ scope.row.isProxy ? '是' : '否' }}
100 |
101 |
102 |
103 |
104 |
105 |
106 |
112 |
113 |
116 |
117 | {{ scope.row.isRelay ? '是' : '否' }}
118 |
119 |
120 |
121 |
122 |
123 |
124 |
130 |
131 |
134 |
135 | {{ scope.row.isVpn ? '是' : '否' }}
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
302 |
303 |
426 |
--------------------------------------------------------------------------------
/proxy-vue/src/views/Donate.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 本捐赠的 Key 仅用于维持 IP 检测的使用。若您支持,可在
6 | IPRegistry
7 | 注册账号,获取 Key。您也可以发送邮件至
8 | linux@xlike.email
9 | ,或者在下方的输入框中输入 Key 并发送即可,非常感谢您的支持!
10 |
11 |
12 | 原项目在此处,需要的可以查看
13 |
14 |
15 |
16 |
28 |
29 |
30 |
31 |
35 |
39 |
40 | {{ message }}
41 |
42 |
43 |
44 |
45 |
46 |
90 |
91 |
193 |
--------------------------------------------------------------------------------
/proxy-vue/src/views/Home.vue:
--------------------------------------------------------------------------------
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 |
43 |
44 |
80 |
--------------------------------------------------------------------------------
/proxy-vue/src/views/Proxy-wb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
54 |
55 |
56 |
57 |
58 |
82 |
83 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
288 |
289 |
552 |
--------------------------------------------------------------------------------
/proxy-vue/src/views/Proxy.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
节点数据获取工具
6 |
目前只支持vless协议 和 vmess协议,其他后续更新
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 获取数据
47 |
48 |
49 |
50 |
51 |
52 |
53 |
62 |
63 |
64 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
156 |
157 |
245 |
--------------------------------------------------------------------------------
/proxy-vue/src/views/Text.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
23 |
24 |
25 |
26 |
32 |
33 |
36 |
37 |
38 | 文本已成功复制到剪贴板!
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
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
--------------------------------------------------------------------------------