Files
vless-api/app.js
aixianling f4057ae956 feat(auth): 添加 3xui 登录功能
- 新增 3xuiLogin.js 文件实现登录功能
- 添加 getCookie 函数用于获取登录 Cookie
- 集成 HTTP 实例和表单数据处理
2025-02-26 15:38:31 +08:00

101 lines
3.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

require("dotenv").config();
const Koa = require("koa");
const Router = require("koa-router");
const jwt = require("jsonwebtoken");
const koaJwt = require("koa-jwt");
const fs = require("fs");
const path = require("path");
const bodyParser = require("koa-bodyparser");
const verifyThirdPartyToken = require("./auth/verifyThirdPartyToken");
const app = new Koa();
app.use(bodyParser()); // 添加在路由中间件之前
const router = new Router();
// 自动加载API路由函数
const loadAPIRoutes = (baseDir = 'api', baseRoute = '/api') => {
const scanDirectory = (dir, routePrefix) => {
const entries = fs.readdirSync(dir, { withFileTypes: true });
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路径
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) => {
ctx.body = "Public content";
});
// 自定义中间件解析并验证第三方Token
app.use(async (ctx, next) => {
const authHeader = ctx.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
const thirdPartyToken = authHeader.split(' ')[1];
try {
// 这里假设第三方Token可以通过某种方式验证并转换为JWT Token
const decoded = verifyThirdPartyToken(thirdPartyToken); // 假设有一个验证函数
const jwtToken = jwt.sign(decoded, process.env.JWT_SECRET, { expiresIn: "1h" });
ctx.state.user = decoded; // 将用户信息存储在ctx.state中
ctx.headers.authorization = `Bearer ${jwtToken}`; // 替换为JWT Token
} catch (err) {
ctx.throw(401, 'Invalid third-party token');
}
}
await next();
});
// JWT中间件保护下方所有路由
app.use(
koaJwt({
secret: process.env.JWT_SECRET,
}).unless({
path: [/^\/public/, /^\/login/],
})
);
// 加载自动生成的路由
loadAPIRoutes();
// 受保护路由
router.get("/protected", (ctx) => {
ctx.body = `Protected content for ${ctx.state.user.username}`;
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(process.env.PORT || 3000, () => {
console.log(`Server running on http://localhost:${process.env.PORT || 3000}`);
});