feat: 初始提交(仅核心代码,已排除大文件)
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// ============================================================
|
||||
// data_gateway/auth.js - 对外 API 鉴权模块
|
||||
// 职责:校验 X-API-Id + X-API-Key,匹配 skill config.json 中的哈希
|
||||
// ============================================================
|
||||
const crypto = require('crypto')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const SKILLS_DIR = path.join(__dirname, 'skills')
|
||||
|
||||
// 缓存已加载的 skill 配置
|
||||
const skillConfigCache = new Map()
|
||||
|
||||
const loadSkillConfig = (skillId) => {
|
||||
if (skillConfigCache.has(skillId)) return skillConfigCache.get(skillId)
|
||||
const cfgPath = path.join(SKILLS_DIR, skillId, 'config.json')
|
||||
if (!fs.existsSync(cfgPath)) return null
|
||||
try {
|
||||
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'))
|
||||
skillConfigCache.set(skillId, cfg)
|
||||
return cfg
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新缓存(用于配置热更新)
|
||||
const refreshCache = (skillId) => {
|
||||
skillConfigCache.delete(skillId)
|
||||
return loadSkillConfig(skillId)
|
||||
}
|
||||
|
||||
// 枚举所有已启用的 skill
|
||||
const listEnabledSkills = () => {
|
||||
const result = []
|
||||
if (!fs.existsSync(SKILLS_DIR)) return result
|
||||
const dirs = fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
|
||||
for (const d of dirs) {
|
||||
if (!d.isDirectory()) continue
|
||||
const cfg = loadSkillConfig(d.name)
|
||||
if (cfg && cfg.enabled !== false) {
|
||||
result.push({ id: cfg.id, name: cfg.name })
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 校验外部 API 请求
|
||||
const verifyApiKey = (req) => {
|
||||
const apiId = String(req.headers['x-api-id'] || '').trim()
|
||||
const apiKey = String(req.headers['x-api-key'] || '').trim()
|
||||
if (!apiId || !apiKey) return { ok: false, error: 'missing api id or key' }
|
||||
|
||||
const cfg = loadSkillConfig(apiId)
|
||||
if (!cfg) return { ok: false, error: 'skill not found' }
|
||||
if (cfg.enabled === false) return { ok: false, error: 'skill disabled' }
|
||||
|
||||
const expectedHash = String(cfg.api_key_hash || '')
|
||||
if (!expectedHash) return { ok: false, error: 'skill not configured' }
|
||||
|
||||
const actualHash = 'sha256:' + crypto.createHash('sha256').update(apiKey).digest('hex')
|
||||
if (actualHash !== expectedHash) return { ok: false, error: 'invalid api key' }
|
||||
|
||||
return { ok: true, skill: cfg }
|
||||
}
|
||||
|
||||
// 校验内部 Tool 读取请求
|
||||
const verifyInternalToken = (req) => {
|
||||
const token = String(req.headers['x-internal-token'] || '').trim()
|
||||
const skillId = String(req.headers['x-skill-id'] || req.params?.skillId || '').trim()
|
||||
if (!token || !skillId) return { ok: false, error: 'missing token or skill id' }
|
||||
|
||||
const cfg = loadSkillConfig(skillId)
|
||||
if (!cfg) return { ok: false, error: 'skill not found' }
|
||||
|
||||
const expectedHash = String(cfg.read_token_hash || '')
|
||||
if (!expectedHash) return { ok: false, error: 'skill not configured for internal read' }
|
||||
|
||||
const actualHash = 'sha256:' + crypto.createHash('sha256').update(token).digest('hex')
|
||||
if (actualHash !== expectedHash) return { ok: false, error: 'invalid internal token' }
|
||||
|
||||
return { ok: true, skill: cfg }
|
||||
}
|
||||
|
||||
module.exports = { loadSkillConfig, refreshCache, listEnabledSkills, verifyApiKey, verifyInternalToken }
|
||||
Reference in New Issue
Block a user