初始提交 - FutureOSS v1.0 插件化运行时框架

一切皆为插件的开发者工具运行时框架

🧩 核心特性:
  - 插件热插拔 (importlib 动态加载)
  - 依赖自动解析 (拓扑排序 + 循环检测)
  - 企业级稳定 (熔断/降级/重试/隔离)
  - 事件驱动 (发布/订阅事件总线)
  - 完整配置 (YAML 配置 + 热重载)
This commit is contained in:
Falck
2026-04-06 09:57:10 +08:00
commit 76147bae94
174 changed files with 15626 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
/**
* OSS Community - 实时轮询系统
* 每2秒从数据库刷新所有数据
*/
class CommunityPollingSystem {
constructor() {
this.interval = 2000;
this.timer = null;
this.isRunning = false;
this.listeners = { user: [], posts: [], stats: [], categories: [] };
this.cache = { user: null, posts: null, stats: null, categories: null };
}
start() {
if (this.isRunning) return;
this.isRunning = true;
this._fetchAll();
this.timer = setInterval(() => this._fetchAll(), this.interval);
}
stop() {
clearInterval(this.timer);
this.isRunning = false;
this.timer = null;
}
on(event, callback) {
if (this.listeners[event]) this.listeners[event].push(callback);
}
async _fetch(url) {
try {
const res = await fetch(url);
return res.ok ? await res.json() : null;
} catch { return null; }
}
async _fetchAll() {
// 并行请求所有接口
const [userData, postsData, statsData, catsData] = await Promise.all([
this._fetch('api/auth.php?action=current-user'),
this._fetch('api/index.php?action=posts'),
this._fetch('api/index.php?action=stats'),
this._fetch('api/index.php?action=categories')
]);
// 用户数据
if (userData && userData.success) {
const changed = JSON.stringify(userData) !== JSON.stringify(this.cache.user);
this.cache.user = userData;
if (changed) this._notify('user', userData);
}
// 帖子数据(含 views, likes, replies
if (postsData && postsData.posts) {
const changed = JSON.stringify(postsData.posts) !== JSON.stringify(this.cache.posts);
this.cache.posts = postsData.posts;
if (changed) this._notify('posts', postsData);
}
// 统计数据(含 hot_posts
if (statsData) {
const changed = JSON.stringify(statsData) !== JSON.stringify(this.cache.stats);
this.cache.stats = statsData;
if (changed) this._notify('stats', statsData);
}
// 分类
if (catsData && catsData.categories) {
const changed = JSON.stringify(catsData.categories) !== JSON.stringify(this.cache.categories);
this.cache.categories = catsData.categories;
if (changed) this._notify('categories', catsData);
}
}
_notify(event, data) {
(this.listeners[event] || []).forEach(fn => {
try { fn(data); } catch(e) {}
});
}
}
window.Polling = new CommunityPollingSystem();
document.addEventListener('DOMContentLoaded', () => {
const dock = document.getElementById('dock');
if (dock && dock.dataset.loggedIn === '1') {
window.Polling.start();
}
});