create-dwy 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/create.js +50 -3
- package/src/utils.js +8 -0
- package/templates/claude-global/CLAUDE.md +81 -6
- package/templates/claude-global/rules/dwy-postgres.md +89 -0
- package/templates/claude-global/rules/dwy-python-backend.md +492 -0
- package/templates/claude-global/rules/dwy-python-core.md +94 -634
- package/templates/claude-global/rules/dwy-python-testing.md +115 -13
- package/templates/claude-global/rules/dwy-redis.md +88 -0
- package/templates/claude-global/rules/dwy-vue-core.md +341 -645
- package/templates/claude-global/rules/dwy-vue-pinia.md +47 -34
- package/templates/claude-global/rules/dwy-vue-testing.md +120 -55
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +1 -1
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +1 -1
- package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +22 -15
- package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +23 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +100 -1
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +142 -276
- package/templates/claude-global/skills/dwy-eui/SKILL.md +264 -182
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +117 -129
- package/templates/claude-global/skills/dwy-git-commit/SKILL.md +125 -0
- package/templates/claude-global/skills/dwy-publish/SKILL.md +1 -1
- package/templates/claude-global/{rules/dwy-tech-stack.md → skills/dwy-tech-stack/SKILL.md} +4 -3
- package/templates/project/fullstack-monorepo/CLAUDE.md.hbs +81 -0
- package/templates/project/fullstack-monorepo/_env.example.hbs +24 -0
- package/templates/project/fullstack-monorepo/_gitignore +37 -0
- package/templates/project/fullstack-monorepo/backend/.python-version +1 -0
- package/templates/project/fullstack-monorepo/backend/CLAUDE.md.hbs +68 -0
- package/templates/project/fullstack-monorepo/backend/Dockerfile.dev +23 -0
- package/templates/project/fullstack-monorepo/backend/alembic/env.py.hbs +68 -0
- package/templates/project/fullstack-monorepo/backend/alembic/script.py.mako +26 -0
- package/templates/project/fullstack-monorepo/backend/alembic.ini.hbs +43 -0
- package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/pyproject.toml +16 -0
- package/templates/project/fullstack-monorepo/backend/packages/dwy-shared/src/dwy_shared/__init__.py +12 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/pyproject.toml.hbs +20 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/__init__.py.hbs +40 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/config.py +9 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/models.py.hbs +17 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/protocols.py +14 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/router.py.hbs +22 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/schemas.py +21 -0
- package/templates/project/fullstack-monorepo/backend/packages/{{scopePrefix}}-{{initialDomain}}/src/{{scopePrefix}}_{{initialDomain}}/service.py +11 -0
- package/templates/project/fullstack-monorepo/backend/pyproject.toml.hbs +84 -0
- package/templates/project/fullstack-monorepo/backend/src/app/__init__.py +3 -0
- package/templates/project/fullstack-monorepo/backend/src/app/config.py.hbs +21 -0
- package/templates/project/fullstack-monorepo/backend/src/app/exceptions.py +26 -0
- package/templates/project/fullstack-monorepo/backend/src/app/factories.py +16 -0
- package/templates/project/fullstack-monorepo/backend/src/app/infra/__init__.py +1 -0
- package/templates/project/fullstack-monorepo/backend/src/app/main.py.hbs +65 -0
- package/templates/project/fullstack-monorepo/backend/src/app/middleware/__init__.py +1 -0
- package/templates/project/fullstack-monorepo/backend/tests/__init__.py +0 -0
- package/templates/project/fullstack-monorepo/backend/tests/conftest.py.hbs +42 -0
- package/templates/project/fullstack-monorepo/dev.sh.hbs +83 -0
- package/templates/project/fullstack-monorepo/docker-compose.dev.yml.hbs +36 -0
- package/templates/project/fullstack-monorepo/frontend/CLAUDE.md.hbs +35 -0
- package/templates/project/fullstack-monorepo/frontend/Dockerfile +18 -0
- package/templates/project/fullstack-monorepo/frontend/_npmrc +3 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/index.html.hbs +13 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/package.json.hbs +31 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/App.vue.hbs +10 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/api/client.ts.hbs +11 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/features/{{initialDomain}}/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/index.css +12 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/main.ts +17 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/router/index.ts.hbs +24 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/stores/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/types/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/src/views/HomeView.vue.hbs +36 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/tsconfig.json +7 -0
- package/templates/project/fullstack-monorepo/frontend/apps/web/vite.config.ts.hbs +27 -0
- package/templates/project/fullstack-monorepo/frontend/nginx.conf +11 -0
- package/templates/project/fullstack-monorepo/frontend/package.json.hbs +13 -0
- package/templates/project/fullstack-monorepo/frontend/packages/core/package.json.hbs +15 -0
- package/templates/project/fullstack-monorepo/frontend/packages/core/src/index.ts +3 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/package.json.hbs +14 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/index.ts.hbs +16 -0
- package/templates/project/fullstack-monorepo/frontend/packages/features/{{initialDomain}}/src/pages/IndexPage.vue.hbs +9 -0
- package/templates/project/fullstack-monorepo/frontend/packages/ui/package.json.hbs +15 -0
- package/templates/project/fullstack-monorepo/frontend/packages/ui/src/index.ts +3 -0
- package/templates/project/fullstack-monorepo/frontend/pnpm-workspace.yaml.hbs +23 -0
- package/templates/project/fullstack-monorepo/frontend/tsconfig.base.json.hbs +23 -0
- package/templates/claude-global/rules/dwy-api-design.md +0 -110
- package/templates/claude-global/rules/dwy-git-commit-convention.md +0 -82
- package/templates/claude-global/rules/dwy-git-security.md +0 -53
- package/templates/claude-global/rules/dwy-payload-limits.md +0 -186
- package/templates/claude-global/rules/dwy-prefer-open-source.md +0 -32
- package/templates/claude-global/rules/dwy-project-structure.md +0 -181
- package/templates/claude-global/rules/dwy-python-fastapi.md +0 -272
- package/templates/claude-global/rules/dwy-python-orm.md +0 -61
- package/templates/claude-global/rules/dwy-python-security.md +0 -636
- package/templates/claude-global/rules/dwy-server-security.md +0 -518
- package/templates/claude-global/rules/dwy-vue-api.md +0 -146
- package/templates/claude-global/skills/dwy-ekit/references/data.md +0 -135
- package/templates/claude-global/skills/dwy-ekit/references/form.md +0 -68
- package/templates/claude-global/skills/dwy-ekit/references/request.md +0 -183
- package/templates/claude-global/skills/dwy-ekit/references/ui.md +0 -109
- package/templates/claude-global/skills/dwy-vue-core/SKILL.md +0 -890
- /package/templates/{claude-global/commands → project/fullstack-monorepo/backend/alembic/versions}/.gitkeep +0 -0
package/package.json
CHANGED
package/src/create.js
CHANGED
|
@@ -26,9 +26,10 @@ export async function createProject(name) {
|
|
|
26
26
|
name: 'template',
|
|
27
27
|
message: '选择项目模板:',
|
|
28
28
|
choices: [
|
|
29
|
-
{ name: '
|
|
30
|
-
{ name: '
|
|
31
|
-
{ name: '
|
|
29
|
+
{ name: 'fullstack-monorepo — Vue 3 + FastAPI 全栈 monorepo (域分包 + Provider 模式)', value: 'fullstack-monorepo' },
|
|
30
|
+
{ name: 'web — Vue 3 前端 (pnpm monorepo, eui + ekit + Tailwind)', value: 'web' },
|
|
31
|
+
{ name: 'backend — FastAPI 后端 (eapi + PostgreSQL + Redis + Docker)', value: 'backend' },
|
|
32
|
+
{ name: 'mobile — 移动端 (暂未实现)', value: 'mobile', disabled: '即将推出' },
|
|
32
33
|
],
|
|
33
34
|
},
|
|
34
35
|
])
|
|
@@ -76,6 +77,39 @@ export async function createProject(name) {
|
|
|
76
77
|
])
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
if (common.template === 'fullstack-monorepo') {
|
|
81
|
+
extra = await inquirer.prompt([
|
|
82
|
+
{
|
|
83
|
+
type: 'input',
|
|
84
|
+
name: 'scopePrefix',
|
|
85
|
+
message: '业务包前缀 (生成 packages/<prefix>-<domain>/):',
|
|
86
|
+
default: 'app',
|
|
87
|
+
validate: (v) => /^[a-z][a-z0-9]*$/.test(v) || '只允许小写字母开头,字母数字',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'input',
|
|
91
|
+
name: 'initialDomain',
|
|
92
|
+
message: '初始业务域 (示例域名):',
|
|
93
|
+
default: 'core',
|
|
94
|
+
validate: (v) => /^[a-z][a-z0-9]*$/.test(v) || '只允许小写字母开头,字母数字',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
type: 'confirm',
|
|
98
|
+
name: 'includeFrontend',
|
|
99
|
+
message: '是否生成 frontend?',
|
|
100
|
+
default: true,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'input',
|
|
104
|
+
name: 'portPrefix',
|
|
105
|
+
message: '端口前缀数字 (1-9,避免与其他项目冲突,如 2 → 28001/25173/25432/26379):',
|
|
106
|
+
default: '2',
|
|
107
|
+
validate: (v) => /^[1-9]$/.test(v) || '只允许 1-9 的单个数字',
|
|
108
|
+
},
|
|
109
|
+
])
|
|
110
|
+
extra.apiProxy = `http://127.0.0.1:${extra.portPrefix}8001`
|
|
111
|
+
}
|
|
112
|
+
|
|
79
113
|
const answers = { ...common, ...extra }
|
|
80
114
|
|
|
81
115
|
const context = {
|
|
@@ -99,6 +133,10 @@ export async function createProject(name) {
|
|
|
99
133
|
await fs.ensureDir(destDir)
|
|
100
134
|
await renderTemplate(templateDir, destDir, context)
|
|
101
135
|
|
|
136
|
+
if (context.template === 'fullstack-monorepo' && context.includeFrontend === false) {
|
|
137
|
+
await fs.remove(path.join(destDir, 'frontend'))
|
|
138
|
+
}
|
|
139
|
+
|
|
102
140
|
console.log(chalk.green(`\n✅ 项目 ${context.projectName} 创建成功!\n`))
|
|
103
141
|
console.log(chalk.gray('Next steps:'))
|
|
104
142
|
console.log(chalk.gray(` cd ${context.projectName}`))
|
|
@@ -114,4 +152,13 @@ export async function createProject(name) {
|
|
|
114
152
|
console.log(chalk.gray(' pnpm install'))
|
|
115
153
|
console.log(chalk.gray(' pnpm dev'))
|
|
116
154
|
}
|
|
155
|
+
|
|
156
|
+
if (context.template === 'fullstack-monorepo') {
|
|
157
|
+
console.log(chalk.gray(' cp .env.example .env'))
|
|
158
|
+
console.log(chalk.gray(' cd backend && uv sync --dev && cd ..'))
|
|
159
|
+
if (context.includeFrontend) {
|
|
160
|
+
console.log(chalk.gray(' cd frontend && pnpm install && cd ..'))
|
|
161
|
+
}
|
|
162
|
+
console.log(chalk.gray(' ./dev.sh'))
|
|
163
|
+
}
|
|
117
164
|
}
|
package/src/utils.js
CHANGED
|
@@ -34,6 +34,14 @@ export async function renderTemplate(templateDir, destDir, context) {
|
|
|
34
34
|
const srcPath = path.join(templateDir, entry.name)
|
|
35
35
|
let destName = entry.name
|
|
36
36
|
|
|
37
|
+
if (destName.includes('{{')) {
|
|
38
|
+
destName = Handlebars.compile(destName)(context)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (destName.startsWith('_')) {
|
|
42
|
+
destName = '.' + destName.slice(1)
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
if (entry.isDirectory()) {
|
|
38
46
|
const destPath = path.join(destDir, destName)
|
|
39
47
|
await fs.ensureDir(destPath)
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
任务执行优先使用 agent team 模式执行。
|
|
12
12
|
|
|
13
|
-
## 减少常见 LLM 编码错误的行为指南
|
|
13
|
+
## 减少常见 LLM 编码错误的行为指南
|
|
14
14
|
|
|
15
15
|
与项目特定指令合并使用。
|
|
16
16
|
|
|
17
17
|
权衡:这些指南偏向谨慎而非速度。对于琐碎任务,自行判断。
|
|
18
18
|
|
|
19
|
-
### 1.
|
|
19
|
+
### 1. 先思考再编码
|
|
20
20
|
|
|
21
21
|
不要假设。不要隐藏困惑。把权衡摊开。
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
- 如果有更简单的方案,说出来。在必要时提出异议。
|
|
27
27
|
- 如果有什么不清晰,停下来。说出困惑是什么。然后提问。
|
|
28
28
|
|
|
29
|
-
### 2.
|
|
29
|
+
### 2. 简单优先
|
|
30
30
|
|
|
31
31
|
解决问题的最小代码。没有推测性代码。
|
|
32
32
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
- 如果你写了 200 行但可以写成 50 行,重写它。
|
|
38
38
|
- 问自己:"一个资深工程师会说这过于复杂吗?"如果是,简化。
|
|
39
39
|
|
|
40
|
-
### 3.
|
|
40
|
+
### 3. 精准改动
|
|
41
41
|
|
|
42
42
|
只动必须动的代码。只清理自己造成的混乱。
|
|
43
43
|
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
检验标准:每一行改动的代码都应能直接追溯到用户的需求。
|
|
55
55
|
|
|
56
|
-
### 4.
|
|
56
|
+
### 4. 目标驱动执行
|
|
57
57
|
|
|
58
58
|
定义成功标准。循环直到验证通过。
|
|
59
59
|
|
|
@@ -90,4 +90,79 @@
|
|
|
90
90
|
- 安全警告和风险提示
|
|
91
91
|
- 不可逆操作确认(删除、强制推送等)
|
|
92
92
|
- 需要用户决策的复杂多步骤流程
|
|
93
|
-
- 用户明确要求详细解释时
|
|
93
|
+
- 用户明确要求详细解释时
|
|
94
|
+
|
|
95
|
+
## 优先使用成熟开源方案
|
|
96
|
+
|
|
97
|
+
开发新功能时,优先选用成熟开源项目/库,而不是手写实现。只有确认没有合适开源方案时才自行实现。
|
|
98
|
+
|
|
99
|
+
### 决策流程
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
需要某项功能
|
|
103
|
+
→ 搜索成熟开源库(npm/PyPI/GitHub stars > 1k、近期有维护)
|
|
104
|
+
→ 有 → 评估是否适配当前技术栈
|
|
105
|
+
→ 适配 → 直接使用,不重复造轮子
|
|
106
|
+
→ 不适配 → 寻找替代方案
|
|
107
|
+
→ 没有合适的 → 自行实现,代码注释说明为什么没用现成方案
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 强制规则
|
|
111
|
+
|
|
112
|
+
- **禁止**手写已有主流开源方案的功能,一律使用社区主流库
|
|
113
|
+
- 选用开源库优先级:项目已在用的依赖 > 社区主流方案 > 小众但适合的方案
|
|
114
|
+
|
|
115
|
+
## 代码简约原则
|
|
116
|
+
|
|
117
|
+
只写**必要的逻辑**,不写"以防万一"的冗余代码。信任内部代码和框架保证,只在系统**外边界**(用户输入、外部 API、文件 / 网络读入)做校验。
|
|
118
|
+
|
|
119
|
+
### 禁止的冗余模式
|
|
120
|
+
|
|
121
|
+
- **多余 fallback**:值已有明确来源时,不再 `value or default` / `value ?? default`
|
|
122
|
+
- **多余类型转换**:上游已是目标类型时,不再 `int(x)` / `Number(x)` / `String(x)`
|
|
123
|
+
- **多余 else**:上面已 return,下面不再写 else 分支(用 early return)
|
|
124
|
+
- **多余默认值**:调用方都传值时,不写形参默认值 + 空值检查
|
|
125
|
+
- **多余异常兜底**:`try { ... } catch { /* swallow */ }` 掩盖 bug;只捕获**预期**的具体异常,其余让它自然抛出
|
|
126
|
+
- **多余空集合检查**:能直接 `for ... in collection` 时,不要 `if len(x) > 0` 再循环
|
|
127
|
+
|
|
128
|
+
### 判断标准
|
|
129
|
+
|
|
130
|
+
写每一行防御代码前问自己:**这个情况在当前上下文真的会发生吗?**
|
|
131
|
+
|
|
132
|
+
- 会 → 写防御,注释说明触发条件
|
|
133
|
+
- 不会 → 不写,信任上游
|
|
134
|
+
- 不确定 → 查调用链确认,不要"以防万一"
|
|
135
|
+
|
|
136
|
+
| 位置 | 是否校验 |
|
|
137
|
+
|---|---|
|
|
138
|
+
| 用户输入、外部 API 响应、文件 / 网络读入 | 必须校验 |
|
|
139
|
+
| 内部模块调用(已有类型签名) | 信任,不重复校验 |
|
|
140
|
+
|
|
141
|
+
## 用户输入长度限制
|
|
142
|
+
|
|
143
|
+
所有用户输入必须限长 / 限大小,**前端 + 后端双层**:
|
|
144
|
+
|
|
145
|
+
- 前端:即时反馈,避免无效输入浪费带宽
|
|
146
|
+
- 后端:防绕过、防 DoS、防内存耗尽(前端可被绕过,后端必须再校验一次)
|
|
147
|
+
|
|
148
|
+
具体数字按业务语义推断(用户名几十字符、描述几百、正文几千、图片几 MB),无须硬编码统一表。
|
|
149
|
+
|
|
150
|
+
### 前端
|
|
151
|
+
|
|
152
|
+
凡接收用户输入的控件(`input` / `textarea` / 富文本 / 文件选择 / 批量项)→ 都必须有长度 / 大小上限,并配 schema 层校验兜底(防粘贴绕过原生 maxlength)。
|
|
153
|
+
|
|
154
|
+
### 后端
|
|
155
|
+
|
|
156
|
+
凡接收用户数据的字段 / Query / Path / Header → 必须显式声明长度上限。
|
|
157
|
+
|
|
158
|
+
- 分页 `page_size` 必须有上限
|
|
159
|
+
- 禁止用「松散字典 / map / any 类型」直接接收 body,必须有 schema
|
|
160
|
+
- 图片上传额外校验像素尺寸,防 decompression bomb
|
|
161
|
+
|
|
162
|
+
### 禁止
|
|
163
|
+
|
|
164
|
+
- 任一层无限制上线
|
|
165
|
+
- 仅前端限而后端不校验
|
|
166
|
+
- 后端用 dict / map / any 接收 body
|
|
167
|
+
- 分页 `page_size` 无上限
|
|
168
|
+
- 图片只校验文件大小不校验像素
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: PostgreSQL 安全与配置规范(参数化查询、应用用户最小权限、连接池上限、SSL、敏感字段加密)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# PostgreSQL 安全与配置规范
|
|
6
|
+
|
|
7
|
+
适用于使用 PostgreSQL 作为主数据库的 Python 后端项目。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 一、强制规则
|
|
12
|
+
|
|
13
|
+
| 规则 | 说明 |
|
|
14
|
+
|------|------|
|
|
15
|
+
| 参数化查询 | **禁止**字符串拼接 SQL(用 ORM 或 `text(... :param)` 参数绑定) |
|
|
16
|
+
| 应用用户权限 | 只授予 SELECT / INSERT / UPDATE / DELETE,**不给** DROP / CREATE / ALTER / SUPERUSER |
|
|
17
|
+
| 连接池 | `pool_size ≤ 20`,`max_overflow ≤ 10` |
|
|
18
|
+
| SSL 连接 | 生产环境启用 `sslmode=require` |
|
|
19
|
+
| 敏感字段加密 | 身份证号 / 银行卡 / 手机号原文等使用 AES 加密**存储**,不仅展示脱敏 |
|
|
20
|
+
| 迁移 | 通过 Alembic 管理 schema,**禁止**手动执行 DDL(详见 `dwy-db-migration` rule) |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 二、应用专用数据库用户(最小权限)
|
|
25
|
+
|
|
26
|
+
生产环境**禁止**使用 `postgres` 超级用户跑应用;为每个应用创建独立用户,授权严格限定到 DML。
|
|
27
|
+
|
|
28
|
+
```sql
|
|
29
|
+
-- 创建应用专用用户(非超级用户)
|
|
30
|
+
CREATE USER app_user WITH PASSWORD 'strong_password';
|
|
31
|
+
|
|
32
|
+
-- 库与 schema 访问
|
|
33
|
+
GRANT CONNECT ON DATABASE mydb TO app_user;
|
|
34
|
+
GRANT USAGE ON SCHEMA public TO app_user;
|
|
35
|
+
|
|
36
|
+
-- 现有对象权限
|
|
37
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
|
|
38
|
+
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user;
|
|
39
|
+
|
|
40
|
+
-- 默认权限(迁移新增表自动生效)
|
|
41
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
42
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
|
|
43
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
44
|
+
GRANT USAGE, SELECT ON SEQUENCES TO app_user;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 禁止授予应用用户的权限
|
|
48
|
+
|
|
49
|
+
- `GRANT ALL PRIVILEGES`
|
|
50
|
+
- `SUPERUSER`
|
|
51
|
+
- `CREATEDB` / `CREATEROLE`
|
|
52
|
+
- 任何能修改 schema 的权限(DROP / CREATE / ALTER)
|
|
53
|
+
|
|
54
|
+
> 迁移操作使用单独的 migrator 账号(具备 DDL 权限),与应用账号严格区分。
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 三、连接串与 SSL
|
|
59
|
+
|
|
60
|
+
- 连接串必须从环境变量读取,**禁止**硬编码
|
|
61
|
+
- 生产环境必须 `sslmode=require`(或更严的 `verify-full`)
|
|
62
|
+
- 连接池参数走 dwyeapi 引擎工厂(具体 API 查 `dwy-eapi` skill),**禁止**手动 `create_async_engine` 跳过框架约束
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 四、敏感字段加密
|
|
67
|
+
|
|
68
|
+
仅展示脱敏(详见 `dwy-python-backend` 7.4 响应安全)**不足以**保护落库数据。以下字段在写入前必须用 AES 加密:
|
|
69
|
+
|
|
70
|
+
- 身份证号
|
|
71
|
+
- 银行卡号
|
|
72
|
+
- 手机号(视业务敏感等级)
|
|
73
|
+
- 真实姓名 + 地址组合(PII)
|
|
74
|
+
|
|
75
|
+
加密密钥从环境变量或 KMS 读取,**禁止**与库一起备份。
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 五、违规检测清单
|
|
80
|
+
|
|
81
|
+
| 检查项 | 违规模式 | 严重程度 |
|
|
82
|
+
|--------|---------|---------|
|
|
83
|
+
| SQL 拼接 | f-string / `%` / 字符串相加构造 SQL | **致命 → STOP** |
|
|
84
|
+
| 应用用户超权 | 应用账号拥有 DROP / CREATE / ALTER / SUPERUSER | **致命 → STOP** |
|
|
85
|
+
| 超级用户跑应用 | 应用以 `postgres` 或其他超级用户身份连接 | **致命 → STOP** |
|
|
86
|
+
| 连接池超限 | `pool_size > 20` 或 `max_overflow > 10` | 高 |
|
|
87
|
+
| 无 SSL | 生产环境连接串无 `sslmode=require` | 高 |
|
|
88
|
+
| 敏感字段明文 | 身份证 / 银行卡等明文落库 | 高 |
|
|
89
|
+
| 硬编码连接串 | 连接串写死在代码中 | **致命 → STOP** |
|