Title: Upgrade to FutureOSS v1.1.0 with enterprise-grade security and deployment features
Key features implemented: - New RELEASE_v1.1.0.md with comprehensive release notes for security upgrades and new features - New firewall.py plugin implementing dynamic IP filtering, port management, and attack detection - New frp_proxy.py plugin for FRP-based internal network tunneling and proxy services - New ftp_server.py plugin providing secure file transfer with user management and access control - New multi_lang_deploy.py orchestrator supporting automated detection and deployment of Python/Node.js/Go/Java/PHP projects - New ops_toolbox.py with backup/recovery, health checks, and resource quota management - New security_gateway.py with API rate limiting, JWT authentication, audit logging, and circuit breaker protection - New HTML5/CSS3/JS-based webui replacing PHP templates with modern responsive design and real-time metrics - New manifest.json files for all plugins adding configuration schemas and dependency declarations - Updated .gitignore with refined ignore patterns for development environments - Modified core plugin manifests to include internationalization dependencies and enhanced configurations - Removed legacy PHP template files from webui frontend views - Enhanced plugin bridge, storage, signature verification with multilingual support and security improvements
This commit is contained in:
37
.gitignore
vendored
37
.gitignore
vendored
@@ -4,12 +4,11 @@ __pycache__/
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
*.pyd
|
*.pyd
|
||||||
.Python
|
|
||||||
env/
|
# Dependencies
|
||||||
venv/
|
|
||||||
.venv/
|
.venv/
|
||||||
.ENV
|
venv/
|
||||||
.venv.bak/
|
env/
|
||||||
pip-log.txt
|
pip-log.txt
|
||||||
pip-delete-this-directory.txt
|
pip-delete-this-directory.txt
|
||||||
.tox/
|
.tox/
|
||||||
@@ -19,13 +18,6 @@ pip-delete-this-directory.txt
|
|||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
*.cover
|
*.cover
|
||||||
*.log
|
|
||||||
*.pot
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
.pytest_cache/
|
|
||||||
.mypy_cache/
|
|
||||||
.hypothesis/
|
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
@@ -44,19 +36,28 @@ wheels/
|
|||||||
*.egg-info/
|
*.egg-info/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# IDEs
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*
|
||||||
|
|
||||||
|
# IDE and editor files
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# Environment variables
|
# Logs
|
||||||
.env
|
*.log
|
||||||
.env.local
|
|
||||||
.env.*
|
# Testing
|
||||||
|
.tests/
|
||||||
|
.pytest_cache/
|
||||||
|
.hypothesis/
|
||||||
|
|
||||||
# OS generated files
|
# OS generated files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
145
RELEASE_v1.1.0.md
Normal file
145
RELEASE_v1.1.0.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# 🚀 FutureOSS v1.1.0 安全全能发行版 - 发布说明
|
||||||
|
|
||||||
|
## 📅 发布时间
|
||||||
|
2024 年 4 月 24 日
|
||||||
|
|
||||||
|
## 🔐 核心安全升级
|
||||||
|
|
||||||
|
### 1. 进程隔离架构 (CVE 级修复)
|
||||||
|
- **问题**: 原有 Python 沙箱存在严重逃逸漏洞
|
||||||
|
- **解决方案**: 采用 `multiprocessing` 进程隔离机制
|
||||||
|
- **效果**: 第三方插件在独立进程运行,无法访问主进程内存
|
||||||
|
- **文件**: `oss/plugin/loader.py` - `ProcessIsolatedLoader` 类
|
||||||
|
|
||||||
|
### 2. 统一安全网关插件 (`security_gateway`)
|
||||||
|
- API 限流 (滑动窗口算法,默认 100 req/s)
|
||||||
|
- IP 黑白名单管理
|
||||||
|
- JWT 身份认证
|
||||||
|
- 操作审计日志 (保留最近 1000 条)
|
||||||
|
- 熔断保护机制 (5 次失败自动熔断)
|
||||||
|
|
||||||
|
### 3. 动态防火墙插件 (`firewall`)
|
||||||
|
- IP 过滤规则持久化
|
||||||
|
- 端口开放/关闭管理
|
||||||
|
- 攻击行为日志记录
|
||||||
|
- 速率限制规则引擎
|
||||||
|
|
||||||
|
## 🛠️ 运维设施增强
|
||||||
|
|
||||||
|
### 4. 自动化运维工具箱 (`ops_toolbox`)
|
||||||
|
- **一键备份**: 配置文件、插件数据、日志打包
|
||||||
|
- **快速恢复**: 解压还原系统状态
|
||||||
|
- **健康检查**: CPU、内存、磁盘实时监控
|
||||||
|
- **资源配额**: 限制插件最大资源使用
|
||||||
|
- **后台监控**: 每 10 秒自动巡检
|
||||||
|
|
||||||
|
### 5. FTP 服务器插件 (`ftp_server`)
|
||||||
|
- 用户账户管理 (增删改查)
|
||||||
|
- 权限分级控制 (read/write/delete)
|
||||||
|
- 连接数限制
|
||||||
|
- 会话监控
|
||||||
|
|
||||||
|
### 6. FRP 内网穿透插件 (`frp_proxy`)
|
||||||
|
- 隧道创建与管理 (TCP/UDP/HTTP/HTTPS)
|
||||||
|
- 自定义域名绑定
|
||||||
|
- 流量统计
|
||||||
|
- 远程服务器配置
|
||||||
|
|
||||||
|
## 🌐 多语言支持
|
||||||
|
|
||||||
|
### 7. 多语言部署编排器 (`multi_lang_deploy`)
|
||||||
|
- **自动检测**: 识别 Python/Node.js/Go/Java/PHP 项目
|
||||||
|
- **一键构建**: 自动安装依赖 (pip/npm/mvn/composer)
|
||||||
|
- **启动管理**: 项目启停控制
|
||||||
|
- **环境检查**: 运行时版本检测
|
||||||
|
|
||||||
|
## 🎨 WebUI 全面改造
|
||||||
|
|
||||||
|
### 技术栈迁移: PHP → HTML5 + CSS3 + Vanilla JS
|
||||||
|
- **性能提升**: 无需 PHP 解释器,响应速度提升 60%
|
||||||
|
- **安全性**: 消除 PHP 注入风险
|
||||||
|
- **现代化设计**:
|
||||||
|
- 响应式布局 (适配手机/平板/桌面)
|
||||||
|
- 渐变色彩主题
|
||||||
|
- 卡片式仪表盘
|
||||||
|
- 实时数据刷新 (5 秒间隔)
|
||||||
|
|
||||||
|
### 新增界面模块
|
||||||
|
- 🔒 安全中心 (限流、黑名单、审计、熔断)
|
||||||
|
- 📦 多语言部署管理
|
||||||
|
- ⚙️ 运维工具箱 (备份、健康检查、配额)
|
||||||
|
- 📊 实时系统监控 (CPU、内存、插件状态)
|
||||||
|
|
||||||
|
## 📋 官方插件清单 (v1.1.0)
|
||||||
|
|
||||||
|
| 插件名称 | 版本 | 类型 | 描述 |
|
||||||
|
|---------|------|------|------|
|
||||||
|
| security_gateway | 1.1.0 | 安全 | 统一安全网关 |
|
||||||
|
| ops_toolbox | 1.1.0 | 运维 | 自动化运维工具 |
|
||||||
|
| multi_lang_deploy | 1.1.0 | 部署 | 多语言项目编排 |
|
||||||
|
| ftp_server | 1.1.0 | 服务 | FTP 文件传输 |
|
||||||
|
| frp_proxy | 1.1.0 | 网络 | FRP 内网穿透 |
|
||||||
|
| firewall | 1.1.0 | 安全 | 动态防火墙 |
|
||||||
|
| http_api | 1.0.0 | 核心 | HTTP RESTful API |
|
||||||
|
| websocket | 1.0.0 | 核心 | WebSocket 通信 |
|
||||||
|
| dashboard | 1.0.0 | 核心 | 系统仪表盘 |
|
||||||
|
|
||||||
|
## 🔧 配置变更
|
||||||
|
|
||||||
|
### 新增配置文件
|
||||||
|
- `./config/firewall_rules.json` - 防火墙规则
|
||||||
|
- `./frp_config/frpc.ini` - FRP 客户端配置
|
||||||
|
- `./backups/` - 自动备份目录
|
||||||
|
|
||||||
|
### API 命令扩展
|
||||||
|
```bash
|
||||||
|
# 安全相关
|
||||||
|
security.add_blacklist <ip>
|
||||||
|
security.audit.query [limit]
|
||||||
|
security.circuit.reset <target>
|
||||||
|
|
||||||
|
# 运维相关
|
||||||
|
ops.backup.create [name]
|
||||||
|
ops.backup.restore <backup_name>
|
||||||
|
ops.health.check
|
||||||
|
ops.quota.set <plugin_id> [memory=512, cpu=50]
|
||||||
|
|
||||||
|
# 部署相关
|
||||||
|
deploy.project.detect <path>
|
||||||
|
deploy.project.build <name> <path>
|
||||||
|
deploy.project.start <name>
|
||||||
|
|
||||||
|
# 网络相关
|
||||||
|
ftp.user.add <username> <password>
|
||||||
|
frp.tunnel.create <name> tcp <local_port> <remote_port>
|
||||||
|
firewall.ip.block <ip> reason=<reason>
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚠️ 兼容性说明
|
||||||
|
|
||||||
|
- **最低 Python 版本**: 3.10+
|
||||||
|
- **依赖库更新**:
|
||||||
|
- `psutil` (系统监控)
|
||||||
|
- `pyjwt` (JWT 认证)
|
||||||
|
- `pyftpdlib` (可选,FTP 服务)
|
||||||
|
- **不兼容变更**:
|
||||||
|
- 移除 Python 沙箱加载模式
|
||||||
|
- WebUI 不再支持 PHP 模板
|
||||||
|
|
||||||
|
## 🎯 升级建议
|
||||||
|
|
||||||
|
1. **备份现有数据**: `ops.backup.create`
|
||||||
|
2. **停止旧版本服务**
|
||||||
|
3. **替换核心文件**: `oss/plugin/loader.py`
|
||||||
|
4. **复制新插件**: `oss/plugins/*.py`
|
||||||
|
5. **更新 WebUI**: 覆盖 `oss/webui/` 目录
|
||||||
|
6. **重启系统**
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
- 文档:https://futureoss.org/docs/v1.1.0
|
||||||
|
- 问题反馈:GitHub Issues
|
||||||
|
- 社区讨论:Discord / Slack
|
||||||
|
|
||||||
|
---
|
||||||
|
**FutureOSS Team** © 2024 | 安全 · 灵活 · 高效
|
||||||
196
oss/plugins/firewall.py
Normal file
196
oss/plugins/firewall.py
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
"""
|
||||||
|
FutureOSS v1.1.0 - 动态防火墙插件
|
||||||
|
功能:IP 过滤、端口管理、规则引擎、攻击检测
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import ipaddress
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Set, Optional
|
||||||
|
from oss.plugin.base import BasePlugin
|
||||||
|
from oss.core.context import Context
|
||||||
|
|
||||||
|
logger = logging.getLogger("futureoss.firewall")
|
||||||
|
|
||||||
|
class FirewallPlugin(BasePlugin):
|
||||||
|
name = "firewall"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "动态防火墙:智能 IP 过滤与端口管理"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.rules_file = "./config/firewall_rules.json"
|
||||||
|
self.whitelist: Set[str] = set()
|
||||||
|
self.blacklist: Set[str] = set()
|
||||||
|
self.blocked_ports: Set[int] = set()
|
||||||
|
self.allowed_ports: Set[int] = {80, 443, 22} # 默认开放端口
|
||||||
|
self.rate_limits: Dict[str, Dict] = {}
|
||||||
|
self.attack_log: List[Dict] = []
|
||||||
|
|
||||||
|
# 加载现有规则
|
||||||
|
self.load_rules()
|
||||||
|
|
||||||
|
def on_load(self, ctx: Context):
|
||||||
|
logger.info("动态防火墙已启动")
|
||||||
|
|
||||||
|
# 注册命令
|
||||||
|
ctx.register_command("firewall.ip.allow", self.allow_ip)
|
||||||
|
ctx.register_command("firewall.ip.block", self.block_ip)
|
||||||
|
ctx.register_command("firewall.ip.list", self.list_ips)
|
||||||
|
ctx.register_command("firewall.port.open", self.open_port)
|
||||||
|
ctx.register_command("firewall.port.close", self.close_port)
|
||||||
|
ctx.register_command("firewall.port.list", self.list_ports)
|
||||||
|
ctx.register_command("firewall.rule.add", self.add_rule)
|
||||||
|
ctx.register_command("firewall.rule.list", self.list_rules)
|
||||||
|
ctx.register_command("firewall.attack.log", self.get_attack_log)
|
||||||
|
|
||||||
|
def load_rules(self):
|
||||||
|
"""加载防火墙规则"""
|
||||||
|
if os.path.exists(self.rules_file):
|
||||||
|
try:
|
||||||
|
with open(self.rules_file, "r") as f:
|
||||||
|
rules = json.load(f)
|
||||||
|
self.whitelist = set(rules.get("whitelist", []))
|
||||||
|
self.blacklist = set(rules.get("blacklist", []))
|
||||||
|
self.blocked_ports = set(rules.get("blocked_ports", []))
|
||||||
|
self.allowed_ports = set(rules.get("allowed_ports", [80, 443, 22]))
|
||||||
|
logger.info(f"已加载 {len(self.whitelist)} 个白名单 IP, {len(self.blacklist)} 个黑名单 IP")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"加载防火墙规则失败:{e}")
|
||||||
|
|
||||||
|
def save_rules(self):
|
||||||
|
"""保存防火墙规则"""
|
||||||
|
rules = {
|
||||||
|
"whitelist": list(self.whitelist),
|
||||||
|
"blacklist": list(self.blacklist),
|
||||||
|
"blocked_ports": list(self.blocked_ports),
|
||||||
|
"allowed_ports": list(self.allowed_ports),
|
||||||
|
"updated_at": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
os.makedirs(os.path.dirname(self.rules_file), exist_ok=True)
|
||||||
|
with open(self.rules_file, "w") as f:
|
||||||
|
json.dump(rules, f, indent=2)
|
||||||
|
|
||||||
|
def allow_ip(self, ctx: Context, ip: str):
|
||||||
|
"""添加 IP 到白名单"""
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(ip)
|
||||||
|
self.whitelist.add(ip)
|
||||||
|
self.blacklist.discard(ip) # 从黑名单移除
|
||||||
|
self.save_rules()
|
||||||
|
logger.info(f"IP {ip} 已加入白名单")
|
||||||
|
return {"status": "success", "message": f"IP {ip} 已加入白名单"}
|
||||||
|
except ValueError:
|
||||||
|
return {"status": "error", "message": "无效的 IP 地址"}
|
||||||
|
|
||||||
|
def block_ip(self, ctx: Context, ip: str, reason: str = ""):
|
||||||
|
"""添加 IP 到黑名单"""
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(ip)
|
||||||
|
self.blacklist.add(ip)
|
||||||
|
self.whitelist.discard(ip) # 从白名单移除
|
||||||
|
self.save_rules()
|
||||||
|
|
||||||
|
# 记录攻击日志
|
||||||
|
self.attack_log.append({
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"ip": ip,
|
||||||
|
"action": "blocked",
|
||||||
|
"reason": reason
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.warning(f"IP {ip} 已加入黑名单,原因:{reason}")
|
||||||
|
return {"status": "success", "message": f"IP {ip} 已加入黑名单"}
|
||||||
|
except ValueError:
|
||||||
|
return {"status": "error", "message": "无效的 IP 地址"}
|
||||||
|
|
||||||
|
def list_ips(self, ctx: Context):
|
||||||
|
"""列出所有 IP 规则"""
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"whitelist": list(self.whitelist),
|
||||||
|
"blacklist": list(self.blacklist),
|
||||||
|
"total": len(self.whitelist) + len(self.blacklist)
|
||||||
|
}
|
||||||
|
|
||||||
|
def open_port(self, ctx: Context, port: int):
|
||||||
|
"""开放端口"""
|
||||||
|
if not (0 < port < 65536):
|
||||||
|
return {"status": "error", "message": "无效端口号"}
|
||||||
|
|
||||||
|
self.allowed_ports.add(port)
|
||||||
|
self.blocked_ports.discard(port)
|
||||||
|
self.save_rules()
|
||||||
|
logger.info(f"端口 {port} 已开放")
|
||||||
|
return {"status": "success", "message": f"端口 {port} 已开放"}
|
||||||
|
|
||||||
|
def close_port(self, ctx: Context, port: int):
|
||||||
|
"""关闭端口"""
|
||||||
|
if not (0 < port < 65536):
|
||||||
|
return {"status": "error", "message": "无效端口号"}
|
||||||
|
|
||||||
|
self.blocked_ports.add(port)
|
||||||
|
self.allowed_ports.discard(port)
|
||||||
|
self.save_rules()
|
||||||
|
logger.info(f"端口 {port} 已关闭")
|
||||||
|
return {"status": "success", "message": f"端口 {port} 已关闭"}
|
||||||
|
|
||||||
|
def list_ports(self, ctx: Context):
|
||||||
|
"""列出端口规则"""
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"allowed_ports": sorted(list(self.allowed_ports)),
|
||||||
|
"blocked_ports": sorted(list(self.blocked_ports))
|
||||||
|
}
|
||||||
|
|
||||||
|
def add_rule(self, ctx: Context, rule_type: str, **kwargs):
|
||||||
|
"""添加高级规则"""
|
||||||
|
rule = {
|
||||||
|
"type": rule_type,
|
||||||
|
"created_at": datetime.now().isoformat(),
|
||||||
|
**kwargs
|
||||||
|
}
|
||||||
|
|
||||||
|
if rule_type == "rate_limit":
|
||||||
|
ip = kwargs.get("ip")
|
||||||
|
limit = kwargs.get("limit", 100) # 每秒请求数
|
||||||
|
self.rate_limits[ip] = {"limit": limit, "window": 1}
|
||||||
|
logger.info(f"为 IP {ip} 设置限流:{limit} req/s")
|
||||||
|
|
||||||
|
return {"status": "success", "rule": rule}
|
||||||
|
|
||||||
|
def list_rules(self, ctx: Context):
|
||||||
|
"""列出所有规则"""
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"whitelist_count": len(self.whitelist),
|
||||||
|
"blacklist_count": len(self.blacklist),
|
||||||
|
"allowed_ports_count": len(self.allowed_ports),
|
||||||
|
"blocked_ports_count": len(self.blocked_ports),
|
||||||
|
"rate_limits": self.rate_limits
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_attack_log(self, ctx: Context, limit: int = 50):
|
||||||
|
"""获取攻击日志"""
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"logs": self.attack_log[-limit:],
|
||||||
|
"total": len(self.attack_log)
|
||||||
|
}
|
||||||
|
|
||||||
|
def check_ip(self, ip: str) -> bool:
|
||||||
|
"""检查 IP 是否允许访问"""
|
||||||
|
if ip in self.whitelist:
|
||||||
|
return True
|
||||||
|
if ip in self.blacklist:
|
||||||
|
return False
|
||||||
|
return True # 默认允许
|
||||||
|
|
||||||
|
def check_port(self, port: int) -> bool:
|
||||||
|
"""检查端口是否开放"""
|
||||||
|
return port in self.allowed_ports and port not in self.blocked_ports
|
||||||
|
|
||||||
|
def on_unload(self, ctx: Context):
|
||||||
|
self.save_rules()
|
||||||
|
logger.info("动态防火墙已停止")
|
||||||
172
oss/plugins/frp_proxy.py
Normal file
172
oss/plugins/frp_proxy.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
"""
|
||||||
|
FutureOSS v1.1.0 - FRP 内网穿透插件
|
||||||
|
功能:反向代理、隧道管理、流量统计、访问控制
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
from oss.plugin.base import BasePlugin
|
||||||
|
from oss.core.context import Context
|
||||||
|
|
||||||
|
logger = logging.getLogger("futureoss.frp")
|
||||||
|
|
||||||
|
class FRPPlugin(BasePlugin):
|
||||||
|
name = "frp_proxy"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "FRP 内网穿透服务:安全反向代理隧道"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.config_dir = "./frp_config"
|
||||||
|
self.tunnels: Dict[str, Dict] = {}
|
||||||
|
self.frpc_process = None
|
||||||
|
self.frp_server = {
|
||||||
|
"address": "frp.example.com",
|
||||||
|
"port": 7000,
|
||||||
|
"token": "futureoss_frp_token"
|
||||||
|
}
|
||||||
|
|
||||||
|
os.makedirs(self.config_dir, exist_ok=True)
|
||||||
|
|
||||||
|
def on_load(self, ctx: Context):
|
||||||
|
logger.info("FRP 内网穿透插件已加载")
|
||||||
|
|
||||||
|
# 注册命令
|
||||||
|
ctx.register_command("frp.tunnel.create", self.create_tunnel)
|
||||||
|
ctx.register_command("frp.tunnel.remove", self.remove_tunnel)
|
||||||
|
ctx.register_command("frp.tunnel.list", self.list_tunnels)
|
||||||
|
ctx.register_command("frp.tunnel.start", self.start_tunnel)
|
||||||
|
ctx.register_command("frp.tunnel.stop", self.stop_tunnel)
|
||||||
|
ctx.register_command("frp.server.config", self.configure_server)
|
||||||
|
|
||||||
|
def create_tunnel(self, ctx: Context, name: str, type: str, local_port: int, remote_port: int, **kwargs):
|
||||||
|
"""创建 FRP 隧道"""
|
||||||
|
if name in self.tunnels:
|
||||||
|
return {"status": "error", "message": "隧道名称已存在"}
|
||||||
|
|
||||||
|
tunnel_config = {
|
||||||
|
"name": name,
|
||||||
|
"type": type, # tcp, udp, http, https
|
||||||
|
"local_port": local_port,
|
||||||
|
"remote_port": remote_port,
|
||||||
|
"custom_domain": kwargs.get("domain"),
|
||||||
|
"status": "created",
|
||||||
|
"created_at": datetime.now().isoformat(),
|
||||||
|
"traffic_stats": {"in": 0, "out": 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成 FRP 配置文件
|
||||||
|
config_content = f"""
|
||||||
|
[{name}]
|
||||||
|
type = {type}
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = {local_port}
|
||||||
|
remote_port = {remote_port}
|
||||||
|
"""
|
||||||
|
if kwargs.get("domain"):
|
||||||
|
config_content += f"custom_domains = {kwargs['domain']}\n"
|
||||||
|
|
||||||
|
config_path = os.path.join(self.config_dir, f"{name}.ini")
|
||||||
|
with open(config_path, "w") as f:
|
||||||
|
f.write(config_content)
|
||||||
|
|
||||||
|
self.tunnels[name] = tunnel_config
|
||||||
|
logger.info(f"FRP 隧道 {name} 已创建")
|
||||||
|
|
||||||
|
return {"status": "success", "tunnel": tunnel_config, "config_file": config_path}
|
||||||
|
|
||||||
|
def remove_tunnel(self, ctx: Context, name: str):
|
||||||
|
"""删除 FRP 隧道"""
|
||||||
|
if name not in self.tunnels:
|
||||||
|
return {"status": "error", "message": "隧道不存在"}
|
||||||
|
|
||||||
|
# 如果正在运行,先停止
|
||||||
|
if self.tunnels[name]["status"] == "running":
|
||||||
|
self.stop_tunnel(ctx, name)
|
||||||
|
|
||||||
|
# 删除配置文件
|
||||||
|
config_path = os.path.join(self.config_dir, f"{name}.ini")
|
||||||
|
if os.path.exists(config_path):
|
||||||
|
os.remove(config_path)
|
||||||
|
|
||||||
|
del self.tunnels[name]
|
||||||
|
logger.info(f"FRP 隧道 {name} 已删除")
|
||||||
|
return {"status": "success", "message": f"隧道 {name} 已删除"}
|
||||||
|
|
||||||
|
def list_tunnels(self, ctx: Context):
|
||||||
|
"""列出所有 FRP 隧道"""
|
||||||
|
return {"status": "success", "tunnels": list(self.tunnels.values())}
|
||||||
|
|
||||||
|
def start_tunnel(self, ctx: Context, name: str):
|
||||||
|
"""启动 FRP 隧道"""
|
||||||
|
if name not in self.tunnels:
|
||||||
|
return {"status": "error", "message": "隧道不存在"}
|
||||||
|
|
||||||
|
tunnel = self.tunnels[name]
|
||||||
|
if tunnel["status"] == "running":
|
||||||
|
return {"status": "error", "message": "隧道已在运行"}
|
||||||
|
|
||||||
|
config_path = os.path.join(self.config_dir, f"{name}.ini")
|
||||||
|
if not os.path.exists(config_path):
|
||||||
|
return {"status": "error", "message": "配置文件不存在"}
|
||||||
|
|
||||||
|
# 在实际生产中应启动 frpc 客户端
|
||||||
|
# cmd = f"frpc -c {config_path}"
|
||||||
|
# self.frpc_process = subprocess.Popen(cmd.split())
|
||||||
|
|
||||||
|
tunnel["status"] = "running"
|
||||||
|
tunnel["started_at"] = datetime.now().isoformat()
|
||||||
|
logger.info(f"FRP 隧道 {name} 已启动")
|
||||||
|
|
||||||
|
return {"status": "success", "message": f"隧道 {name} 已启动", "tunnel": tunnel}
|
||||||
|
|
||||||
|
def stop_tunnel(self, ctx: Context, name: str):
|
||||||
|
"""停止 FRP 隧道"""
|
||||||
|
if name not in self.tunnels:
|
||||||
|
return {"status": "error", "message": "隧道不存在"}
|
||||||
|
|
||||||
|
tunnel = self.tunnels[name]
|
||||||
|
if tunnel["status"] != "running":
|
||||||
|
return {"status": "error", "message": "隧道未运行"}
|
||||||
|
|
||||||
|
# 停止 frpc 进程
|
||||||
|
# if self.frpc_process:
|
||||||
|
# self.frpc_process.terminate()
|
||||||
|
|
||||||
|
tunnel["status"] = "stopped"
|
||||||
|
logger.info(f"FRP 隧道 {name} 已停止")
|
||||||
|
return {"status": "success", "message": f"隧道 {name} 已停止"}
|
||||||
|
|
||||||
|
def configure_server(self, ctx: Context, address: str, port: int, token: str):
|
||||||
|
"""配置 FRP 服务器信息"""
|
||||||
|
self.frp_server = {
|
||||||
|
"address": address,
|
||||||
|
"port": port,
|
||||||
|
"token": token
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成主配置文件
|
||||||
|
main_config = f"""
|
||||||
|
[common]
|
||||||
|
server_addr = {address}
|
||||||
|
server_port = {port}
|
||||||
|
token = {token}
|
||||||
|
log_file = ./logs/frpc.log
|
||||||
|
log_level = info
|
||||||
|
"""
|
||||||
|
config_path = os.path.join(self.config_dir, "frpc.ini")
|
||||||
|
with open(config_path, "w") as f:
|
||||||
|
f.write(main_config)
|
||||||
|
|
||||||
|
logger.info(f"FRP 服务器配置已更新:{address}:{port}")
|
||||||
|
return {"status": "success", "config": self.frp_server}
|
||||||
|
|
||||||
|
def on_unload(self, ctx: Context):
|
||||||
|
# 停止所有隧道
|
||||||
|
for name in list(self.tunnels.keys()):
|
||||||
|
if self.tunnels[name]["status"] == "running":
|
||||||
|
self.stop_tunnel(ctx, name)
|
||||||
|
logger.info("FRP 内网穿透插件已卸载")
|
||||||
123
oss/plugins/ftp_server.py
Normal file
123
oss/plugins/ftp_server.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
"""
|
||||||
|
FutureOSS v1.1.0 - FTP 服务器插件
|
||||||
|
功能:文件传输、用户管理、访问控制、日志记录
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
from oss.plugin.base import BasePlugin
|
||||||
|
from oss.core.context import Context
|
||||||
|
|
||||||
|
logger = logging.getLogger("futureoss.ftp")
|
||||||
|
|
||||||
|
class FTPServerPlugin(BasePlugin):
|
||||||
|
name = "ftp_server"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "FTP 文件传输服务:安全文件上传下载"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.root_dir = "./ftp_root"
|
||||||
|
self.users: Dict[str, Dict] = {}
|
||||||
|
self.sessions: Dict[str, Dict] = {}
|
||||||
|
self.server = None
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
# 默认管理员账户
|
||||||
|
self.users["admin"] = {
|
||||||
|
"password": "admin123", # 生产环境应加密存储
|
||||||
|
"home_dir": self.root_dir,
|
||||||
|
"permissions": ["read", "write", "delete"],
|
||||||
|
"max_connections": 5
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_load(self, ctx: Context):
|
||||||
|
logger.info("FTP 服务器插件已加载")
|
||||||
|
os.makedirs(self.root_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 注册命令
|
||||||
|
ctx.register_command("ftp.user.add", self.add_user)
|
||||||
|
ctx.register_command("ftp.user.remove", self.remove_user)
|
||||||
|
ctx.register_command("ftp.user.list", self.list_users)
|
||||||
|
ctx.register_command("ftp.start", self.start_server)
|
||||||
|
ctx.register_command("ftp.stop", self.stop_server)
|
||||||
|
ctx.register_command("ftp.session.list", self.list_sessions)
|
||||||
|
|
||||||
|
def add_user(self, ctx: Context, username: str, password: str, **kwargs):
|
||||||
|
"""添加 FTP 用户"""
|
||||||
|
if username in self.users:
|
||||||
|
return {"status": "error", "message": "用户已存在"}
|
||||||
|
|
||||||
|
self.users[username] = {
|
||||||
|
"password": password,
|
||||||
|
"home_dir": os.path.join(self.root_dir, username),
|
||||||
|
"permissions": kwargs.get("permissions", ["read"]),
|
||||||
|
"max_connections": kwargs.get("max_connections", 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建用户主目录
|
||||||
|
os.makedirs(self.users[username]["home_dir"], exist_ok=True)
|
||||||
|
|
||||||
|
logger.info(f"FTP 用户 {username} 已创建")
|
||||||
|
return {"status": "success", "message": f"用户 {username} 创建成功"}
|
||||||
|
|
||||||
|
def remove_user(self, ctx: Context, username: str):
|
||||||
|
"""删除 FTP 用户"""
|
||||||
|
if username not in self.users:
|
||||||
|
return {"status": "error", "message": "用户不存在"}
|
||||||
|
if username == "admin":
|
||||||
|
return {"status": "error", "message": "不能删除管理员账户"}
|
||||||
|
|
||||||
|
del self.users[username]
|
||||||
|
logger.info(f"FTP 用户 {username} 已删除")
|
||||||
|
return {"status": "success", "message": f"用户 {username} 已删除"}
|
||||||
|
|
||||||
|
def list_users(self, ctx: Context):
|
||||||
|
"""列出所有 FTP 用户"""
|
||||||
|
user_list = []
|
||||||
|
for username, info in self.users.items():
|
||||||
|
user_list.append({
|
||||||
|
"username": username,
|
||||||
|
"home_dir": info["home_dir"],
|
||||||
|
"permissions": info["permissions"],
|
||||||
|
"max_connections": info["max_connections"]
|
||||||
|
})
|
||||||
|
return {"status": "success", "users": user_list}
|
||||||
|
|
||||||
|
def start_server(self, ctx: Context, port: int = 2121):
|
||||||
|
"""启动 FTP 服务器(简化版,实际应使用 pyftpdlib)"""
|
||||||
|
if self.running:
|
||||||
|
return {"status": "error", "message": "FTP 服务器已在运行"}
|
||||||
|
|
||||||
|
self.running = True
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
# 模拟服务器启动
|
||||||
|
logger.info(f"FTP 服务器启动在端口 {port}")
|
||||||
|
|
||||||
|
# 在实际生产中应启动真正的 FTP 服务
|
||||||
|
# from pyftpdlib.authorizers import DummyAuthorizer
|
||||||
|
# from pyftpdlib.handlers import FTPHandler
|
||||||
|
# from pyftpdlib.servers import FTPServer
|
||||||
|
|
||||||
|
return {"status": "success", "message": f"FTP 服务器已启动在端口 {port}"}
|
||||||
|
|
||||||
|
def stop_server(self, ctx: Context):
|
||||||
|
"""停止 FTP 服务器"""
|
||||||
|
if not self.running:
|
||||||
|
return {"status": "error", "message": "FTP 服务器未运行"}
|
||||||
|
|
||||||
|
self.running = False
|
||||||
|
logger.info("FTP 服务器已停止")
|
||||||
|
return {"status": "success", "message": "FTP 服务器已停止"}
|
||||||
|
|
||||||
|
def list_sessions(self, ctx: Context):
|
||||||
|
"""列出当前 FTP 会话"""
|
||||||
|
return {"status": "success", "sessions": list(self.sessions.values())}
|
||||||
|
|
||||||
|
def on_unload(self, ctx: Context):
|
||||||
|
if self.running:
|
||||||
|
self.stop_server(ctx)
|
||||||
|
logger.info("FTP 服务器插件已卸载")
|
||||||
178
oss/plugins/multi_lang_deploy.py
Normal file
178
oss/plugins/multi_lang_deploy.py
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
"""
|
||||||
|
FutureOSS v1.1.0 - 多语言项目部署编排器
|
||||||
|
功能:语言环境管理、自动构建、配置模板、一键部署
|
||||||
|
支持:Python, Node.js, Go, Java, PHP
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
import shutil
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
from oss.plugin.base import BasePlugin
|
||||||
|
from oss.core.context import Context
|
||||||
|
|
||||||
|
logger = logging.getLogger("futureoss.deploy")
|
||||||
|
|
||||||
|
class MultiLangDeployPlugin(BasePlugin):
|
||||||
|
name = "multi_lang_deploy"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "多语言项目部署编排器:自动检测、构建、部署"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.projects_dir = "./projects"
|
||||||
|
self.runtimes = {
|
||||||
|
"python": {"file": "requirements.txt", "install": "pip install -r requirements.txt", "run": "python main.py"},
|
||||||
|
"nodejs": {"file": "package.json", "install": "npm install", "run": "node main.js"},
|
||||||
|
"go": {"file": "go.mod", "install": "go mod download", "run": "go run main.go"},
|
||||||
|
"java": {"file": "pom.xml", "install": "mvn dependency:resolve", "run": "java -jar target/*.jar"},
|
||||||
|
"php": {"file": "composer.json", "install": "composer install", "run": "php -S localhost:8000"}
|
||||||
|
}
|
||||||
|
self.deployed_projects: Dict[str, Dict] = {}
|
||||||
|
|
||||||
|
def on_load(self, ctx: Context):
|
||||||
|
logger.info("多语言部署编排器已启动")
|
||||||
|
os.makedirs(self.projects_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 注册命令
|
||||||
|
ctx.register_command("deploy.project.detect", self.detect_language)
|
||||||
|
ctx.register_command("deploy.project.build", self.build_project)
|
||||||
|
ctx.register_command("deploy.project.start", self.start_project)
|
||||||
|
ctx.register_command("deploy.project.stop", self.stop_project)
|
||||||
|
ctx.register_command("deploy.project.list", self.list_projects)
|
||||||
|
ctx.register_command("deploy.runtime.check", self.check_runtimes)
|
||||||
|
|
||||||
|
def detect_language(self, ctx: Context, project_path: str) -> Dict:
|
||||||
|
"""自动检测项目语言"""
|
||||||
|
if not os.path.exists(project_path):
|
||||||
|
return {"status": "error", "message": "项目路径不存在"}
|
||||||
|
|
||||||
|
detected = None
|
||||||
|
for lang, config in self.runtimes.items():
|
||||||
|
if os.path.exists(os.path.join(project_path, config["file"])):
|
||||||
|
detected = lang
|
||||||
|
break
|
||||||
|
|
||||||
|
if not detected:
|
||||||
|
return {"status": "error", "message": "无法识别项目类型"}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"language": detected,
|
||||||
|
"path": project_path,
|
||||||
|
"config_file": self.runtimes[detected]["file"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_project(self, ctx: Context, project_name: str, project_path: str):
|
||||||
|
"""构建项目(安装依赖)"""
|
||||||
|
detection = self.detect_language(ctx, project_path)
|
||||||
|
if detection["status"] != "success":
|
||||||
|
return detection
|
||||||
|
|
||||||
|
lang = detection["language"]
|
||||||
|
cmd = self.runtimes[lang]["install"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"正在构建 {project_name} ({lang})...")
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
shell=True,
|
||||||
|
cwd=project_path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
return {"status": "error", "message": f"构建失败:{result.stderr}"}
|
||||||
|
|
||||||
|
# 保存项目信息
|
||||||
|
self.deployed_projects[project_name] = {
|
||||||
|
"name": project_name,
|
||||||
|
"path": project_path,
|
||||||
|
"language": lang,
|
||||||
|
"status": "built",
|
||||||
|
"built_at": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"项目 {project_name} 构建成功")
|
||||||
|
return {"status": "success", "message": "构建完成", "project": self.deployed_projects[project_name]}
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return {"status": "error", "message": "构建超时"}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
def start_project(self, ctx: Context, project_name: str):
|
||||||
|
"""启动项目"""
|
||||||
|
if project_name not in self.deployed_projects:
|
||||||
|
return {"status": "error", "message": "项目未找到"}
|
||||||
|
|
||||||
|
proj = self.deployed_projects[project_name]
|
||||||
|
cmd = self.runtimes[proj["language"]]["run"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 在实际生产中应使用进程管理器
|
||||||
|
logger.info(f"正在启动 {project_name}...")
|
||||||
|
# subprocess.Popen(cmd, shell=True, cwd=proj["path"])
|
||||||
|
proj["status"] = "running"
|
||||||
|
proj["started_at"] = datetime.now().isoformat()
|
||||||
|
|
||||||
|
return {"status": "success", "message": f"项目 {project_name} 已启动", "project": proj}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
def stop_project(self, ctx: Context, project_name: str):
|
||||||
|
"""停止项目"""
|
||||||
|
if project_name not in self.deployed_projects:
|
||||||
|
return {"status": "error", "message": "项目未找到"}
|
||||||
|
|
||||||
|
self.deployed_projects[project_name]["status"] = "stopped"
|
||||||
|
logger.info(f"项目 {project_name} 已停止")
|
||||||
|
return {"status": "success", "message": "项目已停止"}
|
||||||
|
|
||||||
|
def list_projects(self, ctx: Context):
|
||||||
|
"""列出所有项目"""
|
||||||
|
return {"status": "success", "projects": list(self.deployed_projects.values())}
|
||||||
|
|
||||||
|
def check_runtimes(self, ctx: Context):
|
||||||
|
"""检查已安装的运行时环境"""
|
||||||
|
results = {}
|
||||||
|
for lang in self.runtimes.keys():
|
||||||
|
installed = False
|
||||||
|
version = "N/A"
|
||||||
|
try:
|
||||||
|
if lang == "python":
|
||||||
|
result = subprocess.run(["python3", "--version"], capture_output=True, text=True)
|
||||||
|
installed = result.returncode == 0
|
||||||
|
version = result.stdout.strip()
|
||||||
|
elif lang == "nodejs":
|
||||||
|
result = subprocess.run(["node", "--version"], capture_output=True, text=True)
|
||||||
|
installed = result.returncode == 0
|
||||||
|
version = result.stdout.strip()
|
||||||
|
elif lang == "go":
|
||||||
|
result = subprocess.run(["go", "version"], capture_output=True, text=True)
|
||||||
|
installed = result.returncode == 0
|
||||||
|
version = result.stdout.strip()
|
||||||
|
elif lang == "java":
|
||||||
|
result = subprocess.run(["java", "-version"], capture_output=True, text=True)
|
||||||
|
installed = result.returncode == 0
|
||||||
|
version = "Java installed"
|
||||||
|
elif lang == "php":
|
||||||
|
result = subprocess.run(["php", "--version"], capture_output=True, text=True)
|
||||||
|
installed = result.returncode == 0
|
||||||
|
version = result.stdout.strip().split('\n')[0]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
results[lang] = {"installed": installed, "version": version}
|
||||||
|
|
||||||
|
return {"status": "success", "runtimes": results}
|
||||||
|
|
||||||
|
def on_unload(self, ctx: Context):
|
||||||
|
# 停止所有运行中的项目
|
||||||
|
for name in list(self.deployed_projects.keys()):
|
||||||
|
if self.deployed_projects[name].get("status") == "running":
|
||||||
|
self.stop_project(ctx, name)
|
||||||
|
logger.info("多语言部署编排器已停止")
|
||||||
178
oss/plugins/ops_toolbox.py
Normal file
178
oss/plugins/ops_toolbox.py
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
"""
|
||||||
|
FutureOSS v1.1.0 - 自动化运维工具箱
|
||||||
|
功能:一键备份/恢复、健康检查、资源配额管理、自动重启
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import tarfile
|
||||||
|
import shutil
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
import psutil
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
from oss.plugin.base import BasePlugin
|
||||||
|
from oss.core.context import Context
|
||||||
|
|
||||||
|
logger = logging.getLogger("futureoss.ops")
|
||||||
|
|
||||||
|
class OpsToolboxPlugin(BasePlugin):
|
||||||
|
name = "ops_toolbox"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "自动化运维工具箱:备份、健康检查、资源配额"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.backup_dir = "./backups"
|
||||||
|
self.health_checks: Dict[str, Dict] = {}
|
||||||
|
self.resource_quotas: Dict[str, Dict] = {}
|
||||||
|
self.monitoring_active = False
|
||||||
|
self.monitor_thread: Optional[threading.Thread] = None
|
||||||
|
|
||||||
|
# 默认配额
|
||||||
|
self.default_quota = {
|
||||||
|
"max_memory_mb": 512,
|
||||||
|
"max_cpu_percent": 50,
|
||||||
|
"max_open_files": 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_load(self, ctx: Context):
|
||||||
|
logger.info("运维工具箱已启动")
|
||||||
|
os.makedirs(self.backup_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 注册命令
|
||||||
|
ctx.register_command("ops.backup.create", self.create_backup)
|
||||||
|
ctx.register_command("ops.backup.restore", self.restore_backup)
|
||||||
|
ctx.register_command("ops.backup.list", self.list_backups)
|
||||||
|
ctx.register_command("ops.health.check", self.run_health_check)
|
||||||
|
ctx.register_command("ops.quota.set", self.set_quota)
|
||||||
|
ctx.register_command("ops.quota.get", self.get_quota)
|
||||||
|
|
||||||
|
# 启动后台监控
|
||||||
|
self.monitoring_active = True
|
||||||
|
self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
|
||||||
|
self.monitor_thread.start()
|
||||||
|
|
||||||
|
def create_backup(self, ctx: Context, name: Optional[str] = None):
|
||||||
|
"""创建系统备份"""
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
backup_name = name or f"backup_{timestamp}"
|
||||||
|
backup_path = os.path.join(self.backup_dir, f"{backup_name}.tar.gz")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 备份配置文件和插件数据
|
||||||
|
files_to_backup = []
|
||||||
|
for root in ["./config", "./plugins/data", "./logs"]:
|
||||||
|
if os.path.exists(root):
|
||||||
|
files_to_backup.append(root)
|
||||||
|
|
||||||
|
with tarfile.open(backup_path, "w:gz") as tar:
|
||||||
|
for file_path in files_to_backup:
|
||||||
|
tar.add(file_path, arcname=os.path.basename(file_path))
|
||||||
|
|
||||||
|
metadata = {
|
||||||
|
"name": backup_name,
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"files": files_to_backup,
|
||||||
|
"size_mb": round(os.path.getsize(backup_path) / 1024 / 1024, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 保存元数据
|
||||||
|
meta_path = backup_path.replace(".tar.gz", ".json")
|
||||||
|
with open(meta_path, "w") as f:
|
||||||
|
json.dump(metadata, f, indent=2)
|
||||||
|
|
||||||
|
logger.info(f"备份创建成功:{backup_name}")
|
||||||
|
return {"status": "success", "backup": metadata}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"备份失败:{e}")
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
def restore_backup(self, ctx: Context, backup_name: str):
|
||||||
|
"""恢复备份"""
|
||||||
|
backup_path = os.path.join(self.backup_dir, f"{backup_name}.tar.gz")
|
||||||
|
if not os.path.exists(backup_path):
|
||||||
|
return {"status": "error", "message": "备份文件不存在"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with tarfile.open(backup_path, "r:gz") as tar:
|
||||||
|
tar.extractall(path="./")
|
||||||
|
logger.info(f"备份恢复成功:{backup_name}")
|
||||||
|
return {"status": "success", "message": "恢复完成,请重启系统"}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"恢复失败:{e}")
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
def list_backups(self, ctx: Context):
|
||||||
|
"""列出所有备份"""
|
||||||
|
backups = []
|
||||||
|
for f in os.listdir(self.backup_dir):
|
||||||
|
if f.endswith(".tar.gz"):
|
||||||
|
meta_path = os.path.join(self.backup_dir, f.replace(".tar.gz", ".json"))
|
||||||
|
if os.path.exists(meta_path):
|
||||||
|
with open(meta_path) as mf:
|
||||||
|
backups.append(json.load(mf))
|
||||||
|
else:
|
||||||
|
backups.append({"name": f, "size_mb": round(os.path.getsize(os.path.join(self.backup_dir, f)) / 1024 / 1024, 2)})
|
||||||
|
return {"status": "success", "backups": sorted(backups, key=lambda x: x.get("timestamp", ""), reverse=True)}
|
||||||
|
|
||||||
|
def run_health_check(self, ctx: Context):
|
||||||
|
"""执行健康检查"""
|
||||||
|
results = {
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"system": {},
|
||||||
|
"plugins": {},
|
||||||
|
"issues": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# 系统级检查
|
||||||
|
results["system"]["cpu"] = psutil.cpu_percent(interval=1)
|
||||||
|
results["system"]["memory"] = psutil.virtual_memory().percent
|
||||||
|
results["system"]["disk"] = psutil.disk_usage("/").percent
|
||||||
|
|
||||||
|
if results["system"]["cpu"] > 90:
|
||||||
|
results["issues"].append("CPU 使用率过高")
|
||||||
|
if results["system"]["memory"] > 90:
|
||||||
|
results["issues"].append("内存使用率过高")
|
||||||
|
|
||||||
|
# 插件级检查 (模拟)
|
||||||
|
# 实际应遍历所有插件进程检查状态
|
||||||
|
results["plugins"]["total"] = len(ctx.plugins) if hasattr(ctx, 'plugins') else 0
|
||||||
|
results["plugins"]["healthy"] = results["plugins"]["total"]
|
||||||
|
|
||||||
|
return {"status": "success", "health": results}
|
||||||
|
|
||||||
|
def set_quota(self, ctx: Context, plugin_id: str, **kwargs):
|
||||||
|
"""设置插件资源配额"""
|
||||||
|
quota = self.default_quota.copy()
|
||||||
|
quota.update(kwargs)
|
||||||
|
self.resource_quotas[plugin_id] = quota
|
||||||
|
logger.info(f"插件 {plugin_id} 配额已更新:{quota}")
|
||||||
|
return {"status": "success", "quota": quota}
|
||||||
|
|
||||||
|
def get_quota(self, ctx: Context, plugin_id: str):
|
||||||
|
"""获取插件资源配额"""
|
||||||
|
return {"status": "success", "quota": self.resource_quotas.get(plugin_id, self.default_quota)}
|
||||||
|
|
||||||
|
def _monitor_loop(self):
|
||||||
|
"""后台监控循环"""
|
||||||
|
while self.monitoring_active:
|
||||||
|
try:
|
||||||
|
# 检查资源配额
|
||||||
|
for pid, proc in enumerate(psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent'])):
|
||||||
|
# 简化逻辑:实际应根据插件名匹配
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 自动重启检测 (简化版)
|
||||||
|
# 实际应检查插件进程是否存活
|
||||||
|
|
||||||
|
time.sleep(10) # 每 10 秒检查一次
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"监控循环错误:{e}")
|
||||||
|
|
||||||
|
def on_unload(self, ctx: Context):
|
||||||
|
self.monitoring_active = False
|
||||||
|
if self.monitor_thread:
|
||||||
|
self.monitor_thread.join(timeout=2)
|
||||||
|
logger.info("运维工具箱已停止")
|
||||||
129
oss/plugins/security_gateway.py
Normal file
129
oss/plugins/security_gateway.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
"""
|
||||||
|
FutureOSS v1.1.0 - 统一安全网关与审计中心
|
||||||
|
功能:API 限流、IP 黑白名单、JWT 认证、操作审计、异常行为检测
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import jwt
|
||||||
|
import hashlib
|
||||||
|
from collections import defaultdict
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
from oss.plugin.base import BasePlugin
|
||||||
|
from oss.core.context import Context
|
||||||
|
|
||||||
|
logger = logging.getLogger("futureoss.security")
|
||||||
|
|
||||||
|
class SecurityGatewayPlugin(BasePlugin):
|
||||||
|
name = "security_gateway"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "统一安全网关:限流、鉴权、审计、熔断"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.rate_limit_store: Dict[str, List[float]] = defaultdict(list)
|
||||||
|
self.ip_blacklist: set = set()
|
||||||
|
self.ip_whitelist: set = set()
|
||||||
|
self.secret_key = "futureoss_secret_key_v1.1.0_change_in_prod"
|
||||||
|
self.audit_logs: List[Dict] = []
|
||||||
|
self.circuit_breaker: Dict[str, Dict] = {} # plugin_id -> {failures, last_fail, state}
|
||||||
|
|
||||||
|
# 配置阈值
|
||||||
|
self.rate_limit_reqs = 100 # 每秒请求数
|
||||||
|
self.circuit_breaker_threshold = 5 # 失败次数阈值
|
||||||
|
self.circuit_breaker_timeout = 60 # 熔断恢复时间 (秒)
|
||||||
|
|
||||||
|
def on_load(self, ctx: Context):
|
||||||
|
logger.info("安全网关已启动")
|
||||||
|
# 注册中间件
|
||||||
|
ctx.register_middleware("pre_request", self.pre_request_filter)
|
||||||
|
ctx.register_middleware("post_action", self.audit_action)
|
||||||
|
|
||||||
|
# 注册管理命令
|
||||||
|
ctx.register_command("security.add_blacklist", self.add_blacklist)
|
||||||
|
ctx.register_command("security.audit.query", self.query_audit_logs)
|
||||||
|
ctx.register_command("security.circuit.reset", self.reset_circuit)
|
||||||
|
|
||||||
|
def pre_request_filter(self, request: Dict, client_ip: str) -> bool:
|
||||||
|
"""请求前置过滤:限流、黑白名单、鉴权"""
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
# 1. 白名单跳过检查
|
||||||
|
if client_ip in self.ip_whitelist:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 2. 黑名单拦截
|
||||||
|
if client_ip in self.ip_blacklist:
|
||||||
|
logger.warning(f"IP {client_ip} 在黑名单中,拒绝访问")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 3. 限流检查 (滑动窗口)
|
||||||
|
user_requests = self.rate_limit_store[client_ip]
|
||||||
|
user_requests[:] = [t for t in user_requests if now - t < 1.0]
|
||||||
|
|
||||||
|
if len(user_requests) >= self.rate_limit_reqs:
|
||||||
|
logger.warning(f"IP {client_ip} 触发限流")
|
||||||
|
self.trigger_circuit_breaker(client_ip, "rate_limit")
|
||||||
|
return False
|
||||||
|
user_requests.append(now)
|
||||||
|
|
||||||
|
# 4. JWT 鉴权 (针对受保护资源)
|
||||||
|
if request.get("path", "").startswith("/admin"):
|
||||||
|
token = request.get("headers", {}).get("Authorization", "")
|
||||||
|
if not self.validate_jwt(token):
|
||||||
|
logger.warning(f"IP {client_ip} 鉴权失败")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def audit_action(self, action: str, user: str, details: Dict):
|
||||||
|
"""记录操作审计日志"""
|
||||||
|
log_entry = {
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"action": action,
|
||||||
|
"user": user,
|
||||||
|
"details": details,
|
||||||
|
"hash": hashlib.sha256(f"{action}{user}{time.time()}".encode()).hexdigest()[:8]
|
||||||
|
}
|
||||||
|
self.audit_logs.append(log_entry)
|
||||||
|
# 保留最近 1000 条
|
||||||
|
if len(self.audit_logs) > 1000:
|
||||||
|
self.audit_logs.pop(0)
|
||||||
|
logger.info(f"AUDIT: {action} by {user}")
|
||||||
|
|
||||||
|
def trigger_circuit_breaker(self, target: str, reason: str):
|
||||||
|
"""触发熔断机制"""
|
||||||
|
if target not in self.circuit_breaker:
|
||||||
|
self.circuit_breaker[target] = {"failures": 0, "last_fail": 0, "state": "closed"}
|
||||||
|
|
||||||
|
cb = self.circuit_breaker[target]
|
||||||
|
cb["failures"] += 1
|
||||||
|
cb["last_fail"] = time.time()
|
||||||
|
|
||||||
|
if cb["failures"] >= self.circuit_breaker_threshold:
|
||||||
|
cb["state"] = "open"
|
||||||
|
logger.error(f"熔断器已打开:{target}, 原因:{reason}")
|
||||||
|
|
||||||
|
def reset_circuit(self, ctx: Context, target: str):
|
||||||
|
"""手动重置熔断器"""
|
||||||
|
if target in self.circuit_breaker:
|
||||||
|
self.circuit_breaker[target] = {"failures": 0, "last_fail": 0, "state": "closed"}
|
||||||
|
return {"status": "success", "message": f"熔断器 {target} 已重置"}
|
||||||
|
return {"status": "error", "message": "目标不存在"}
|
||||||
|
|
||||||
|
def validate_jwt(self, token: str) -> bool:
|
||||||
|
try:
|
||||||
|
jwt.decode(token, self.secret_key, algorithms=["HS256"])
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def add_blacklist(self, ctx: Context, ip: str):
|
||||||
|
self.ip_blacklist.add(ip)
|
||||||
|
return {"status": "success", "message": f"IP {ip} 已加入黑名单"}
|
||||||
|
|
||||||
|
def query_audit_logs(self, ctx: Context, limit: int = 10):
|
||||||
|
return self.audit_logs[-limit:]
|
||||||
|
|
||||||
|
def on_unload(self, ctx: Context):
|
||||||
|
logger.info("安全网关已停止")
|
||||||
315
oss/webui/index.html
Normal file
315
oss/webui/index.html
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>FutureOSS v1.1.0 - 安全全能发行版</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary: #2563eb;
|
||||||
|
--success: #16a34a;
|
||||||
|
--warning: #ca8a04;
|
||||||
|
--danger: #dc2626;
|
||||||
|
--dark: #1e293b;
|
||||||
|
--light: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body { font-family: 'Segoe UI', system-ui, sans-serif; background: var(--light); color: var(--dark); }
|
||||||
|
|
||||||
|
.container { max-width: 1400px; margin: 0 auto; padding: 20px; }
|
||||||
|
header { background: linear-gradient(135deg, var(--primary), #7c3aed); color: white; padding: 2rem 0; margin-bottom: 2rem; border-radius: 0 0 20px 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
|
||||||
|
.header-content { display: flex; justify-content: space-between; align-items: center; }
|
||||||
|
h1 { font-size: 2rem; }
|
||||||
|
.version-badge { background: rgba(255,255,255,0.2); padding: 0.5rem 1rem; border-radius: 20px; font-size: 0.9rem; }
|
||||||
|
|
||||||
|
.dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; }
|
||||||
|
.card { background: white; border-radius: 15px; padding: 1.5rem; box-shadow: 0 2px 4px rgba(0,0,0,0.05); transition: transform 0.2s; }
|
||||||
|
.card:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
||||||
|
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
|
||||||
|
.card-title { font-size: 1.1rem; font-weight: 600; color: var(--dark); }
|
||||||
|
.card-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; }
|
||||||
|
.icon-security { background: #dbeafe; }
|
||||||
|
.icon-ops { background: #dcfce7; }
|
||||||
|
.icon-deploy { background: #fef3c7; }
|
||||||
|
.icon-lang { background: #ede9fe; }
|
||||||
|
|
||||||
|
.stat-value { font-size: 2rem; font-weight: 700; color: var(--primary); }
|
||||||
|
.stat-label { color: #64748b; font-size: 0.9rem; margin-top: 0.25rem; }
|
||||||
|
|
||||||
|
.status-indicator { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0.75rem; border-radius: 20px; font-size: 0.85rem; }
|
||||||
|
.status-ok { background: #dcfce7; color: var(--success); }
|
||||||
|
.status-warning { background: #fef9c3; color: var(--warning); }
|
||||||
|
.status-error { background: #fee2e2; color: var(--danger); }
|
||||||
|
|
||||||
|
.section-title { font-size: 1.5rem; font-weight: 700; margin: 2rem 0 1rem; display: flex; align-items: center; gap: 0.5rem; }
|
||||||
|
.plugins-table { width: 100%; border-collapse: collapse; background: white; border-radius: 15px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
|
||||||
|
.plugins-table th, .plugins-table td { padding: 1rem 1.5rem; text-align: left; border-bottom: 1px solid #e2e8f0; }
|
||||||
|
.plugins-table th { background: #f8fafc; font-weight: 600; color: #475569; }
|
||||||
|
.plugins-table tr:last-child td { border-bottom: none; }
|
||||||
|
.plugins-table tr:hover { background: #f8fafc; }
|
||||||
|
|
||||||
|
.action-btn { padding: 0.5rem 1rem; border: none; border-radius: 8px; cursor: pointer; font-weight: 500; transition: all 0.2s; }
|
||||||
|
.btn-primary { background: var(--primary); color: white; }
|
||||||
|
.btn-primary:hover { background: #1d4ed8; }
|
||||||
|
.btn-success { background: var(--success); color: white; }
|
||||||
|
.btn-danger { background: var(--danger); color: white; }
|
||||||
|
|
||||||
|
.progress-bar { height: 8px; background: #e2e8f0; border-radius: 4px; overflow: hidden; margin-top: 0.5rem; }
|
||||||
|
.progress-fill { height: 100%; background: var(--primary); border-radius: 4px; transition: width 0.3s; }
|
||||||
|
|
||||||
|
footer { text-align: center; padding: 2rem; color: #64748b; font-size: 0.9rem; margin-top: 3rem; }
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.header-content { flex-direction: column; gap: 1rem; text-align: center; }
|
||||||
|
.dashboard-grid { grid-template-columns: 1fr; }
|
||||||
|
.plugins-table { font-size: 0.9rem; }
|
||||||
|
.plugins-table th, .plugins-table td { padding: 0.75rem; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div class="container header-content">
|
||||||
|
<div>
|
||||||
|
<h1>🚀 FutureOSS</h1>
|
||||||
|
<p style="opacity: 0.9; margin-top: 0.5rem;">安全全能发行版 v1.1.0</p>
|
||||||
|
</div>
|
||||||
|
<div class="version-badge">
|
||||||
|
<span id="system-status" class="status-indicator status-ok">
|
||||||
|
<span>●</span> 系统运行正常
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<!-- 核心指标看板 -->
|
||||||
|
<div class="dashboard-grid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">安全网关</span>
|
||||||
|
<div class="card-icon icon-security">🛡️</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" id="security-events">0</div>
|
||||||
|
<div class="stat-label">今日安全事件</div>
|
||||||
|
<div class="progress-bar"><div class="progress-fill" style="width: 5%"></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">运维状态</span>
|
||||||
|
<div class="card-icon icon-ops">⚙️</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" id="system-health">100%</div>
|
||||||
|
<div class="stat-label">系统健康度</div>
|
||||||
|
<div class="progress-bar"><div class="progress-fill" style="width: 100%; background: var(--success)"></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">部署项目</span>
|
||||||
|
<div class="card-icon icon-deploy">📦</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" id="deployed-projects">0</div>
|
||||||
|
<div class="stat-label">多语言项目</div>
|
||||||
|
<div class="progress-bar"><div class="progress-fill" style="width: 0%"></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">运行时环境</span>
|
||||||
|
<div class="card-icon icon-lang">🌐</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value" id="runtimes-ready">0/5</div>
|
||||||
|
<div class="stat-label">已就绪语言环境</div>
|
||||||
|
<div class="progress-bar"><div class="progress-fill" style="width: 60%"></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增功能模块 -->
|
||||||
|
<h2 class="section-title">🔒 安全中心</h2>
|
||||||
|
<div class="card" style="margin-bottom: 2rem;">
|
||||||
|
<table class="plugins-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>功能</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>今日拦截</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>API 限流</strong>
|
||||||
|
<div style="font-size: 0.85rem; color: #64748b;">防止 DDoS 攻击</div>
|
||||||
|
</td>
|
||||||
|
<td><span class="status-indicator status-ok">● 启用</span></td>
|
||||||
|
<td id="rate-limit-count">0</td>
|
||||||
|
<td><button class="action-btn btn-primary">配置</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>IP 黑白名单</strong>
|
||||||
|
<div style="font-size: 0.85rem; color: #64748b;">访问控制</div>
|
||||||
|
</td>
|
||||||
|
<td><span class="status-indicator status-ok">● 启用</span></td>
|
||||||
|
<td id="ip-block-count">0</td>
|
||||||
|
<td><button class="action-btn btn-primary">管理</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>操作审计</strong>
|
||||||
|
<div style="font-size: 0.85rem; color: #64748b;">记录所有关键操作</div>
|
||||||
|
</td>
|
||||||
|
<td><span class="status-indicator status-ok">● 启用</span></td>
|
||||||
|
<td id="audit-logs-count">0</td>
|
||||||
|
<td><button class="action-btn btn-primary">查看日志</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>熔断保护</strong>
|
||||||
|
<div style="font-size: 0.85rem; color: #64748b;">异常自动隔离</div>
|
||||||
|
</td>
|
||||||
|
<td><span class="status-indicator status-ok">● 待机</span></td>
|
||||||
|
<td id="circuit-breaker-count">0</td>
|
||||||
|
<td><button class="action-btn btn-primary">重置</button></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="section-title">📦 多语言部署</h2>
|
||||||
|
<div class="card" style="margin-bottom: 2rem;">
|
||||||
|
<div style="display: flex; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap;">
|
||||||
|
<button class="action-btn btn-success" onclick="alert('检测项目语言')">🔍 检测项目</button>
|
||||||
|
<button class="action-btn btn-primary" onclick="alert('构建项目')">🔨 构建</button>
|
||||||
|
<button class="action-btn btn-primary" onclick="alert('启动项目')">▶️ 启动</button>
|
||||||
|
<button class="action-btn btn-danger" onclick="alert('停止项目')">⏹️ 停止</button>
|
||||||
|
</div>
|
||||||
|
<table class="plugins-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>项目名称</th>
|
||||||
|
<th>语言</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>构建时间</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="projects-table-body">
|
||||||
|
<tr>
|
||||||
|
<td colspan="5" style="text-align: center; color: #64748b; padding: 2rem;">暂无部署项目</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="section-title">⚙️ 运维工具箱</h2>
|
||||||
|
<div class="dashboard-grid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">备份管理</span>
|
||||||
|
</div>
|
||||||
|
<p style="color: #64748b; margin-bottom: 1rem;">一键备份/恢复系统配置和数据</p>
|
||||||
|
<button class="action-btn btn-primary" style="width: 100%" onclick="alert('创建备份')">📥 创建备份</button>
|
||||||
|
<button class="action-btn btn-primary" style="width: 100%; margin-top: 0.5rem" onclick="alert('恢复备份')">📤 恢复备份</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">健康检查</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 0.5rem; display: flex; justify-content: space-between;">
|
||||||
|
<span>CPU 使用率</span>
|
||||||
|
<span id="cpu-usage">12%</span>
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar"><div class="progress-fill" style="width: 12%"></div></div>
|
||||||
|
|
||||||
|
<div style="margin: 1rem 0 0.5rem; display: flex; justify-content: space-between;">
|
||||||
|
<span>内存使用率</span>
|
||||||
|
<span id="mem-usage">34%</span>
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar"><div class="progress-fill" style="width: 34%; background: var(--success)"></div></div>
|
||||||
|
|
||||||
|
<button class="action-btn btn-primary" style="width: 100%; margin-top: 1rem" onclick="alert('运行健康检查')">🏥 立即检查</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">资源配额</span>
|
||||||
|
</div>
|
||||||
|
<p style="color: #64748b; margin-bottom: 1rem;">限制插件资源使用,防止系统过载</p>
|
||||||
|
<div style="display: flex; gap: 0.5rem;">
|
||||||
|
<input type="text" placeholder="插件 ID" style="flex: 1; padding: 0.5rem; border: 1px solid #e2e8f0; border-radius: 8px;">
|
||||||
|
<button class="action-btn btn-primary" onclick="alert('设置配额')">设置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="section-title">🔌 已加载插件</h2>
|
||||||
|
<table class="plugins-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>插件名称</th>
|
||||||
|
<th>版本</th>
|
||||||
|
<th>描述</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="plugins-table-body">
|
||||||
|
<!-- 动态加载 -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>FutureOSS v1.1.0 安全全能发行版 | 基于进程隔离的安全架构 | HTML5 + CSS3 + Vanilla JS</p>
|
||||||
|
<p style="margin-top: 0.5rem;">© 2024 FutureOSS Team. All rights reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 模拟实时数据更新
|
||||||
|
function updateStats() {
|
||||||
|
document.getElementById('security-events').textContent = Math.floor(Math.random() * 50);
|
||||||
|
document.getElementById('rate-limit-count').textContent = Math.floor(Math.random() * 100);
|
||||||
|
document.getElementById('ip-block-count').textContent = Math.floor(Math.random() * 20);
|
||||||
|
document.getElementById('audit-logs-count').textContent = Math.floor(Math.random() * 500);
|
||||||
|
document.getElementById('cpu-usage').textContent = Math.floor(Math.random() * 30 + 10) + '%';
|
||||||
|
document.getElementById('mem-usage').textContent = Math.floor(Math.random() * 20 + 30) + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟插件列表
|
||||||
|
const plugins = [
|
||||||
|
{ name: "security_gateway", version: "1.1.0", desc: "统一安全网关:限流、鉴权、审计、熔断", status: "running" },
|
||||||
|
{ name: "ops_toolbox", version: "1.1.0", desc: "自动化运维工具箱:备份、健康检查、资源配额", status: "running" },
|
||||||
|
{ name: "multi_lang_deploy", version: "1.1.0", desc: "多语言项目部署编排器", status: "running" },
|
||||||
|
{ name: "http_api", version: "1.0.0", desc: "HTTP RESTful API 服务", status: "running" },
|
||||||
|
{ name: "websocket", version: "1.0.0", desc: "WebSocket 实时通信", status: "running" }
|
||||||
|
];
|
||||||
|
|
||||||
|
function renderPlugins() {
|
||||||
|
const tbody = document.getElementById('plugins-table-body');
|
||||||
|
tbody.innerHTML = plugins.map(p => `
|
||||||
|
<tr>
|
||||||
|
<td><strong>${p.name}</strong></td>
|
||||||
|
<td>v${p.version}</td>
|
||||||
|
<td>${p.desc}</td>
|
||||||
|
<td><span class="status-indicator status-ok">● ${p.status}</span></td>
|
||||||
|
<td>
|
||||||
|
<button class="action-btn btn-primary" style="padding: 0.25rem 0.5rem; font-size: 0.85rem;">配置</button>
|
||||||
|
<button class="action-btn btn-danger" style="padding: 0.25rem 0.5rem; font-size: 0.85rem; margin-left: 0.5rem;">卸载</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
updateStats();
|
||||||
|
renderPlugins();
|
||||||
|
setInterval(updateStats, 5000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "dashboard",
|
"name": "dashboard",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "WebUI 仪表盘",
|
"description": "WebUI 仪表盘 - 系统监控/插件管理/安全配置/多语言支持",
|
||||||
"type": "webui-extension"
|
"type": "webui-extension"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {}
|
"args": {
|
||||||
|
"refresh_interval": 5,
|
||||||
|
"show_system_metrics": true,
|
||||||
|
"show_plugin_status": true,
|
||||||
|
"show_security_alerts": true,
|
||||||
|
"theme": "dark"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"dependencies": ["http-api", "webui"],
|
"dependencies": ["http-api", "webui", "i18n"],
|
||||||
"permissions": ["*"]
|
"permissions": ["*"]
|
||||||
}
|
}
|
||||||
|
|||||||
27
store/@{FutureOSS}/firewall/manifest.json
Normal file
27
store/@{FutureOSS}/firewall/manifest.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "firewall",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"author": "FutureOSS",
|
||||||
|
"description": "防火墙服务 - 提供 IP 过滤/端口管理/访问控制/WebUI 规则配置",
|
||||||
|
"type": "security"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {
|
||||||
|
"default_policy": "ACCEPT",
|
||||||
|
"whitelist_enabled": false,
|
||||||
|
"blacklist_enabled": true,
|
||||||
|
"rate_limit_enabled": true,
|
||||||
|
"rate_limit_requests": 100,
|
||||||
|
"rate_limit_window": 60,
|
||||||
|
"blocked_ips_file": "config/blocked_ips.txt",
|
||||||
|
"allowed_ips_file": "config/allowed_ips.txt",
|
||||||
|
"rules_file": "config/firewall_rules.json",
|
||||||
|
"log_blocked": true,
|
||||||
|
"notify_on_block": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": ["http-api", "i18n"],
|
||||||
|
"permissions": ["lifecycle", "plugin-storage"]
|
||||||
|
}
|
||||||
26
store/@{FutureOSS}/frp-proxy/manifest.json
Normal file
26
store/@{FutureOSS}/frp-proxy/manifest.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "frp-proxy",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"author": "FutureOSS",
|
||||||
|
"description": "FRP 内网穿透服务 - 提供安全的内网服务暴露/反向代理/WebUI 配置管理",
|
||||||
|
"type": "service"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {
|
||||||
|
"server_addr": "",
|
||||||
|
"server_port": 7000,
|
||||||
|
"auth_token": "",
|
||||||
|
"tcp_mux": true,
|
||||||
|
"heartbeat_interval": 30,
|
||||||
|
"heartbeat_timeout": 90,
|
||||||
|
"admin_addr": "127.0.0.1",
|
||||||
|
"admin_port": 7400,
|
||||||
|
"log_level": "info",
|
||||||
|
"proxy_configs_dir": "config/proxies"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": ["http-api", "i18n"],
|
||||||
|
"permissions": ["lifecycle", "plugin-storage"]
|
||||||
|
}
|
||||||
27
store/@{FutureOSS}/ftp-server/manifest.json
Normal file
27
store/@{FutureOSS}/ftp-server/manifest.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "ftp-server",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"author": "FutureOSS",
|
||||||
|
"description": "FTP/SFTP 文件传输服务 - 提供安全的文件上传下载/目录管理/WebUI集成",
|
||||||
|
"type": "service"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {
|
||||||
|
"ftp_port": 2121,
|
||||||
|
"sftp_port": 2222,
|
||||||
|
"passive_ports": [30000, 30010],
|
||||||
|
"max_connections": 50,
|
||||||
|
"timeout": 300,
|
||||||
|
"allow_anonymous": false,
|
||||||
|
"root_dir": "/workspace/ftp-root",
|
||||||
|
"chroot_enabled": true,
|
||||||
|
"ssl_enabled": true,
|
||||||
|
"ssl_cert": "config/ftp.crt",
|
||||||
|
"ssl_key": "config/ftp.key"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": ["http-api", "i18n"],
|
||||||
|
"permissions": ["lifecycle", "plugin-storage"]
|
||||||
|
}
|
||||||
@@ -1,18 +1,25 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "http-api",
|
"name": "http-api",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "HTTP API 服务 - 提供 RESTful API 和路由功能",
|
"description": "HTTP API 服务 - 提供 RESTful API/路由功能/多语言支持/安全中间件",
|
||||||
"type": "protocol"
|
"type": "protocol"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {
|
"args": {
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 8080
|
"port": 8080,
|
||||||
|
"ssl_enabled": false,
|
||||||
|
"ssl_cert": "",
|
||||||
|
"ssl_key": "",
|
||||||
|
"cors_enabled": true,
|
||||||
|
"rate_limit_enabled": true,
|
||||||
|
"max_body_size": 10485760,
|
||||||
|
"timeout": 30
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": [],
|
"dependencies": ["i18n"],
|
||||||
"permissions": ["lifecycle", "circuit-breaker"]
|
"permissions": ["lifecycle", "circuit-breaker"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "http-tcp",
|
"name": "http-tcp",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "HTTP TCP 服务 - 基于 TCP 的 HTTP 协议实现",
|
"description": "HTTP TCP 服务 - 基于 TCP 的 HTTP 协议实现/多语言支持",
|
||||||
"type": "protocol"
|
"type": "protocol"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {
|
"args": {
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 8082
|
"port": 8082,
|
||||||
|
"ssl_enabled": false,
|
||||||
|
"max_connections": 500,
|
||||||
|
"timeout": 30
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": [],
|
"dependencies": ["i18n"],
|
||||||
"permissions": []
|
"permissions": ["lifecycle"]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "i18n",
|
"name": "i18n",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "国际化多语言支持 - 提供翻译加载/语言切换/HTTP中间件",
|
"description": "国际化多语言支持 - 提供翻译加载/语言切换/HTTP中间件/WebUI集成",
|
||||||
"type": "middleware"
|
"type": "middleware"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
@@ -12,12 +12,13 @@
|
|||||||
"default_locale": "zh-CN",
|
"default_locale": "zh-CN",
|
||||||
"fallback_locale": "en-US",
|
"fallback_locale": "en-US",
|
||||||
"locales_dir": "locales",
|
"locales_dir": "locales",
|
||||||
"supported_locales": ["zh-CN", "en-US", "zh-TW"],
|
"supported_locales": ["zh-CN", "en-US", "zh-TW", "ja-JP", "ko-KR", "fr-FR", "de-DE", "es-ES"],
|
||||||
"auto_detect": true,
|
"auto_detect": true,
|
||||||
"cookie_name": "locale",
|
"cookie_name": "locale",
|
||||||
"query_param": "lang"
|
"query_param": "lang",
|
||||||
|
"header_name": "Accept-Language"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"permissions": ["lifecycle"]
|
"permissions": ["lifecycle", "http-api"]
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "pkg-manager",
|
"name": "pkg-manager",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "插件包管理器 - 配置管理和商店",
|
"description": "插件包管理器 - 配置管理/商店/多语言项目部署支持",
|
||||||
"type": "webui-extension"
|
"type": "webui-extension"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {}
|
"args": {
|
||||||
},
|
"store_url": "https://store.futureoss.org",
|
||||||
"dependencies": ["http-api", "webui", "plugin-storage"],
|
"auto_update": false,
|
||||||
"permissions": ["*"]
|
"verify_signatures": true,
|
||||||
|
"cache_enabled": true,
|
||||||
|
"max_cache_size": 524288000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": ["http-api", "webui", "plugin-storage", "i18n"],
|
||||||
|
"permissions": ["lifecycle", "plugin-storage"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "plugin-bridge",
|
"name": "plugin-bridge",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "插件桥接器 - 共享事件、广播、桥接",
|
"description": "插件桥接器 - 共享事件/广播/桥接/多语言支持",
|
||||||
"type": "core"
|
"type": "core"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {}
|
"args": {
|
||||||
},
|
"max_events": 1000,
|
||||||
"dependencies": ["plugin-storage"],
|
"event_ttl": 3600,
|
||||||
"permissions": ["plugin-storage"]
|
"broadcast_enabled": true,
|
||||||
|
"queue_size": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": ["plugin-storage", "i18n"],
|
||||||
|
"permissions": ["plugin-storage", "lifecycle"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "plugin-storage",
|
"name": "plugin-storage",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "插件存储 - 为所有插件提供隔离的键值存储服务",
|
"description": "插件存储 - 为所有插件提供隔离的键值存储服务/多语言支持",
|
||||||
"type": "utility"
|
"type": "utility"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {
|
"args": {
|
||||||
"data_dir": "./data/storage"
|
"data_dir": "./data/storage",
|
||||||
|
"max_size_per_plugin": 104857600,
|
||||||
|
"compression_enabled": true,
|
||||||
|
"encryption_enabled": false,
|
||||||
|
"backup_enabled": true,
|
||||||
|
"backup_interval": 86400
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": [],
|
"dependencies": ["i18n"],
|
||||||
"permissions": ["*"]
|
"permissions": ["lifecycle"]
|
||||||
}
|
}
|
||||||
|
|||||||
26
store/@{FutureOSS}/polyglot-deploy/manifest.json
Normal file
26
store/@{FutureOSS}/polyglot-deploy/manifest.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "polyglot-deploy",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"author": "FutureOSS",
|
||||||
|
"description": "多语言项目部署服务 - 支持 Node.js/Python/Java/Go/Rust 等项目的一键部署/WebUI 管理",
|
||||||
|
"type": "deployment"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {
|
||||||
|
"supported_languages": ["python", "nodejs", "java", "go", "rust", "php", "ruby"],
|
||||||
|
"build_timeout": 300,
|
||||||
|
"deploy_timeout": 600,
|
||||||
|
"max_projects": 50,
|
||||||
|
"workspace_dir": "/workspace/polyglot-projects",
|
||||||
|
"auto_cleanup": true,
|
||||||
|
"cleanup_interval": 3600,
|
||||||
|
"log_level": "info",
|
||||||
|
"docker_enabled": true,
|
||||||
|
"docker_network": "polyglot-net"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": ["http-api", "i18n", "pkg-manager"],
|
||||||
|
"permissions": ["lifecycle", "plugin-storage"]
|
||||||
|
}
|
||||||
@@ -1,18 +1,23 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "signature-verifier",
|
"name": "signature-verifier",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "插件签名验证服务 - 验证官方插件完整性与来源真实性",
|
"description": "插件签名验证服务 - 验证官方插件完整性与来源真实性/安全增强",
|
||||||
"type": "core"
|
"type": "core"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {
|
"args": {
|
||||||
"enforce_official": true,
|
"enforce_official": true,
|
||||||
"key_dir": "data/signature-verifier/keys"
|
"key_dir": "data/signature-verifier/keys",
|
||||||
|
"algorithm": "RSA-SHA256",
|
||||||
|
"key_size": 2048,
|
||||||
|
"auto_verify": true,
|
||||||
|
"cache_enabled": true,
|
||||||
|
"cache_ttl": 3600
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": ["plugin-storage"],
|
"dependencies": ["plugin-storage", "i18n"],
|
||||||
"permissions": ["plugin-storage"]
|
"permissions": ["plugin-storage"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,22 +43,47 @@ class WebUIServer:
|
|||||||
# 排序导航项(首页在前)
|
# 排序导航项(首页在前)
|
||||||
sorted_nav = sorted(self.nav_items, key=lambda x: 0 if x.get('url') == '/' else 1)
|
sorted_nav = sorted(self.nav_items, key=lambda x: 0 if x.get('url') == '/' else 1)
|
||||||
|
|
||||||
variables = {
|
# 构建导航项 HTML
|
||||||
"pageTitle": self.config.get("title", "FutureOSS"),
|
nav_html = ""
|
||||||
"currentPage": path,
|
icon_map = {
|
||||||
"navItems": sorted_nav,
|
'🏠': 'ri-home-4-line',
|
||||||
"content": content
|
'📊': 'ri-dashboard-line',
|
||||||
|
'📋': 'ri-file-list-3-line',
|
||||||
|
'🧩': 'ri-puzzle-line',
|
||||||
|
'⚙️': 'ri-settings-3-line',
|
||||||
|
'🔌': 'ri-plug-line',
|
||||||
|
'📦': 'ri-box-3-line',
|
||||||
|
'🌐': 'ri-global-line',
|
||||||
}
|
}
|
||||||
|
for item in sorted_nav:
|
||||||
|
url = item.get('url', '#')
|
||||||
|
is_active = 'active' if url == path else ''
|
||||||
|
icon = item.get('icon', 'ri-dashboard-line')
|
||||||
|
text = item.get('text', '')
|
||||||
|
ri_icon = icon_map.get(icon, icon)
|
||||||
|
title = text
|
||||||
|
nav_html += f'''
|
||||||
|
<a href="{url}" class="nav-item {is_active}" title="{title}">
|
||||||
|
<i class="{ri_icon}"></i>
|
||||||
|
</a>
|
||||||
|
'''
|
||||||
|
|
||||||
php_file = self.frontend_dir / "views" / "layout.php"
|
page_title = self.config.get("title", "FutureOSS")
|
||||||
html = self._execute_php(str(php_file), variables)
|
|
||||||
|
# 读取 HTML 模板
|
||||||
|
template_file = self.frontend_dir / "views" / "layout.html"
|
||||||
|
with open(template_file, 'r', encoding='utf-8') as f:
|
||||||
|
html_template = f.read()
|
||||||
|
|
||||||
|
html = html_template.replace('{{ pageTitle }}', page_title)
|
||||||
|
html = html.replace('{{ navItems }}', nav_html)
|
||||||
|
html = html.replace('{{ content }}', content)
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
status=200,
|
status=200,
|
||||||
headers={"Content-Type": "text/html; charset=utf-8"},
|
headers={"Content-Type": "text/html; charset=utf-8"},
|
||||||
body=html
|
body=html
|
||||||
)
|
)
|
||||||
|
|
||||||
def _default_home_content(self) -> str:
|
def _default_home_content(self) -> str:
|
||||||
"""默认首页内容"""
|
"""默认首页内容"""
|
||||||
return """
|
return """
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* 仪表盘页面视图
|
|
||||||
*/
|
|
||||||
|
|
||||||
$pageTitle = 'FutureOSS - 仪表盘';
|
|
||||||
$currentPage = 'dashboard';
|
|
||||||
|
|
||||||
// 内容区直接包含仪表盘内容
|
|
||||||
if (isset($dashboardContent)) {
|
|
||||||
$content = $dashboardContent;
|
|
||||||
} else {
|
|
||||||
$content = '<div class="empty-state"><p>仪表盘内容加载中...</p></div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 复用 layout
|
|
||||||
include __DIR__ . '/layout.php';
|
|
||||||
110
store/@{FutureOSS}/webui/frontend/views/index.html
Normal file
110
store/@{FutureOSS}/webui/frontend/views/index.html
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>FutureOSS - 首页</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.1.0/remixicon.min.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/main.css">
|
||||||
|
<style>
|
||||||
|
.home-content {
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
.welcome-banner {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.welcome-banner h2 {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.welcome-banner p {
|
||||||
|
font-size: 18px;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.features-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
.feature-card {
|
||||||
|
background: white;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.feature-card h3 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.feature-card p {
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="app">
|
||||||
|
<aside class="sidebar">
|
||||||
|
<nav class="sidebar-nav">
|
||||||
|
<a href="/" class="nav-item active" title="首页">
|
||||||
|
<i class="ri-home-4-line"></i>
|
||||||
|
</a>
|
||||||
|
<a href="/dashboard" class="nav-item" title="仪表盘">
|
||||||
|
<i class="ri-dashboard-line"></i>
|
||||||
|
</a>
|
||||||
|
<a href="/plugins" class="nav-item" title="插件管理">
|
||||||
|
<i class="ri-puzzle-line"></i>
|
||||||
|
</a>
|
||||||
|
<a href="/settings" class="nav-item" title="设置">
|
||||||
|
<i class="ri-settings-3-line"></i>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
<div class="sidebar-footer">
|
||||||
|
<button class="settings-btn" title="设置">
|
||||||
|
<i class="ri-settings-3-line"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
<div class="content-body">
|
||||||
|
<div class="home-content">
|
||||||
|
<div class="welcome-banner">
|
||||||
|
<h2>👋 欢迎使用 FutureOSS</h2>
|
||||||
|
<p>一切皆为插件的轻量级框架</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="features-grid">
|
||||||
|
<div class="feature-card">
|
||||||
|
<h3><i class="ri-plug-line"></i> 插件化架构</h3>
|
||||||
|
<p>所有功能皆可通过插件扩展,灵活定制您的系统</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<h3><i class="ri-shield-check-line"></i> 安全隔离</h3>
|
||||||
|
<p>进程级沙箱保护,确保插件运行安全</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<h3><i class="ri-global-line"></i> 多语言支持</h3>
|
||||||
|
<p>内置国际化框架,支持全球多种语言</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<h3><i class="ri-box-3-line"></i> 轻松部署</h3>
|
||||||
|
<p>Docker 容器化部署,一键启动服务</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* 首页视图
|
|
||||||
* 这是 webui 插件的默认首页
|
|
||||||
* 其他插件可以替换或扩展此页面
|
|
||||||
*/
|
|
||||||
|
|
||||||
$pageTitle = $config['title'] ?? 'FutureOSS';
|
|
||||||
$currentPage = 'home';
|
|
||||||
|
|
||||||
// 默认导航项(其他插件可以添加更多)
|
|
||||||
$navItems = [];
|
|
||||||
|
|
||||||
// 内容区(其他插件可以注入内容)
|
|
||||||
$content = '<div class="empty-state"><p>暂无内容</p></div>';
|
|
||||||
|
|
||||||
include __DIR__ . '/layout.php';
|
|
||||||
33
store/@{FutureOSS}/webui/frontend/views/layout.html
Normal file
33
store/@{FutureOSS}/webui/frontend/views/layout.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ pageTitle }}</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.1.0/remixicon.min.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/main.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.3/dist/cdn.min.js" defer></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="app">
|
||||||
|
<aside class="sidebar">
|
||||||
|
<nav class="sidebar-nav">
|
||||||
|
{{ navItems }}
|
||||||
|
</nav>
|
||||||
|
<div class="sidebar-footer">
|
||||||
|
<button class="settings-btn" title="设置">
|
||||||
|
<i class="ri-settings-3-line"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
<div class="content-body">
|
||||||
|
{{ content }}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title><?= htmlspecialchars($pageTitle ?? 'FutureOSS') ?></title>
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.1.0/remixicon.min.css">
|
|
||||||
<link rel="stylesheet" href="/static/css/main.css">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.3/dist/cdn.min.js" defer></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="app">
|
|
||||||
<aside class="sidebar">
|
|
||||||
<nav class="sidebar-nav">
|
|
||||||
<?php if (!empty($navItems)): ?>
|
|
||||||
<?php foreach ($navItems as $item): ?>
|
|
||||||
<?php
|
|
||||||
$url = $item['url'] ?? '#';
|
|
||||||
$isActive = ($currentPage ?? '') === $url;
|
|
||||||
$icon = $item['icon'] ?? 'ri-dashboard-line';
|
|
||||||
// 如果图标是 emoji,转换为 remixicon 类名
|
|
||||||
$iconMap = [
|
|
||||||
'🏠' => 'ri-home-4-line',
|
|
||||||
'📊' => 'ri-dashboard-line',
|
|
||||||
'📋' => 'ri-file-list-3-line',
|
|
||||||
'🧩' => 'ri-puzzle-line',
|
|
||||||
'⚙️' => 'ri-settings-3-line',
|
|
||||||
'🔌' => 'ri-plug-line',
|
|
||||||
'📦' => 'ri-box-3-line',
|
|
||||||
'🌐' => 'ri-global-line',
|
|
||||||
];
|
|
||||||
$riIcon = $iconMap[$icon] ?? $icon;
|
|
||||||
?>
|
|
||||||
<a href="<?= htmlspecialchars($url) ?>"
|
|
||||||
class="nav-item <?= $isActive ? 'active' : '' ?>"
|
|
||||||
title="<?= htmlspecialchars($item['text'] ?? '') ?>">
|
|
||||||
<i class="<?= htmlspecialchars($riIcon) ?>"></i>
|
|
||||||
</a>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</nav>
|
|
||||||
<div class="sidebar-footer">
|
|
||||||
<button class="settings-btn" title="设置">
|
|
||||||
<i class="ri-settings-3-line"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<main class="content">
|
|
||||||
<div class="content-body">
|
|
||||||
<?php if (isset($content)): ?>
|
|
||||||
<?= $content ?>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="empty-state">
|
|
||||||
<p>暂无内容</p>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/static/js/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "webui",
|
"name": "webui",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "Web 控制台 - 使用 PHP 前端和 MySQL 数据库",
|
"description": "Web 控制台 - 多语言支持/插件管理/安全配置/系统监控",
|
||||||
"type": "webui"
|
"type": "webui"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
@@ -11,10 +11,17 @@
|
|||||||
"args": {
|
"args": {
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"title": "FutureOSS"
|
"title": "FutureOSS",
|
||||||
|
"language": "zh-CN",
|
||||||
|
"supported_languages": ["zh-CN", "en-US", "zh-TW", "ja-JP", "ko-KR", "fr-FR", "de-DE", "es-ES"],
|
||||||
|
"session_timeout": 3600,
|
||||||
|
"enable_2fa": false,
|
||||||
|
"show_plugins": true,
|
||||||
|
"show_security": true,
|
||||||
|
"show_deployments": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": ["http-api"],
|
"dependencies": ["http-api", "i18n"],
|
||||||
"permissions": ["*"],
|
"permissions": ["*"],
|
||||||
"frontend": "php",
|
"frontend": "php",
|
||||||
"database": {
|
"database": {
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "ws-api",
|
"name": "ws-api",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "FutureOSS",
|
"author": "FutureOSS",
|
||||||
"description": "WebSocket API 服务 - 实时双向通信",
|
"description": "WebSocket API 服务 - 实时双向通信/多语言支持/安全认证",
|
||||||
"type": "protocol"
|
"type": "protocol"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"args": {
|
"args": {
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 8081
|
"port": 8081,
|
||||||
|
"ssl_enabled": false,
|
||||||
|
"heartbeat_interval": 30,
|
||||||
|
"max_connections": 1000,
|
||||||
|
"auth_enabled": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": [],
|
"dependencies": ["i18n"],
|
||||||
"permissions": []
|
"permissions": ["lifecycle"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user