mirror of
https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 13:55:51 +00:00
总线开发工程-init
This commit is contained in:
59
SL/bus/Amily2Bus.js
Normal file
59
SL/bus/Amily2Bus.js
Normal file
@@ -0,0 +1,59 @@
|
||||
class Amily2Bus {
|
||||
constructor() {
|
||||
/** @type {Logger|null} */
|
||||
this.Logger = null;
|
||||
/** @type {FilePipe|null} */
|
||||
this.FilePipe = null;
|
||||
|
||||
// 已注册插件列表,防止重复注册
|
||||
this.registry = new Set();
|
||||
|
||||
console.log('[Amily2Bus] Core container initialized with secure registry.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册插件并获取专属上下文
|
||||
* @param {string} pluginName 插件名称
|
||||
* @returns {Object} 包含该插件专属 API 的上下文对象
|
||||
*/
|
||||
register(pluginName) {
|
||||
if (!pluginName || typeof pluginName !== 'string') {
|
||||
throw new Error('[Amily2Bus] Invalid plugin name.');
|
||||
}
|
||||
|
||||
if (this.registry.has(pluginName)) {
|
||||
console.warn(`[Amily2Bus] Plugin '${pluginName}' is already registered.`);
|
||||
} else {
|
||||
this.registry.add(pluginName);
|
||||
console.log(`[Amily2Bus] Plugin registered: ${pluginName}`);
|
||||
}
|
||||
|
||||
// 返回该插件专属的 API 上下文 (Capability Token)
|
||||
return {
|
||||
// 绑定了身份的日志接口
|
||||
log: (origin, type, message) => {
|
||||
if (this.Logger) {
|
||||
// 自动填充 plugin 参数
|
||||
this.Logger.log(pluginName, origin, type, message);
|
||||
}
|
||||
},
|
||||
|
||||
// 绑定了身份的文件接口
|
||||
file: {
|
||||
read: (path) => {
|
||||
return this.FilePipe ? this.FilePipe.read(pluginName, path) : null;
|
||||
},
|
||||
write: (path, data) => {
|
||||
return this.FilePipe ? this.FilePipe.write(pluginName, path, data) : false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 挂载全局单例
|
||||
if (!window.Amily2Bus) {
|
||||
window.Amily2Bus = new Amily2Bus();
|
||||
}
|
||||
|
||||
export default Amily2Bus;
|
||||
3
SL/bus/README.md
Normal file
3
SL/bus/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Amily2Bus
|
||||
|
||||
Amily2总线类,提供基础的文件操作方法和标准日志操作方法,便于开发者使用和规范化日志处理。同时提供了插件注册和事件监听机制。
|
||||
61
SL/bus/file/FilePipe.js
Normal file
61
SL/bus/file/FilePipe.js
Normal file
@@ -0,0 +1,61 @@
|
||||
class FilePipe {
|
||||
constructor() {
|
||||
this.name = "FilePipe";
|
||||
// 模拟的根存储路径,实际环境可能对应 IndexedDB 的 Store Name 或 Node 的 baseDir
|
||||
this.basePath = "/virtual_fs/";
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全路径解析与校验
|
||||
* @param {string} plugin 插件名称(命名空间)
|
||||
* @param {string} relativePath 相对路径
|
||||
* @returns {string|null} 合法的绝对路径,如果违规则返回 null
|
||||
*/
|
||||
_resolvePath(plugin, relativePath) {
|
||||
if (!plugin || typeof plugin !== 'string') {
|
||||
console.error(`[FilePipe] Security Error: Invalid plugin identity.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 简单防越权:禁止包含 ".."
|
||||
if (relativePath.includes('..')) {
|
||||
console.error(`[FilePipe] Security Error: Directory traversal attempt blocked for plugin '${plugin}'. Path: ${relativePath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 强制限定在插件目录下
|
||||
// 格式: /virtual_fs/PluginName/filename
|
||||
return `${this.basePath}${plugin}/${relativePath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件
|
||||
* @param {string} plugin 调用方插件名
|
||||
* @param {string} path 文件相对路径
|
||||
*/
|
||||
async read(plugin, path) {
|
||||
const safePath = this._resolvePath(plugin, path);
|
||||
if (!safePath) return null;
|
||||
|
||||
console.log(`[FilePipe] Reading from: ${safePath}`);
|
||||
// TODO: Implement actual file reading logic
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入文件
|
||||
* @param {string} plugin 调用方插件名
|
||||
* @param {string} path 文件相对路径
|
||||
* @param {any} data 数据
|
||||
*/
|
||||
async write(plugin, path, data) {
|
||||
const safePath = this._resolvePath(plugin, path);
|
||||
if (!safePath) return false;
|
||||
|
||||
console.log(`[FilePipe] Writing to: ${safePath}`);
|
||||
// TODO: Implement actual file writing logic
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default FilePipe;
|
||||
137
SL/bus/log/Logger.js
Normal file
137
SL/bus/log/Logger.js
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 日志总类,用于记录日志信息
|
||||
*/
|
||||
class Logger {
|
||||
|
||||
static LOG_HEADER_DEBUG = '[DEBUG]';
|
||||
static LOG_HEADER_INFO = '[INFO]';
|
||||
static LOG_HEADER_WARN = '[WARN]';
|
||||
static LOG_HEADER_ERROR = '[ERROR]';
|
||||
|
||||
static LEVEL_WEIGHTS = {
|
||||
'debug': 0,
|
||||
'info': 1,
|
||||
'warn': 2,
|
||||
'error': 3
|
||||
};
|
||||
|
||||
constructor() {
|
||||
// 全局默认级别
|
||||
this.globalLevel = 'info';
|
||||
|
||||
// 针对特定插件或模块的配置
|
||||
// 结构示例:
|
||||
// {
|
||||
// "PluginA": "debug", // PluginA 下所有模块默认为 debug
|
||||
// "PluginB::ModuleX": "error" // 仅 PluginB 下的 ModuleX 为 error
|
||||
// }
|
||||
this.levelConfig = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日志级别
|
||||
* @param {string} target 目标范围,可以是 'Global'、'PluginName' 或 'PluginName::ModuleName'
|
||||
* @param {string} level 'debug' | 'info' | 'warn' | 'error'
|
||||
*/
|
||||
setLevel(target, level) {
|
||||
if (!Logger.LEVEL_WEIGHTS.hasOwnProperty(level)) {
|
||||
console.warn(`[Logger] Invalid log level: ${level}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target === 'Global') {
|
||||
this.globalLevel = level;
|
||||
console.log(`[Logger] Global log level set to: ${level}`);
|
||||
} else {
|
||||
this.levelConfig[target] = level;
|
||||
console.log(`[Logger] Log level for '${target}' set to: ${level}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定上下文的生效日志级别(级联查找)
|
||||
* @param {string} plugin
|
||||
* @param {string} origin (Module)
|
||||
*/
|
||||
_getEffectiveLevel(plugin, origin) {
|
||||
// 1. 检查精确匹配 "Plugin::Module"
|
||||
const specificKey = `${plugin}::${origin}`;
|
||||
if (this.levelConfig.hasOwnProperty(specificKey)) {
|
||||
return this.levelConfig[specificKey];
|
||||
}
|
||||
|
||||
// 2. 检查插件级匹配 "Plugin"
|
||||
if (this.levelConfig.hasOwnProperty(plugin)) {
|
||||
return this.levelConfig[plugin];
|
||||
}
|
||||
|
||||
// 3. 返回全局默认
|
||||
return this.globalLevel;
|
||||
}
|
||||
|
||||
log(plugin, origin, type, message, inFile = false) {
|
||||
// 获取当前上下文生效的日志级别权重
|
||||
const effectiveLevelName = this._getEffectiveLevel(plugin, origin);
|
||||
const currentWeight = Logger.LEVEL_WEIGHTS[effectiveLevelName];
|
||||
|
||||
const weight = Logger.LEVEL_WEIGHTS[type] !== undefined ? Logger.LEVEL_WEIGHTS[type] : 1;
|
||||
|
||||
// 级别筛选
|
||||
if (weight < currentWeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
// 格式: [12:00:00] [PluginName::ClassName] [INFO]: message
|
||||
const fullMessage = `[${timestamp}] [${plugin}::${origin}] [${type.toUpperCase()}]: ${message}`;
|
||||
|
||||
// 1. Console Output
|
||||
switch (type) {
|
||||
case 'debug':
|
||||
console.debug(fullMessage);
|
||||
break;
|
||||
case 'info':
|
||||
console.info(fullMessage);
|
||||
break;
|
||||
case 'warn':
|
||||
console.warn(fullMessage);
|
||||
break;
|
||||
case 'error':
|
||||
console.error(fullMessage);
|
||||
break;
|
||||
default:
|
||||
console.log(fullMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
// 2. File Output (via FilePipe)
|
||||
if (inFile) {
|
||||
// Logger 自身也需要作为系统组件注册,获取写入权限
|
||||
// 注意:为了性能,建议在 constructor 里注册一次保存起来,这里为了逻辑展示暂时简化
|
||||
if (!this.sysBus) {
|
||||
if (window.Amily2Bus && window.Amily2Bus.register) {
|
||||
this.sysBus = window.Amily2Bus.register('SystemLogger');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.sysBus && this.sysBus.file) {
|
||||
// 使用注册后的安全接口写入,无需再手动传 'SystemLogger'
|
||||
// TODO: 这里的路径 'runtime.log' 后续可以做成配置项
|
||||
this.sysBus.file.write('runtime.log', fullMessage + '\n');
|
||||
} else {
|
||||
// Fallback: 如果总线未就绪,仅在控制台警告一次,避免死循环
|
||||
if (!this._warned) {
|
||||
console.warn('[Logger] FilePipe system not linked. Log not saved to file.');
|
||||
this._warned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ensure Amily2Bus namespace exists to prevent crash if loaded out of order
|
||||
window.Amily2Bus = window.Amily2Bus || {};
|
||||
window.Amily2Bus.Logger = new Logger();
|
||||
|
||||
export default Logger;
|
||||
Reference in New Issue
Block a user