refactor: CMS从Sage子模块重构为独立ahserver Web应用
架构变更: - CMS作为独立进程运行(端口9090),不再嵌入Sage - 使用ahserver框架,复用rbac模块做认证授权 - 所有模块共享sage数据库(配置在conf/config.json) 新增文件: - app/cms.py: 独立Web应用主入口(webapp(init)) - app/global_func.py: 全局函数(get_module_dbname/UiWindow等) - conf/config.json: 应用配置模板(数据库/路径/处理器/Redis) - start.sh/stop.sh: 进程管理脚本 - pyproject.toml: 顶层Python包配置 路径重构(去掉/entcms前缀): - 官网首页: /entcms/index.ui → /index.ui - 管理后台: /entcms/admin.ui → /admin.ui - API: /entcms/api/xxx.dspy → /api/xxx.dspy - CRUD: /entcms/cms_content_list → /cms_content_list - dingdingflow保持/dingdingflow前缀(映射子目录) config.json路径映射: - entcms/wwwroot → / (根路径) - dingdingflow/wwwroot → /dingdingflow - bricks/dist → /bricks 构建脚本(build.sh): - 创建独立venv(py3/) - 安装核心依赖(apppublic/sqlor/ahserver/bricks/rbac等) - json2ddl生成CMS业务表DDL - xls2ui生成CRUD UI - 生成systemd服务文件 load_path.py更新: - entcms: 所有路径去掉/entcms前缀 - dingdingflow: 保持/dingdingflow前缀 - 查找set_role_perm.py支持CMS和Sage两种环境 init_superuser.py更新: - 支持CMS独立环境(自动检测py3/conf) - 创建superuser角色并分配全部权限
This commit is contained in:
parent
52f4632dfc
commit
569c8e6715
32
.gitignore
vendored
32
.gitignore
vendored
@ -1,3 +1,31 @@
|
|||||||
*.pyc
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
mysql.ddl.sql
|
*.py[cod]
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Virtual environment
|
||||||
|
py3/
|
||||||
|
pkgs/
|
||||||
|
|
||||||
|
# Runtime
|
||||||
|
logs/
|
||||||
|
files/
|
||||||
|
*.pid
|
||||||
|
cms.service
|
||||||
|
|
||||||
|
# DDL generated
|
||||||
|
*/mysql.ddl.sql
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Config with credentials
|
||||||
|
# conf/config.json # Keep template in git
|
||||||
|
|||||||
42
app/cms.py
Normal file
42
app/cms.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
"""
|
||||||
|
开元云科技CMS — 独立Web应用主入口
|
||||||
|
启动: py3/bin/python app/cms.py -p 9090 -w $(pwd)
|
||||||
|
"""
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
# Ensure app/ is in path for local imports
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
from appPublic.log import MyLogger, info
|
||||||
|
from appPublic.folderUtils import ProgramPath
|
||||||
|
from appPublic.jsonConfig import getConfig
|
||||||
|
from appPublic.registerfunction import RegisterFunction
|
||||||
|
from bricks_for_python.init import load_pybricks
|
||||||
|
from ahserver.webapp import webapp
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
from sqlor.dbpools import DBPools
|
||||||
|
|
||||||
|
# CMS业务模块
|
||||||
|
from entcms.init import load_entcms
|
||||||
|
from dingdingflow.init import load_dingdingflow
|
||||||
|
|
||||||
|
# RBAC认证(复用sage的rbac模块)
|
||||||
|
from rbac.init import load_rbac
|
||||||
|
from appbase.init import load_appbase
|
||||||
|
|
||||||
|
# 全局函数
|
||||||
|
from global_func import set_globalvariable
|
||||||
|
|
||||||
|
__version__ = '1.0.0'
|
||||||
|
|
||||||
|
def init():
|
||||||
|
rf = RegisterFunction()
|
||||||
|
set_globalvariable()
|
||||||
|
load_pybricks()
|
||||||
|
load_appbase()
|
||||||
|
load_rbac()
|
||||||
|
load_entcms()
|
||||||
|
load_dingdingflow()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
webapp(init)
|
||||||
56
app/global_func.py
Normal file
56
app/global_func.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
"""
|
||||||
|
CMS全局函数 — 注册到ServerEnv供.dspy和.ui调用
|
||||||
|
"""
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
|
||||||
|
def get_module_dbname(mname):
|
||||||
|
"""所有模块共享sage数据库"""
|
||||||
|
return 'sage'
|
||||||
|
|
||||||
|
def UiWindow(title, icon, content, cheight=10, cwidth=15):
|
||||||
|
return {
|
||||||
|
"widgettype": "PopupWindow",
|
||||||
|
"options": {
|
||||||
|
"author": "cms",
|
||||||
|
"cwidth": cwidth,
|
||||||
|
"cheight": cheight,
|
||||||
|
"title": title,
|
||||||
|
"content": content,
|
||||||
|
"icon": icon or entire_url('/bricks/imgs/app.png'),
|
||||||
|
"movable": True,
|
||||||
|
"auto_open": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def UiError(title="出错", message="出错啦", timeout=5):
|
||||||
|
return {
|
||||||
|
"widgettype": "Error",
|
||||||
|
"options": {
|
||||||
|
"author": "cms",
|
||||||
|
"timeout": timeout,
|
||||||
|
"cwidth": 15,
|
||||||
|
"cheight": 10,
|
||||||
|
"title": title,
|
||||||
|
"message": message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def UiMessage(title="消息", message="后台消息", timeout=5):
|
||||||
|
return {
|
||||||
|
"widgettype": "Message",
|
||||||
|
"options": {
|
||||||
|
"author": "cms",
|
||||||
|
"timeout": timeout,
|
||||||
|
"cwidth": 15,
|
||||||
|
"cheight": 10,
|
||||||
|
"title": title,
|
||||||
|
"message": message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_globalvariable():
|
||||||
|
g = ServerEnv()
|
||||||
|
g.get_module_dbname = get_module_dbname
|
||||||
|
g.UiError = UiError
|
||||||
|
g.UiMessage = UiMessage
|
||||||
|
g.UiWindow = UiWindow
|
||||||
261
build.sh
261
build.sh
@ -1,87 +1,198 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# CMS项目构建脚本
|
# 开元云科技CMS — 独立Web应用构建脚本
|
||||||
# 构建 entcms + dingdingflow 模块并集成到Sage系统
|
# 用法: cd ~/repos/cms && ./build.sh
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
cdir=$(pwd)
|
||||||
echo "=== CMS项目构建 ==="
|
uname=$(id -un)
|
||||||
echo "Script dir: $SCRIPT_DIR"
|
gname=$(id -gn)
|
||||||
|
|
||||||
# 查找Sage根目录
|
echo "============================================"
|
||||||
SAGE_ROOT=""
|
echo " 开元云科技CMS — 独立Web应用构建"
|
||||||
for candidate in "$SCRIPT_DIR/../.." "$HOME/repos/sage" "$HOME/sage"; do
|
echo "============================================"
|
||||||
if [ -d "$candidate/wwwroot" ] && [ -d "$candidate/py3/bin" ]; then
|
|
||||||
SAGE_ROOT="$(cd "$candidate" && pwd)"
|
# ===========================================
|
||||||
break
|
# Step 1: Python虚拟环境
|
||||||
|
# ===========================================
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 1: 创建Python虚拟环境 ---"
|
||||||
|
if [ ! -d "py3" ]; then
|
||||||
|
python3 -m venv py3
|
||||||
|
fi
|
||||||
|
source py3/bin/activate
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Step 2: 核心依赖
|
||||||
|
# ===========================================
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 2: 安装核心依赖 ---"
|
||||||
|
mkdir -p pkgs
|
||||||
|
|
||||||
|
# 核心框架包
|
||||||
|
for m in apppublic sqlor ahserver bricks-for-python xls2ddl
|
||||||
|
do
|
||||||
|
echo " install $m..."
|
||||||
|
cd $cdir/pkgs
|
||||||
|
if [ ! -d "$m" ]; then
|
||||||
|
git clone https://git.opencomputing.cn/yumoqing/$m
|
||||||
fi
|
fi
|
||||||
|
cd $m
|
||||||
|
$cdir/py3/bin/pip install . 2>/dev/null || echo " WARN: $m install failed"
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -z "$SAGE_ROOT" ]; then
|
# bricks前端
|
||||||
echo "ERROR: 找不到Sage根目录"
|
echo " install bricks..."
|
||||||
exit 1
|
cd $cdir/pkgs
|
||||||
|
if [ ! -d "bricks" ]; then
|
||||||
|
git clone https://git.opencomputing.cn/yumoqing/bricks
|
||||||
|
fi
|
||||||
|
cd bricks/bricks
|
||||||
|
./build.sh 2>/dev/null || echo " WARN: bricks build skipped"
|
||||||
|
|
||||||
|
# bricks符号链接
|
||||||
|
mkdir -p $cdir/bricks
|
||||||
|
if [ -d "$cdir/pkgs/bricks/dist" ]; then
|
||||||
|
rm -f $cdir/bricks
|
||||||
|
ln -sf $cdir/pkgs/bricks/dist $cdir/bricks
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Sage root: $SAGE_ROOT"
|
# ===========================================
|
||||||
PY="$SAGE_ROOT/py3/bin/python"
|
# Step 3: RBAC + AppBase模块(认证依赖)
|
||||||
PIP="$SAGE_ROOT/py3/bin/pip"
|
# ===========================================
|
||||||
|
echo ""
|
||||||
# 安装模块
|
echo "--- Step 3: 安装RBAC/AppBase模块 ---"
|
||||||
for mod in entcms dingdingflow; do
|
for m in appbase rbac checklang
|
||||||
echo ""
|
do
|
||||||
echo "=== 安装 $mod ==="
|
echo " install $m..."
|
||||||
cd "$SCRIPT_DIR/$mod"
|
cd $cdir/pkgs
|
||||||
$PIP install -e .
|
if [ ! -d "$m" ]; then
|
||||||
|
git clone https://git.opencomputing.cn/yumoqing/$m
|
||||||
# 生成DDL
|
|
||||||
if [ -d "models" ] && [ "$(ls models/*.json 2>/dev/null)" ]; then
|
|
||||||
echo "生成DDL..."
|
|
||||||
cd models
|
|
||||||
$PY -c "from sqlor.ddl_template_mysql import DDLTemplate; print(DDLTemplate().generate('.'))" > ../mysql.ddl.sql 2>/dev/null || json2ddl mysql . > ../mysql.ddl.sql 2>/dev/null || echo "DDL generation skipped (json2ddl not available)"
|
|
||||||
cd ..
|
|
||||||
fi
|
fi
|
||||||
|
cd $m
|
||||||
# 生成CRUD UI
|
$cdir/py3/bin/pip install . 2>/dev/null || echo " WARN: $m install failed"
|
||||||
if [ -d "json" ] && [ "$(ls json/*.json 2>/dev/null)" ]; then
|
|
||||||
echo "生成CRUD UI..."
|
|
||||||
cd json
|
|
||||||
xls2ui -m ../models -o ../wwwroot $mod *.json 2>/dev/null || echo "CRUD UI generation skipped (xls2ui not available)"
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 链接wwwroot到Sage
|
|
||||||
MODULE_WWWROOT="$SAGE_ROOT/wwwroot/$mod"
|
|
||||||
mkdir -p "$MODULE_WWWROOT/api"
|
|
||||||
|
|
||||||
# 链接UI/CSS/JS文件
|
|
||||||
for f in "$SCRIPT_DIR/$mod/wwwroot"/*.ui "$SCRIPT_DIR/$mod/wwwroot"/*.css "$SCRIPT_DIR/$mod/wwwroot"/*.js; do
|
|
||||||
[ -f "$f" ] || continue
|
|
||||||
fname=$(basename "$f")
|
|
||||||
ln -sf "$f" "$MODULE_WWWROOT/$fname"
|
|
||||||
done
|
|
||||||
|
|
||||||
# 链接api目录下的.dspy文件
|
|
||||||
for f in "$SCRIPT_DIR/$mod/wwwroot/api"/*.dspy; do
|
|
||||||
[ -f "$f" ] || continue
|
|
||||||
fname=$(basename "$f")
|
|
||||||
ln -sf "$f" "$MODULE_WWWROOT/api/$fname"
|
|
||||||
done
|
|
||||||
|
|
||||||
# 链接生成的CRUD目录
|
|
||||||
for d in "$SCRIPT_DIR/$mod/wwwroot"/*/; do
|
|
||||||
[ -d "$d" ] || continue
|
|
||||||
dname=$(basename "$d")
|
|
||||||
case "$dname" in api|styles|scripts) continue ;; esac
|
|
||||||
ln -sf "$d" "$MODULE_WWWROOT/$dname"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "$mod 安装完成"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Step 4: CMS业务模块
|
||||||
|
# ===========================================
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== 构建完成 ==="
|
echo "--- Step 4: 安装CMS业务模块 ---"
|
||||||
echo "请执行以下步骤完成集成:"
|
|
||||||
echo "1. 编辑 $SAGE_ROOT/app/sage.py 添加模块导入"
|
# entcms模块
|
||||||
echo "2. 编辑 $SAGE_ROOT/build.sh 添加模块到安装循环"
|
echo " install entcms..."
|
||||||
echo "3. 执行 RBAC 权限配置"
|
cd $cdir/entcms
|
||||||
echo "4. 重启Sage服务"
|
$cdir/py3/bin/pip install . 2>/dev/null || echo " WARN: entcms install failed"
|
||||||
|
|
||||||
|
# dingdingflow模块
|
||||||
|
echo " install dingdingflow..."
|
||||||
|
cd $cdir/dingdingflow
|
||||||
|
$cdir/py3/bin/pip install . 2>/dev/null || echo " WARN: dingdingflow install failed"
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Step 5: 数据库DDL(CMS业务表)
|
||||||
|
# ===========================================
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 5: 生成数据库DDL ---"
|
||||||
|
|
||||||
|
# entcms表DDL
|
||||||
|
if [ -d "$cdir/entcms/models" ]; then
|
||||||
|
cd $cdir/entcms/models
|
||||||
|
echo " 生成 entcms DDL..."
|
||||||
|
$cdir/py3/bin/json2ddl mysql . > $cdir/entcms/mysql.ddl.sql 2>/dev/null || echo " WARN: json2ddl failed for entcms"
|
||||||
|
echo " DDL已生成: entcms/mysql.ddl.sql"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# dingdingflow表DDL
|
||||||
|
if [ -d "$cdir/dingdingflow/models" ]; then
|
||||||
|
cd $cdir/dingdingflow/models
|
||||||
|
echo " 生成 dingdingflow DDL..."
|
||||||
|
$cdir/py3/bin/json2ddl mysql . > $cdir/dingdingflow/mysql.ddl.sql 2>/dev/null || echo " WARN: json2ddl failed for dingdingflow"
|
||||||
|
echo " DDL已生成: dingdingflow/mysql.ddl.sql"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Step 6: CRUD UI生成
|
||||||
|
# ===========================================
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 6: 生成CRUD UI ---"
|
||||||
|
|
||||||
|
# entcms CRUD
|
||||||
|
if [ -d "$cdir/entcms/json" ]; then
|
||||||
|
cd $cdir/entcms/json
|
||||||
|
echo " 生成 entcms CRUD UI..."
|
||||||
|
for f in *.json; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
echo " $f"
|
||||||
|
$cdir/py3/bin/xls2ui -m ../models -o ../wwwroot entcms $f 2>/dev/null || echo " WARN: xls2ui failed for $f"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# dingdingflow CRUD
|
||||||
|
if [ -d "$cdir/dingdingflow/json" ]; then
|
||||||
|
cd $cdir/dingdingflow/json
|
||||||
|
echo " 生成 dingdingflow CRUD UI..."
|
||||||
|
for f in *.json; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
echo " $f"
|
||||||
|
$cdir/py3/bin/xls2ui -m ../models -o ../wwwroot dingdingflow $f 2>/dev/null || echo " WARN: xls2ui failed for $f"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Step 7: 日志和文件目录
|
||||||
|
# ===========================================
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 7: 创建运行时目录 ---"
|
||||||
|
mkdir -p $cdir/logs
|
||||||
|
mkdir -p $cdir/files
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Step 8: systemd服务文件
|
||||||
|
# ===========================================
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 8: 生成systemd服务文件 ---"
|
||||||
|
cat > $cdir/cms.service <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=KaiYuan Cloud CMS Web Application
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=$uname
|
||||||
|
Group=$gname
|
||||||
|
Type=forking
|
||||||
|
WorkingDirectory=$cdir
|
||||||
|
ExecStart=$cdir/start.sh
|
||||||
|
ExecStop=$cdir/stop.sh
|
||||||
|
StandardOutput=append:$cdir/logs/cms.log
|
||||||
|
StandardError=append:$cdir/logs/cms.log
|
||||||
|
SyslogIdentifier=cms
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
echo " cms.service 已生成"
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Done
|
||||||
|
# ===========================================
|
||||||
|
cd $cdir
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " 构建完成!"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "后续步骤:"
|
||||||
|
echo " 1. 编辑 conf/config.json 填入数据库密码"
|
||||||
|
echo " 2. 执行DDL创建CMS业务表:"
|
||||||
|
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 " 3. 加载RBAC权限:"
|
||||||
|
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 " ./start.sh"
|
||||||
|
echo ""
|
||||||
|
echo "访问地址: http://localhost:9090/"
|
||||||
|
echo "管理后台: http://localhost:9090/admin.ui"
|
||||||
|
|||||||
80
conf/config.json
Normal file
80
conf/config.json
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"password_key": "CHANGE_ME_16_CHARS",
|
||||||
|
"logger": {
|
||||||
|
"name": "cms",
|
||||||
|
"levelname": "info",
|
||||||
|
"logfile": "$[workdir]$/logs/cms.log"
|
||||||
|
},
|
||||||
|
"filesroot": "$[workdir]$/files",
|
||||||
|
"databases": {
|
||||||
|
"sage": {
|
||||||
|
"driver": "aiomysql",
|
||||||
|
"async_mode": true,
|
||||||
|
"coding": "utf8",
|
||||||
|
"dbname": "sage",
|
||||||
|
"kwargs": {
|
||||||
|
"user": "test",
|
||||||
|
"db": "sage",
|
||||||
|
"password": "ENCRYPTED_PASSWORD_HERE",
|
||||||
|
"host": "localhost"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"website": {
|
||||||
|
"paths": [
|
||||||
|
[
|
||||||
|
"$[workdir]$/entcms/wwwroot",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"$[workdir]$/dingdingflow/wwwroot",
|
||||||
|
"/dingdingflow"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"$[workdir]$/bricks",
|
||||||
|
"/bricks"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"port": 9090,
|
||||||
|
"coding": "utf-8",
|
||||||
|
"session_redis": {
|
||||||
|
"url": "redis://127.0.0.1:6379/0"
|
||||||
|
},
|
||||||
|
"indexes": [
|
||||||
|
"index.ui",
|
||||||
|
"index.html",
|
||||||
|
"index.tmpl"
|
||||||
|
],
|
||||||
|
"processors": [
|
||||||
|
[
|
||||||
|
".xlsxds",
|
||||||
|
"xlsxds"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
".sqlds",
|
||||||
|
"sqlds"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
".tmpl",
|
||||||
|
"tmpl"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
".dspy",
|
||||||
|
"dspy"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
".ui",
|
||||||
|
"bui"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
".md",
|
||||||
|
"md"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"langMapping": {
|
||||||
|
"zh-Hans-CN": "zh-cn",
|
||||||
|
"en-US": "en"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,24 +1,28 @@
|
|||||||
"""
|
"""
|
||||||
dingdingflow RBAC权限配置 — 企业类型: owner
|
dingdingflow RBAC权限配置 — 企业类型: owner
|
||||||
角色: superuser(继承全部), webmaster(提交审批), reviewer(审批管理),
|
CMS独立部署,dingdingflow路径保持/dingdingflow前缀
|
||||||
supervisor(审批配置)
|
|
||||||
|
|
||||||
用法: cd ~/repos/sage && ./py3/bin/python ~/repos/cms/dingdingflow/scripts/load_path.py
|
用法: cd ~/repos/cms && py3/bin/python dingdingflow/scripts/load_path.py
|
||||||
"""
|
"""
|
||||||
import os, sys, subprocess
|
import os, sys, subprocess
|
||||||
|
|
||||||
def find_sage_root():
|
def find_app_root():
|
||||||
for c in [os.path.expanduser("~/repos/sage"), os.path.expanduser("~/sage")]:
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
if os.path.isdir(os.path.join(c, "wwwroot")) and os.path.isdir(os.path.join(c, "py3")):
|
return os.path.dirname(os.path.dirname(script_dir))
|
||||||
return c
|
|
||||||
return None
|
|
||||||
|
|
||||||
sage_root = find_sage_root()
|
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:
|
if not sage_root:
|
||||||
print("ERROR: Cannot find Sage root"); sys.exit(1)
|
sage_root = app_root
|
||||||
|
|
||||||
py = os.path.join(sage_root, "py3", "bin", "python")
|
py = os.path.join(app_root, "py3", "bin", "python")
|
||||||
sp = os.path.join(sage_root, "set_role_perm.py")
|
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):
|
def run(role, paths):
|
||||||
for p in paths:
|
for p in paths:
|
||||||
@ -30,7 +34,6 @@ any_paths = [
|
|||||||
"/dingdingflow/menu.ui",
|
"/dingdingflow/menu.ui",
|
||||||
]
|
]
|
||||||
|
|
||||||
# webmaster: 提交审批
|
|
||||||
webmaster_paths = [
|
webmaster_paths = [
|
||||||
"/dingdingflow",
|
"/dingdingflow",
|
||||||
"/dingdingflow/index.ui",
|
"/dingdingflow/index.ui",
|
||||||
@ -39,7 +42,6 @@ webmaster_paths = [
|
|||||||
"/dingdingflow/api/dd_approvals_list.dspy",
|
"/dingdingflow/api/dd_approvals_list.dspy",
|
||||||
]
|
]
|
||||||
|
|
||||||
# reviewer: 审批管理(查看全部 + 更新审批状态)
|
|
||||||
reviewer_paths = [
|
reviewer_paths = [
|
||||||
"/dingdingflow",
|
"/dingdingflow",
|
||||||
"/dingdingflow/index.ui",
|
"/dingdingflow/index.ui",
|
||||||
@ -48,7 +50,6 @@ reviewer_paths = [
|
|||||||
"/dingdingflow/api/dd_approvals_update.dspy",
|
"/dingdingflow/api/dd_approvals_update.dspy",
|
||||||
]
|
]
|
||||||
|
|
||||||
# supervisor: 审批配置管理 + 全部审批记录
|
|
||||||
supervisor_paths = [
|
supervisor_paths = [
|
||||||
"/dingdingflow",
|
"/dingdingflow",
|
||||||
"/dingdingflow/index.ui",
|
"/dingdingflow/index.ui",
|
||||||
@ -66,12 +67,8 @@ supervisor_paths = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
print("=== dingdingflow RBAC权限配置 ===")
|
print("=== dingdingflow RBAC权限配置 ===")
|
||||||
print(f"\n--- any (匿名/钉钉回调) ---")
|
|
||||||
run("any", any_paths)
|
run("any", any_paths)
|
||||||
print(f"\n--- owner.webmaster ---")
|
|
||||||
run("owner.webmaster", webmaster_paths)
|
run("owner.webmaster", webmaster_paths)
|
||||||
print(f"\n--- owner.reviewer ---")
|
|
||||||
run("owner.reviewer", reviewer_paths)
|
run("owner.reviewer", reviewer_paths)
|
||||||
print(f"\n--- owner.supervisor ---")
|
|
||||||
run("owner.supervisor", supervisor_paths)
|
run("owner.supervisor", supervisor_paths)
|
||||||
print("\n完成")
|
print("\n完成")
|
||||||
|
|||||||
@ -29,9 +29,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editable": {
|
"editable": {
|
||||||
"new_data_url": "{{entire_url('../api/cms_categories_create.dspy')}}",
|
"new_data_url": "{{entire_url('api/cms_categories_create.dspy')}}",
|
||||||
"update_data_url": "{{entire_url('../api/cms_categories_update.dspy')}}",
|
"update_data_url": "{{entire_url('api/cms_categories_update.dspy')}}",
|
||||||
"delete_data_url": "{{entire_url('../api/cms_categories_delete.dspy')}}"
|
"delete_data_url": "{{entire_url('api/cms_categories_delete.dspy')}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,9 +89,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editable": {
|
"editable": {
|
||||||
"new_data_url": "{{entire_url('../api/cms_content_create.dspy')}}",
|
"new_data_url": "{{entire_url('api/cms_content_create.dspy')}}",
|
||||||
"update_data_url": "{{entire_url('../api/cms_content_update.dspy')}}",
|
"update_data_url": "{{entire_url('api/cms_content_update.dspy')}}",
|
||||||
"delete_data_url": "{{entire_url('../api/cms_content_delete.dspy')}}"
|
"delete_data_url": "{{entire_url('api/cms_content_delete.dspy')}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,9 +85,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editable": {
|
"editable": {
|
||||||
"new_data_url": "{{entire_url('../api/cms_leads_create.dspy')}}",
|
"new_data_url": "{{entire_url('api/cms_leads_create.dspy')}}",
|
||||||
"update_data_url": "{{entire_url('../api/cms_leads_update.dspy')}}",
|
"update_data_url": "{{entire_url('api/cms_leads_update.dspy')}}",
|
||||||
"delete_data_url": "{{entire_url('../api/cms_leads_delete.dspy')}}"
|
"delete_data_url": "{{entire_url('api/cms_leads_delete.dspy')}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,9 +84,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editable": {
|
"editable": {
|
||||||
"new_data_url": "{{entire_url('../api/cms_sections_create.dspy')}}",
|
"new_data_url": "{{entire_url('api/cms_sections_create.dspy')}}",
|
||||||
"update_data_url": "{{entire_url('../api/cms_sections_update.dspy')}}",
|
"update_data_url": "{{entire_url('api/cms_sections_update.dspy')}}",
|
||||||
"delete_data_url": "{{entire_url('../api/cms_sections_delete.dspy')}}"
|
"delete_data_url": "{{entire_url('api/cms_sections_delete.dspy')}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,9 +55,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editable": {
|
"editable": {
|
||||||
"new_data_url": "{{entire_url('../api/cms_site_config_create.dspy')}}",
|
"new_data_url": "{{entire_url('api/cms_site_config_create.dspy')}}",
|
||||||
"update_data_url": "{{entire_url('../api/cms_site_config_update.dspy')}}",
|
"update_data_url": "{{entire_url('api/cms_site_config_update.dspy')}}",
|
||||||
"delete_data_url": "{{entire_url('../api/cms_site_config_delete.dspy')}}"
|
"delete_data_url": "{{entire_url('api/cms_site_config_delete.dspy')}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,25 +1,34 @@
|
|||||||
"""
|
"""
|
||||||
entcms RBAC权限配置 — 企业类型: owner
|
entcms RBAC权限配置 — 企业类型: owner
|
||||||
角色: superuser(继承全部), webmaster(内容管理), reviewer(审核),
|
CMS独立部署,路径不带/entcms前缀
|
||||||
supervisor(主管), customer-support(客服)
|
|
||||||
匿名: any (公开页面)
|
|
||||||
|
|
||||||
用法: cd ~/repos/sage && ./py3/bin/python ~/repos/cms/entcms/scripts/load_path.py
|
用法: cd ~/repos/cms && py3/bin/python entcms/scripts/load_path.py
|
||||||
"""
|
"""
|
||||||
import os, sys, subprocess
|
import os, sys, subprocess
|
||||||
|
|
||||||
def find_sage_root():
|
def find_app_root():
|
||||||
for c in [os.path.expanduser("~/repos/sage"), os.path.expanduser("~/sage")]:
|
"""查找CMS应用根目录"""
|
||||||
if os.path.isdir(os.path.join(c, "wwwroot")) and os.path.isdir(os.path.join(c, "py3")):
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
return c
|
# scripts/ -> entcms/ -> cms root
|
||||||
return None
|
return os.path.dirname(os.path.dirname(script_dir))
|
||||||
|
|
||||||
sage_root = find_sage_root()
|
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:
|
if not sage_root:
|
||||||
print("ERROR: Cannot find Sage root"); sys.exit(1)
|
# 使用CMS自己的py3
|
||||||
|
sage_root = app_root
|
||||||
|
|
||||||
py = os.path.join(sage_root, "py3", "bin", "python")
|
py = os.path.join(app_root, "py3", "bin", "python")
|
||||||
sp = os.path.join(sage_root, "set_role_perm.py")
|
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):
|
def run(role, paths):
|
||||||
for p in paths:
|
for p in paths:
|
||||||
@ -28,105 +37,97 @@ def run(role, paths):
|
|||||||
|
|
||||||
# ─── anonymous (any) — 公开页面 + 公开API ───
|
# ─── anonymous (any) — 公开页面 + 公开API ───
|
||||||
any_paths = [
|
any_paths = [
|
||||||
"/entcms/index.ui",
|
"/index.ui",
|
||||||
"/entcms/news.ui",
|
"/news.ui",
|
||||||
"/entcms/news_detail.ui",
|
"/news_detail.ui",
|
||||||
"/entcms/cases.ui",
|
"/cases.ui",
|
||||||
"/entcms/products.ui",
|
"/products.ui",
|
||||||
"/entcms/cms_styles.css",
|
"/cms_styles.css",
|
||||||
"/entcms/cms_scripts.js",
|
"/cms_scripts.js",
|
||||||
"/entcms/menu.ui",
|
"/menu.ui",
|
||||||
"/entcms/api/submit_lead.dspy",
|
"/api/submit_lead.dspy",
|
||||||
"/entcms/api/get_config.dspy",
|
"/api/get_config.dspy",
|
||||||
"/entcms/api/get_published_content.dspy",
|
"/api/get_published_content.dspy",
|
||||||
"/entcms/api/get_content_detail.dspy",
|
"/api/get_content_detail.dspy",
|
||||||
"/entcms/api/get_sections.dspy",
|
"/api/get_sections.dspy",
|
||||||
]
|
]
|
||||||
|
|
||||||
# ─── webmaster — 内容/分类/栏目/配置/线索 全部CRUD ───
|
# ─── webmaster — 内容/分类/栏目/配置/线索 全部CRUD ───
|
||||||
webmaster_paths = [
|
webmaster_paths = [
|
||||||
"/entcms",
|
"/admin.ui",
|
||||||
"/entcms/admin.ui",
|
|
||||||
# 内容
|
# 内容
|
||||||
"/entcms/cms_content_list", "/entcms/cms_content_list/%",
|
"/cms_content_list", "/cms_content_list/%",
|
||||||
"/entcms/api/cms_content_create.dspy",
|
"/api/cms_content_create.dspy",
|
||||||
"/entcms/api/cms_content_update.dspy",
|
"/api/cms_content_update.dspy",
|
||||||
"/entcms/api/cms_content_delete.dspy",
|
"/api/cms_content_delete.dspy",
|
||||||
"/entcms/api/cms_content_list.dspy",
|
"/api/cms_content_list.dspy",
|
||||||
# 分类
|
# 分类
|
||||||
"/entcms/cms_categories_list", "/entcms/cms_categories_list/%",
|
"/cms_categories_list", "/cms_categories_list/%",
|
||||||
"/entcms/api/cms_categories_create.dspy",
|
"/api/cms_categories_create.dspy",
|
||||||
"/entcms/api/cms_categories_update.dspy",
|
"/api/cms_categories_update.dspy",
|
||||||
"/entcms/api/cms_categories_delete.dspy",
|
"/api/cms_categories_delete.dspy",
|
||||||
"/entcms/api/cms_categories_list.dspy",
|
"/api/cms_categories_list.dspy",
|
||||||
"/entcms/api/category_options.dspy",
|
"/api/category_options.dspy",
|
||||||
# 栏目
|
# 栏目
|
||||||
"/entcms/cms_sections_list", "/entcms/cms_sections_list/%",
|
"/cms_sections_list", "/cms_sections_list/%",
|
||||||
"/entcms/api/cms_sections_create.dspy",
|
"/api/cms_sections_create.dspy",
|
||||||
"/entcms/api/cms_sections_update.dspy",
|
"/api/cms_sections_update.dspy",
|
||||||
"/entcms/api/cms_sections_delete.dspy",
|
"/api/cms_sections_delete.dspy",
|
||||||
"/entcms/api/cms_sections_list.dspy",
|
"/api/cms_sections_list.dspy",
|
||||||
# 站点配置
|
# 站点配置
|
||||||
"/entcms/cms_site_config_list", "/entcms/cms_site_config_list/%",
|
"/cms_site_config_list", "/cms_site_config_list/%",
|
||||||
"/entcms/api/cms_site_config_create.dspy",
|
"/api/cms_site_config_create.dspy",
|
||||||
"/entcms/api/cms_site_config_update.dspy",
|
"/api/cms_site_config_update.dspy",
|
||||||
"/entcms/api/cms_site_config_delete.dspy",
|
"/api/cms_site_config_delete.dspy",
|
||||||
"/entcms/api/cms_site_config_list.dspy",
|
"/api/cms_site_config_list.dspy",
|
||||||
# 线索管理
|
# 线索管理
|
||||||
"/entcms/cms_leads_list", "/entcms/cms_leads_list/%",
|
"/cms_leads_list", "/cms_leads_list/%",
|
||||||
"/entcms/api/cms_leads_create.dspy",
|
"/api/cms_leads_create.dspy",
|
||||||
"/entcms/api/cms_leads_update.dspy",
|
"/api/cms_leads_update.dspy",
|
||||||
"/entcms/api/cms_leads_delete.dspy",
|
"/api/cms_leads_delete.dspy",
|
||||||
"/entcms/api/cms_leads_list.dspy",
|
"/api/cms_leads_list.dspy",
|
||||||
# 审批
|
# 审批
|
||||||
"/entcms/api/submit_content_approval.dspy",
|
"/api/submit_content_approval.dspy",
|
||||||
]
|
]
|
||||||
|
|
||||||
# ─── reviewer — 查看内容 + 审批(只改status) ───
|
# ─── reviewer — 查看内容 + 审批(只改status) ───
|
||||||
reviewer_paths = [
|
reviewer_paths = [
|
||||||
"/entcms",
|
"/admin.ui",
|
||||||
"/entcms/admin.ui",
|
"/cms_content_list", "/cms_content_list/%",
|
||||||
"/entcms/cms_content_list", "/entcms/cms_content_list/%",
|
"/api/cms_content_list.dspy",
|
||||||
"/entcms/api/cms_content_list.dspy",
|
"/api/cms_content_update.dspy",
|
||||||
"/entcms/api/cms_content_update.dspy", # 仅更新status字段
|
"/api/category_options.dspy",
|
||||||
"/entcms/api/category_options.dspy",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# ─── supervisor — 查看全部 + 审批配置 + 线索管理 ───
|
# ─── supervisor — 查看全部 + 审批配置 + 线索管理 ───
|
||||||
supervisor_paths = [
|
supervisor_paths = [
|
||||||
"/entcms",
|
"/admin.ui",
|
||||||
"/entcms/admin.ui",
|
"/cms_content_list", "/cms_content_list/%",
|
||||||
# 只读
|
"/cms_categories_list", "/cms_categories_list/%",
|
||||||
"/entcms/cms_content_list", "/entcms/cms_content_list/%",
|
"/cms_sections_list", "/cms_sections_list/%",
|
||||||
"/entcms/cms_categories_list", "/entcms/cms_categories_list/%",
|
"/cms_site_config_list", "/cms_site_config_list/%",
|
||||||
"/entcms/cms_sections_list", "/entcms/cms_sections_list/%",
|
"/api/cms_content_list.dspy",
|
||||||
"/entcms/cms_site_config_list", "/entcms/cms_site_config_list/%",
|
"/api/cms_categories_list.dspy",
|
||||||
# 列表API(只读)
|
"/api/cms_sections_list.dspy",
|
||||||
"/entcms/api/cms_content_list.dspy",
|
"/api/cms_site_config_list.dspy",
|
||||||
"/entcms/api/cms_categories_list.dspy",
|
"/api/category_options.dspy",
|
||||||
"/entcms/api/cms_sections_list.dspy",
|
"/cms_leads_list", "/cms_leads_list/%",
|
||||||
"/entcms/api/cms_site_config_list.dspy",
|
"/api/cms_leads_create.dspy",
|
||||||
"/entcms/api/category_options.dspy",
|
"/api/cms_leads_update.dspy",
|
||||||
# 线索全权
|
"/api/cms_leads_delete.dspy",
|
||||||
"/entcms/cms_leads_list", "/entcms/cms_leads_list/%",
|
"/api/cms_leads_list.dspy",
|
||||||
"/entcms/api/cms_leads_create.dspy",
|
"/api/submit_content_approval.dspy",
|
||||||
"/entcms/api/cms_leads_update.dspy",
|
|
||||||
"/entcms/api/cms_leads_delete.dspy",
|
|
||||||
"/entcms/api/cms_leads_list.dspy",
|
|
||||||
# 审批
|
|
||||||
"/entcms/api/submit_content_approval.dspy",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# ─── customer-support — 线索查看和更新 ───
|
# ─── customer-support — 线索查看和更新 ───
|
||||||
support_paths = [
|
support_paths = [
|
||||||
"/entcms",
|
"/admin.ui",
|
||||||
"/entcms/admin.ui",
|
"/cms_leads_list", "/cms_leads_list/%",
|
||||||
"/entcms/cms_leads_list", "/entcms/cms_leads_list/%",
|
"/api/cms_leads_list.dspy",
|
||||||
"/entcms/api/cms_leads_list.dspy",
|
"/api/cms_leads_update.dspy",
|
||||||
"/entcms/api/cms_leads_update.dspy",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
print("=== entcms RBAC权限配置 ===")
|
print("=== CMS RBAC权限配置 ===")
|
||||||
print(f"\n--- any (匿名用户) ---")
|
print(f"\n--- any (匿名用户) ---")
|
||||||
run("any", any_paths)
|
run("any", any_paths)
|
||||||
print(f"\n--- owner.webmaster (内容管理员) ---")
|
print(f"\n--- owner.webmaster (内容管理员) ---")
|
||||||
|
|||||||
@ -47,7 +47,7 @@
|
|||||||
"actiontype": "urlwidget",
|
"actiontype": "urlwidget",
|
||||||
"target": "app.sage_main_content",
|
"target": "app.sage_main_content",
|
||||||
"options": {
|
"options": {
|
||||||
"url": "{{entire_url('/entcms/cms_content_list')}}"
|
"url": "{{entire_url('/cms_content_list')}}"
|
||||||
},
|
},
|
||||||
"mode": "replace"
|
"mode": "replace"
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@
|
|||||||
"actiontype": "urlwidget",
|
"actiontype": "urlwidget",
|
||||||
"target": "app.sage_main_content",
|
"target": "app.sage_main_content",
|
||||||
"options": {
|
"options": {
|
||||||
"url": "{{entire_url('/entcms/cms_sections_list')}}"
|
"url": "{{entire_url('/cms_sections_list')}}"
|
||||||
},
|
},
|
||||||
"mode": "replace"
|
"mode": "replace"
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@
|
|||||||
"actiontype": "urlwidget",
|
"actiontype": "urlwidget",
|
||||||
"target": "app.sage_main_content",
|
"target": "app.sage_main_content",
|
||||||
"options": {
|
"options": {
|
||||||
"url": "{{entire_url('/entcms/cms_categories_list')}}"
|
"url": "{{entire_url('/cms_categories_list')}}"
|
||||||
},
|
},
|
||||||
"mode": "replace"
|
"mode": "replace"
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@
|
|||||||
"actiontype": "urlwidget",
|
"actiontype": "urlwidget",
|
||||||
"target": "app.sage_main_content",
|
"target": "app.sage_main_content",
|
||||||
"options": {
|
"options": {
|
||||||
"url": "{{entire_url('/entcms/cms_leads_list')}}"
|
"url": "{{entire_url('/cms_leads_list')}}"
|
||||||
},
|
},
|
||||||
"mode": "replace"
|
"mode": "replace"
|
||||||
}
|
}
|
||||||
@ -267,7 +267,7 @@
|
|||||||
"actiontype": "urlwidget",
|
"actiontype": "urlwidget",
|
||||||
"target": "app.sage_main_content",
|
"target": "app.sage_main_content",
|
||||||
"options": {
|
"options": {
|
||||||
"url": "{{entire_url('/entcms/cms_site_config_list')}}"
|
"url": "{{entire_url('/cms_site_config_list')}}"
|
||||||
},
|
},
|
||||||
"mode": "replace"
|
"mode": "replace"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,37 +6,37 @@
|
|||||||
{
|
{
|
||||||
"name": "cms_content_list",
|
"name": "cms_content_list",
|
||||||
"label": "内容管理",
|
"label": "内容管理",
|
||||||
"url": "{{entire_url('/entcms/cms_content_list')}}",
|
"url": "{{entire_url('/cms_content_list')}}",
|
||||||
"target": "app.sage_main_content"
|
"target": "app.sage_main_content"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cms_sections_list",
|
"name": "cms_sections_list",
|
||||||
"label": "栏目管理",
|
"label": "栏目管理",
|
||||||
"url": "{{entire_url('/entcms/cms_sections_list')}}",
|
"url": "{{entire_url('/cms_sections_list')}}",
|
||||||
"target": "app.sage_main_content"
|
"target": "app.sage_main_content"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cms_categories_list",
|
"name": "cms_categories_list",
|
||||||
"label": "内容分类",
|
"label": "内容分类",
|
||||||
"url": "{{entire_url('/entcms/cms_categories_list')}}",
|
"url": "{{entire_url('/cms_categories_list')}}",
|
||||||
"target": "app.sage_main_content"
|
"target": "app.sage_main_content"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cms_leads_list",
|
"name": "cms_leads_list",
|
||||||
"label": "商机线索",
|
"label": "商机线索",
|
||||||
"url": "{{entire_url('/entcms/cms_leads_list')}}",
|
"url": "{{entire_url('/cms_leads_list')}}",
|
||||||
"target": "app.sage_main_content"
|
"target": "app.sage_main_content"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cms_site_config_list",
|
"name": "cms_site_config_list",
|
||||||
"label": "站点配置",
|
"label": "站点配置",
|
||||||
"url": "{{entire_url('/entcms/cms_site_config_list')}}",
|
"url": "{{entire_url('/cms_site_config_list')}}",
|
||||||
"target": "app.sage_main_content"
|
"target": "app.sage_main_content"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "public_site",
|
"name": "public_site",
|
||||||
"label": "官网预览",
|
"label": "官网预览",
|
||||||
"url": "{{entire_url('/entcms/index.ui')}}",
|
"url": "{{entire_url('/index.ui')}}",
|
||||||
"target": "app.sage_main_content"
|
"target": "app.sage_main_content"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
17
pyproject.toml
Normal file
17
pyproject.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=45", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "kaiyuan-cms"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "开元云科技企业官网CMS系统 — 独立Web应用"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = [
|
||||||
|
"sqlor",
|
||||||
|
"bricks_for_python",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["."]
|
||||||
|
include = ["entcms*", "dingdingflow*"]
|
||||||
@ -1,15 +1,41 @@
|
|||||||
"""
|
"""
|
||||||
初始化超级用户
|
初始化超级用户
|
||||||
用法: cd ~/repos/sage && ./py3/bin/python ~/repos/cms/scripts/init_superuser.py [username] [password]
|
用法:
|
||||||
|
CMS环境: py3/bin/python scripts/init_superuser.py [username] [password]
|
||||||
|
Sage环境: cd ~/repos/sage && ./py3/bin/python ~/repos/cms/scripts/init_superuser.py [username] [password]
|
||||||
默认: admin / admin123
|
默认: admin / admin123
|
||||||
"""
|
"""
|
||||||
import os, sys, asyncio
|
import os, sys, asyncio
|
||||||
|
|
||||||
|
# 自动检测运行环境
|
||||||
|
def find_workdir():
|
||||||
|
"""查找应用根目录"""
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
cms_root = os.path.dirname(script_dir)
|
||||||
|
|
||||||
|
# 检查是否是CMS独立环境
|
||||||
|
if os.path.isdir(os.path.join(cms_root, "py3", "bin")) and \
|
||||||
|
os.path.isfile(os.path.join(cms_root, "conf", "config.json")):
|
||||||
|
return cms_root
|
||||||
|
|
||||||
|
# 检查Sage环境
|
||||||
|
for c in [os.path.expanduser("~/repos/sage"), os.path.expanduser("~/sage")]:
|
||||||
|
if os.path.isdir(os.path.join(c, "py3", "bin")):
|
||||||
|
return c
|
||||||
|
return None
|
||||||
|
|
||||||
|
workdir = find_workdir()
|
||||||
|
if not workdir:
|
||||||
|
print("ERROR: 找不到CMS或Sage应用目录")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 确保Python路径
|
||||||
|
sys.path.insert(0, os.path.join(workdir, "py3", "lib"))
|
||||||
|
os.chdir(workdir)
|
||||||
|
|
||||||
from sqlor.dbpools import DBPools
|
from sqlor.dbpools import DBPools
|
||||||
from appPublic.jsonConfig import getConfig
|
from appPublic.jsonConfig import getConfig
|
||||||
from appPublic.uniqueID import getID
|
from appPublic.uniqueID import getID
|
||||||
|
|
||||||
# Sage环境导入password_encode
|
|
||||||
sys.path.insert(0, os.path.expanduser("~/repos/sage"))
|
|
||||||
from appPublic.password import password_encode
|
from appPublic.password import password_encode
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
@ -23,7 +49,6 @@ async def main():
|
|||||||
existing = await sor.R('users', {'username': username})
|
existing = await sor.R('users', {'username': username})
|
||||||
if existing:
|
if existing:
|
||||||
print(f"用户 {username} 已存在 (id={existing[0]['id']})")
|
print(f"用户 {username} 已存在 (id={existing[0]['id']})")
|
||||||
# 更新密码
|
|
||||||
await sor.U('users', {
|
await sor.U('users', {
|
||||||
'id': existing[0]['id'],
|
'id': existing[0]['id'],
|
||||||
'passwd': password_encode(password)
|
'passwd': password_encode(password)
|
||||||
@ -59,8 +84,7 @@ async def main():
|
|||||||
users = await sor.R('users', {'username': username})
|
users = await sor.R('users', {'username': username})
|
||||||
uid = users[0]['id']
|
uid = users[0]['id']
|
||||||
|
|
||||||
# 查找或创建用户-角色关联 (userrole表)
|
# 分配角色
|
||||||
# 检查userrole表是否存在
|
|
||||||
try:
|
try:
|
||||||
ur = await sor.R('userrole', {'userid': uid, 'roleid': role_id})
|
ur = await sor.R('userrole', {'userid': uid, 'roleid': role_id})
|
||||||
if not ur:
|
if not ur:
|
||||||
@ -76,6 +100,24 @@ async def main():
|
|||||||
print(f"注意: userrole表操作异常: {e}")
|
print(f"注意: userrole表操作异常: {e}")
|
||||||
print("可能需要手动分配角色")
|
print("可能需要手动分配角色")
|
||||||
|
|
||||||
|
# 给superuser分配全部权限
|
||||||
|
try:
|
||||||
|
all_perms = await sor.R('permission', {})
|
||||||
|
for perm in all_perms:
|
||||||
|
existing_rp = await sor.R('rolepermission', {
|
||||||
|
'roleid': role_id,
|
||||||
|
'permid': perm['id']
|
||||||
|
})
|
||||||
|
if not existing_rp:
|
||||||
|
await sor.C('rolepermission', {
|
||||||
|
'id': getID(),
|
||||||
|
'roleid': role_id,
|
||||||
|
'permid': perm['id']
|
||||||
|
})
|
||||||
|
print(f"已将全部 {len(all_perms)} 条权限分配给 owner.superuser")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"注意: 权限分配异常: {e}")
|
||||||
|
|
||||||
print(f"\n登录信息:")
|
print(f"\n登录信息:")
|
||||||
print(f" 用户名: {username}")
|
print(f" 用户名: {username}")
|
||||||
print(f" 密码: {password}")
|
print(f" 密码: {password}")
|
||||||
|
|||||||
11
start.sh
Executable file
11
start.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
# CMS独立应用启动脚本
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
WORKDIR="$(pwd)"
|
||||||
|
PIDFILE="$WORKDIR/cms.pid"
|
||||||
|
|
||||||
|
echo "启动 CMS Web Application (port 9090)..."
|
||||||
|
$WORKDIR/py3/bin/python $WORKDIR/app/cms.py -p 9090 -w $WORKDIR &
|
||||||
|
echo $! > $PIDFILE
|
||||||
|
echo "CMS started (PID: $(cat $PIDFILE))"
|
||||||
|
exit 0
|
||||||
22
stop.sh
Executable file
22
stop.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
# CMS独立应用停止脚本
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
PIDFILE="$(pwd)/cms.pid"
|
||||||
|
|
||||||
|
if [ -f "$PIDFILE" ]; then
|
||||||
|
PID=$(cat "$PIDFILE")
|
||||||
|
if kill -0 "$PID" 2>/dev/null; then
|
||||||
|
echo "停止 CMS (PID: $PID)..."
|
||||||
|
kill "$PID"
|
||||||
|
sleep 2
|
||||||
|
if kill -0 "$PID" 2>/dev/null; then
|
||||||
|
kill -9 "$PID"
|
||||||
|
fi
|
||||||
|
echo "CMS已停止"
|
||||||
|
else
|
||||||
|
echo "CMS进程已不存在"
|
||||||
|
fi
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
else
|
||||||
|
echo "未找到PID文件"
|
||||||
|
fi
|
||||||
Loading…
x
Reference in New Issue
Block a user