1) project-directory-spec 新增第八章「部署环境信息(env/)」:
- 唯一事实源 projects/{项目名}/env/{test,prod}.json;禁止 apps/ 下放部署凭据
(人事项目7 实测三份 env 矛盾:项目 9187 / 应用 9288+postgresql / 实际 mariadb)
- 固定字段 schema;db.engine 必须与 DDL 方言一致
- 铁律:环境信息由人提供,agent 不得编造;禁「待明确」占位当交付;缺字段冒泡 need_info
- PM 在 need_info 未答结时不得 review_rollback(回退上游解决不了外部输入缺口)
2) qc/review-develop 新增第六章「应用脚手架硬门禁」(原技能只有模块检查,
应用脚手架零检查项,Flask 因此两次过审):
- 6.1 入口必须 ahserver,grep 命中 Flask/FastAPI/Django/jsonify 直接 reject
- 6.2 基础模块 appbase/rbac 必须真正调 load_XXX(),只 import 标 loaded 属反模式
- 6.3 端口三处一致 6.4 DDL 方言与 engine 一致 6.5 依赖完整性
3) web-application-spec 补「基础模块只 import 不调 load_XXX()」反模式警示
17 KiB
| name | version | description | trigger_conditions | |||
|---|---|---|---|---|---|---|
| web-application-spec | 1.0.0 | 开发「应用脚手架」时必读——应用是唯一部署单元(一个入口 app/{应用名}.py + 一个端口),init() 里定义 get_module_dbname 挂 ServerEnv 再逐个 load_{模块}() 挂载业务模块,含 conf/config.json、build.sh 一键部署。开发「业务模块」(Python 包)时不要用本技能,改用 module-development-spec。 |
|
Web Application Specification
Overview
This specification defines the standard architecture for web applications built using:
- Frontend: bricks framework
- Backend: ahserver application framework
- Foundation: apppublic and sqlor modules
- Optional Modules: appbase, rbac, and other developed modules
All web applications must follow this exact structure and configuration pattern.
框架铁律(不可违反,禁止让 LLM 猜)
-
应用入口必须用 ahserver:
app/{应用名}.py必须from ahserver.webapp import webapp,末尾webapp(init)启动。绝对禁止 Flask / http.server / Django / FastAPI 等任何其它 Web 框架。原因:bricks 的.ui界面只有 ahserver 的 BricksUIProcessor 能渲染,用 Flask/http.server 会导致模块.ui界面(DataViewer CRUD 等)无法渲染,根路径/只剩一段 JSON 元信息——「测试环境界面显示 JSON 就停」的直接根因(2026-08-24 人事项目6 hrs6 实测:develop 用 Flask 写 app/hrs6.py,design 在 architecture.md 写「基础模块 ahserver 直接复用」,实现偏离设计)。 -
应用必须有主页 UI:
wwwroot/index.ui必须是完整 bricks widget 树(顶栏 + 导航菜单 + 主内容区),访问/渲染出可交互界面。绝对禁止占位敷衍(如 index.ui 只写「卡片导航」4 个字)。主页是应用门面,不是 JSON 元信息接口。
Required Dependencies
Core Dependencies (Mandatory)
pip install git+https://git.opencomputing.cn/yumoqing/apppublic
pip install git+https://git.opencomputing.cn/yumoqing/sqlor
pip install git+https://git.opencomputing.cn/yumoqing/ahserver
Database Web Application Dependencies (If using database)
pip install git+https://git.opencomputing.cn/yumoqing/appbase
pip install git+https://git.opencomputing.cn/yumoqing/rbac
应用本地仓库位置
应用本地仓库在机构工作空间 apps/{应用名}/(见 project-directory-spec)。应用是唯一部署单元——一个应用一个仓库、一个入口(app/{应用名}.py)、一个端口,业务模块通过 load_{模块}() 挂到应用这个唯一入口下。
部署测试前必须给应用仓库设置远程仓库(git remote add origin <远程地址>),否则部署时无法 git pull 拉取最新代码。
Application Directory Structure
${appname}/
├── app/
│ └── ${appname}.py # Main application entry point
├── conf/
│ └── config.json # Application configuration
├── files/ # File storage directory
├── logs/ # Log files directory
├── wwwroot/
│ ├── imgs/ # SVG and image files
│ └── bricks/ # Bricks framework distribution
├── build.sh # Initialization and deployment script
└── .env # Environment variables
File Specifications
app/${appname}.py (Main Application)
from ahserver.webapp import webapp
from ahserver.serverenv import ServerEnv
import bricks_for_python # registers bui processor
from bricks_for_python.init import load_pybricks # registers UiWindow etc.
from appbase.init import load_appbase
from rbac.init import load_rbac
def get_module_dbname(m):
# 返回模块 m 对应的数据库名。库名由应用统一决定,模块禁止硬编码 DBNAME(见 module-development-spec)。
# 实现方式:应用级「模块名 → 库名」映射,从 appbase params 表 / conf/config.json 读,或直接映射表。
# 例:单库应用所有模块返回同一主库名;多库应用按模块返回各自库名。勿写死 'dbname'/'hrs6'。
return module_dbname_map(m)
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()
# ── 业务模块导入(关键:一个应用一个入口一个端口,所有模块挂在这里)──
from organization.init import load_organization
from payroll.init import load_payroll
load_organization()
load_payroll()
if __name__ == '__main__':
webapp(init)
应用对模块的完整导入(关键,build.sh 一键部署必须覆盖):一个应用 = 一个入口(
app/${appname}.py)= 一个端口。业务模块(organization/payroll/recruitment/...)是 Python 包,不是独立服务。应用通过 build.sh 把各模块完整集成进来,步骤如下:
- 代码导入(git pull):模块从 git clone 到
pkgs/{模块名}/(基础包 apppublic/sqlor/ahserver/rbac/xls2ddl/appbase/bricks 同)。禁止手动复制源码。- 依赖安装(pip install):
pip install -e pkgs/{模块名}(或pip install)安装所有模块;无 pyproject.toml 的用 PYTHONPATH。- 前端链接(wwwroot 软链):
ln -sf ../pkgs/{模块名}/wwwroot wwwroot/{模块名}(软链接非 cp,保持同步)。- i18n 导入:merge_i18n.py 合并各模块
i18n/{zh,en}/msg.txt→wwwroot/i18n/{lang}/i18n.json(i18n_getmsgs.dspy 供 bricks.js 读取)。- 建表(DDL):json2ddl 从模块
models/*.json生成mysql.ddl.sql,执行建表。- CRUD 生成:xls2ui 从模块
json/*.json生成 CRUD(.dspy + .ui)。- 初始化数据:
python scripts/init_data.py导入模块init/data.json(appcodes 等种子数据)。- 运行挂载(load_{module}()):应用
app/{appname}.py的init()里逐个load_{module}()(见上)。不要给每个模块单独建
app.py、单独占端口、单独起服务——模块不是独立部署单元,应用才是。以上 8 步是应用一键部署正确执行的完整清单,缺一不可。
🔴 反模式:基础模块只 import 不调 load_XXX()
appbase和rbac同样有load_XXX()(appbase/init.py: load_appbase()、rbac/init.py: load_rbac()),必须像业务模块一样显式调用。只import包不等于已挂载。# ❌ 错误:只 import 就当成 loaded(rbac 权限/登录、appbase params 全没注册到 ServerEnv) for name in ["apppublic","sqlor","ahserver","appbase","rbac"]: pkg = importlib.import_module(name) status = "loaded" # ← 误报成功,掩盖问题 # ✅ 正确:import 后取 loader 并调用 from appbase.init import load_appbase from rbac.init import load_rbac env = ServerEnv() # ← 必须在 load_rbac() 之前 load_appbase(); load_rbac(); load_pybricks()症状:应用能起、
/healthz也可能 200,但登录必失败(rbac 未注册)、读 params 报错(appbase 未注册),日志却显示基础模块「loaded」。加载顺序固定:
ServerEnv()→ 基础模块(appbase/rbac/pybricks)→ 业务模块。基础模块晚于业务模块 load,业务模块拿不到权限/配置。(2026-08-25 人事项目7 hrs7 实测:
load_base_modules()对 5 个基础模块只importlib.import_module就标status="loaded",而同文件的load_business_modules()却正确地getattr(pkg, loader)+ 调用——两套逻辑不一致,基础能力全部缺失。)
conf/config.json (Configuration)
Must include the following sections:
password_key: System encryption keylogger: Logging configuration with name, level, and file pathfilesroot: File storage root path using$[workdir]$placeholderdatabases: Database connection configurations with driver and kwargswebsite: Web server configuration including paths, processors, session settings
Key Configuration Patterns:
- Use
$[workdir]$placeholder for work directory references - Database names in
databasessection must matchget_module_name()return values - Critical: processors must include
.tmpl→tmpl. The BricksUIProcessor (handler for.uifiles) internally loads/bricks/header.tmpland other templates. Without[".tmpl", "tmpl"], the app returns 500'NoneType' has no attribute 'be_call'on/and/index.ui:"processors": [ [".tmpl", "tmpl"], [".ui", "bui"], [".dspy", "dspy"] ] - Website processors must include
.ui→buiand.dspy→dspymappings - Website indexes: Must include
"index.ui"for bricks-based apps. Without it, visiting/or/module/returns 500 "invalid path" because ahserver cannot resolve directory URLs:"indexes": ["index.ui", "index.html"] - WebSocket support: Must include
[".wss", "ws"]in processors list, otherwise.wssfiles are served as static files and WebSocket connections fail. Nginx/wss/block strips the prefix viaproxy_pass, so the backend receives the path without/wss(e.g.,/module/file.wss).
WebSocket Configuration Details
When adding WebSocket (.wss) endpoints to a module:
-
Backend config.json: Add
[".wss", "ws"]towebsite.processors:"processors": [ [".wss", "ws"], [".ws", "ws"], [".ui", "bui"], [".dspy", "dspy"] ] -
Nginx: The
/wss/location block handles WebSocket upgrade and strips the prefix:location /wss/ { proxy_pass http://localhost:9180/; # strips /wss prefix proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } -
Frontend: Connect to
/wss/module/file.wss(with/wssprefix):var url = protocol + '//' + host + '/wss/module/file.wss'; var ws = new WebSocket(url); // browser auto-sends cookie -
RBAC permissions: Register paths without
/wssprefix (nginx strips it):python set_role_perm.py "logined" "/module/file.wss" -
Common pitfall: If ahserver logs
handle as a normal filefor a.wssrequest, the[".wss","ws"]processor mapping is missing fromconfig.json. ahserver only recognizes extensions explicitly listed inprocessors.
build.sh (Deployment Script)
The build script must perform these operations in order:
A) Module Dependencies Setup
- Install xls2ddl tool: After creating virtual environment, install xls2ddl first:
pip install xls2ddl - Module Installation: Clone and pip install all required modules
- Database DDL Generation: For modules with
models/directory:- If models contain
.xlsxfiles:cd models/ xls2ddl mysql . > mysql.ddl.sql mysql -h db -u[user] -p[password] [dbname] < mysql.ddl.sql - If models contain
.jsonfiles:cd models/ json2ddl mysql . > mysql.ddl.sql mysql -h db -u[user] -p[password] [dbname] < mysql.ddl.sql
- If models contain
- Initial Data Import: For modules with
data/directory:dbloader $cdir [dbname] data.xlsx - CRUD Generation: For modules with
json/directory:cd json/ xls2ui -m ../models -o ../wwwroot ${modulename} *.json - wwwroot Symlinking: Create symbolic links for each module:
ln -s $cdir/pkgs/$m/wwwroot $cdir/wwwroot/$m
B) Bricks Framework Setup
- Execute bricks build.sh and symlink dist directory:
ln -s $cdir/pkgs/bricks/dist $cdir/wwwroot/bricks
C) System Service Configuration
- Create systemd service file with proper user/group context
- Generate start.sh and stop.sh scripts with environment loading
- Enable and start the service on system boot
D) Cron Job Management
- Add application-specific cron jobs without duplication
.env (Environment Variables)
- Export all runtime environment variables required by the application
- Include database credentials, API keys, and system paths
Build Script Requirements
Directory Creation
- Create
pkgs/directory for module clones - Create
logs/directory for application logging
Module Processing Order
- Install foundation modules (apppublic, sqlor, ahserver, bricks)
- Clone and process database modules (appbase, rbac):
- Clone from Git repositories to pkgs/ directory
- Install via pip
- Generate DDL from models/ directory (.xlsx or .json files)
- Generate CRUD UI from json/ directory using xls2ui
- Install business modules (accounting, pricing, contract_management, etc.) following the same pattern as database modules
Database Operations
- Process all modules that have
models/,json/, ordata/directories (including appbase and rbac) - Use proper error handling for database operations
- Maintain database isolation per module when configured
Symbolic Links
- All module wwwroot directories must be symlinked to application wwwroot
- Bricks dist directory must be symlinked to wwwroot/bricks
Service Management
- Create proper systemd service with logging redirection
- Implement graceful start/stop scripts
- Set appropriate file permissions (chmod +x for scripts)
Bricks UI Development Pitfalls
See references/ragserver-pitfalls.md for complete debugging recipes.
See references/sage-module-pitfalls.md for SQL column names, params_kw=None, deployment, and error investigation rules.
- Widget
idat top level (NOT inoptions) - DOM order for
app.<id>resolution .tmplprocessor required in configbricks_for_pythonimport +load_pybricks()call- DataViewer response format (
{status, data:{rows,total}}) - Menu widget target pattern
- Float model fields needing
length+dec
Widget id at Top Level (Not in options)
// CORRECT
{"widgettype":"VBox","id":"main_content","options":{"css":"filler"}}
// WRONG — id inside options is ignored
{"widgettype":"VBox","options":{"id":"main_content","css":"filler"}}
DOM Order Affects app.<id> Resolution
Put the content area BEFORE the sidebar in subwidgets. Bricks initializes widgets left-to-right.
Configuration Best Practices
Security
- Store sensitive data (passwords, keys) in encrypted form in config.json
- Use environment variables for runtime secrets via .env file
- Implement proper file permissions for logs and files directories
Performance
- Configure appropriate client_max_size for file uploads
- Set optimal session timeout values (session_max_time, session_issue_time)
- Use Redis for session storage in production
Maintainability
- Use consistent naming patterns across all configuration sections
- Document custom processor mappings in website.processors
- Maintain clear separation between development and production configurations
Verification Checklist
.tmpl→tmplprocessor inwebsite.processors(required for bricks UI, or/returns 500)bricks_for_pythonimported andload_pybricks()called ininit()ServerEnvinitialized BEFOREload_rbac()/load_appbase()- Website
indexesconfig includes"index.ui"(required for directory URL resolution) session_max_timeandsession_issue_timein config for Redis session persistence (restart won't lose login)- All required dependencies are installed from correct Git repositories
- Application directory structure matches specification exactly
- Main application file includes proper module loading pattern
- Configuration file uses correct placeholders and structure
- Build script handles all module types (foundation, database, business)
- Database DDL generation works for all modules with models/
- CRUD generation executes for all modules with json/
- Initial data loads correctly for modules with data/
- All wwwroot symlinks are created properly
- Bricks framework is built and linked correctly
- System service starts and stops gracefully
- Environment variables are loaded from .env file
- Application runs with proper logging and error handling