Skip to content

Commit 0c5068f

Browse files
committed
feat: 添加 FreeEye 插件配置和实现代码
1 parent d4a1be4 commit 0c5068f

File tree

3 files changed

+177
-10
lines changed

3 files changed

+177
-10
lines changed

packages/core/src/modules/plugin/free-eye/.gitignore

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module.exports = {
2+
name: '网络检测',
3+
statusOff: true,
4+
enabled: false,
5+
tip: '运行网络检测来评估当前网络环境',
6+
startup: {},
7+
// FreeEye 自带一套 tests(位于本目录的 checkpoints/ 和 config.json),
8+
// 这里保留最小配置以便在 dev-sidecar 中显示和切换插件。
9+
setting: {
10+
// 如果需要覆盖内置测试配置,可以在这里提供相对于插件目录或工作目录的路径
11+
testsConfigFile: 'config.json',
12+
testsDir: 'checkpoints',
13+
// 默认网络请求超时时间(秒),插件内部的测试可以参考或覆盖
14+
defaultTimeout: 3,
15+
},
16+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
const fs = require('node:fs')
2+
const path = require('node:path')
3+
const clientModule = require('./client')
4+
5+
const runTests = (clientModule && (clientModule.runTests || (clientModule.default && clientModule.default.runTests)))
6+
const freeEyeConfig = require('./config')
7+
8+
const PLUGIN_STATUS_KEY = 'plugin.free_eye'
9+
10+
const FreeEyePlugin = function (context) {
11+
const { config, event, log } = context
12+
let lastResult = null
13+
14+
const resolvePath = (targetPath, defaultRelative) => {
15+
const fallback = path.join(__dirname, defaultRelative)
16+
if (!targetPath) {
17+
return fallback
18+
}
19+
if (path.isAbsolute(targetPath)) {
20+
return targetPath
21+
}
22+
const candidates = [
23+
path.join(__dirname, targetPath),
24+
path.join(process.cwd(), targetPath),
25+
]
26+
for (const candidate of candidates) {
27+
if (fs.existsSync(candidate)) {
28+
return candidate
29+
}
30+
}
31+
return fallback
32+
}
33+
34+
const emitStatus = (key, value) => {
35+
event.fire('status', { key, value })
36+
}
37+
38+
const captureLogs = async (executor) => {
39+
const logs = []
40+
const originalLog = console.log
41+
const originalError = console.error
42+
const push = (level, args) => {
43+
const message = args.map((item) => {
44+
if (item instanceof Error) {
45+
return item.stack || item.message
46+
}
47+
if (typeof item === 'object') {
48+
try {
49+
return JSON.stringify(item)
50+
} catch (err) {
51+
return String(item)
52+
}
53+
}
54+
return String(item)
55+
}).join(' ')
56+
logs.push({ level, message, timestamp: Date.now() })
57+
// Also write to system log so it follows configured logging format
58+
if (level === 'error') {
59+
log.error(message)
60+
} else {
61+
log.info(message)
62+
}
63+
}
64+
console.log = (...args) => {
65+
push('info', args)
66+
originalLog(...args)
67+
}
68+
console.error = (...args) => {
69+
push('error', args)
70+
originalError(...args)
71+
}
72+
try {
73+
const result = await executor()
74+
return { result, logs }
75+
} finally {
76+
console.log = originalLog
77+
console.error = originalError
78+
}
79+
}
80+
81+
const storeResult = (payload) => {
82+
lastResult = payload
83+
emitStatus(`${PLUGIN_STATUS_KEY}.result`, lastResult)
84+
}
85+
86+
const executeTests = async () => {
87+
const currentConfig = config.get()
88+
const pluginConfig = currentConfig.plugin.free_eye || {}
89+
const setting = pluginConfig.setting || {}
90+
const configFilePath = resolvePath(setting.testsConfigFile, 'config.json')
91+
const testsDir = resolvePath(setting.testsDir, 'checkpoints')
92+
log.info(`FreeEye tests triggering, config=${configFilePath}, testsDir=${testsDir}`)
93+
try {
94+
const { result, logs } = await captureLogs(() => runTests({ configPath: configFilePath, testsDir }))
95+
const payload = {
96+
finishedAt: new Date().toISOString(),
97+
totalTests: result.totalTests,
98+
completedTests: result.completedTests,
99+
summaries: result.summaries,
100+
results: result.results,
101+
logs,
102+
}
103+
storeResult(payload)
104+
return payload
105+
} catch (err) {
106+
const payload = {
107+
finishedAt: new Date().toISOString(),
108+
error: err.message,
109+
}
110+
storeResult(payload)
111+
throw err
112+
}
113+
}
114+
115+
const api = {
116+
async start() {
117+
emitStatus(`${PLUGIN_STATUS_KEY}.enabled`, true)
118+
log.info('启动【FreeEye】插件')
119+
try {
120+
return await executeTests()
121+
} catch (err) {
122+
log.error('FreeEye runTests failed:', err)
123+
throw err
124+
}
125+
},
126+
127+
async close() {
128+
emitStatus(`${PLUGIN_STATUS_KEY}.enabled`, false)
129+
log.info('关闭【FreeEye】插件')
130+
},
131+
132+
async restart() {
133+
await api.close()
134+
return api.start()
135+
},
136+
137+
isEnabled() {
138+
const pluginConfig = config.get().plugin.free_eye
139+
return pluginConfig && pluginConfig.enabled
140+
},
141+
142+
async run() {
143+
return executeTests()
144+
},
145+
146+
async getLastResult() {
147+
return lastResult
148+
},
149+
}
150+
return api
151+
}
152+
153+
module.exports = {
154+
key: 'free_eye',
155+
config: freeEyeConfig,
156+
status: {
157+
enabled: false,
158+
result: null,
159+
},
160+
plugin: FreeEyePlugin,
161+
}

0 commit comments

Comments
 (0)