Compare commits
	
		
			4 Commits
		
	
	
		
			7ab5d7dbbf
			...
			3f3d19bc6d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3f3d19bc6d | ||
|  | c9a2cad670 | ||
|  | f4057ae956 | ||
|  | b958beaf2f | 
| @@ -1,8 +0,0 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| module.exports = (ctx) => { |  | ||||||
|   ctx.body = {  |  | ||||||
|     message: 'Example POST API', |  | ||||||
|     data: ctx.request.body  |  | ||||||
|   }; |  | ||||||
| }; |  | ||||||
							
								
								
									
										45
									
								
								api/client/add.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								api/client/add.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | const { randomUUID } = require("crypto"); | ||||||
|  | const ins = require("../../utils/http"); | ||||||
|  | const dayjs = require("dayjs"); | ||||||
|  | const getCookie = require("../../auth/3xuiLogin"); | ||||||
|  | const { log } = require("console"); | ||||||
|  |  | ||||||
|  | module.exports = async (ctx) => { | ||||||
|  |   let { id = 4, limitIp, expiryTime = 0, enable = !0, totalGB = 1, subId = "2rv0gb458kbfl532" } = ctx.request.body; | ||||||
|  |   if (!ctx.state.cookie) { | ||||||
|  |     ctx.state.cookie = await getCookie(); | ||||||
|  |     ins.interceptors.request.use((config) => { | ||||||
|  |       config.headers.Cookie = ctx.state.cookie; | ||||||
|  |       return config; | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   const inbound = await ins.get(`/panel/api/inbounds/get/${id}`); | ||||||
|  |   if (!inbound?.success) return (ctx.body = { code: "1", msg: "获取节点失败" }); | ||||||
|  |   const uuid = randomUUID(), | ||||||
|  |     email = uuid.split("-")[0]; | ||||||
|  |   if (expiryTime > 0) { | ||||||
|  |     expiryTime = dayjs(expiryTime, "YYYY-MM-DD HH:mm:ss").valueOf(); | ||||||
|  |   } | ||||||
|  |   totalGB = totalGB * 1024 * 1024 * 1024; | ||||||
|  |   const settings = { clients: [{ id: uuid, flow: "", email, limitIp, enable, tgId: "", subId, reset: 0, totalGB, expiryTime }] }; | ||||||
|  |   const result = await ins.post("/panel/api/inbounds/addClient", { id, settings: JSON.stringify(settings) }); | ||||||
|  |   if (result?.success) { | ||||||
|  |     const { remark, port, protocol, streamSettings = "{}" } = inbound.obj || {}; | ||||||
|  |     const { | ||||||
|  |       network = "ws", | ||||||
|  |       security = "none", | ||||||
|  |       wsSettings: { host, path }, | ||||||
|  |     } = JSON.parse(streamSettings); | ||||||
|  |     ctx.body = { | ||||||
|  |       code: "0", | ||||||
|  |       data: `${protocol}://${uuid}@vless.jjcp52.com:${port}?type=${network}&path=${path}&host=${host}&security=${security}#${remark}-${email}`, | ||||||
|  |       message: "success", | ||||||
|  |     }; | ||||||
|  |   } else { | ||||||
|  |     ctx.body = { | ||||||
|  |       code: 1, | ||||||
|  |       message: "添加失败", | ||||||
|  |       data: ctx.request.body, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										50
									
								
								app.js
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								app.js
									
									
									
									
									
								
							| @@ -12,22 +12,47 @@ app.use(bodyParser()); // 添加在路由中间件之前 | |||||||
| const router = new Router(); | const router = new Router(); | ||||||
|  |  | ||||||
| // 自动加载API路由函数 | // 自动加载API路由函数 | ||||||
| const loadAPIRoutes = () => { | const loadAPIRoutes = (baseDir = 'api', baseRoute = '/api') => { | ||||||
|   const apiDir = path.join(__dirname, "api"); |   const scanDirectory = (dir, routePrefix) => { | ||||||
|   const files = fs.readdirSync(apiDir); |     const entries = fs.readdirSync(dir, { withFileTypes: true }); | ||||||
|  |  | ||||||
|   files.forEach((file) => { |     entries.forEach(entry => { | ||||||
|     if (file.endsWith(".js") && file !== "index.js") { |       const fullPath = path.join(dir, entry.name); | ||||||
|       const routePath = `/api/${file.replace(".js", "")}`; |       const relativePath = path.relative(baseDir, dir); | ||||||
|       const handler = require(path.join(apiDir, file)); |  | ||||||
|        |        | ||||||
|       router.post(routePath, async (ctx) => { |       // 构建路由路径:工程目录结构 -> URL路径 | ||||||
|         await handler(ctx); |       const routePath = path.join( | ||||||
|       }); |         routePrefix, | ||||||
|  |         relativePath, | ||||||
|  |         entry.name.replace(/\.js$/, '') | ||||||
|  |       ).replace(/\\/g, '/'); // Windows路径转URL路径 | ||||||
|  |  | ||||||
|  |       if (entry.isDirectory()) { | ||||||
|  |         scanDirectory(fullPath, routePrefix); // 递归扫描子目录 | ||||||
|  |       } else if ( | ||||||
|  |         entry.isFile() &&  | ||||||
|  |         path.extname(entry.name) === '.js' && | ||||||
|  |         entry.name !== 'index.js' | ||||||
|  |       ) { | ||||||
|  |         registerRoute(fullPath, routePath); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   // 路由注册器(支持扩展其他HTTP方法) | ||||||
|  |   const registerRoute = (filePath, routePath) => { | ||||||
|  |     try { | ||||||
|  |       const handler = require(filePath); | ||||||
|  |       router.post(routePath, async ctx => await handler(ctx)); | ||||||
|  |       console.log(`[Route] POST ${routePath} -> ${filePath}`); | ||||||
|  |     } catch (err) { | ||||||
|  |       console.error(`[Error] Failed to load route ${routePath}:`, err); | ||||||
|     } |     } | ||||||
|   }); |   }; | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |   // 初始化扫描 | ||||||
|  |   scanDirectory(path.join(__dirname, baseDir), baseRoute); | ||||||
|  | }; | ||||||
| // 公开路由 | // 公开路由 | ||||||
| router.get("/public", (ctx) => { | router.get("/public", (ctx) => { | ||||||
|   ctx.body = "Public content"; |   ctx.body = "Public content"; | ||||||
| @@ -50,7 +75,6 @@ app.use(async (ctx, next) => { | |||||||
|   } |   } | ||||||
|   await next(); |   await next(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| // JWT中间件(保护下方所有路由) | // JWT中间件(保护下方所有路由) | ||||||
| app.use( | app.use( | ||||||
|   koaJwt({ |   koaJwt({ | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								auth/3xuiLogin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								auth/3xuiLogin.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | const instance = require("../utils/http"); | ||||||
|  |  | ||||||
|  | function getCookie() { | ||||||
|  |   const formData = new FormData(); | ||||||
|  |   formData.append("username", "lsw"); | ||||||
|  |   formData.append("password", "lsw@2024"); | ||||||
|  |   formData.append("loginSecret", "IEuVG4csTWLuaq3ysuUSHdwOcnoQRfScURwJVBjCMjRRpjVyYhWcgaHIJvU0SV4P"); | ||||||
|  |   return instance | ||||||
|  |     .post("/login", formData, { | ||||||
|  |       maxRedirects: 0, // 禁止自动重定向以保留Cookie | ||||||
|  |       withCredentials: true, // 确保携带凭证 | ||||||
|  |     }) | ||||||
|  |     .then((res) => { | ||||||
|  |       const rawCookies = res.headers["set-cookie"] || []; | ||||||
|  |       const cookies =["lang=zh-CN",rawCookies.at(-1)?.split(";")[0]].join(";"); | ||||||
|  |       return cookies; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | module.exports = getCookie; | ||||||
| @@ -11,6 +11,7 @@ | |||||||
|   "license": "ISC", |   "license": "ISC", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "axios": "^1.7.9", |     "axios": "^1.7.9", | ||||||
|  |     "dayjs": "^1.11.13", | ||||||
|     "dotenv": "^16.4.7", |     "dotenv": "^16.4.7", | ||||||
|     "jsonwebtoken": "^9.0.2", |     "jsonwebtoken": "^9.0.2", | ||||||
|     "koa": "^2.15.4", |     "koa": "^2.15.4", | ||||||
|   | |||||||
| @@ -1,3 +1,16 @@ | |||||||
| require("dotenv").config(); | require("dotenv").config(); | ||||||
| const { default: axios } = require("axios"); | const { default: axios } = require("axios"); | ||||||
| module.exports = (baseURL = process.env.BASE_URL) => axios.create({ baseURL: BASE_URL }); | const instance = axios.create({ baseURL: process.env.BASE_URL }); | ||||||
|  | instance.interceptors.response.use( | ||||||
|  |   (response) => { | ||||||
|  |     if (response.status === 200 && !["/login"].includes(response.config.url)) { | ||||||
|  |       return response.data; | ||||||
|  |     } | ||||||
|  |     return response; | ||||||
|  |   }, | ||||||
|  |   (error) => { | ||||||
|  |     console.log(error); | ||||||
|  |     return Promise.reject(error); | ||||||
|  |   } | ||||||
|  | ); | ||||||
|  | module.exports = instance; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user