205 lines
8.5 KiB
JavaScript
205 lines
8.5 KiB
JavaScript
// http.js 的 main 函数是http服务的所有脚本函数执行的预处理入口
|
||
function main() {
|
||
// 当前时间
|
||
let time = new Date()
|
||
let timeStr = time.toUTCString()
|
||
response.headers.set("Date", timeStr)
|
||
payload.set.header("Timestamp", time.getTime())
|
||
|
||
// 安全性检查
|
||
'use api/lib/sourceAnti.js'
|
||
|
||
'use api/lib/setResponseHeaders.js'
|
||
|
||
let req = payload.get()
|
||
|
||
if (!req?.headers['Cache-Control'] || !req?.headers['Cache-Control']['skip-cache']) {
|
||
// 从Header、Query、Cookie、Body中获取token
|
||
let userToken = req?.headers?.Authorization || req?.query?.token || req?.headers["X-Token"] || req?.cookie?.token
|
||
var signature = `${req.method}:${req.host}:${req.url}::${req.url}:${userToken}:${req.body ? req.body.toString() : '>'}`
|
||
signature = crypto.hash.md5(signature).toHEX.encoding().toString()
|
||
// 缓存5秒 - 缓存命中则直接返回
|
||
let cacheData = cache.get('results', signature)
|
||
if (cacheData && cacheData != '') {
|
||
response.contentType.json()
|
||
response.headers.set("Cache-Status", "hit")
|
||
response.headers.set("Cache-Key", signature)
|
||
// 如果请求头中包含 Accept-Encoding 并且 Accept-Encoding 的值包含 br,则设置 Content-Encoding 为 br。
|
||
if (req.headers['Accept-Encoding'] && req.headers['Accept-Encoding'][0].includes('br')) {
|
||
response.headers.set("Content-Encoding", 'br')
|
||
}
|
||
|
||
var currentTime = new Date();
|
||
let timeDiff = currentTime - time
|
||
response.headers.set("Server-Timing", `RequestCost;dur=${timeDiff}`)
|
||
// currentTime.setSeconds(currentTime.getSeconds() + 5);
|
||
// var timeAfter = currentTime.toUTCString();
|
||
// response.headers.set("Expires", timeAfter)
|
||
response.headers.set("Cache-Control", `no-transform,no-siteapp,max-age=5`)
|
||
|
||
response.write(cacheData)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 声明路径
|
||
let filePath = req.path.substring(1)
|
||
|
||
// 如果Header加上Cache-Control:*则移除缓存,本次访问将不会命中缓存,有刷新效果。
|
||
if (req?.headers['Cache-Control'] && req?.headers['Cache-Control']['clear']) {
|
||
runtime.cache.remove(filePath)
|
||
response.headers.set("Clear-Site-Data", '"*"')
|
||
}
|
||
|
||
switch (req.method) {
|
||
case 'OPTIONS':
|
||
// 这样对于所有的 OPTIONS 请求都会返回 200 (OK) 状态码
|
||
response.status.ok()
|
||
return
|
||
// 当接收的请求为 GET 时
|
||
case 'GET':
|
||
// 特殊访问路径
|
||
// JWK
|
||
if (filePath == '.well-known/jwks.json') {
|
||
response.contentType.json()
|
||
response.write(`{"keys":[{"kty":"SM2","e":"AQAB","n":"${SYSTEM_CONFIG.find(item => item.name === 'system_jwt_public').value.replace(/--.*\n/, "").replace(/\n.*--/, "")}","alg":"SS256","use":"sig","kid":"${ex.suid().base58()}"}]}`)
|
||
return
|
||
}
|
||
|
||
// 【安全须知】
|
||
// 原则上所有命名以.和_开头的目录或文件都不允许直接被访问
|
||
// 因为它们很大概率是系统文件或非开放文件
|
||
if (!filePath.startsWith('.well-known') && (filePath.startsWith('.') || filePath.startsWith('_'))) {
|
||
response.status.code(404)
|
||
return `404 page not found`
|
||
}
|
||
|
||
// 可访问的脚本路径
|
||
let allowScriptPath = [
|
||
'static/index.js',
|
||
'static/js/md5.js',
|
||
]
|
||
|
||
// 【安全须知】
|
||
// 原则上所有的js文件都不允许直接被访问,以免源代码泄露,所以返回404。
|
||
// 如果需要将js对外暴露,请在allowScriptPath中添加。
|
||
// 但app目录内的所有js文件除外,因为该目录用于放置前端页面
|
||
if (filePath.endsWith('.js') && !filePath.startsWith('app') && !allowScriptPath.includes(filePath)) {
|
||
response.status.code(404)
|
||
return `404 page not found`
|
||
}
|
||
|
||
response.headers.set("Content-Type", "text/html; charset=utf-8;");
|
||
|
||
// 判断是否为目录以及否存在index.html或index.html文件,如果存在则返回index.html或index.htm文件
|
||
if (filePath[filePath.length - 1] == "/" || filePath == "" || fs.isDir(filePath)) {
|
||
if (filePath == "") {
|
||
filePath = rootPath
|
||
}
|
||
let tmp = fs.join(filePath, 'index.htm')
|
||
if (fs.exists(tmp)) {
|
||
filePath = tmp
|
||
response.file(filePath)
|
||
return
|
||
}
|
||
tmp += "l"
|
||
if (fs.exists(tmp)) {
|
||
filePath = tmp
|
||
response.file(filePath)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 获取文件类型
|
||
'use api/lib/getFileMimeType.js'
|
||
response.headers.set('Content-Type', getFileMimeType(filePath))
|
||
|
||
// 如果不是目录,则尝试返回文件
|
||
if (fs.exists(filePath) && !fs.isDir(filePath)) {
|
||
const fileSqlPath = req.path
|
||
|
||
if (fileSqlPath.includes('/uploads/')) {
|
||
// 查询数据库中是否有该文件的信息
|
||
const file_info = SQL.query('system_sql', 'SELECT * FROM sys_files WHERE file_id = ?', req.path.split("/").pop())
|
||
if (file_info.length > 0) {
|
||
// 判断文件是否已删除
|
||
if (file_info[0].delete_time) {
|
||
return errMsg(404, '文件已删除')
|
||
}
|
||
|
||
filePath = rootPath + file_info[0].file_path
|
||
|
||
response.headers.add('X-File-Id', file_info[0].file_id)
|
||
response.headers.add('X-File-Name', file_info[0].file_name)
|
||
response.headers.add('X-File-Size', file_info[0].file_size)
|
||
response.headers.add('X-File-Owner', file_info[0].user_id)
|
||
response.headers.add('X-File-Create-Date', file_info[0].create_time)
|
||
response.headers.set('Content-Type', file_info[0].file_type)
|
||
|
||
// 设置 Content-Disposition
|
||
response.headers.set('Content-Disposition', 'filename=' + file_info[0].file_name)
|
||
}
|
||
}
|
||
|
||
if (req.query.download) {
|
||
// 设置 Content-Disposition
|
||
response.headers.add('Content-Disposition', 'attachment')
|
||
}
|
||
|
||
var currentTime = new Date();
|
||
let timeDiff = currentTime - time
|
||
response.headers.set("Server-Timing", `RequestCost;dur=${timeDiff}`)
|
||
// 如果文件存在,则返回文件
|
||
response.file(filePath)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 默认情况下返回的内容都是JSON格式的,所以在这里预设 Content-Type 为 application/json
|
||
response.headers.set("Content-Type", "application/json; charset=utf-8;");
|
||
|
||
// 声明缓存
|
||
response.headers.set(
|
||
'Cache-Control',
|
||
'no-transform,no-cache,immutable'
|
||
)
|
||
|
||
// payload.set.header('Signet', '/__/vlan/')
|
||
|
||
//
|
||
// 判断filePath存在,则将请求移交给 filePath 下的 function.js 进行处理。
|
||
if (fs.exists(filePath)) {
|
||
runtime.goto(filePath, ...args)
|
||
return
|
||
}
|
||
// 如果不存在,则尝试调用filepath的上一级目录下的function.js
|
||
// const parentDir = filePath.substr(0, filePath.lastIndexOf('/'))
|
||
runtime.goto(findValidPath(filePath), ...args) // goto会将 return 的内容写到 response.body 里面。但call不会。
|
||
return
|
||
}
|
||
|
||
// 递归路径
|
||
cache.new('route-path')
|
||
function findValidPath(filePath) {
|
||
// 尝试从缓存获取
|
||
const cachedPath = cache.get('route-path', filePath);
|
||
if (cachedPath) return cachedPath;
|
||
|
||
// 自上而下查找最深的有效路径
|
||
const pathParts = filePath.split('/').filter(Boolean);
|
||
let currentPath = '';
|
||
|
||
for (const part of pathParts) {
|
||
const testPath = currentPath ? fs.join(currentPath, part) : part;
|
||
if (!fs.exists(testPath)) break;
|
||
currentPath = testPath;
|
||
}
|
||
|
||
const result = currentPath || './';
|
||
|
||
// 缓存结果
|
||
cache.set('route-path', filePath, result, 60 * 1000); // 60秒缓存
|
||
|
||
return result;
|
||
}
|