-
Notifications
You must be signed in to change notification settings - Fork 283
Expand file tree
/
Copy pathapi_validate.js
More file actions
147 lines (127 loc) · 5.24 KB
/
api_validate.js
File metadata and controls
147 lines (127 loc) · 5.24 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import path from 'path';
import {readFile} from 'fs/promises';
import fileHeaderManager from "./fileHeaderManager.js";
// 检查是否运行在Vercel环境
const IS_VERCEL = process.env.VERCEL;
// 剪切板安全码
const SECURITY_CODE = process.env.CLIPBOARD_SECURITY_CODE || '';
// 接口basic验证
export const validateBasicAuth = (request, reply, done) => {
if (!process.env.hasOwnProperty('API_AUTH_NAME') && !process.env.hasOwnProperty('API_AUTH_CODE')) {
done();
return
}
if (request.url.startsWith('/config/')) {
let cf_path = request.url.slice(8).split('?')[0];
// console.log(cf_path);
if (!['index.js', 'index.js.md5', 'index.config.js', 'index.config.js.md5'].includes(cf_path)) {
done();
return
}
console.log(`[validateBasicAuth] 猫配置文件 ${cf_path} 进入Basic登录鉴权`);
}
// console.log('进入了basic验证');
const authHeader = request.headers.authorization;
if (!authHeader) {
reply.header('WWW-Authenticate', 'Basic');
return reply.code(401).send('Authentication required');
}
const base64Credentials = authHeader.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8');
const [username, password] = credentials.split(':');
const validUsername = process.env.API_AUTH_NAME || '';
const validPassword = process.env.API_AUTH_CODE || '';
if (username === validUsername && password === validPassword) {
done(); // 验证通过,继续处理请求
} else {
reply.header('WWW-Authenticate', 'Basic');
return reply.code(401).send('Invalid credentials');
}
};
// 接口密码验证
export const validatePwd = async (request, reply) => {
const apiPwd = process.env.API_PWD;
if (!apiPwd) {
return; // 如果未配置 API_PWD,直接通过
}
if (request.url.startsWith('/config/')) {
let cf_path = request.url.slice(8).split('?')[0];
// console.log(cf_path);
if (['index.js', 'index.js.md5', 'index.config.js', 'index.config.js.md5'].includes(cf_path)) {
console.log(`[validatePwd] 猫配置文件 ${cf_path} 跳过接口密码鉴权`);
return
}
}
// 从查询参数或请求体中获取 pwd
const pwd = request.query.pwd || request.body?.pwd;
// 如果 pwd 不存在或与 API_PWD 不匹配,返回 403
if (pwd !== apiPwd) {
return reply.code(403).send({error: 'Forbidden: Invalid or missing pwd'});
}
};
// JS文件验证
export const validateJs = async (request, reply, dr2Dir) => {
if (request.url.startsWith('/js/')) {
try {
const fileName = decodeURIComponent(request.url.replace('/js/', '').split('?')[0]);
// console.log('fileName', fileName);
// 获取文件系统路径
const filePath = path.join(dr2Dir, fileName);
// console.log('filePath', filePath);
// 读取文件内容
let content = await readFile(filePath, 'utf8');
if (/var rule|function|let |var |const|class Rule|async|this\./.test(content)) {
// 添加版权信息
const copyright = `/*!
* Copyright © ${new Date().getFullYear()} Taoist
* Licensed under LGPL3 (https://github.com/hjdhnx/drpy-node/blob/main/LICENSE)
*/
`;
content = `${copyright}${content}`;
} else {
content = await fileHeaderManager.removeHeader(content, {
mode: 'top-comments',
fileType: '.js'
});
}
// 设置响应头并发送修改后的内容
return reply
.header('Content-Type', 'application/javascript; charset=utf-8')
.send(content);
} catch (error) {
// 文件不存在时继续后续处理(由fastify-static处理404)
if (error.code === 'ENOENT') return;
// 其他错误处理
console.error(`File processing error: ${error.message}`);
return reply.code(500).send('Internal Server Error');
}
}
}
export const validatHtml = async (request, reply, rootDir) => {
if (request.url.endsWith('index.html')) {
try {
const filePath = path.join(rootDir, request.url);
// console.log('filePath', filePath);
// 读取文件内容
let content = await readFile(filePath, 'utf8');
content = content.replaceAll('$SECURITY_CODE', SECURITY_CODE);
// 设置响应头并发送修改后的内容
return reply
.header('Content-Type', 'text/html; charset=utf-8')
.send(content);
} catch (error) {
// 文件不存在时继续后续处理(由fastify-static处理404)
if (error.code === 'ENOENT') return;
// 其他错误处理
console.error(`File processing error: ${error.message}`);
return reply.code(500).send('Internal Server Error');
}
}
}
// Vercel环境检测中间件
export const validateVercel = (request, reply, done) => {
if (IS_VERCEL) {
return reply.status(503).send('API not available on Vercel platform');
}
done();
};