feat: build.sh 按 webapp-deploy 技能重写一键部署(venv+clone全模块+四步安装+/healthz门禁);加 start/stop;/healthz钩子;密码加密
This commit is contained in:
parent
93dadab835
commit
8057ba2888
@ -53,5 +53,22 @@ def init():
|
|||||||
load_world_sync()
|
load_world_sync()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ── /healthz 健康检查(部署门禁:GET /healthz 必须 200)──
|
||||||
|
# ahapp_built 钩子在应用构建完成时由 ConfiguredServer 触发,拿到 aiohttp app 注册路由。
|
||||||
|
from appPublic.registerfunction import RegisterFunction
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
|
||||||
|
async def _on_ahapp_built(app):
|
||||||
|
async def healthz(request):
|
||||||
|
return web.json_response({"status": "ok", "app": "scense", "port": 9380})
|
||||||
|
app.router.add_get("/healthz", healthz)
|
||||||
|
|
||||||
|
|
||||||
|
RegisterFunction().register("ahapp_built", _on_ahapp_built)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
webapp(init)
|
webapp(init)
|
||||||
|
|||||||
171
build.sh
Normal file → Executable file
171
build.sh
Normal file → Executable file
@ -1,73 +1,88 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 唤景平台世界模式(scense)一键部署脚本 —— 测试环境
|
# 唤景平台世界模式(scense)一键部署脚本 —— 测试环境
|
||||||
# 部署目录 ~/scense_app,应用端口 9380,数据库 localhost:3306/scense (test/test123)
|
# 部署目录 ~/scense_app,应用端口 9380,数据库 localhost:3306/scense
|
||||||
|
# 规范:产线技能库 webapp-deploy(含「模块安装四步」)
|
||||||
#
|
#
|
||||||
# 按 web-application-spec 的 8 步完整集成:
|
# 步骤:
|
||||||
# 1) 代码导入(git clone 模块到 pkgs/) 2) 依赖安装(pip install -e)
|
# 0) venv
|
||||||
# 3) 前端软链 wwwroot/{模块} 4) i18n 合并
|
# 1) 创建 pkgs/,全部模块 git clone 进来(基础共享包 + 6 业务模块;禁止 mv 源码)
|
||||||
# 5) 建表(json2ddl) 6) CRUD 生成(xls2ui)
|
# 2) pip install 全部模块(-e,失败即退出)
|
||||||
# 7) 初始化数据 8) 运行挂载(load_{模块})
|
# 3) bricks 前端构建 + wwwroot/bricks 软链
|
||||||
|
# 4) 建库 + 基础框架表与初始业务表(scripts/ddl.sql,幂等 IF NOT EXISTS)
|
||||||
|
# 5) i18n 合并
|
||||||
|
# 6) 逐模块四步安装:① models/json2ddl 建表 ② json/build.sh(xls2ui) ③ wwwroot 软链 ④ 数据导入
|
||||||
|
# 7) 应用代码同步 + runtime 目录
|
||||||
|
# 8) 重启 + 健康检查(/healthz 必须 200,否则 exit 1)
|
||||||
|
# 硬性约束:set -euo pipefail,任何一步失败立即终止,禁止带病启动。
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
APP_NAME="scense"
|
|
||||||
APP_PORT=9380
|
|
||||||
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
DEPLOY_DIR="${DEPLOY_DIR:-$HOME/scense_app}"
|
DEPLOY_DIR="${DEPLOY_DIR:-$HOME/scense_app}"
|
||||||
|
VENV="${DEPLOY_DIR}/venv"
|
||||||
|
APP_PORT="${APP_PORT:-9380}"
|
||||||
DB_HOST="${DB_HOST:-localhost}"
|
DB_HOST="${DB_HOST:-localhost}"
|
||||||
DB_PORT="${DB_PORT:-3306}"
|
DB_PORT="${DB_PORT:-3306}"
|
||||||
DB_NAME="${DB_NAME:-scense}"
|
DB_NAME="${DB_NAME:-scense}"
|
||||||
DB_USER="${DB_USER:-test}"
|
DB_USER="${DB_USER:-test}"
|
||||||
DB_PASSWORD="${DB_PASSWORD:-test123}"
|
DB_PASSWORD="${DB_PASSWORD:-test123}"
|
||||||
|
GIT_HTTPS="https://git.opencomputing.cn/yumoqing"
|
||||||
|
|
||||||
# 基础模块(框架层)与业务模块
|
FOUNDATION_PKGS="apppublic sqlor ahserver rbac xls2ddl appbase bricks-for-python bricks"
|
||||||
FOUNDATION_MODULES="apppublic sqlor ahserver bricks"
|
|
||||||
DB_MODULES="appbase rbac"
|
|
||||||
BUSINESS_MODULES="world scene entity script_engine world_snapshot world_sync"
|
BUSINESS_MODULES="world scene entity script_engine world_snapshot world_sync"
|
||||||
|
|
||||||
echo "==> [1/8] sync code"
|
APP_SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
mkdir -p "${DEPLOY_DIR}/app" "${DEPLOY_DIR}/conf" "${DEPLOY_DIR}/wwwroot" \
|
mkdir -p "${DEPLOY_DIR}"
|
||||||
"${DEPLOY_DIR}/files" "${DEPLOY_DIR}/logs" "${DEPLOY_DIR}/pkgs"
|
|
||||||
rsync -avz --delete \
|
|
||||||
"${APP_DIR}/app/" "${DEPLOY_DIR}/app/" \
|
|
||||||
"${APP_DIR}/conf/" "${DEPLOY_DIR}/conf/" \
|
|
||||||
"${APP_DIR}/wwwroot/" "${DEPLOY_DIR}/wwwroot/" \
|
|
||||||
"${APP_DIR}/scripts/" "${DEPLOY_DIR}/scripts/"
|
|
||||||
|
|
||||||
echo "==> [2/8] clone + install modules"
|
echo "==> [0/9] venv"
|
||||||
|
if [ ! -x "${VENV}/bin/python" ]; then
|
||||||
|
python3 -m venv "${VENV}"
|
||||||
|
fi
|
||||||
|
"${VENV}/bin/pip" install -q --upgrade pip >/dev/null 2>&1 || true
|
||||||
|
"${VENV}/bin/pip" install -q wheel >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "==> [1/9] clone modules into pkgs/ (git clone only, no mv)"
|
||||||
cd "${DEPLOY_DIR}"
|
cd "${DEPLOY_DIR}"
|
||||||
mkdir -p pkgs
|
mkdir -p pkgs
|
||||||
for m in ${FOUNDATION_MODULES} ${DB_MODULES} ${BUSINESS_MODULES}; do
|
for m in ${FOUNDATION_PKGS} ${BUSINESS_MODULES}; do
|
||||||
if [ ! -d "pkgs/${m}" ]; then
|
if [ ! -d "pkgs/${m}/.git" ]; then
|
||||||
git clone "git@git.opencomputing.cn:org/${m}.git" "pkgs/${m}" || \
|
rm -rf "pkgs/${m}"
|
||||||
git clone "https://git.opencomputing.cn/org/${m}.git" "pkgs/${m}" || true
|
git clone -q "${GIT_HTTPS}/${m}.git" "pkgs/${m}" || { echo "ERROR: clone ${m} failed" >&2; exit 1; }
|
||||||
fi
|
echo " cloned ${m}"
|
||||||
if [ -d "pkgs/${m}" ]; then
|
|
||||||
(cd "pkgs/${m}" && git pull --ff-only || true)
|
|
||||||
if [ -f "pkgs/${m}/pyproject.toml" ] || [ -f "pkgs/${m}/setup.py" ]; then
|
|
||||||
pip install -e "pkgs/${m}" || true
|
|
||||||
else
|
else
|
||||||
export PYTHONPATH="${DEPLOY_DIR}/pkgs/${m}:${PYTHONPATH:-}"
|
git -C "pkgs/${m}" pull -q --ff-only || echo "WARN: pull ${m} failed, using existing"
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "==> [3/8] wwwroot symlinks"
|
echo "==> [2/9] pip install all modules"
|
||||||
for m in ${DB_MODULES} ${BUSINESS_MODULES}; do
|
for m in ${FOUNDATION_PKGS} ${BUSINESS_MODULES}; do
|
||||||
if [ -d "pkgs/${m}/wwwroot" ]; then
|
if [ -f "pkgs/${m}/pyproject.toml" ] || [ -f "pkgs/${m}/setup.py" ] || [ -f "pkgs/${m}/setup.cfg" ]; then
|
||||||
ln -sfn "../pkgs/${m}/wwwroot" "wwwroot/${m}"
|
"${VENV}/bin/pip" install -q -e "pkgs/${m}" || { echo "ERROR: pip install ${m} failed" >&2; exit 1; }
|
||||||
fi
|
echo " installed ${m}"
|
||||||
|
else
|
||||||
|
echo "WARN: ${m} has no packaging metadata, skipped pip install"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
if [ -d "pkgs/bricks/dist" ]; then
|
|
||||||
ln -sfn "../pkgs/bricks/dist" "wwwroot/bricks"
|
echo "==> [3/9] bricks frontend build + symlink"
|
||||||
|
if [ ! -f "pkgs/bricks/dist/bricks.js" ]; then
|
||||||
|
(cd pkgs/bricks/bricks && bash build.sh) || { echo "ERROR: bricks build failed" >&2; exit 1; }
|
||||||
fi
|
fi
|
||||||
|
mkdir -p wwwroot
|
||||||
|
ln -sfn "../pkgs/bricks/dist" "wwwroot/bricks"
|
||||||
|
|
||||||
echo "==> [4/8] i18n merge"
|
echo "==> [4/9] database + foundation/initial tables"
|
||||||
|
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" \
|
||||||
|
-e "CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" \
|
||||||
|
|| { echo "ERROR: create database failed" >&2; exit 1; }
|
||||||
|
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" \
|
||||||
|
< "${APP_SRC_DIR}/scripts/ddl.sql" || { echo "ERROR: ddl.sql failed" >&2; exit 1; }
|
||||||
|
|
||||||
|
echo "==> [5/9] i18n merge"
|
||||||
mkdir -p wwwroot/i18n/zh wwwroot/i18n/en
|
mkdir -p wwwroot/i18n/zh wwwroot/i18n/en
|
||||||
python - <<'PY' || true
|
"${VENV}/bin/python" - <<'PY'
|
||||||
import json, os
|
import json, os
|
||||||
|
ok = 0
|
||||||
for lang in ("zh", "en"):
|
for lang in ("zh", "en"):
|
||||||
merged = {}
|
merged = {}
|
||||||
for m in ("apppublic","sqlor","ahserver","appbase","rbac","world","scene","entity","script_engine","world_snapshot","world_sync"):
|
for m in ("apppublic","sqlor","ahserver","appbase","rbac","world","scene","entity","script_engine","world_snapshot","world_sync"):
|
||||||
@ -79,36 +94,58 @@ for lang in ("zh", "en"):
|
|||||||
k, v = line.split("=", 1)
|
k, v = line.split("=", 1)
|
||||||
merged[k.strip()] = v.strip()
|
merged[k.strip()] = v.strip()
|
||||||
out = f"wwwroot/i18n/{lang}/i18n.json"
|
out = f"wwwroot/i18n/{lang}/i18n.json"
|
||||||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
|
||||||
json.dump(merged, open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
|
json.dump(merged, open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
|
||||||
print("i18n merged")
|
ok += len(merged)
|
||||||
|
print(f"i18n merged: {ok} keys")
|
||||||
PY
|
PY
|
||||||
|
|
||||||
echo "==> [5/8] init db (ddl)"
|
echo "==> [6/9] per-module install: ① json2ddl建表 ② json/build.sh ③ wwwroot软链 ④ 数据导入"
|
||||||
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" \
|
|
||||||
< "${APP_DIR}/scripts/ddl.sql"
|
|
||||||
|
|
||||||
echo "==> [6/8] CRUD generate"
|
|
||||||
for m in ${BUSINESS_MODULES}; do
|
for m in ${BUSINESS_MODULES}; do
|
||||||
if [ -d "pkgs/${m}/json" ] && [ -d "pkgs/${m}/models" ]; then
|
echo " -- ${m}"
|
||||||
(cd "pkgs/${m}" && xls2ui -m ../models -o "${DEPLOY_DIR}/wwwroot" "${m}" json/*.json || true)
|
# ① 建表:模块 models 是唯一事实源;去掉 DROP 行,重跑不丢数据
|
||||||
fi
|
(cd "pkgs/${m}/models" && "${VENV}/bin/json2ddl" mysql .) > "/tmp/${m}_ddl.sql" \
|
||||||
|
|| { echo "ERROR: ${m} json2ddl failed" >&2; exit 1; }
|
||||||
|
[ -s "/tmp/${m}_ddl.sql" ] || { echo "ERROR: ${m} DDL empty" >&2; exit 1; }
|
||||||
|
grep -vi "^drop table" "/tmp/${m}_ddl.sql" > "/tmp/${m}_ddl_nodrop.sql"
|
||||||
|
mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" \
|
||||||
|
< "/tmp/${m}_ddl_nodrop.sql" || { echo "ERROR: ${m} create tables failed" >&2; exit 1; }
|
||||||
|
# ② CRUD 页面(模块仓库自带 json/build.sh)
|
||||||
|
(cd "pkgs/${m}/json" && bash build.sh) || { echo "ERROR: ${m} json/build.sh failed" >&2; exit 1; }
|
||||||
|
# ③ wwwroot 软链(非 cp)
|
||||||
|
ln -sfn "../pkgs/${m}/wwwroot" "wwwroot/${m}"
|
||||||
|
# ④ 初始化数据(有 data/*.xlsx 才导)
|
||||||
|
if ls "pkgs/${m}/data/"*.xlsx >/dev/null 2>&1; then
|
||||||
|
(cd "pkgs/${m}/data" && for f in *.xlsx; do "${VENV}/bin/dbloader" "${DEPLOY_DIR}" "${DB_NAME}" "$f"; done) \
|
||||||
|
|| { echo "ERROR: ${m} dbloader failed" >&2; exit 1; }
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "==> [7/8] init data"
|
echo "==> [7/9] sync app code + runtime dirs"
|
||||||
for m in ${BUSINESS_MODULES}; do
|
for d in app conf scripts wwwroot; do
|
||||||
if [ -d "pkgs/${m}/data" ]; then
|
if [ -d "${APP_SRC_DIR}/${d}" ] && [ "$(realpath "${APP_SRC_DIR}/${d}")" != "$(realpath "${DEPLOY_DIR}/${d}")" ]; then
|
||||||
(cd "pkgs/${m}/data" && for f in *.xlsx; do
|
mkdir -p "${DEPLOY_DIR}/${d}"
|
||||||
[ -e "$f" ] && dbloader "${DEPLOY_DIR}" "${DB_NAME}" "$f" || true
|
cp -a "${APP_SRC_DIR}/${d}/." "${DEPLOY_DIR}/${d}/"
|
||||||
done)
|
fi
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
mkdir -p logs files
|
||||||
|
|
||||||
echo "==> [8/8] restart"
|
echo "==> [8/9] restart service"
|
||||||
if [ -f "${DEPLOY_DIR}/run.sh" ]; then
|
bash "${APP_SRC_DIR}/stop.sh" || true
|
||||||
bash "${DEPLOY_DIR}/run.sh" restart
|
sleep 1
|
||||||
else
|
bash "${APP_SRC_DIR}/start.sh"
|
||||||
echo "warning: run.sh not found, start via: nohup python app/scense.py > logs/scense.log 2>&1 &"
|
|
||||||
|
echo "==> [9/9] health check: GET /healthz"
|
||||||
|
ok=""
|
||||||
|
code=""
|
||||||
|
for i in $(seq 1 15); do
|
||||||
|
code=$(curl -s -o /tmp/scense_healthz.json -w "%{http_code}" "http://127.0.0.1:${APP_PORT}/healthz" || true)
|
||||||
|
if [ "${code}" = "200" ]; then ok=1; break; fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
if [ -z "${ok}" ]; then
|
||||||
|
echo "ERROR: /healthz not 200 after 30s (last=${code:-000}); logs/scense.log tail:" >&2
|
||||||
|
tail -30 logs/scense.log 2>/dev/null >&2 || true
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo "healthz: $(cat /tmp/scense_healthz.json)"
|
||||||
echo "==> done, app port ${APP_PORT}"
|
echo "==> deploy OK: app=scense port=${APP_PORT} healthz=200"
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"app": {"name": "scense", "port": 9380, "title": "唤景平台世界模式"},
|
"app": {
|
||||||
|
"name": "scense",
|
||||||
|
"port": 9380,
|
||||||
|
"title": "唤景平台世界模式"
|
||||||
|
},
|
||||||
"password_key": "scense_test_env_key",
|
"password_key": "scense_test_env_key",
|
||||||
"logger": {
|
"logger": {
|
||||||
"name": "scense",
|
"name": "scense",
|
||||||
@ -14,7 +18,7 @@
|
|||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": 3306,
|
"port": 3306,
|
||||||
"user": "test",
|
"user": "test",
|
||||||
"password": "test123",
|
"password": "Kj2wTl+LYpPtDSk3B01U7w==",
|
||||||
"database": "scense",
|
"database": "scense",
|
||||||
"charset": "utf8mb4"
|
"charset": "utf8mb4"
|
||||||
}
|
}
|
||||||
@ -24,18 +28,48 @@
|
|||||||
"port": 9380,
|
"port": 9380,
|
||||||
"root": "$[workdir]$/wwwroot",
|
"root": "$[workdir]$/wwwroot",
|
||||||
"processors": [
|
"processors": [
|
||||||
[".tmpl", "tmpl"],
|
[
|
||||||
[".ui", "bui"],
|
".tmpl",
|
||||||
[".dspy", "dspy"],
|
"tmpl"
|
||||||
[".wss", "ws"]
|
],
|
||||||
|
[
|
||||||
|
".ui",
|
||||||
|
"bui"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
".dspy",
|
||||||
|
"dspy"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
".wss",
|
||||||
|
"ws"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
"index.ui",
|
||||||
|
"index.html"
|
||||||
],
|
],
|
||||||
"indexes": ["index.ui", "index.html"],
|
|
||||||
"session_max_time": 28800,
|
"session_max_time": 28800,
|
||||||
"session_issue_time": 7200
|
"session_issue_time": 7200,
|
||||||
|
"paths": [
|
||||||
|
[
|
||||||
|
"$[workdir]$/wwwroot",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"modules": [
|
"modules": [
|
||||||
"apppublic", "sqlor", "ahserver", "appbase", "rbac",
|
"apppublic",
|
||||||
"world", "scene", "entity", "script_engine", "world_snapshot", "world_sync"
|
"sqlor",
|
||||||
|
"ahserver",
|
||||||
|
"appbase",
|
||||||
|
"rbac",
|
||||||
|
"world",
|
||||||
|
"scene",
|
||||||
|
"entity",
|
||||||
|
"script_engine",
|
||||||
|
"world_snapshot",
|
||||||
|
"world_sync"
|
||||||
],
|
],
|
||||||
"wwwroot": "wwwroot"
|
"wwwroot": "wwwroot"
|
||||||
}
|
}
|
||||||
155
scripts/ddl.sql
155
scripts/ddl.sql
@ -6,3 +6,158 @@ CREATE TABLE IF NOT EXISTS entity (id BIGINT NOT NULL AUTO_INCREMENT, scene_id B
|
|||||||
CREATE TABLE IF NOT EXISTS script_engine (id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(128) NOT NULL, script_type VARCHAR(32) NOT NULL DEFAULT 'lua', content MEDIUMTEXT, status VARCHAR(16) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_script_name (name)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='脚本引擎';
|
CREATE TABLE IF NOT EXISTS script_engine (id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(128) NOT NULL, script_type VARCHAR(32) NOT NULL DEFAULT 'lua', content MEDIUMTEXT, status VARCHAR(16) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_script_name (name)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='脚本引擎';
|
||||||
CREATE TABLE IF NOT EXISTS world_snapshot (id BIGINT NOT NULL AUTO_INCREMENT, world_id BIGINT NOT NULL, version INT NOT NULL DEFAULT 1, snapshot_json LONGTEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_snapshot_world (world_id, version)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='世界快照';
|
CREATE TABLE IF NOT EXISTS world_snapshot (id BIGINT NOT NULL AUTO_INCREMENT, world_id BIGINT NOT NULL, version INT NOT NULL DEFAULT 1, snapshot_json LONGTEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_snapshot_world (world_id, version)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='世界快照';
|
||||||
CREATE TABLE IF NOT EXISTS world_sync (id BIGINT NOT NULL AUTO_INCREMENT, world_id BIGINT NOT NULL, sync_type VARCHAR(32) NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'pending', payload_json TEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_sync_world (world_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='世界同步';
|
CREATE TABLE IF NOT EXISTS world_sync (id BIGINT NOT NULL AUTO_INCREMENT, world_id BIGINT NOT NULL, sync_type VARCHAR(32) NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'pending', payload_json TEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_sync_world (world_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='世界同步';
|
||||||
|
|
||||||
|
-- ========== 基础框架表(rbac/appbase 单库模式,全新库需先建)==========
|
||||||
|
/*M!999999\- enable the sandbox mode */
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40101 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `users` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '用户id',
|
||||||
|
`username` varchar(255) DEFAULT NULL COMMENT '用户名',
|
||||||
|
`password` varchar(255) DEFAULT NULL COMMENT '密码',
|
||||||
|
`email` varchar(255) DEFAULT NULL COMMENT '邮件地址',
|
||||||
|
`orgid` varchar(32) DEFAULT NULL COMMENT '所属机构',
|
||||||
|
`nick_name` varchar(255) DEFAULT NULL COMMENT '显示名',
|
||||||
|
`address` varchar(255) DEFAULT NULL COMMENT '地址',
|
||||||
|
`mobile` varchar(255) DEFAULT NULL COMMENT '手机',
|
||||||
|
`user_status` varchar(1) DEFAULT '0' COMMENT '用户状态',
|
||||||
|
`created_at` date DEFAULT NULL COMMENT '注册日期',
|
||||||
|
`last_login` timestamp NULL DEFAULT NULL COMMENT '最后登录时间',
|
||||||
|
`login_fail_count` smallint(6) NOT NULL DEFAULT 0 COMMENT '连续登录失败次数',
|
||||||
|
`last_login_fail` timestamp NULL DEFAULT NULL COMMENT '最后登录失败时间',
|
||||||
|
`sync_from` varchar(32) DEFAULT NULL COMMENT '同步应用id',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `users_idx2` (`username`),
|
||||||
|
KEY `users_idx1` (`orgid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `organization` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '机构编码',
|
||||||
|
`orgname` varchar(100) DEFAULT NULL COMMENT '机构名称',
|
||||||
|
`alias_name` varchar(100) DEFAULT NULL COMMENT '机构别名',
|
||||||
|
`contactor` varchar(32) DEFAULT NULL COMMENT '联系人',
|
||||||
|
`contactor_phone` varchar(100) DEFAULT NULL COMMENT '联系人电话',
|
||||||
|
`province_id` varchar(32) DEFAULT NULL COMMENT '所在省id',
|
||||||
|
`city_id` varchar(32) DEFAULT NULL COMMENT '所在城市id',
|
||||||
|
`distinct_id` varchar(32) DEFAULT NULL COMMENT '所在地区id',
|
||||||
|
`emailaddress` varchar(256) DEFAULT NULL COMMENT '邮箱',
|
||||||
|
`address` varchar(400) DEFAULT NULL COMMENT '地址',
|
||||||
|
`main_business` varchar(1000) DEFAULT NULL COMMENT '主营业务描述',
|
||||||
|
`orgcode` varchar(100) DEFAULT NULL COMMENT '组织结构代码',
|
||||||
|
`license_img` varchar(400) DEFAULT NULL COMMENT '营业执照',
|
||||||
|
`id_img` varchar(400) DEFAULT NULL COMMENT '身份证',
|
||||||
|
`parentid` varchar(32) DEFAULT NULL COMMENT '父机构id',
|
||||||
|
`org_type` varchar(32) DEFAULT NULL COMMENT '机构类型',
|
||||||
|
`accountid` varchar(32) DEFAULT NULL COMMENT '账号',
|
||||||
|
`del_flg` varchar(1) DEFAULT '0' COMMENT '删除标志',
|
||||||
|
`create_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '创建时间戳',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='机构';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `orgtypes` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT 'id',
|
||||||
|
`orgid` varchar(32) DEFAULT NULL COMMENT '机构id',
|
||||||
|
`orgtypeid` varchar(32) DEFAULT NULL COMMENT '机构类型',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='机构拥有角色';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `org_roles` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT 'id',
|
||||||
|
`orgid` varchar(32) DEFAULT NULL COMMENT '机构编码',
|
||||||
|
`roleid` varchar(32) DEFAULT NULL COMMENT '角色编码',
|
||||||
|
`del_flg` varchar(1) DEFAULT '0' COMMENT '删除标志',
|
||||||
|
`create_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '创建时间戳',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='机构角色';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `role` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '角色id',
|
||||||
|
`orgtypeid` varchar(32) DEFAULT '0' COMMENT '机构类型',
|
||||||
|
`role` varchar(100) DEFAULT NULL COMMENT '角色',
|
||||||
|
`rolelabel` varchar(100) DEFAULT NULL COMMENT '角色名称',
|
||||||
|
`name` varchar(100) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `role_idx1` (`orgtypeid`,`role`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT=' 角色';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `rolepermission` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT 'id',
|
||||||
|
`roleid` varchar(32) DEFAULT NULL COMMENT '角色id',
|
||||||
|
`permid` varchar(32) DEFAULT NULL COMMENT '权限id',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色权限表';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `userrole` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '用户id',
|
||||||
|
`userid` varchar(32) DEFAULT NULL COMMENT '用户id',
|
||||||
|
`roleid` varchar(32) DEFAULT NULL COMMENT '角色id',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户角色';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `permission` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '权限id',
|
||||||
|
`name` varchar(255) DEFAULT NULL COMMENT '名称',
|
||||||
|
`description` varchar(255) DEFAULT NULL COMMENT '描述',
|
||||||
|
`ptype` varchar(20) DEFAULT NULL COMMENT '类型',
|
||||||
|
`parentid` varchar(32) DEFAULT NULL COMMENT '父权限id',
|
||||||
|
`path` varchar(255) DEFAULT NULL COMMENT '路径',
|
||||||
|
`title` varchar(255) DEFAULT NULL COMMENT '标题',
|
||||||
|
`icon` varchar(255) DEFAULT NULL COMMENT '图标',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `permission_idx1` (`ptype`),
|
||||||
|
KEY `permission_idx2` (`parentid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='权限';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE IF NOT EXISTS `params` (
|
||||||
|
`id` varchar(32) NOT NULL,
|
||||||
|
`params_name` varchar(255) NOT NULL,
|
||||||
|
`params_value` varchar(4000) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
|
|||||||
23
start.sh
Executable file
23
start.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# scense 启动脚本(部署目录 ~/scense_app,端口 9380)
|
||||||
|
set -euo pipefail
|
||||||
|
DEPLOY_DIR="${DEPLOY_DIR:-$HOME/scense_app}"
|
||||||
|
APP_PORT="${APP_PORT:-9380}"
|
||||||
|
cd "${DEPLOY_DIR}"
|
||||||
|
mkdir -p logs
|
||||||
|
|
||||||
|
if pgrep -f "python.*app/scense.py" >/dev/null 2>&1; then
|
||||||
|
echo "scense already running: $(pgrep -f 'python.*app/scense.py' | tr '\n' ' ')"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
nohup "${DEPLOY_DIR}/venv/bin/python" app/scense.py -w "${DEPLOY_DIR}" -p "${APP_PORT}" \
|
||||||
|
>> logs/scense.log 2>&1 </dev/null &
|
||||||
|
sleep 2
|
||||||
|
pid=$(pgrep -f "python.*app/scense.py" | head -1 || true)
|
||||||
|
if [ -z "${pid}" ]; then
|
||||||
|
echo "ERROR: scense failed to start; logs/scense.log tail:" >&2
|
||||||
|
tail -30 logs/scense.log >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "scense started: pid=${pid} port=${APP_PORT}"
|
||||||
18
stop.sh
Executable file
18
stop.sh
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# scense 停止脚本(优雅停止,保留日志)
|
||||||
|
pids=$(pgrep -f "python.*app/scense.py" || true)
|
||||||
|
if [ -z "${pids}" ]; then
|
||||||
|
echo "scense not running"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
pkill -TERM -f "python.*app/scense.py" || true
|
||||||
|
sleep 2
|
||||||
|
pids=$(pgrep -f "python.*app/scense.py" || true)
|
||||||
|
if [ -n "${pids}" ]; then
|
||||||
|
sleep 2
|
||||||
|
pids=$(pgrep -f "python.*app/scense.py" || true)
|
||||||
|
if [ -n "${pids}" ]; then
|
||||||
|
echo "WARN: still alive: ${pids}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "scense stopped"
|
||||||
Loading…
x
Reference in New Issue
Block a user