refactor: 统一wwwroot目录结构
- 将 entcms/wwwroot/* 移到 wwwroot/ - 将 dingdingflow/wwwroot/* 移到 wwwroot/dingdingflow/ - 更新 config.json 使用单一 wwwroot 映射 - 更新 init_any_permissions.py 扫描新路径 - 更新 init_superuser_permissions.py 用法说明 - 废弃 entcms/scripts/load_path.py 和 dingdingflow/scripts/load_path.py - 更新 build.sh 构建步骤 - 更新 README.md 和 docs/architecture.md 目录说明
This commit is contained in:
parent
f57bb0b0af
commit
208625415a
21
README.md
21
README.md
@ -9,6 +9,27 @@
|
|||||||
| **entcms** | 企业CMS - 新闻/案例/产品/Banner/线索管理 |
|
| **entcms** | 企业CMS - 新闻/案例/产品/Banner/线索管理 |
|
||||||
| **dingdingflow** | 钉钉审批流程 - 内容发布审批工作流 |
|
| **dingdingflow** | 钉钉审批流程 - 内容发布审批工作流 |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
cms/
|
||||||
|
├── conf/config.json # 应用配置
|
||||||
|
├── wwwroot/ # 前端静态文件(统一目录)
|
||||||
|
│ ├── index.ui, news.ui, ... # 企业官网页面
|
||||||
|
│ ├── api/*.dspy # CMS后端API
|
||||||
|
│ └── dingdingflow/ # 钉钉审批模块前端
|
||||||
|
│ ├── index.ui, menu.ui
|
||||||
|
│ └── api/*.dspy
|
||||||
|
├── entcms/ # 企业CMS Python模块
|
||||||
|
├── dingdingflow/ # 钉钉审批Python模块
|
||||||
|
├── bricks -> pkgs/bricks/dist # 前端框架(符号链接)
|
||||||
|
├── build.sh # 构建脚本
|
||||||
|
├── start.sh / stop.sh # 启停脚本
|
||||||
|
├── init_superuser_permissions.py # superuser权限初始化
|
||||||
|
├── init_any_permissions.py # any权限初始化
|
||||||
|
└── scripts/init_superuser.py # 超级用户账号初始化
|
||||||
|
```
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
11
build.sh
11
build.sh
@ -186,15 +186,12 @@ echo " 1. 编辑 conf/config.json 填入数据库密码"
|
|||||||
echo " 2. 执行DDL创建CMS业务表:"
|
echo " 2. 执行DDL创建CMS业务表:"
|
||||||
echo " mysql -h HOST -u USER -pPASS sage < entcms/mysql.ddl.sql"
|
echo " mysql -h HOST -u USER -pPASS sage < entcms/mysql.ddl.sql"
|
||||||
echo " mysql -h HOST -u USER -pPASS sage < dingdingflow/mysql.ddl.sql"
|
echo " mysql -h HOST -u USER -pPASS sage < dingdingflow/mysql.ddl.sql"
|
||||||
echo " 3. 加载RBAC权限:"
|
echo " 3. 初始化权限:"
|
||||||
echo " py3/bin/python entcms/scripts/load_path.py"
|
|
||||||
echo " py3/bin/python dingdingflow/scripts/load_path.py"
|
|
||||||
echo " 4. 初始化超级用户:"
|
|
||||||
echo " py3/bin/python scripts/init_superuser.py"
|
|
||||||
echo " 5. 初始化权限:"
|
|
||||||
echo " py3/bin/python init_superuser_permissions.py"
|
echo " py3/bin/python init_superuser_permissions.py"
|
||||||
echo " py3/bin/python init_any_permissions.py"
|
echo " py3/bin/python init_any_permissions.py"
|
||||||
echo " 6. 启动应用:"
|
echo " 4. 初始化超级用户:"
|
||||||
|
echo " py3/bin/python scripts/init_superuser.py"
|
||||||
|
echo " 5. 启动应用:"
|
||||||
echo " ./start.sh"
|
echo " ./start.sh"
|
||||||
echo ""
|
echo ""
|
||||||
echo "访问地址: http://localhost:9090/"
|
echo "访问地址: http://localhost:9090/"
|
||||||
|
|||||||
@ -23,13 +23,9 @@
|
|||||||
"website": {
|
"website": {
|
||||||
"paths": [
|
"paths": [
|
||||||
[
|
[
|
||||||
"$[workdir]$/entcms/wwwroot",
|
"$[workdir]$/wwwroot",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
[
|
|
||||||
"$[workdir]$/dingdingflow/wwwroot",
|
|
||||||
"/dingdingflow"
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
"$[workdir]$/bricks",
|
"$[workdir]$/bricks",
|
||||||
"/bricks"
|
"/bricks"
|
||||||
|
|||||||
@ -1,74 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
dingdingflow RBAC权限配置 — 企业类型: owner
|
dingdingflow RBAC权限配置 — 已废弃
|
||||||
CMS独立部署,dingdingflow路径保持/dingdingflow前缀
|
|
||||||
|
|
||||||
用法: cd ~/repos/cms && py3/bin/python dingdingflow/scripts/load_path.py
|
dingdingflow模块的wwwroot内容已移到应用根目录的 wwwroot/dingdingflow/ 下。
|
||||||
|
请使用:
|
||||||
|
cd ~/repos/cms && py3/bin/python init_any_permissions.py
|
||||||
|
cd ~/repos/cms && py3/bin/python init_superuser_permissions.py
|
||||||
"""
|
"""
|
||||||
import os, sys, subprocess
|
|
||||||
|
|
||||||
def find_app_root():
|
|
||||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
return os.path.dirname(os.path.dirname(script_dir))
|
|
||||||
|
|
||||||
app_root = find_app_root()
|
|
||||||
sage_root = None
|
|
||||||
for c in [os.path.expanduser("~/repos/sage"), os.path.expanduser("~/sage")]:
|
|
||||||
if os.path.isdir(os.path.join(c, "py3", "bin")):
|
|
||||||
sage_root = c
|
|
||||||
break
|
|
||||||
if not sage_root:
|
|
||||||
sage_root = app_root
|
|
||||||
|
|
||||||
py = os.path.join(app_root, "py3", "bin", "python")
|
|
||||||
sp = os.path.join(sage_root, "set_role_perm.py") if os.path.exists(os.path.join(sage_root, "set_role_perm.py")) else None
|
|
||||||
if not sp:
|
|
||||||
print("ERROR: 找不到set_role_perm.py"); sys.exit(1)
|
|
||||||
|
|
||||||
def run(role, paths):
|
|
||||||
for p in paths:
|
|
||||||
print(f" {role:30s} {p}")
|
|
||||||
subprocess.run([py, sp, role, p], cwd=sage_root, capture_output=True)
|
|
||||||
|
|
||||||
any_paths = [
|
|
||||||
"/dingdingflow/api/dingtalk_callback.dspy",
|
|
||||||
"/dingdingflow/menu.ui",
|
|
||||||
]
|
|
||||||
|
|
||||||
webmaster_paths = [
|
|
||||||
"/dingdingflow",
|
|
||||||
"/dingdingflow/index.ui",
|
|
||||||
"/dingdingflow/api/submit_approval.dspy",
|
|
||||||
"/dingdingflow/dd_approvals", "/dingdingflow/dd_approvals/%",
|
|
||||||
"/dingdingflow/api/dd_approvals_list.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
reviewer_paths = [
|
|
||||||
"/dingdingflow",
|
|
||||||
"/dingdingflow/index.ui",
|
|
||||||
"/dingdingflow/dd_approvals", "/dingdingflow/dd_approvals/%",
|
|
||||||
"/dingdingflow/api/dd_approvals_list.dspy",
|
|
||||||
"/dingdingflow/api/dd_approvals_update.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
supervisor_paths = [
|
|
||||||
"/dingdingflow",
|
|
||||||
"/dingdingflow/index.ui",
|
|
||||||
"/dingdingflow/dd_approvals", "/dingdingflow/dd_approvals/%",
|
|
||||||
"/dingdingflow/dd_approval_configs", "/dingdingflow/dd_approval_configs/%",
|
|
||||||
"/dingdingflow/api/dd_approvals_create.dspy",
|
|
||||||
"/dingdingflow/api/dd_approvals_update.dspy",
|
|
||||||
"/dingdingflow/api/dd_approvals_delete.dspy",
|
|
||||||
"/dingdingflow/api/dd_approvals_list.dspy",
|
|
||||||
"/dingdingflow/api/dd_approval_configs_create.dspy",
|
|
||||||
"/dingdingflow/api/dd_approval_configs_update.dspy",
|
|
||||||
"/dingdingflow/api/dd_approval_configs_delete.dspy",
|
|
||||||
"/dingdingflow/api/dd_approval_configs_list.dspy",
|
|
||||||
"/dingdingflow/api/submit_approval.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
print("=== dingdingflow RBAC权限配置 ===")
|
|
||||||
run("any", any_paths)
|
|
||||||
run("owner.webmaster", webmaster_paths)
|
|
||||||
run("owner.reviewer", reviewer_paths)
|
|
||||||
run("owner.supervisor", supervisor_paths)
|
|
||||||
print("\n完成")
|
|
||||||
|
|||||||
@ -17,6 +17,22 @@
|
|||||||
| cms_leads | 商机线索(网站访客提交 + 未来AI抽取) |
|
| cms_leads | 商机线索(网站访客提交 + 未来AI抽取) |
|
||||||
| cms_site_config | 站点配置(Hero标语、页脚信息等KV配置) |
|
| cms_site_config | 站点配置(Hero标语、页脚信息等KV配置) |
|
||||||
|
|
||||||
|
**目录结构**:
|
||||||
|
```
|
||||||
|
wwwroot/ # 统一前端目录
|
||||||
|
├── index.ui # 官网首页(7个模块:导航/Hero/产品/案例/新闻/页脚/浮动入口)
|
||||||
|
├── products.ui # 产品架构列表
|
||||||
|
├── news.ui # 新闻列表
|
||||||
|
├── news_detail.ui # 新闻详情
|
||||||
|
├── cases.ui # 案例列表
|
||||||
|
├── admin.ui # 管理后台仪表盘
|
||||||
|
├── api/*.dspy # CMS后端API
|
||||||
|
└── dingdingflow/ # 钉钉审批模块
|
||||||
|
├── index.ui
|
||||||
|
├── menu.ui
|
||||||
|
└── api/*.dspy
|
||||||
|
```
|
||||||
|
|
||||||
**公开页面 (any权限)**:
|
**公开页面 (any权限)**:
|
||||||
- `/index.ui` - 官网首页(7个模块:导航/Hero/产品/案例/新闻/页脚/浮动入口)
|
- `/index.ui` - 官网首页(7个模块:导航/Hero/产品/案例/新闻/页脚/浮动入口)
|
||||||
- `/products.ui` - 产品架构列表
|
- `/products.ui` - 产品架构列表
|
||||||
|
|||||||
@ -1,141 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
entcms RBAC权限配置 — 企业类型: owner
|
entcms RBAC权限配置 — 已废弃
|
||||||
CMS独立部署,路径不带/entcms前缀
|
|
||||||
|
|
||||||
用法: cd ~/repos/cms && py3/bin/python entcms/scripts/load_path.py
|
entcms模块的wwwroot内容已移到应用根目录的 wwwroot/ 下。
|
||||||
|
请使用:
|
||||||
|
cd ~/repos/cms && py3/bin/python init_any_permissions.py
|
||||||
|
cd ~/repos/cms && py3/bin/python init_superuser_permissions.py
|
||||||
"""
|
"""
|
||||||
import os, sys, subprocess
|
|
||||||
|
|
||||||
def find_app_root():
|
|
||||||
"""查找CMS应用根目录"""
|
|
||||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
# scripts/ -> entcms/ -> cms root
|
|
||||||
return os.path.dirname(os.path.dirname(script_dir))
|
|
||||||
|
|
||||||
app_root = find_app_root()
|
|
||||||
# 查找Sage的set_role_perm.py(RBAC工具)
|
|
||||||
sage_root = None
|
|
||||||
for c in [os.path.expanduser("~/repos/sage"), os.path.expanduser("~/sage")]:
|
|
||||||
if os.path.isdir(os.path.join(c, "py3", "bin")):
|
|
||||||
sage_root = c
|
|
||||||
break
|
|
||||||
if not sage_root:
|
|
||||||
# 使用CMS自己的py3
|
|
||||||
sage_root = app_root
|
|
||||||
|
|
||||||
py = os.path.join(app_root, "py3", "bin", "python")
|
|
||||||
sp = os.path.join(sage_root, "set_role_perm.py") if os.path.exists(os.path.join(sage_root, "set_role_perm.py")) else None
|
|
||||||
|
|
||||||
if not sp:
|
|
||||||
print("ERROR: 找不到set_role_perm.py,请确保Sage或CMS已构建")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def run(role, paths):
|
|
||||||
for p in paths:
|
|
||||||
print(f" {role:30s} {p}")
|
|
||||||
subprocess.run([py, sp, role, p], cwd=sage_root, capture_output=True)
|
|
||||||
|
|
||||||
# ─── anonymous (any) — 公开页面 + 公开API ───
|
|
||||||
any_paths = [
|
|
||||||
"/index.ui",
|
|
||||||
"/news.ui",
|
|
||||||
"/news_detail.ui",
|
|
||||||
"/cases.ui",
|
|
||||||
"/products.ui",
|
|
||||||
"/cms_styles.css",
|
|
||||||
"/cms_scripts.js",
|
|
||||||
"/menu.ui",
|
|
||||||
"/api/submit_lead.dspy",
|
|
||||||
"/api/get_config.dspy",
|
|
||||||
"/api/get_published_content.dspy",
|
|
||||||
"/api/get_content_detail.dspy",
|
|
||||||
"/api/get_sections.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
# ─── webmaster — 内容/分类/栏目/配置/线索 全部CRUD ───
|
|
||||||
webmaster_paths = [
|
|
||||||
"/admin.ui",
|
|
||||||
# 内容
|
|
||||||
"/cms_content_list", "/cms_content_list/%",
|
|
||||||
"/api/cms_content_create.dspy",
|
|
||||||
"/api/cms_content_update.dspy",
|
|
||||||
"/api/cms_content_delete.dspy",
|
|
||||||
"/api/cms_content_list.dspy",
|
|
||||||
# 分类
|
|
||||||
"/cms_categories_list", "/cms_categories_list/%",
|
|
||||||
"/api/cms_categories_create.dspy",
|
|
||||||
"/api/cms_categories_update.dspy",
|
|
||||||
"/api/cms_categories_delete.dspy",
|
|
||||||
"/api/cms_categories_list.dspy",
|
|
||||||
"/api/category_options.dspy",
|
|
||||||
# 栏目
|
|
||||||
"/cms_sections_list", "/cms_sections_list/%",
|
|
||||||
"/api/cms_sections_create.dspy",
|
|
||||||
"/api/cms_sections_update.dspy",
|
|
||||||
"/api/cms_sections_delete.dspy",
|
|
||||||
"/api/cms_sections_list.dspy",
|
|
||||||
# 站点配置
|
|
||||||
"/cms_site_config_list", "/cms_site_config_list/%",
|
|
||||||
"/api/cms_site_config_create.dspy",
|
|
||||||
"/api/cms_site_config_update.dspy",
|
|
||||||
"/api/cms_site_config_delete.dspy",
|
|
||||||
"/api/cms_site_config_list.dspy",
|
|
||||||
# 线索管理
|
|
||||||
"/cms_leads_list", "/cms_leads_list/%",
|
|
||||||
"/api/cms_leads_create.dspy",
|
|
||||||
"/api/cms_leads_update.dspy",
|
|
||||||
"/api/cms_leads_delete.dspy",
|
|
||||||
"/api/cms_leads_list.dspy",
|
|
||||||
# 审批
|
|
||||||
"/api/submit_content_approval.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
# ─── reviewer — 查看内容 + 审批(只改status) ───
|
|
||||||
reviewer_paths = [
|
|
||||||
"/admin.ui",
|
|
||||||
"/cms_content_list", "/cms_content_list/%",
|
|
||||||
"/api/cms_content_list.dspy",
|
|
||||||
"/api/cms_content_update.dspy",
|
|
||||||
"/api/category_options.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
# ─── supervisor — 查看全部 + 审批配置 + 线索管理 ───
|
|
||||||
supervisor_paths = [
|
|
||||||
"/admin.ui",
|
|
||||||
"/cms_content_list", "/cms_content_list/%",
|
|
||||||
"/cms_categories_list", "/cms_categories_list/%",
|
|
||||||
"/cms_sections_list", "/cms_sections_list/%",
|
|
||||||
"/cms_site_config_list", "/cms_site_config_list/%",
|
|
||||||
"/api/cms_content_list.dspy",
|
|
||||||
"/api/cms_categories_list.dspy",
|
|
||||||
"/api/cms_sections_list.dspy",
|
|
||||||
"/api/cms_site_config_list.dspy",
|
|
||||||
"/api/category_options.dspy",
|
|
||||||
"/cms_leads_list", "/cms_leads_list/%",
|
|
||||||
"/api/cms_leads_create.dspy",
|
|
||||||
"/api/cms_leads_update.dspy",
|
|
||||||
"/api/cms_leads_delete.dspy",
|
|
||||||
"/api/cms_leads_list.dspy",
|
|
||||||
"/api/submit_content_approval.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
# ─── customer-support — 线索查看和更新 ───
|
|
||||||
support_paths = [
|
|
||||||
"/admin.ui",
|
|
||||||
"/cms_leads_list", "/cms_leads_list/%",
|
|
||||||
"/api/cms_leads_list.dspy",
|
|
||||||
"/api/cms_leads_update.dspy",
|
|
||||||
]
|
|
||||||
|
|
||||||
print("=== CMS RBAC权限配置 ===")
|
|
||||||
print(f"\n--- any (匿名用户) ---")
|
|
||||||
run("any", any_paths)
|
|
||||||
print(f"\n--- owner.webmaster (内容管理员) ---")
|
|
||||||
run("owner.webmaster", webmaster_paths)
|
|
||||||
print(f"\n--- owner.reviewer (内容审核) ---")
|
|
||||||
run("owner.reviewer", reviewer_paths)
|
|
||||||
print(f"\n--- owner.supervisor (主管) ---")
|
|
||||||
run("owner.supervisor", supervisor_paths)
|
|
||||||
print(f"\n--- owner.customer-support (客服) ---")
|
|
||||||
run("owner.customer-support", support_paths)
|
|
||||||
print("\n完成")
|
|
||||||
|
|||||||
@ -3,8 +3,8 @@ CMS RBAC权限初始化 — any (匿名) 角色
|
|||||||
自动扫描 wwwroot 和 bricks 下所有文件,授予 any 角色权限
|
自动扫描 wwwroot 和 bricks 下所有文件,授予 any 角色权限
|
||||||
|
|
||||||
规则:
|
规则:
|
||||||
- entcms/wwwroot/* → /<file>
|
- wwwroot/* → /<file>
|
||||||
- dingdingflow/wwwroot/* → /dingdingflow/<file>
|
- wwwroot/dingdingflow/* → /dingdingflow/<file>
|
||||||
- bricks/* → /bricks/<file>
|
- bricks/* → /bricks/<file>
|
||||||
- 排除: .pyc, __pycache__, .git, 指向其他模块的符号链接
|
- 排除: .pyc, __pycache__, .git, 指向其他模块的符号链接
|
||||||
|
|
||||||
@ -108,20 +108,35 @@ print("=== CMS RBAC权限初始化 — any (匿名访问) ===")
|
|||||||
print(f"Sage: {sage_root}")
|
print(f"Sage: {sage_root}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# 1. entcms/wwwroot → /
|
# 1. wwwroot/ 根目录文件 → / (排除dingdingflow子目录)
|
||||||
entcms_paths = scan_wwwroot(
|
wwwroot_root = os.path.join(app_root, "wwwroot")
|
||||||
os.path.join(app_root, "entcms", "wwwroot"),
|
root_paths = []
|
||||||
""
|
if os.path.isdir(wwwroot_root):
|
||||||
)
|
for f in sorted(os.listdir(wwwroot_root)):
|
||||||
print(f"--- entcms/wwwroot → / ({len(entcms_paths)} 个文件) ---")
|
fpath = os.path.join(wwwroot_root, f)
|
||||||
n1 = set_any_perms(entcms_paths)
|
if os.path.isfile(fpath) and not f.startswith("."):
|
||||||
|
_, ext = os.path.splitext(f)
|
||||||
|
if ext.lower() not in SKIP_EXTS:
|
||||||
|
root_paths.append("/" + f)
|
||||||
|
# api 子目录
|
||||||
|
api_dir = os.path.join(wwwroot_root, "api")
|
||||||
|
if os.path.isdir(api_dir):
|
||||||
|
for f in sorted(os.listdir(api_dir)):
|
||||||
|
fpath = os.path.join(api_dir, f)
|
||||||
|
if os.path.isfile(fpath) and not f.startswith("."):
|
||||||
|
_, ext = os.path.splitext(f)
|
||||||
|
if ext.lower() not in SKIP_EXTS:
|
||||||
|
root_paths.append("/api/" + f)
|
||||||
|
|
||||||
# 2. dingdingflow/wwwroot → /dingdingflow
|
print(f"--- wwwroot/ → / ({len(root_paths)} 个文件) ---")
|
||||||
|
n1 = set_any_perms(root_paths)
|
||||||
|
|
||||||
|
# 2. wwwroot/dingdingflow/ → /dingdingflow/
|
||||||
dd_paths = scan_wwwroot(
|
dd_paths = scan_wwwroot(
|
||||||
os.path.join(app_root, "dingdingflow", "wwwroot"),
|
os.path.join(app_root, "wwwroot", "dingdingflow"),
|
||||||
"/dingdingflow"
|
"/dingdingflow"
|
||||||
)
|
)
|
||||||
print(f"\n--- dingdingflow/wwwroot → /dingdingflow ({len(dd_paths)} 个文件) ---")
|
print(f"\n--- wwwroot/dingdingflow/ → /dingdingflow ({len(dd_paths)} 个文件) ---")
|
||||||
n2 = set_any_perms(dd_paths)
|
n2 = set_any_perms(dd_paths)
|
||||||
|
|
||||||
# 3. bricks → /bricks
|
# 3. bricks → /bricks
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
CMS RBAC权限初始化 — superuser角色
|
CMS RBAC权限初始化 — superuser角色
|
||||||
为owner.superuser授予CMS所有权限
|
为owner.superuser授予CMS所有权限
|
||||||
|
|
||||||
用法: cd ~/repos/cms && py3/bin/python init_superuser.py
|
用法: cd ~/repos/cms && py3/bin/python init_superuser_permissions.py
|
||||||
"""
|
"""
|
||||||
import os, sys, subprocess
|
import os, sys, subprocess
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user