feat: complete pccs application scaffold per web-application-spec
- add app/pccs.py (webapp entry with ServerEnv/load_rbac/load_pybricks) - add conf/config.json (password_key, logger, databases, website processors) - add .env (environment variables for deployment) - add .gitignore - rewrite build.sh per spec (xls2ddl, json2ddl, xls2ui, symlinks, systemd)
This commit is contained in:
parent
b5b5784df5
commit
43695b238d
19
.env
Normal file
19
.env
Normal file
@ -0,0 +1,19 @@
|
||||
# pccs 环境变量
|
||||
# 部署时根据实际环境修改
|
||||
|
||||
# 工作目录
|
||||
PCCS_HOME=/d/pccs
|
||||
|
||||
# 数据库 (如不在 config.json 中配置可通过环境变量覆盖)
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_USER=test
|
||||
# DB_PASSWORD=test123
|
||||
# DB_NAME=pccs
|
||||
|
||||
# Python 路径
|
||||
PYTHONPATH=$PCCS_HOME/repos/ahserver:$PCCS_HOME/repos/apppublic:$PCCS_HOME/repos/sqlor:$PCCS_HOME/repos/appbase:$PCCS_HOME/repos/rbac:$PCCS_HOME/repos/bricks_for_python:$PYTHONPATH
|
||||
|
||||
# NFS 共享存储端点 (供集群节点使用)
|
||||
NFS_SERVER=pccs.opencomputing.cn
|
||||
NFS_PATH=/d
|
||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
venv/
|
||||
logs/
|
||||
files/
|
||||
*.log
|
||||
.env.local
|
||||
40
app/pccs.py
Normal file
40
app/pccs.py
Normal file
@ -0,0 +1,40 @@
|
||||
"""
|
||||
pccs — 算力中心集群系统 (Pooled Computing Cluster System)
|
||||
入口: webapp(init) 模式
|
||||
"""
|
||||
import os, sys
|
||||
|
||||
from ahserver.webapp import webapp
|
||||
from ahserver.serverenv import ServerEnv
|
||||
|
||||
import bricks_for_python
|
||||
from bricks_for_python.init import load_pybricks
|
||||
from appbase.init import load_appbase
|
||||
from rbac.init import load_rbac
|
||||
|
||||
WORKDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def get_module_dbname(m):
|
||||
"""所有模块共用 pccs 数据库"""
|
||||
return 'pccs'
|
||||
|
||||
|
||||
def password_encode(s):
|
||||
if s is None:
|
||||
return ''
|
||||
from ahserver.globalEnv import password_encode as _orig
|
||||
return _orig(s)
|
||||
|
||||
|
||||
def init():
|
||||
env = ServerEnv() # MUST be before load_rbac
|
||||
env.get_module_dbname = get_module_dbname
|
||||
env.password_encode = password_encode
|
||||
load_appbase()
|
||||
load_rbac()
|
||||
load_pybricks()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
webapp(init, workdir=WORKDIR)
|
||||
523
build.sh
523
build.sh
@ -1,279 +1,208 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# pccs build.sh — 一键部署脚本
|
||||
# 在部署节点 (pccs@pccs.opencomputing.cn) 上直接运行:
|
||||
# pccs build.sh — 一键部署脚本 (符合 web-application-spec)
|
||||
#
|
||||
# 在部署节点上运行:
|
||||
# cd /d/pccs/repos/pccs && bash build.sh
|
||||
#
|
||||
# 前提:
|
||||
# 1. /d 已作为 NFS 共享盘挂载
|
||||
# 2. MariaDB 已安装, 数据库 pccs 已创建, 用户 test/test123 已授权
|
||||
# 3. 部署节点已安装 git, python3.10+, pip
|
||||
# - MariaDB 已安装, pccs 库已创建, test/test123 已授权
|
||||
# - /d 已作为 NFS 共享盘导出
|
||||
# - git, python3.10+, pip, npm 已安装
|
||||
# ============================================================
|
||||
set -e
|
||||
|
||||
SERVER_PATH="${SERVER_PATH:-/d/pccs}"
|
||||
REPOS_DIR="${SERVER_PATH}/repos"
|
||||
VENV_DIR="${SERVER_PATH}/venv"
|
||||
CONF_DIR="${SERVER_PATH}/conf"
|
||||
LOG_DIR="${SERVER_PATH}/logs"
|
||||
RUN_DIR="${SERVER_PATH}/run"
|
||||
APP_PORT="${APP_PORT:-9180}"
|
||||
# --------------- 配置 ---------------
|
||||
WORKDIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CDIR="${WORKDIR}" # 应用根目录 (/d/pccs)
|
||||
PKGDIR="${CDIR}/repos" # 模块 clone 目录
|
||||
VENV="${CDIR}/venv"
|
||||
APPNAME="pccs"
|
||||
APP_PORT=9180
|
||||
|
||||
# --------------- 模块列表 ---------------
|
||||
# 共享底层库 (pip install)
|
||||
SHARED_LIBS=(
|
||||
apppublic
|
||||
sqlor
|
||||
ahserver
|
||||
appbase
|
||||
rbac
|
||||
)
|
||||
# pccs 业务模块 (pip install)
|
||||
PCCS_MODULES=(
|
||||
pcpool
|
||||
pcc
|
||||
storage_mgr
|
||||
image_mgr
|
||||
)
|
||||
# 前端 bricks (需要构建)
|
||||
BRICKS_DIR="${REPOS_DIR}/bricks"
|
||||
# Git 仓库基地址
|
||||
GIT_BASE="https://git.opencomputing.cn/yumoqing"
|
||||
|
||||
# --------------- 颜色输出 ---------------
|
||||
# --------------- 颜色 ---------------
|
||||
red() { echo -e "\033[31m$1\033[0m"; }
|
||||
green() { echo -e "\033[32m$1\033[0m"; }
|
||||
blue() { echo -e "\033[34m$1\033[0m"; }
|
||||
|
||||
echo "=========================================="
|
||||
blue "PCCS 算力中心集群系统 — 一键部署"
|
||||
echo "=========================================="
|
||||
echo "部署路径: ${SERVER_PATH}"
|
||||
echo "监听端口: ${APP_PORT}"
|
||||
echo ""
|
||||
blue "=========================================="
|
||||
blue " pccs — 算力中心集群系统 部署"
|
||||
blue "=========================================="
|
||||
|
||||
# ============================================================
|
||||
# Phase 0: 创建目录结构
|
||||
# Phase 0: 创建目录
|
||||
# ============================================================
|
||||
blue "[Phase 0] 创建目录结构..."
|
||||
mkdir -p "${REPOS_DIR}" "${VENV_DIR}" "${CONF_DIR}" "${LOG_DIR}" "${RUN_DIR}"
|
||||
green " 目录结构已创建"
|
||||
mkdir -p "${CDIR}/app" "${CDIR}/conf" "${CDIR}/files" "${CDIR}/logs" \
|
||||
"${CDIR}/wwwroot/imgs" "${PKGDIR}"
|
||||
|
||||
# ============================================================
|
||||
# Phase 1: 安装系统依赖
|
||||
# ============================================================
|
||||
blue "[Phase 1] 安装系统依赖..."
|
||||
blue "[1/10] 系统依赖..."
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq python3-venv python3-pip python3-dev build-essential nfs-common
|
||||
green " 系统依赖已安装"
|
||||
sudo apt-get install -y -qq python3-venv python3-pip python3-dev \
|
||||
build-essential nfs-common mysql-client
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Clone/Pull 所有仓库 (HTTPS, 只读)
|
||||
# Phase 2: Python 虚拟环境
|
||||
# ============================================================
|
||||
blue "[Phase 2] Clone/Pull 代码仓库..."
|
||||
|
||||
GIT_BASE="https://git.opencomputing.cn/yumoqing"
|
||||
ALL_REPOS=("${SHARED_LIBS[@]}" "${PCCS_MODULES[@]}" "bricks" "pccs")
|
||||
|
||||
for repo in "${ALL_REPOS[@]}"; do
|
||||
if [ -d "${REPOS_DIR}/${repo}/.git" ]; then
|
||||
echo " [pull] ${repo}..."
|
||||
(cd "${REPOS_DIR}/${repo}" && git pull origin main 2>/dev/null) || true
|
||||
else
|
||||
echo " [clone] ${repo}..."
|
||||
git clone "${GIT_BASE}/${repo}.git" "${REPOS_DIR}/${repo}" 2>/dev/null || {
|
||||
red " 警告: ${repo} clone 失败, 跳过"
|
||||
}
|
||||
fi
|
||||
done
|
||||
green " 代码仓库已同步"
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: 创建 Python 虚拟环境
|
||||
# ============================================================
|
||||
blue "[Phase 3] 创建 Python 虚拟环境..."
|
||||
python3 -m venv "${VENV_DIR}" --clear
|
||||
PIP="${VENV_DIR}/bin/pip"
|
||||
PYTHON="${VENV_DIR}/bin/python"
|
||||
# 升级 pip
|
||||
blue "[2/10] Python 虚拟环境..."
|
||||
python3 -m venv "${VENV}" --clear
|
||||
PIP="${VENV}/bin/pip"
|
||||
PYTHON="${VENV}/bin/python"
|
||||
"${PIP}" install --upgrade pip setuptools wheel -q
|
||||
green " 虚拟环境已创建: ${VENV_DIR}"
|
||||
# 先安装 xls2ddl (spec 要求, 用于 DDL 生成和数据导入)
|
||||
"${PIP}" install xls2ddl -q 2>/dev/null || true
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: 安装共享底层库 (develop 模式)
|
||||
# Phase 3: Clone/Pull 所有模块
|
||||
# ============================================================
|
||||
blue "[Phase 4] 安装共享底层库..."
|
||||
for lib in "${SHARED_LIBS[@]}"; do
|
||||
lib_path="${REPOS_DIR}/${lib}"
|
||||
if [ -f "${lib_path}/setup.py" ] || [ -f "${lib_path}/setup.cfg" ] || [ -f "${lib_path}/pyproject.toml" ]; then
|
||||
echo " [install] ${lib}..."
|
||||
"${PIP}" install -e "${lib_path}" -q 2>&1 | tail -1
|
||||
blue "[3/10] 同步代码仓库..."
|
||||
ALL_REPOS=(
|
||||
# 基础层
|
||||
apppublic sqlor ahserver
|
||||
# 数据库层
|
||||
appbase rbac
|
||||
# 前端
|
||||
bricks bricks_for_python
|
||||
# 业务模块
|
||||
pcpool pcc storage_mgr image_mgr
|
||||
# 主应用
|
||||
pccs
|
||||
)
|
||||
for repo in "${ALL_REPOS[@]}"; do
|
||||
if [ -d "${PKGDIR}/${repo}/.git" ]; then
|
||||
(cd "${PKGDIR}/${repo}" && git pull origin main 2>/dev/null) || true
|
||||
else
|
||||
echo " [skip] ${lib} — 无 setup"
|
||||
echo " clone ${repo}..."
|
||||
git clone "${GIT_BASE}/${repo}.git" "${PKGDIR}/${repo}" 2>/dev/null || {
|
||||
red " WARN: ${repo} clone failed"
|
||||
}
|
||||
fi
|
||||
done
|
||||
green " 共享底层库已安装"
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: 安装 pccs 业务模块 (develop 模式)
|
||||
# Phase 4: pip install 所有模块
|
||||
# ============================================================
|
||||
blue "[Phase 5] 安装 pccs 业务模块..."
|
||||
for mod in "${PCCS_MODULES[@]}"; do
|
||||
mod_path="${REPOS_DIR}/${mod}"
|
||||
if [ -f "${mod_path}/setup.json" ] || [ -f "${mod_path}/setup.py" ] || [ -f "${mod_path}/setup.cfg" ]; then
|
||||
echo " [install] ${mod}..."
|
||||
"${PIP}" install -e "${mod_path}" -q 2>&1 | tail -1
|
||||
else
|
||||
echo " [skip] ${mod} — 无 setup"
|
||||
blue "[4/10] 安装 Python 模块..."
|
||||
MODULES=(
|
||||
apppublic sqlor ahserver
|
||||
appbase rbac
|
||||
bricks_for_python
|
||||
pcpool pcc storage_mgr image_mgr
|
||||
)
|
||||
for m in "${MODULES[@]}"; do
|
||||
mp="${PKGDIR}/${m}"
|
||||
if [ -f "${mp}/setup.py" ] || [ -f "${mp}/setup.cfg" ] || [ -f "${mp}/pyproject.toml" ]; then
|
||||
"${PIP}" install -e "${mp}" -q 2>&1 | tail -1
|
||||
fi
|
||||
done
|
||||
green " pccs 业务模块已安装"
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: 构建 Bricks 前端
|
||||
# Phase 5: 数据库 DDL — json2ddl 生成建表 SQL
|
||||
# ============================================================
|
||||
blue "[Phase 6] 构建 Bricks 前端..."
|
||||
if [ -f "${BRICKS_DIR}/build.sh" ]; then
|
||||
echo " 构建 bricks.js..."
|
||||
(cd "${BRICKS_DIR}" && npm install --silent 2>/dev/null && bash build.sh 2>&1 | tail -3) || {
|
||||
red " 警告: bricks 构建失败, 前端可能不可用"
|
||||
}
|
||||
fi
|
||||
green " Bricks 前端已构建"
|
||||
blue "[5/10] 数据库 DDL 生成..."
|
||||
# 收集所有有 models/ 的模块
|
||||
DB_MODULES=(appbase rbac pcpool pcc storage_mgr image_mgr)
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: 创建配置文件
|
||||
# ============================================================
|
||||
blue "[Phase 7] 创建配置..."
|
||||
cat > "${CONF_DIR}/config.json" <<CONFEOF
|
||||
{
|
||||
"workdir": "${SERVER_PATH}",
|
||||
"host": "0.0.0.0",
|
||||
"port": ${APP_PORT},
|
||||
"static": "${REPOS_DIR}/pccs/wwwroot",
|
||||
"databases": {
|
||||
"pccs": {
|
||||
"db": "mysql",
|
||||
"host": "127.0.0.1",
|
||||
"port": 3306,
|
||||
"user": "test",
|
||||
"password": "test123",
|
||||
"database": "pccs",
|
||||
"charset": "utf8mb4",
|
||||
"minsize": 2,
|
||||
"maxsize": 10
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
{"name": "appbase", "path": "${REPOS_DIR}/appbase"},
|
||||
{"name": "rbac", "path": "${REPOS_DIR}/rbac"},
|
||||
{"name": "pcpool", "path": "${REPOS_DIR}/pcpool"},
|
||||
{"name": "pcc", "path": "${REPOS_DIR}/pcc"},
|
||||
{"name": "pccs", "path": "${REPOS_DIR}/pccs"},
|
||||
{"name": "storage_mgr", "path": "${REPOS_DIR}/storage_mgr"},
|
||||
{"name": "image_mgr", "path": "${REPOS_DIR}/image_mgr"}
|
||||
],
|
||||
"module_dbname": "pccs",
|
||||
"hot_reload": true
|
||||
}
|
||||
CONFEOF
|
||||
green " 配置已生成: ${CONF_DIR}/config.json"
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: 初始化数据库表
|
||||
# ============================================================
|
||||
blue "[Phase 8] 初始化数据库表..."
|
||||
|
||||
# 收集所有模块的 models/ 目录
|
||||
MODEL_DIRS=()
|
||||
for mod in "${PCCS_MODULES[@]}" "appbase" "rbac"; do
|
||||
mdir="${REPOS_DIR}/${mod}/models"
|
||||
for m in "${DB_MODULES[@]}"; do
|
||||
mdir="${PKGDIR}/${m}/models"
|
||||
if [ -d "$mdir" ]; then
|
||||
MODEL_DIRS+=("$mdir")
|
||||
has_json=$(ls "$mdir"/*.json 2>/dev/null | wc -l)
|
||||
has_xlsx=$(ls "$mdir"/*.xlsx 2>/dev/null | wc -l)
|
||||
if [ "$has_json" -gt 0 ]; then
|
||||
echo " [${m}] json2ddl → DDL"
|
||||
(cd "$mdir" && "${VENV}/bin/json2ddl" mysql . > /tmp/pccs_${m}.ddl.sql 2>/dev/null) || true
|
||||
mysql -u test -ptest123 pccs < /tmp/pccs_${m}.ddl.sql 2>/dev/null || true
|
||||
fi
|
||||
if [ "$has_xlsx" -gt 0 ]; then
|
||||
echo " [${m}] xls2ddl → DDL"
|
||||
(cd "$mdir" && xls2ddl mysql . > /tmp/pccs_${m}_xlsx.ddl.sql 2>/dev/null) || true
|
||||
mysql -u test -ptest123 pccs < /tmp/pccs_${m}_xlsx.ddl.sql 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 用 Python 脚本创建表
|
||||
"${PYTHON}" - "$SERVER_PATH" "${MODEL_DIRS[@]}" <<'PYEOF'
|
||||
import sys, os, json, asyncio
|
||||
sys.path.insert(0, sys.argv[1] + '/repos/sqlor')
|
||||
sys.path.insert(0, sys.argv[1] + '/repos/apppublic')
|
||||
|
||||
from appPublic.jsonConfig import getConfig
|
||||
from sqlor.dbpools import DBPools
|
||||
from sqlor.ddl_template_mysql import DDLTemplateMySQL
|
||||
|
||||
async def create_tables():
|
||||
config = getConfig(sys.argv[1])
|
||||
dbname = 'pccs'
|
||||
db = DBPools(config.databases)
|
||||
ddl = DDLTemplateMySQL()
|
||||
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
# 检查已有表
|
||||
existing = await sor.sqlExe("SHOW TABLES", {})
|
||||
existing_tables = set(r[0] for r in existing)
|
||||
|
||||
model_dirs = sys.argv[2:]
|
||||
created = 0
|
||||
for mdir in model_dirs:
|
||||
if not os.path.isdir(mdir):
|
||||
continue
|
||||
for f in sorted(os.listdir(mdir)):
|
||||
if not f.endswith('.json'):
|
||||
continue
|
||||
fpath = os.path.join(mdir, f)
|
||||
try:
|
||||
model = json.load(open(fpath))
|
||||
except Exception:
|
||||
continue
|
||||
tblname = model.get('summary', [{}])[0].get('name', '')
|
||||
if not tblname:
|
||||
continue
|
||||
if tblname in existing_tables:
|
||||
print(f' [exists] {tblname}')
|
||||
continue
|
||||
fields = model.get('fields', [])
|
||||
sql = ddl.create_table(tblname, fields)
|
||||
print(f' [create] {tblname}')
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
try:
|
||||
await sor.sqlExe(sql, {})
|
||||
created += 1
|
||||
except Exception as e:
|
||||
print(f' ERROR: {e}')
|
||||
|
||||
print(f' 共创建 {created} 张表')
|
||||
|
||||
asyncio.run(create_tables())
|
||||
PYEOF
|
||||
green " 数据库表已初始化"
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 9: 加载种子数据 (appcode / RBAC)
|
||||
# Phase 6: 导入初始数据 (data/ 目录)
|
||||
# ============================================================
|
||||
blue "[Phase 9] 加载种子数据..."
|
||||
blue "[6/10] 初始数据导入..."
|
||||
for m in "${DB_MODULES[@]}"; do
|
||||
ddir="${PKGDIR}/${m}/data"
|
||||
if [ -d "$ddir" ]; then
|
||||
for xlsx in "$ddir"/*.xlsx; do
|
||||
[ -f "$xlsx" ] || continue
|
||||
echo " [${m}] dbloader → ${xlsx}"
|
||||
"${VENV}/bin/dbloader" "${CDIR}" pccs "$xlsx" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
done
|
||||
green " OK"
|
||||
|
||||
"${PYTHON}" - "$SERVER_PATH" <<'PYEOF'
|
||||
# ============================================================
|
||||
# Phase 7: CRUD 生成 — xls2ui
|
||||
# ============================================================
|
||||
blue "[7/10] CRUD 界面生成..."
|
||||
for m in "${DB_MODULES[@]}"; do
|
||||
jdir="${PKGDIR}/${m}/json"
|
||||
mdir="${PKGDIR}/${m}/models"
|
||||
odir="${PKGDIR}/${m}/wwwroot"
|
||||
if [ -d "$jdir" ] && [ -d "$mdir" ]; then
|
||||
echo " [${m}] xls2ui..."
|
||||
(cd "$jdir" && "${VENV}/bin/xls2ui" -m "$mdir" -o "$odir" "$m" *.json 2>/dev/null) || true
|
||||
fi
|
||||
done
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: 创建 wwwroot 软链接
|
||||
# ============================================================
|
||||
blue "[8/10] 创建 wwwroot 软链接..."
|
||||
for m in "${DB_MODULES[@]}"; do
|
||||
wdir="${PKGDIR}/${m}/wwwroot"
|
||||
if [ -d "$wdir" ]; then
|
||||
ln -sfn "$wdir" "${CDIR}/wwwroot/${m}" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
# pccs 自身的 wwwroot
|
||||
ln -sfn "${PKGDIR}/pccs/wwwroot" "${CDIR}/wwwroot/${APPNAME}" 2>/dev/null || true
|
||||
|
||||
# Bricks 前端
|
||||
if [ -f "${PKGDIR}/bricks/build.sh" ]; then
|
||||
blue "[bricks] 构建前端..."
|
||||
(cd "${PKGDIR}/bricks" && npm install --silent 2>/dev/null && bash build.sh 2>&1 | tail -3) || true
|
||||
ln -sfn "${PKGDIR}/bricks/dist" "${CDIR}/wwwroot/bricks" 2>/dev/null || true
|
||||
fi
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 9: 种子应用数据 (appcode / RBAC)
|
||||
# ============================================================
|
||||
blue "[9/10] 种子数据..."
|
||||
"${PYTHON}" - "${CDIR}" "${PKGDIR}" <<'PYEOF'
|
||||
import sys, os, json
|
||||
sys.path.insert(0, sys.argv[1] + '/repos/apppublic')
|
||||
sys.path.insert(0, sys.argv[1] + '/repos/sqlor')
|
||||
sys.path.insert(0, sys.argv[1] + '/repos/ahserver')
|
||||
|
||||
CDIR = sys.argv[1]
|
||||
PKGDIR = sys.argv[2]
|
||||
|
||||
sys.path.insert(0, os.path.join(PKGDIR, 'apppublic'))
|
||||
sys.path.insert(0, os.path.join(PKGDIR, 'sqlor'))
|
||||
sys.path.insert(0, os.path.join(PKGDIR, 'ahserver'))
|
||||
|
||||
from appPublic.jsonConfig import getConfig
|
||||
from sqlor.dbpools import DBPools
|
||||
from appPublic.uniqueID import getID
|
||||
import datetime
|
||||
import datetime, asyncio
|
||||
|
||||
async def seed_data():
|
||||
config = getConfig(sys.argv[1])
|
||||
dbname = 'pccs'
|
||||
db = DBPools(config.databases)
|
||||
now = datetime.datetime.now().isoformat()
|
||||
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
# 检查 appcodes_kv 是否有 cluster_type 数据
|
||||
rows = await sor.R('appcodes_kv', {'parentid': 'cluster_type'})
|
||||
if not rows:
|
||||
codes = [
|
||||
APPCODES = [
|
||||
('cluster_type', 'k8s', 'Kubernetes集群'),
|
||||
('cluster_type', 'slurm', 'Slurm集群'),
|
||||
('cluster_type', 'ray', 'Ray集群'),
|
||||
@ -329,102 +258,96 @@ async def seed_data():
|
||||
('sync_status', 'synced', '已同步'),
|
||||
('sync_status', 'failed', '失败'),
|
||||
]
|
||||
for parentid, k, v in codes:
|
||||
await sor.C('appcodes_kv', {
|
||||
'id': getID(),
|
||||
'parentid': parentid,
|
||||
'k': k,
|
||||
'v': v,
|
||||
'created_at': now,
|
||||
})
|
||||
print(f' 已写入 {len(codes)} 条 appcode')
|
||||
else:
|
||||
|
||||
async def seed():
|
||||
config = getConfig(CDIR)
|
||||
db = DBPools(config.databases)
|
||||
now = datetime.datetime.now().isoformat()
|
||||
async with db.sqlorContext('pccs') as sor:
|
||||
rows = await sor.R('appcodes_kv', {'parentid': 'cluster_type'})
|
||||
if rows:
|
||||
print(' appcode 已存在, 跳过')
|
||||
return
|
||||
for parentid, k, v in APPCODES:
|
||||
await sor.C('appcodes_kv', {
|
||||
'id': getID(), 'parentid': parentid,
|
||||
'k': k, 'v': v, 'created_at': now,
|
||||
})
|
||||
print(f' 已写入 {len(APPCODES)} 条 appcode')
|
||||
|
||||
asyncio.run(seed_data())
|
||||
asyncio.run(seed())
|
||||
PYEOF
|
||||
green " 种子数据已加载"
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 10: 创建启动脚本
|
||||
# Phase 10: 创建 systemd 服务 + 启动脚本
|
||||
# ============================================================
|
||||
blue "[Phase 10] 创建启动脚本..."
|
||||
cat > "${RUN_DIR}/start.sh" <<STARTEOF
|
||||
blue "[10/10] 服务配置..."
|
||||
|
||||
# start.sh
|
||||
cat > "${CDIR}/start.sh" <<STARTEOF
|
||||
#!/usr/bin/env bash
|
||||
# pccs 应用启动脚本
|
||||
set -e
|
||||
|
||||
SERVER_PATH="${SERVER_PATH}"
|
||||
VENV_DIR="${VENV_DIR}"
|
||||
CONF_DIR="${CONF_DIR}"
|
||||
LOG_DIR="${LOG_DIR}"
|
||||
|
||||
cd "\${SERVER_PATH}"
|
||||
export PYTHONPATH="\${REPOS_DIR}/ahserver:\${REPOS_DIR}/apppublic:\${REPOS_DIR}/sqlor:\${REPOS_DIR}/appbase:\${REPOS_DIR}/rbac:\${PYTHONPATH}"
|
||||
|
||||
echo "Starting pccs on port ${APP_PORT}..."
|
||||
exec "\${VENV_DIR}/bin/python" -c "
|
||||
import sys, os
|
||||
sys.path.insert(0, '${REPOS_DIR}/ahserver')
|
||||
sys.path.insert(0, '${REPOS_DIR}/apppublic')
|
||||
sys.path.insert(0, '${REPOS_DIR}/sqlor')
|
||||
sys.path.insert(0, '${REPOS_DIR}/appbase')
|
||||
sys.path.insert(0, '${REPOS_DIR}/rbac')
|
||||
|
||||
from ahserver.configuredServer import ConfiguredServer
|
||||
|
||||
class PCCSAuthAPI:
|
||||
'''pccs 认证 API — 开发阶段允许所有访问'''
|
||||
def needAuth(self, path):
|
||||
# 开发阶段: 仅登录页面需要认证
|
||||
if '/api/' in path:
|
||||
return False
|
||||
return False
|
||||
|
||||
async def getPermissionNeed(self, path):
|
||||
return 'any'
|
||||
|
||||
async def checkUserPassword(self, user_id, password):
|
||||
return True
|
||||
|
||||
async def getUserPermissions(self, user):
|
||||
return ['any', 'logined', 'reseller.operator']
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = ConfiguredServer(PCCSAuthAPI, workdir='${SERVER_PATH}')
|
||||
server.run()
|
||||
" 2>&1 | tee -a "\${LOG_DIR}/pccs.log"
|
||||
cd "${CDIR}"
|
||||
source "${CDIR}/.env" 2>/dev/null || true
|
||||
export PYTHONPATH="${PKGDIR}/ahserver:${PKGDIR}/apppublic:${PKGDIR}/sqlor:${PKGDIR}/appbase:${PKGDIR}/rbac:${PKGDIR}/bricks_for_python:\$PYTHONPATH"
|
||||
exec "${VENV}/bin/python" "${CDIR}/app/${APPNAME}.py" 2>&1 | tee -a "${CDIR}/logs/${APPNAME}.log"
|
||||
STARTEOF
|
||||
chmod +x "${RUN_DIR}/start.sh"
|
||||
green " 启动脚本: ${RUN_DIR}/start.sh"
|
||||
chmod +x "${CDIR}/start.sh"
|
||||
|
||||
# stop.sh
|
||||
cat > "${CDIR}/stop.sh" <<STOPEOF
|
||||
#!/usr/bin/env bash
|
||||
pkill -f "${CDIR}/app/${APPNAME}.py" 2>/dev/null && echo "pccs stopped" || echo "pccs not running"
|
||||
STOPEOF
|
||||
chmod +x "${CDIR}/stop.sh"
|
||||
|
||||
# systemd service
|
||||
sudo tee /etc/systemd/system/pccs.service > /dev/null <<SERVEOF
|
||||
[Unit]
|
||||
Description=PCCS 算力中心集群系统
|
||||
After=network.target mariadb.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$(whoami)
|
||||
Group=$(id -gn)
|
||||
WorkingDirectory=${CDIR}
|
||||
ExecStart=${CDIR}/start.sh
|
||||
ExecStop=${CDIR}/stop.sh
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
StandardOutput=append:${CDIR}/logs/pccs.log
|
||||
StandardError=append:${CDIR}/logs/pccs.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SERVEOF
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable pccs 2>/dev/null || true
|
||||
green " OK"
|
||||
|
||||
# ============================================================
|
||||
# Phase 11: 停止旧进程, 启动应用
|
||||
# 启动应用
|
||||
# ============================================================
|
||||
blue "[Phase 11] 启动应用..."
|
||||
# 停止旧进程
|
||||
pkill -f "ahserver.*${APP_PORT}" 2>/dev/null || true
|
||||
blue "启动应用..."
|
||||
"${CDIR}/stop.sh" 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# 后台启动
|
||||
nohup bash "${RUN_DIR}/start.sh" > "${LOG_DIR}/nohup.log" 2>&1 &
|
||||
nohup bash "${CDIR}/start.sh" > "${CDIR}/logs/nohup.log" 2>&1 &
|
||||
sleep 3
|
||||
|
||||
# 健康检查
|
||||
if curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:${APP_PORT}/" 2>/dev/null | grep -q '200\|302\|404'; then
|
||||
green " pccs 应用已启动: http://127.0.0.1:${APP_PORT}"
|
||||
if curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:${APP_PORT}/" 2>/dev/null | grep -q '200\|404'; then
|
||||
green " pccs 已启动: http://127.0.0.1:${APP_PORT}"
|
||||
else
|
||||
red " 警告: 应用可能未正常启动, 检查日志: tail -f ${LOG_DIR}/pccs.log"
|
||||
red " WARN: 无响应, 检查日志: tail -f ${CDIR}/logs/pccs.log"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 完成
|
||||
# ============================================================
|
||||
echo ""
|
||||
green "=========================================="
|
||||
green " pccs 部署完成!"
|
||||
green " 访问地址: http://$(hostname -I | awk '{print $1}'):${APP_PORT}"
|
||||
green " 日志文件: ${LOG_DIR}/pccs.log"
|
||||
green " 启动命令: bash ${RUN_DIR}/start.sh"
|
||||
green " 停止命令: pkill -f 'ahserver.*${APP_PORT}'"
|
||||
green " 部署完成!"
|
||||
green " 访问: http://$(hostname -I | awk '{print $1}'):${APP_PORT}"
|
||||
green " 启动: sudo systemctl start pccs"
|
||||
green " 停止: sudo systemctl stop pccs"
|
||||
green " 日志: tail -f ${CDIR}/logs/pccs.log"
|
||||
green "=========================================="
|
||||
|
||||
45
conf/config.json
Normal file
45
conf/config.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"password_key": "pccs_secret_key_change_in_production",
|
||||
"logger": {
|
||||
"name": "pccs",
|
||||
"level": "debug",
|
||||
"file": "$[workdir]$/logs/pccs.log"
|
||||
},
|
||||
"filesroot": "$[workdir]$/files",
|
||||
"databases": {
|
||||
"pccs": {
|
||||
"driver": "mysql",
|
||||
"kwargs": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 3306,
|
||||
"user": "test",
|
||||
"password": "test123",
|
||||
"db": "pccs",
|
||||
"charset": "utf8mb4",
|
||||
"minsize": 2,
|
||||
"maxsize": 10,
|
||||
"autocommit": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"website": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 9180,
|
||||
"client_max_size": 104857600,
|
||||
"processors": [
|
||||
[".tmpl", "tmpl"],
|
||||
[".ui", "bui"],
|
||||
[".dspy", "dspy"],
|
||||
[".wss", "ws"]
|
||||
],
|
||||
"indexes": ["index.ui", "index.html"],
|
||||
"session": {
|
||||
"session_max_time": 86400,
|
||||
"session_issue_time": 7200
|
||||
},
|
||||
"statics": [
|
||||
["/bricks/", "$[workdir]$/wwwroot/bricks/"],
|
||||
["/imgs/", "$[workdir]$/wwwroot/imgs/"]
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user