diff --git a/api/addClient.js b/api/addClient.js deleted file mode 100644 index 922dd96..0000000 --- a/api/addClient.js +++ /dev/null @@ -1,8 +0,0 @@ - - -module.exports = (ctx) => { - ctx.body = { - message: 'Example POST API', - data: ctx.request.body - }; -}; \ No newline at end of file diff --git a/api/client/add.js b/api/client/add.js new file mode 100644 index 0000000..7a85c79 --- /dev/null +++ b/api/client/add.js @@ -0,0 +1,36 @@ +const { randomUUID } = require("crypto"); +const http = require("../../utils/http"); +const dayjs = require("dayjs"); + +module.exports = async (ctx) => { + let { id = 4, limitIp, expiryTime, enable = !0, totalGB = 1, subId = "2rv0gb458kbfl532" } = ctx.request.body; + const inbound = await http.get(`/api/inbounds/get/${id}`); + if (!inbound?.success) return (ctx.body = { code: "1", msg: "获取节点失败" }); + const uuid = randomUUID(), + email = uuid.split("-")[0]; + expiryTime = dayjs(expiryTime, "YYYY-MM-DD HH:mm:ss").valueOf(); + const settings = { clients: [{ id: uuid, flow: "", email, limitIp, enable, tgId: "", subId, reset: 0, totalGB, expiryTime }] }; + http + .post("/api/inbounds/addClient", { id, settings: JSON.stringify(settings) }) + .then((res) => { + if (res?.success) { + const { remark, port, protocol, streamSettings = "{}" } = inbound.obj || {}; + const { + network = "ws", + security = "none", + wsSettings: { host, path }, + } = JSON.parse(streamSettings); + return ctx.body = { + code: "0", + data: `${protocol}://${uuid}@vless.jjcp52.com:${port}?type=${network}&path=${path}&host=${host}&security=${security}#${remark}-${email}`, + message: "success", + }; + } + }) + .finally(() => { + ctx.body = { + message: "Example POST API", + data: ctx.request.body, + }; + }); +}; diff --git a/app.js b/app.js index 0f130b4..4ac39d4 100644 --- a/app.js +++ b/app.js @@ -12,20 +12,46 @@ app.use(bodyParser()); // 添加在路由中间件之前 const router = new Router(); // 自动加载API路由函数 -const loadAPIRoutes = () => { - const apiDir = path.join(__dirname, "api"); - const files = fs.readdirSync(apiDir); +const loadAPIRoutes = (baseDir = 'api', baseRoute = '/api') => { + const scanDirectory = (dir, routePrefix) => { + const entries = fs.readdirSync(dir, { withFileTypes: true }); - files.forEach((file) => { - if (file.endsWith(".js") && file !== "index.js") { - const routePath = `/api/${file.replace(".js", "")}`; - const handler = require(path.join(apiDir, file)); + entries.forEach(entry => { + const fullPath = path.join(dir, entry.name); + const relativePath = path.relative(baseDir, dir); + + // 构建路由路径:工程目录结构 -> URL路径 + const routePath = path.join( + routePrefix, + relativePath, + entry.name.replace(/\.js$/, '') + ).replace(/\\/g, '/'); // Windows路径转URL路径 - router.post(routePath, async (ctx) => { - await handler(ctx); - }); + 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); }; // 公开路由 @@ -50,7 +76,6 @@ app.use(async (ctx, next) => { } await next(); }); - // JWT中间件(保护下方所有路由) app.use( koaJwt({ diff --git a/package.json b/package.json index 904b132..6e27f3d 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "axios": "^1.7.9", + "dayjs": "^1.11.13", "dotenv": "^16.4.7", "jsonwebtoken": "^9.0.2", "koa": "^2.15.4", diff --git a/utils/http.js b/utils/http.js index 7c03ecb..0e0c0d0 100644 --- a/utils/http.js +++ b/utils/http.js @@ -1,3 +1,15 @@ require("dotenv").config(); 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.data.code === 200) { + return response.data.data; + } else return response; + }, + (error) => { + console.log(error); + return Promise.reject(error); + } +); +module.exports = instance;