feat: filesync 模块 — 从 FOMS 单体拆分出的独立模块

This commit is contained in:
yumoqing 2026-08-26 15:28:18 +08:00
commit b47ca80eda
10 changed files with 211 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
__pycache__/
*.pyc
*.pyo
*.egg-info/
dist/
build/
py3/
models/mysql.ddl.sql
.DS_Store

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# filesync — 运行文件同步模块
FOMS 平台拆出的独立模块之一。松耦合:表 `cluster_id` 仅存字符串 ID不建外键
rsync 执行由 Agent 完成,管理中心只存配置、收结果。
| 功能 | 表 | 说明 |
|---|---|---|
| 文件目录同步 | `filesync_directories` | rsync 同步配置,原 `sync_directories` |
端点:
- `/filesync/api/agent_report_dir_health.dspy` — Agent 上报 rsync 结果
加载:
```python
from filesync.init import load_filesync
load_filesync()
```

1
__init__.py Normal file
View File

@ -0,0 +1 @@
from .init import load_filesync # noqa: F401

18
build.sh Normal file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# filesync 模块 build生成 DDL + CRUD 页面xls2ui
set -e
cdir=$(pwd)
if [ -d models ]; then
json2ddl mysql models/ > "$cdir/models/mysql.ddl.sql" 2>/dev/null || echo " skip ddl"
fi
if [ -d json ]; then
cd "$cdir/json"
for f in *.json; do
echo " xls2ui $f"
xls2ui -m ../models -o ../wwwroot filesync "$f" 2>/dev/null || echo " skipped"
done
fi
echo "=== filesync Build Complete ==="

1
filesync/__init__.py Normal file
View File

@ -0,0 +1 @@
# filesync 模块 — 运行文件同步管理

97
filesync/init.py Normal file
View File

@ -0,0 +1,97 @@
"""filesync 模块 — 运行文件同步rsync 目录同步)管理。
独立模块松耦合设计
· cluster_id 仅存字符串 ID不建外键引用 foms 核心的 clusters
· import foms 核心代码可独立部署独立测试
· rsync 实际执行由 Agent 在被管主机完成管理中心只存配置收结果
filesync_directories 文件目录同步配置 sync_directories
"""
import json
from appPublic.uniqueID import getID
from appPublic.timeUtils import curDateString
from sqlor.dbpools import DBPools, get_sor_context
from ahserver.serverenv import ServerEnv
DBNAME = 'foms'
def _ns(params_kw):
ns = dict(params_kw) if hasattr(params_kw, 'items') else {}
for k in list(ns.keys()):
if k.endswith('_text'):
ns.pop(k)
return ns
def _get(params_kw, key, default=''):
return params_kw.get(key, default) if hasattr(params_kw, 'get') else default
# ═══════════════════════════════════════════
# 目录同步 CRUD
# ═══════════════════════════════════════════
async def create_directory(request, params_kw):
env = ServerEnv()
async with get_sor_context(env, DBNAME) as sor:
ns = _ns(params_kw)
ns['id'] = getID()
ns['created_at'] = curDateString()
ns['updated_at'] = curDateString()
await sor.C('filesync_directories', ns)
return {'widgettype': 'Message', 'options': {'title': '成功', 'message': '同步配置创建成功', 'type': 'success'}}
async def update_directory(request, params_kw):
env = ServerEnv()
async with get_sor_context(env, DBNAME) as sor:
ns = _ns(params_kw)
ns['updated_at'] = curDateString()
await sor.U('filesync_directories', ns)
return {'widgettype': 'Message', 'options': {'title': '成功', 'message': '同步配置更新成功', 'type': 'success'}}
async def delete_directory(request, params_kw):
env = ServerEnv()
async with get_sor_context(env, DBNAME) as sor:
await sor.D('filesync_directories', {'id': _get(params_kw, 'id')})
return {'widgettype': 'Message', 'options': {'title': '成功', 'message': '同步配置已删除', 'type': 'success'}}
# ═══════════════════════════════════════════
# Agent 上报:同步结果(原 agent_report_health 的 dir 分支)
# ═══════════════════════════════════════════
async def agent_report_dir_health(request, params_kw):
"""Agent 上报 rsync 同步结果"""
env = ServerEnv()
dir_statuses = _get(params_kw, 'dir_statuses', [])
if isinstance(dir_statuses, str):
dir_statuses = json.loads(dir_statuses)
async with get_sor_context(env, DBNAME) as sor:
for dir_stat in dir_statuses:
await sor.U('filesync_directories', {
'id': dir_stat.get('id'),
'last_sync_at': curDateString(),
'last_sync_result': dir_stat.get('result'),
'updated_at': curDateString(),
})
return {'status': 'ok'}
# ═══════════════════════════════════════════
# 注册
# ═══════════════════════════════════════════
def load_filesync():
env = ServerEnv()
env.create_directory = create_directory
env.create_filesync_directories = create_directory
env.update_directory = update_directory
env.update_filesync_directories = update_directory
env.delete_directory = delete_directory
env.delete_filesync_directories = delete_directory
env.agent_report_dir_health = agent_report_dir_health
return True

View File

@ -0,0 +1,16 @@
{
"tblname": "filesync_directories",
"alias": "filesync_directories_list",
"title": "文件目录同步配置",
"params": {
"sortby": ["updated_at desc"],
"browserfields": {
"exclouded": ["id"],
"alters": {
"last_sync_result": {"uitype": "code"},
"enabled": {"uitype": "code", "data": [{"value": "1", "text": "启用"}, {"value": "0", "text": "禁用"}]},
"cluster_id": {"uitype": "code", "valueField": "id", "textField": "name"}
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"summary": [
{
"name": "filesync_directories",
"title": "文件目录同步配置",
"primary": ["id"],
"catelog": "entity"
}
],
"fields": [
{"name": "id", "title": "配置ID", "type": "str", "length": 32, "nullable": "no"},
{"name": "cluster_id", "title": "所属集群", "type": "str", "length": 32, "nullable": "no"},
{"name": "name", "title": "同步任务名称", "type": "str", "length": 128, "nullable": "no"},
{"name": "source_host", "title": "源主机", "type": "str", "length": 255},
{"name": "source_path", "title": "源目录路径", "type": "str", "length": 512, "nullable": "no"},
{"name": "dest_host", "title": "目标主机", "type": "str", "length": 255},
{"name": "dest_path", "title": "目标目录路径", "type": "str", "length": 512, "nullable": "no"},
{"name": "sync_interval", "title": "同步间隔(秒)", "type": "int", "nullable": "no", "default": "60"},
{"name": "exclude_patterns", "title": "排除模式", "type": "str", "length": 512},
{"name": "last_sync_at", "title": "上次同步时间", "type": "datetime"},
{"name": "last_sync_result", "title": "上次同步结果", "type": "str", "length": 32},
{"name": "enabled", "title": "启用", "type": "str", "length": 1, "nullable": "no", "default": "1"},
{"name": "created_at", "title": "创建时间", "type": "datetime"},
{"name": "updated_at", "title": "更新时间", "type": "datetime"}
],
"indexes": [
{"name": "idx_filesync_dir_cluster", "idxtype": "index", "idxfields": ["cluster_id"]}
],
"codes": [
{"field": "last_sync_result", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='sync_result'"},
{"field": "cluster_id", "table": "clusters", "valuefield": "id", "textfield": "name"}
]
}

16
pyproject.toml Normal file
View File

@ -0,0 +1,16 @@
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "filesync"
version = "1.0.0"
description = "运行文件同步模块 — rsync 目录同步配置与状态管理"
requires-python = ">=3.8"
dependencies = [
"sqlor",
]
[tool.setuptools.packages.find]
where = ["."]
include = ["filesync*"]

View File

@ -0,0 +1,3 @@
# Agent 上报 rsync 同步结果
result = await agent_report_dir_health(request, params_kw)
return json.dumps(result, ensure_ascii=False)