@fui-org/fui-cli 0.2.0 → 0.3.0
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/README.md +13 -1
- package/dist/fui.js +160 -43
- package/package.json +4 -2
- package/skills/fui/SKILL.md +131 -66
- package/skills/fui-skill/README.md +112 -0
- package/skills/fui-skill/SKILL.md +269 -0
- package/skills/fui-skill/assets/projectdefaultstyle.css +518 -0
- package/skills/fui-skill/design-md/airbnb/DESIGN.md +545 -0
- package/skills/fui-skill/design-md/airbnb/README.md +5 -0
- package/skills/fui-skill/design-md/airtable/DESIGN.md +554 -0
- package/skills/fui-skill/design-md/airtable/README.md +5 -0
- package/skills/fui-skill/design-md/apple/DESIGN.md +562 -0
- package/skills/fui-skill/design-md/apple/README.md +5 -0
- package/skills/fui-skill/design-md/asu/DESIGN.md +179 -0
- package/skills/fui-skill/design-md/asu/README.md +107 -0
- package/skills/fui-skill/design-md/binance/DESIGN.md +634 -0
- package/skills/fui-skill/design-md/binance/README.md +5 -0
- package/skills/fui-skill/design-md/bmw/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/bmw/README.md +5 -0
- package/skills/fui-skill/design-md/bmw-m/DESIGN.md +503 -0
- package/skills/fui-skill/design-md/bmw-m/README.md +5 -0
- package/skills/fui-skill/design-md/bugatti/DESIGN.md +454 -0
- package/skills/fui-skill/design-md/bugatti/README.md +5 -0
- package/skills/fui-skill/design-md/cal/DESIGN.md +542 -0
- package/skills/fui-skill/design-md/cal/README.md +5 -0
- package/skills/fui-skill/design-md/claude/DESIGN.md +589 -0
- package/skills/fui-skill/design-md/claude/README.md +5 -0
- package/skills/fui-skill/design-md/clay/DESIGN.md +541 -0
- package/skills/fui-skill/design-md/clay/README.md +5 -0
- package/skills/fui-skill/design-md/clickhouse/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/clickhouse/README.md +5 -0
- package/skills/fui-skill/design-md/cohere/DESIGN.md +451 -0
- package/skills/fui-skill/design-md/cohere/README.md +5 -0
- package/skills/fui-skill/design-md/coinbase/DESIGN.md +570 -0
- package/skills/fui-skill/design-md/coinbase/README.md +5 -0
- package/skills/fui-skill/design-md/composio/DESIGN.md +506 -0
- package/skills/fui-skill/design-md/composio/README.md +5 -0
- package/skills/fui-skill/design-md/cursor/DESIGN.md +537 -0
- package/skills/fui-skill/design-md/cursor/README.md +5 -0
- package/skills/fui-skill/design-md/elevenlabs/DESIGN.md +504 -0
- package/skills/fui-skill/design-md/elevenlabs/README.md +5 -0
- package/skills/fui-skill/design-md/expo/DESIGN.md +526 -0
- package/skills/fui-skill/design-md/expo/README.md +5 -0
- package/skills/fui-skill/design-md/ferrari/DESIGN.md +531 -0
- package/skills/fui-skill/design-md/ferrari/README.md +5 -0
- package/skills/fui-skill/design-md/figma/DESIGN.md +578 -0
- package/skills/fui-skill/design-md/figma/README.md +5 -0
- package/skills/fui-skill/design-md/framer/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/framer/README.md +5 -0
- package/skills/fui-skill/design-md/fui/DESIGN.md +532 -0
- package/skills/fui-skill/design-md/hashicorp/DESIGN.md +575 -0
- package/skills/fui-skill/design-md/hashicorp/README.md +5 -0
- package/skills/fui-skill/design-md/ibm/DESIGN.md +550 -0
- package/skills/fui-skill/design-md/ibm/README.md +5 -0
- package/skills/fui-skill/design-md/intercom/DESIGN.md +546 -0
- package/skills/fui-skill/design-md/intercom/README.md +5 -0
- package/skills/fui-skill/design-md/kraken/DESIGN.md +125 -0
- package/skills/fui-skill/design-md/kraken/README.md +5 -0
- package/skills/fui-skill/design-md/lamborghini/DESIGN.md +288 -0
- package/skills/fui-skill/design-md/lamborghini/README.md +5 -0
- package/skills/fui-skill/design-md/linear.app/DESIGN.md +548 -0
- package/skills/fui-skill/design-md/linear.app/README.md +5 -0
- package/skills/fui-skill/design-md/lovable/DESIGN.md +298 -0
- package/skills/fui-skill/design-md/lovable/README.md +5 -0
- package/skills/fui-skill/design-md/mastercard/DESIGN.md +365 -0
- package/skills/fui-skill/design-md/mastercard/README.md +5 -0
- package/skills/fui-skill/design-md/meta/DESIGN.md +683 -0
- package/skills/fui-skill/design-md/meta/README.md +5 -0
- package/skills/fui-skill/design-md/minimax/DESIGN.md +746 -0
- package/skills/fui-skill/design-md/minimax/README.md +5 -0
- package/skills/fui-skill/design-md/mintlify/DESIGN.md +852 -0
- package/skills/fui-skill/design-md/mintlify/README.md +5 -0
- package/skills/fui-skill/design-md/miro/DESIGN.md +825 -0
- package/skills/fui-skill/design-md/miro/README.md +5 -0
- package/skills/fui-skill/design-md/mistral.ai/DESIGN.md +773 -0
- package/skills/fui-skill/design-md/mistral.ai/README.md +5 -0
- package/skills/fui-skill/design-md/mongodb/DESIGN.md +767 -0
- package/skills/fui-skill/design-md/mongodb/README.md +5 -0
- package/skills/fui-skill/design-md/nike/DESIGN.md +575 -0
- package/skills/fui-skill/design-md/nike/README.md +5 -0
- package/skills/fui-skill/design-md/notion/DESIGN.md +821 -0
- package/skills/fui-skill/design-md/notion/README.md +5 -0
- package/skills/fui-skill/design-md/nvidia/DESIGN.md +640 -0
- package/skills/fui-skill/design-md/nvidia/README.md +5 -0
- package/skills/fui-skill/design-md/ollama/DESIGN.md +539 -0
- package/skills/fui-skill/design-md/ollama/README.md +5 -0
- package/skills/fui-skill/design-md/opencode.ai/DESIGN.md +521 -0
- package/skills/fui-skill/design-md/opencode.ai/README.md +5 -0
- package/skills/fui-skill/design-md/pinterest/DESIGN.md +597 -0
- package/skills/fui-skill/design-md/pinterest/README.md +5 -0
- package/skills/fui-skill/design-md/playstation/DESIGN.md +661 -0
- package/skills/fui-skill/design-md/playstation/README.md +5 -0
- package/skills/fui-skill/design-md/posthog/DESIGN.md +690 -0
- package/skills/fui-skill/design-md/posthog/README.md +5 -0
- package/skills/fui-skill/design-md/raycast/DESIGN.md +669 -0
- package/skills/fui-skill/design-md/raycast/README.md +5 -0
- package/skills/fui-skill/design-md/renault/DESIGN.md +589 -0
- package/skills/fui-skill/design-md/renault/README.md +5 -0
- package/skills/fui-skill/design-md/replicate/DESIGN.md +616 -0
- package/skills/fui-skill/design-md/replicate/README.md +5 -0
- package/skills/fui-skill/design-md/resend/DESIGN.md +585 -0
- package/skills/fui-skill/design-md/resend/README.md +5 -0
- package/skills/fui-skill/design-md/revolut/DESIGN.md +636 -0
- package/skills/fui-skill/design-md/revolut/README.md +5 -0
- package/skills/fui-skill/design-md/runwayml/DESIGN.md +244 -0
- package/skills/fui-skill/design-md/runwayml/README.md +5 -0
- package/skills/fui-skill/design-md/sanity/DESIGN.md +357 -0
- package/skills/fui-skill/design-md/sanity/README.md +5 -0
- package/skills/fui-skill/design-md/sentry/DESIGN.md +551 -0
- package/skills/fui-skill/design-md/sentry/README.md +5 -0
- package/skills/fui-skill/design-md/shopify/DESIGN.md +516 -0
- package/skills/fui-skill/design-md/shopify/README.md +5 -0
- package/skills/fui-skill/design-md/slack/DESIGN.md +482 -0
- package/skills/fui-skill/design-md/spacex/DESIGN.md +363 -0
- package/skills/fui-skill/design-md/spacex/README.md +5 -0
- package/skills/fui-skill/design-md/spotify/DESIGN.md +246 -0
- package/skills/fui-skill/design-md/spotify/README.md +5 -0
- package/skills/fui-skill/design-md/starbucks/DESIGN.md +580 -0
- package/skills/fui-skill/design-md/starbucks/README.md +5 -0
- package/skills/fui-skill/design-md/stripe/DESIGN.md +487 -0
- package/skills/fui-skill/design-md/stripe/README.md +5 -0
- package/skills/fui-skill/design-md/supabase/DESIGN.md +462 -0
- package/skills/fui-skill/design-md/supabase/README.md +5 -0
- package/skills/fui-skill/design-md/superhuman/DESIGN.md +448 -0
- package/skills/fui-skill/design-md/superhuman/README.md +5 -0
- package/skills/fui-skill/design-md/tesla/DESIGN.md +286 -0
- package/skills/fui-skill/design-md/tesla/README.md +5 -0
- package/skills/fui-skill/design-md/theverge/DESIGN.md +339 -0
- package/skills/fui-skill/design-md/theverge/README.md +5 -0
- package/skills/fui-skill/design-md/together.ai/DESIGN.md +633 -0
- package/skills/fui-skill/design-md/together.ai/README.md +5 -0
- package/skills/fui-skill/design-md/uber/DESIGN.md +636 -0
- package/skills/fui-skill/design-md/uber/README.md +5 -0
- package/skills/fui-skill/design-md/vercel/DESIGN.md +736 -0
- package/skills/fui-skill/design-md/vercel/README.md +5 -0
- package/skills/fui-skill/design-md/vodafone/DESIGN.md +538 -0
- package/skills/fui-skill/design-md/vodafone/README.md +5 -0
- package/skills/fui-skill/design-md/voltagent/DESIGN.md +521 -0
- package/skills/fui-skill/design-md/voltagent/README.md +5 -0
- package/skills/fui-skill/design-md/warp/DESIGN.md +526 -0
- package/skills/fui-skill/design-md/warp/README.md +5 -0
- package/skills/fui-skill/design-md/webflow/DESIGN.md +588 -0
- package/skills/fui-skill/design-md/webflow/README.md +5 -0
- package/skills/fui-skill/design-md/wired/DESIGN.md +497 -0
- package/skills/fui-skill/design-md/wired/README.md +5 -0
- package/skills/fui-skill/design-md/wise/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/wise/README.md +5 -0
- package/skills/fui-skill/design-md/x.ai/DESIGN.md +465 -0
- package/skills/fui-skill/design-md/x.ai/README.md +5 -0
- package/skills/fui-skill/design-md/zapier/DESIGN.md +537 -0
- package/skills/fui-skill/design-md/zapier/README.md +5 -0
- package/skills/fui-skill/examples/component.vue +162 -0
- package/skills/fui-skill/examples/f-table-patterns.json +331 -0
- package/skills/fui-skill/examples/module-patterns.json +973 -0
- package/skills/fui-skill/examples/project-patterns.json +222 -0
- package/skills/fui-skill/metadata.json +75 -0
- package/skills/fui-skill/references/INDEX.md +144 -0
- package/skills/fui-skill/references/advanced-techniques.md +160 -0
- package/skills/fui-skill/references/coding-standards.md +112 -0
- package/skills/fui-skill/references/component-design.md +455 -0
- package/skills/fui-skill/references/component-quickref.md +77 -0
- package/skills/fui-skill/references/component-table.md +276 -0
- package/skills/fui-skill/references/components-dialog.md +192 -0
- package/skills/fui-skill/references/components-display.md +147 -0
- package/skills/fui-skill/references/components-echart.md +391 -0
- package/skills/fui-skill/references/components-input.md +359 -0
- package/skills/fui-skill/references/controls-patterns.md +847 -0
- package/skills/fui-skill/references/controls-styling-vocabulary.md +140 -0
- package/skills/fui-skill/references/db-table-design.md +77 -0
- package/skills/fui-skill/references/db-workflow.md +504 -0
- package/skills/fui-skill/references/default-function.md +415 -0
- package/skills/fui-skill/references/design-modes.md +85 -0
- package/skills/fui-skill/references/echart-templates.md +481 -0
- package/skills/fui-skill/references/fastproject.md +97 -0
- package/skills/fui-skill/references/fsheet.md +218 -0
- package/skills/fui-skill/references/fullstack-workflow.md +351 -0
- package/skills/fui-skill/references/module-data-patterns.md +126 -0
- package/skills/fui-skill/references/module-json-anatomy.md +137 -0
- package/skills/fui-skill/references/module-structure.md +260 -0
- package/skills/fui-skill/references/new-session.md +108 -0
- package/skills/fui-skill/references/pdfmake.md +60 -0
- package/skills/fui-skill/references/permission-system.md +169 -0
- package/skills/fui-skill/references/platform-architecture.md +294 -0
- package/skills/fui-skill/references/project-config.md +335 -0
- package/skills/fui-skill/references/project-provisioning.md +383 -0
- package/skills/fui-skill/references/script-map.md +296 -0
- package/skills/fui-skill/references/sql-clr-functions.md +224 -0
- package/skills/fui-skill/references/system-design.md +116 -0
- package/skills/fui-skill/references/tapi-file-api.md +191 -0
- package/skills/fui-skill/references/tapi-permission-patterns.md +158 -0
- package/skills/fui-skill/references/tapi-reference.md +549 -0
- package/skills/fui-skill/references/tools-registry.md +460 -0
- package/skills/fui-skill/references/ui-crosswindow-patterns.md +317 -0
- package/skills/fui-skill/references/ui-dialog-patterns.md +229 -0
- package/skills/fui-skill/references/ui-layout-patterns.md +176 -0
- package/skills/fui-skill/references/ui-patterns.md +315 -0
- package/skills/fui-skill/references/ui-screenshot-review.md +94 -0
- package/skills/fui-skill/references/ui-table-cell-patterns.md +316 -0
- package/skills/fui-skill/references/ui-templates.md +29 -0
- package/skills/fui-skill/references/verification.md +246 -0
- package/skills/fui-skill/references/watcher-patterns.md +196 -0
- package/skills/fui-skill/references/websocket-realtime.md +254 -0
- package/skills/fui-skill/scripts/component-3.0.js +2549 -0
- package/skills/fui-skill/scripts/component.js +3142 -0
- package/skills/fui-skill/scripts/componentTable-3.0.js +909 -0
- package/skills/fui-skill/scripts/componentTable.js +769 -0
- package/skills/fui-skill/scripts/defaultfunction-3.0.js +781 -0
- package/skills/fui-skill/scripts/defaultfunction.js +966 -0
- package/skills/fui-skill/scripts/fastproject-3.0.js +870 -0
- package/skills/fui-skill/scripts/fastproject.js +828 -0
- package/skills/fui-skill/scripts/fechart.js +890 -0
- package/skills/fui-skill/scripts/fsheet.js +1330 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
# DB Connection Workflow
|
|
2
|
+
|
|
3
|
+
> File này sở hữu: **kết nối DB, dbToken, schema cache, workflow làm việc với SP, rollback**. Mức rủi ro + guard từng tool xem [tools-registry.md](tools-registry.md); checklist SP xem [verification.md](verification.md).
|
|
4
|
+
|
|
5
|
+
Hướng dẫn toàn diện về kết nối database cho một FUI project — từ tìm thông tin kết nối, build token, đến quản lý schema và SP.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Thông tin kết nối lưu ở đâu
|
|
10
|
+
|
|
11
|
+
Mỗi project có thư mục `_db/` riêng, **bên trong là một thư mục con cho MỖI kết nối** — một project
|
|
12
|
+
FUI gọi được nhiều database cùng lúc, mỗi cái qua một alias (`apiName`) riêng:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
{FUI_MCP_WORKDIR}/{projectId}/
|
|
16
|
+
└── _db/
|
|
17
|
+
├── _connections.json ← MỌI kết nối của project + kết nối mặc định
|
|
18
|
+
├── rights.json ← cache quyền từ "acc", khoá theo alias
|
|
19
|
+
├── {alias}/ ← alias = apiName = đoạn đầu trong "API": "/{alias}/Ten"
|
|
20
|
+
│ ├── schema.json ← cache schema của DB đó
|
|
21
|
+
│ └── {name}.sql ← SP của DB đó (KHÔNG có tầng "sp/")
|
|
22
|
+
└── {alias-khác}/…
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Alias là khoá nối mọi thứ lại**: đoạn đầu trong `"API": "/fee/HocPhi_Select"` của module.json chính
|
|
26
|
+
là `apiName` trong tAPI, cũng chính là tên thư mục `_db/fee/`. Đọc module.json là biết ngay lời gọi
|
|
27
|
+
đó chạy trên database nào và phải mở thư mục nào.
|
|
28
|
+
|
|
29
|
+
> Mọi tool DB nhận tham số **`db`** (alias) để chọn kết nối. Bỏ trống thì MCP tự suy từ object đang
|
|
30
|
+
> thao tác; **suy không ra thì nó trả về một MENU các alias** — chọn một cái rồi gọi lại, đừng đoán.
|
|
31
|
+
|
|
32
|
+
Bản cũ **không** nằm cạnh bản hiện tại: mọi thứ sắp bị ghi đè/xoá đi về một kho chung
|
|
33
|
+
`{FUI_MCP_WORKDIR}/_history/`, giữ nguyên hình dạng đường dẫn gốc:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
{FUI_MCP_WORKDIR}/_history/{projectId}/_db/{alias}/{name}_{thời điểm}.sql
|
|
37
|
+
{FUI_MCP_WORKDIR}/_history/{projectId}/_db/{alias}/{name}_{thời điểm}_deleted.sql ← SP bị DROP
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Một kho duy nhất cho cả project lẫn DB: lúc cần bản cũ chỉ phải nhớ **một** chỗ, và mọi bản của cùng
|
|
41
|
+
một file nằm cạnh nhau (tìm theo tên file, không phải theo thời điểm). Kho này **không bao giờ tự
|
|
42
|
+
dọn** — user tự xoá tay khi thấy đủ.
|
|
43
|
+
|
|
44
|
+
**Cấu trúc `_db/_connections.json`:**
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"default": "acc",
|
|
49
|
+
"connections": {
|
|
50
|
+
"acc": {
|
|
51
|
+
"apiDomain": "api.example3.vn",
|
|
52
|
+
"sqlServer": "192.0.2.101",
|
|
53
|
+
"database": "AccountUser",
|
|
54
|
+
"dbToken": "<base64-encoded connection string>",
|
|
55
|
+
"userToken": "<bearer token — tùy chọn>"
|
|
56
|
+
},
|
|
57
|
+
"Dashboard": { "apiDomain": "api.example3.vn", "…": "…" }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`default` chỉ áp cho thao tác **đọc** không nêu `db`. Thao tác **ghi** (`db_sql_execute` có lệnh ghi,
|
|
63
|
+
`db_sql_execute_nonquery`, `db_token_rebuild`) không bao giờ tự dùng mặc định — phải nêu rõ `db`.
|
|
64
|
+
|
|
65
|
+
| Field | Bắt buộc | Mô tả |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| khoá của mục | ✅ | Chính là `apiName`/alias — vừa là tên thư mục `_db/{alias}/`, vừa là đoạn đầu trong `"API": "/{alias}/Ten"` |
|
|
68
|
+
| `apiDomain` | ✅ | **Chỉ host, KHÔNG có alias, KHÔNG có dấu `/` ở cuối** — ví dụ `api.example3.vn` |
|
|
69
|
+
| `sqlServer` | ❌* | Host SQL Server — cần có để dùng `db_token_rebuild` |
|
|
70
|
+
| `database` | ❌* | Tên database — cần có để dùng `db_token_rebuild` |
|
|
71
|
+
| `dbToken` | ✅ | Chuỗi kết nối SQL Server đã base64-encode |
|
|
72
|
+
| `userToken` | ❌ | Bearer token để test `spAPI_*` sau khi deploy |
|
|
73
|
+
|
|
74
|
+
*Nên truyền khi `db_connect` để sau này có thể `db_token_rebuild` mà không cần hỏi lại.
|
|
75
|
+
|
|
76
|
+
> **CRITICAL — `apiDomain` mang HAI nghĩa khác nhau trong hệ thống, đừng lẫn lộn:**
|
|
77
|
+
> - `_db/_connections.json` (MCP, bảng trên) → **host trần**, không alias, không `/` cuối: `api.example3.vn`.
|
|
78
|
+
> - `project.json`'s `data.apiDomain` (runtime FUI, mục 2a bên dưới) → **base URL đầy đủ** đã kèm sẵn `apiName` + `/` cuối: `https://tapi.example.vn/acc/`.
|
|
79
|
+
>
|
|
80
|
+
> Ghép URL thủ công bằng cách lấy `_db/_connections.json`'s `apiDomain` (host trần) rồi nối thẳng `apiName` vào — **không chèn `/`** — là lỗi hay gặp nhất khi tự gõ tay: `tapi.lhu.edu.vn` + `ts` → `tapi.lhu.edu.vnts` (sai, "ts" bị nuốt vào domain) thay vì `tapi.lhu.edu.vn/ts` (đúng). Xem thêm ví dụ đối chiếu ❌/✅ ở [tapi-reference.md](tapi-reference.md) §3. Cách an toàn nhất: đừng tự ghép — copy nguyên văn dòng `Wiring URL` từ `db_sp_verify`/`db_sp_help`.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 2. Tìm thông tin kết nối của một project
|
|
85
|
+
|
|
86
|
+
### 2a. Đọc `apiDomain` từ project.json
|
|
87
|
+
|
|
88
|
+
`project.json` của mỗi project thường có trường `data.apiDomain` — đây là base URL của tAPI server kèm apiName:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
"data": {
|
|
92
|
+
"apiDomain": "https://tapi.example.vn/acc/",
|
|
93
|
+
...
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Từ URL này:
|
|
98
|
+
- **`apiDomain`** → `tapi.example.vn`
|
|
99
|
+
- **`apiName`** → `acc` (phần path đầu tiên sau domain)
|
|
100
|
+
|
|
101
|
+
Một số project có nhiều domain, mỗi domain dùng một tAPI server riêng:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
"domainSetting": {
|
|
105
|
+
"sec.example.vn": {
|
|
106
|
+
"apiDomain": "https://tapi.example.vn/acc/"
|
|
107
|
+
},
|
|
108
|
+
"sec.example3.vn": {
|
|
109
|
+
"apiDomain": "https://api.example3.vn/acc/"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
→ Hỏi user đang làm việc với domain nào để chọn `apiDomain` đúng.
|
|
115
|
+
|
|
116
|
+
### 2b. Chỉ biết alias, chưa có dbToken → `db_alias_list` + `db_connect_by_name`
|
|
117
|
+
|
|
118
|
+
`db_connect` đòi sẵn `dbToken`, mà `dbToken` là base64 của một chuỗi kết nối **kèm UID/PWD**. Khi bạn
|
|
119
|
+
chỉ biết `apiName` thì **đừng đi tra tay bằng PowerShell** (moi UID/PWD ra command line là phơi
|
|
120
|
+
credential, và cả quy trình đó nay đã có tool):
|
|
121
|
+
|
|
122
|
+
**Bước 1 — xem mình được cấp những database nào:**
|
|
123
|
+
```
|
|
124
|
+
db_alias_list()
|
|
125
|
+
```
|
|
126
|
+
**Không cần tham số nào.** `SM_Modules_ManagerSelect` không nhận tham số: tAPI bơm `@sys_UserID` từ
|
|
127
|
+
Bearer token ở phía server, nên danh sách trả về **đã lọc sẵn theo quyền quản lý của chính bạn**.
|
|
128
|
+
Trả về bảng `APIName | SQLServer | DatabaseName | ModuleName | Enable`. Danh sách dài thì thêm
|
|
129
|
+
`search` để lọc phía client (khớp `APIName`/`DatabaseName`/`ModuleName`, không phân biệt hoa-thường).
|
|
130
|
+
|
|
131
|
+
**Bước 2 — kết nối:**
|
|
132
|
+
```
|
|
133
|
+
db_connect_by_name({ apiName: "basic" })
|
|
134
|
+
```
|
|
135
|
+
Tool làm trọn gói: tra `SQLServer`/`DatabaseName` từ acc → **mượn cặp UID/PWD** của một kết nối bạn đã
|
|
136
|
+
có → dựng `dbToken` → **test `SELECT 1` trước khi lưu bất cứ thứ gì** → `saveConnection` +
|
|
137
|
+
fetch schema về `_db/{alias}/`. Báo cáo giống hệt `db_connect`. **Không bao giờ in dbToken/UID/PWD.**
|
|
138
|
+
|
|
139
|
+
Thứ đang mượn là **cặp tài khoản**, còn Server/Database thì lấy từ acc — đúng ranh giới của quy trình
|
|
140
|
+
làm tay cũ. Workspace có nhiều login **khác nhau** → tool trả **menu** để bạn chọn, không đoán; nêu rõ
|
|
141
|
+
bằng `borrowCredentialsFrom: "{projectId}/{alias}"`. (Một login xuất hiện ở nhiều kết nối cùng group
|
|
142
|
+
là chuyện thường và **không** bị coi là nhập nhằng.)
|
|
143
|
+
|
|
144
|
+
**Không tìm thấy `apiName` mang HAI nghĩa** — alias không tồn tại, **hoặc** bạn không được cấp quyền
|
|
145
|
+
quản lý nó. Đừng vội kết luận là gõ sai tên: chạy `db_alias_list()` xem mình thực sự có gì.
|
|
146
|
+
|
|
147
|
+
Project **chưa có kết nối nào** vẫn chạy được (đây đúng là lúc cần tool nhất): `apiDomain` bóc từ
|
|
148
|
+
`project.json` local, `userToken` mượn từ kết nối của project khác cùng domain — chỉ tự chọn khi có
|
|
149
|
+
đúng một ứng viên, nhiều thì trả menu. Bí quá thì truyền thẳng `apiDomain` + `userToken`.
|
|
150
|
+
|
|
151
|
+
> ⚠️ Field `Password` trong response của `SM_Modules_ManagerSelect` là **hash nội bộ của tAPI**, không
|
|
152
|
+
> phải SQL Server password thực — không dùng được để build dbToken. Tool **không in field này ra**.
|
|
153
|
+
|
|
154
|
+
### 2c. Workflow đầy đủ — kết nối DB lần đầu cho project mới
|
|
155
|
+
|
|
156
|
+
> Mục này giả định database **đã tồn tại và đã được đăng ký alias** trong `acc`. Chưa có (dựng dự án
|
|
157
|
+
> từ số 0) → làm [project-provisioning.md](project-provisioning.md) trước: tạo DB, hai tài khoản SQL,
|
|
158
|
+
> rồi `db_alias_new` để có alias — xong mới quay lại đây.
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
1. project_sync(projectId) → có project.json local (nguồn của apiDomain)
|
|
162
|
+
2. session_set({ projectId }) → khỏi phải truyền projectId ở mọi bước sau
|
|
163
|
+
3. db_alias_list() → xem mình được cấp quyền quản lý những alias nào
|
|
164
|
+
4. db_connect_by_name({ apiName }) → tra + dựng token + test + lưu + fetch schema, một lượt
|
|
165
|
+
(KHÔNG gọi db_schema_get thêm — đã fetch rồi)
|
|
166
|
+
5. [Module gọi thêm alias khác?] lặp bước 4 cho từng alias còn thiếu.
|
|
167
|
+
Kết nối thêm KHÔNG tự giành `default` — muốn đổi thì `makeDefault: true`.
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Đã cầm sẵn `dbToken` (ai đó đưa, hoặc chép từ nơi khác) thì bỏ qua bước 3–4, gọi thẳng `db_connect`.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 3. Build dbToken
|
|
175
|
+
|
|
176
|
+
> Chỉ cần khi bạn **có sẵn UID/PWD trong tay** và muốn gọi `db_connect` trực tiếp. Mượn credential từ
|
|
177
|
+
> một kết nối đã có thì dùng `db_connect_by_name` (§2b) — nó dựng token giúp, và không in ra plain text.
|
|
178
|
+
|
|
179
|
+
`dbToken` là chuỗi kết nối SQL Server được **base64-encode**. Chuỗi gốc:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
Server={sqlServer};Database={database};UID={uid};PWD={pwd};Encrypt=True;TrustServerCertificate=True;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Build bằng JavaScript:**
|
|
186
|
+
```js
|
|
187
|
+
Buffer.from("Server=192.0.2.101;Database=AccountUser;UID=sa;PWD=MyPass;Encrypt=True;TrustServerCertificate=True;").toString("base64")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Build bằng PowerShell:**
|
|
191
|
+
```powershell
|
|
192
|
+
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("Server=192.0.2.101;Database=AccountUser;UID=sa;PWD=MyPass;Encrypt=True;TrustServerCertificate=True;"))
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 4. Workflow kết nối lần đầu
|
|
198
|
+
|
|
199
|
+
### Xác định projectId trước khi kết nối
|
|
200
|
+
|
|
201
|
+
Thông tin kết nối DB được lưu vào `{FUI_MCP_WORKDIR}/{projectId}/_db/_connections.json`, dưới khoá
|
|
202
|
+
`apiName`. **Một project có thể có nhiều apiName** — `projectId` và `apiName` là hai thứ khác nhau,
|
|
203
|
+
đừng suy cái này ra cái kia.
|
|
204
|
+
|
|
205
|
+
**Quy tắc bắt buộc:** Nếu user không chỉ rõ projectId, hoặc alias/database chưa rõ thuộc project nào → **phải hỏi xác nhận** trước khi gọi `db_connect`:
|
|
206
|
+
|
|
207
|
+
> _"Thông tin kết nối DB này sẽ được lưu vào project nào? (projectId)"_
|
|
208
|
+
|
|
209
|
+
Không tự đoán projectId rồi gọi luôn — lưu sai project sẽ khiến schema/SP bị đặt nhầm chỗ và khó tìm lại.
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
db_connect(apiDomain, dbToken, apiName, [sqlServer, database, userToken])
|
|
213
|
+
→ schema tự động được fetch và lưu vào _db/{alias}/schema.json
|
|
214
|
+
→ KHÔNG gọi db_schema_get thêm sau bước này
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Project gọi nhiều database → gọi `db_connect` NHIỀU LẦN
|
|
218
|
+
|
|
219
|
+
Mở `module.json` và quét mọi `"API": "/{alias}/…"`: **mỗi alias khác nhau là một kết nối cần có**.
|
|
220
|
+
Gọi `db_connect` lần lượt cho từng alias — lần sau **không đụng** gì tới kết nối đã có, mỗi cái nằm
|
|
221
|
+
trong thư mục `_db/{alias}/` của riêng nó.
|
|
222
|
+
|
|
223
|
+
Thiếu một alias thì mọi lời gọi thuộc alias đó **không được kiểm gì cả**: `module_validate` không có
|
|
224
|
+
schema để đối chiếu tên SP, `db_sp_verify`/`db_sp_get` không tìm thấy SP, và `db_sp_deploy` không có
|
|
225
|
+
đường nào tới đúng database đó.
|
|
226
|
+
|
|
227
|
+
Kết nối **thêm vào không tự giành mặc định** — kết nối đầu tiên vẫn là `default`. Muốn đổi thì
|
|
228
|
+
`db_connect({ …, makeDefault: true })`.
|
|
229
|
+
|
|
230
|
+
**Tham số đầy đủ nên truyền:**
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"projectId": "my-project",
|
|
235
|
+
"apiDomain": "tapi.example.vn",
|
|
236
|
+
"apiName": "acc",
|
|
237
|
+
"sqlServer": "192.0.2.101",
|
|
238
|
+
"database": "MyDatabase",
|
|
239
|
+
"dbToken": "<base64>",
|
|
240
|
+
"userToken": "<bearer token nếu có>"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Truyền `sqlServer` + `database` ngay từ đầu để sau này dùng được `db_token_rebuild` mà không cần hỏi lại.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 5. Khi đổi mật khẩu — db_token_rebuild
|
|
249
|
+
|
|
250
|
+
Khi UID hoặc PWD thay đổi nhưng Server và Database vẫn giữ nguyên:
|
|
251
|
+
|
|
252
|
+
**Bước 1:** Xác nhận kết nối cần sửa đã có `sqlServer` và `database`
|
|
253
|
+
```
|
|
254
|
+
db_config_read(projectId)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Bước 2:** Rebuild token — server tự lấy `sqlServer`/`database` từ kết nối đó, chỉ cần truyền thông tin thay đổi
|
|
258
|
+
```
|
|
259
|
+
db_token_rebuild({ projectId, db, uid, password })
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Tool sẽ tự build lại `dbToken`, test kết nối, và lưu. Nếu `sqlServer`/`database` chưa có → phải dùng `db_connect` đầy đủ thay thế.
|
|
263
|
+
|
|
264
|
+
**`db` là BẮT BUỘC khi project có nhiều hơn một kết nối** — đây là thao tác ghi đè credential, đoán
|
|
265
|
+
sai thì ghi token của DB này đè lên DB kia. Một kết nối duy nhất thì bỏ trống được.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 6. userToken — Mục đích và chia sẻ
|
|
270
|
+
|
|
271
|
+
### Mục đích
|
|
272
|
+
|
|
273
|
+
`userToken` là **Bearer token của người dùng** — dùng để gọi các `spAPI_*` endpoint sau khi deploy, nhằm test API như một user thực. Khác với `dbToken` là kết nối trực tiếp vào SQL Server.
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
dbToken → kết nối SQL Server để chạy schema/SP
|
|
277
|
+
userToken → gọi REST API của tAPI như một end-user (test spAPI_*)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Chia sẻ userToken giữa các project
|
|
281
|
+
|
|
282
|
+
**userToken có thể dùng chung** cho các project trong cùng một **GroupName** (xem trong `_projectInfo.json`):
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{ "GroupName": "GroupA" }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Nếu đã có `userToken` từ project GroupA khác → có thể set thẳng cho project mới mà không cần đăng nhập lại:
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
db_user_token_set({ projectId, db, userToken })
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Hoặc bỏ qua `userToken` khi gọi tool — MCP sẽ tự scan workspace tìm token từ project cùng group.
|
|
295
|
+
|
|
296
|
+
### userToken lưu THEO TỪNG KẾT NỐI
|
|
297
|
+
|
|
298
|
+
`userToken` nằm trong mục của một alias trong `_db/_connections.json`, không phải một giá trị chung
|
|
299
|
+
của project. Project nhiều database thì mỗi kết nối cần token riêng — truyền `db: "<alias>"` để nói
|
|
300
|
+
rõ set vào kết nối nào; bỏ trống thì vào kết nối mặc định.
|
|
301
|
+
|
|
302
|
+
Điều này quan trọng với `right_*`: nhóm tool quyền gọi alias `acc` bằng `userToken` lấy từ **kết nối
|
|
303
|
+
khớp `apiName` đang thao tác** (không khớp thì kết nối mặc định) — thiếu token ở đúng kết nối đó thì
|
|
304
|
+
tool quyền không chạy được dù kết nối khác đã có token.
|
|
305
|
+
|
|
306
|
+
### Set userToken sau khi kết nối
|
|
307
|
+
|
|
308
|
+
Nếu chưa có lúc `db_connect`, set riêng sau:
|
|
309
|
+
```
|
|
310
|
+
db_user_token_set({ projectId, db, userToken })
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## 6a. SSO theo tên miền — cùng domain cha thì token DÙNG ĐƯỢC, đừng hỏi lại
|
|
316
|
+
|
|
317
|
+
**Sự thật nền tảng:** SSO giữa các API của các project FUI được chứng thực **theo tên miền cha**. Mọi
|
|
318
|
+
API nằm dưới cùng một domain cha (ví dụ mọi host `*.lhu.edu.vn`: `tapi.lhu.edu.vn`, `api.lhu.edu.vn`,
|
|
319
|
+
`capnhatluong.lhu.edu.vn`…) **chấp nhận cùng một `userToken`**.
|
|
320
|
+
|
|
321
|
+
Hệ quả trực tiếp — đây là điểm hay bị làm sai:
|
|
322
|
+
|
|
323
|
+
- **Đã có `userToken` của một project/alias thuộc domain cha đó thì dùng thẳng cho project/alias khác
|
|
324
|
+
cùng domain cha. KHÔNG hỏi lại người dùng "token này có gọi được không", KHÔNG đi thử một lời gọi
|
|
325
|
+
để "xác minh", KHÔNG bắt đăng nhập lại.** Câu trả lời đã biết trước từ tên miền.
|
|
326
|
+
- Việc scan workspace tìm token sẵn có (`db_user_token_set` gọi không kèm `userToken`) là hợp lệ và
|
|
327
|
+
nên làm — nó chính là hiện thực hoá luật này.
|
|
328
|
+
|
|
329
|
+
Ranh giới của luật (chỗ vẫn phải hỏi):
|
|
330
|
+
|
|
331
|
+
| Tình huống | Xử lý |
|
|
332
|
+
|---|---|
|
|
333
|
+
| Cùng domain cha (`*.lhu.edu.vn` → `*.lhu.edu.vn`) | **Dùng ngay**, không hỏi, không thử |
|
|
334
|
+
| Khác domain cha (`*.lhu.edu.vn` → `*.dhlh.vn`, `*.fap.vn`) | Token **không** áp dụng — cần token riêng của domain kia |
|
|
335
|
+
| Project nhiều domain (`domainSetting` có nhiều host khác domain cha nhau) | Mỗi domain cha là một vùng SSO riêng — xem `domainSetting` trong `project.json` (mục 2a) để biết đang đứng ở vùng nào |
|
|
336
|
+
|
|
337
|
+
> `GroupName` trong `_projectInfo.json` (mục 6 bên trên) là cách **gộp nhóm ở tầng quản trị**, còn
|
|
338
|
+
> tên miền cha là thứ **thực sự quyết định** token có được chấp nhận hay không. Hai cái thường trùng
|
|
339
|
+
> nhau; khi lệch thì tin vào tên miền.
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 7. Refresh schema
|
|
344
|
+
|
|
345
|
+
| Khi nào | Tool |
|
|
346
|
+
|---|---|
|
|
347
|
+
| Sau `db_connect` | **Không cần** — schema đã tự fetch |
|
|
348
|
+
| User yêu cầu refresh | `db_schema_get({ projectId, db })` |
|
|
349
|
+
| Vừa tạo/đổi tên/xóa table hoặc SP | `db_schema_get({ projectId, db })` |
|
|
350
|
+
| Schema local trông cũ/thiếu | `db_schema_get({ projectId, db })` |
|
|
351
|
+
|
|
352
|
+
Đọc schema đã cache (nhanh, không gọi DB):
|
|
353
|
+
```
|
|
354
|
+
db_schema_read({ projectId, db, objectType?, search? })
|
|
355
|
+
```
|
|
356
|
+
`objectType`: `Table`, `View`, `API`, `API File`, `Procedure`, `Function`
|
|
357
|
+
|
|
358
|
+
**Mỗi kết nối có schema riêng** (`_db/{alias}/schema.json`) và cả hai tool này thao tác trên **đúng
|
|
359
|
+
một** kết nối. Project nhiều database thì `db_schema_get` một alias **không** làm mới alias còn lại —
|
|
360
|
+
tra `db_config_read` xem có bao nhiêu kết nối rồi refresh từng cái. Không thấy một bảng/SP trong
|
|
361
|
+
`db_schema_read` thường không phải "chưa có" mà là **đang đọc nhầm kết nối**.
|
|
362
|
+
|
|
363
|
+
**Cũng tải lại thân SP:** `db_schema_get` làm mới **mọi thứ kết nối đó đang giữ** — `schema.json` lẫn
|
|
364
|
+
từng file `_db/{alias}/{name}.sql`. Bản `.sql` local khác server được chép vào `_history/` trước rồi
|
|
365
|
+
mới ghi đè, nên không mất; nhưng đừng coi file local là bản mới nhất nếu chưa refresh.
|
|
366
|
+
|
|
367
|
+
**Tự dọn SP/function mồ côi khi refresh:** `db_connect`/`db_schema_get` quét **các file `.sql` thật
|
|
368
|
+
trong `_db/{alias}/`** rồi xếp làm ba nhóm — chỉ trong phạm vi kết nối đó, không nhìn sang alias khác:
|
|
369
|
+
|
|
370
|
+
| Nhóm | Điều kiện | Hành vi |
|
|
371
|
+
|---|---|---|
|
|
372
|
+
| Còn sống | tên có trong schema MỚI | giữ + tải lại thân từ server |
|
|
373
|
+
| Đã xóa trên server | có ở schema CŨ, mất ở schema MỚI | **archive** vào `_history/{projectId}/_db/{alias}/{name}_{thời điểm}_deleted.sql` rồi xóa khỏi `_db/{alias}/` |
|
|
374
|
+
| Chỉ có ở local | không có ở cả hai | **chỉ liệt kê, không xóa** — nhiều khả năng là SP đang soạn qua `db_sp_save` chưa deploy. Muốn dọn: `db_schema_get({ pruneLocalOnly: true })` |
|
|
375
|
+
|
|
376
|
+
Không để lại file cũ tưởng là định nghĩa còn hiệu lực; tool in dòng `📋 Archived orphaned local SP
|
|
377
|
+
file(s)...` nếu có. So tên **không phân biệt hoa-thường** (SQL Server không phân biệt, file có thể lệch case).
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 8. Workflow làm việc với SP
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
1. db_sp_list(projectId) → xem danh sách SP — MỌI kết nối, ghi rõ SP nào thuộc alias nào
|
|
385
|
+
2. db_sp_get(name, projectId) → fetch định nghĩa SP từ DB → lưu _db/{alias}/{name}.sql
|
|
386
|
+
3. [sửa file _db/{alias}/{name}.sql]
|
|
387
|
+
4. db_sp_save(name, sql, projectId) → lưu SQL vào local, chưa deploy
|
|
388
|
+
5. db_sp_deploy(name, projectId) → deploy lên DB
|
|
389
|
+
(bản ĐANG CHẠY trên server tự lưu vào _history/
|
|
390
|
+
trước khi ghi — đây là bản để rollback)
|
|
391
|
+
nếu tên là spAPI_*/spAPIFILE_* → tự gọi /help xoá
|
|
392
|
+
cache tAPI (một lần, hỏng thì bỏ qua — không chặn
|
|
393
|
+
deploy, không cần gọi lại)
|
|
394
|
+
6. db_sp_help(name, projectId) → BẮT BUỘC gọi thủ công khi SP được ALTER/deploy bằng
|
|
395
|
+
con đường khác ngoài db_sp_deploy (ví dụ:
|
|
396
|
+
db_sql_execute_nonquery chạy ALTER PROCEDURE
|
|
397
|
+
trực tiếp) — đường đó không có auto-clear.
|
|
398
|
+
Cũng dùng khi muốn xem tham số đầu vào của API
|
|
399
|
+
trước khi wire IN/OUT vào module.json.
|
|
400
|
+
Tool gọi MỘT lần theo URL chuẩn, hỏng thì chỉ báo
|
|
401
|
+
một dòng thông tin — ĐỪNG thử URL biến thể khác
|
|
402
|
+
7. db_sp_verify(name, params) → kiểm chứng TĨNH contract API vừa deploy (đọc hay ghi đều
|
|
403
|
+
qua tool này) — KHÔNG gọi endpoint nào, chỉ đọc thân SP
|
|
404
|
+
thật và suy luận response shape, contract lỗi, phân loại
|
|
405
|
+
đọc/ghi, sinh sẵn snippet apiMocks cho module_simulate.
|
|
406
|
+
BẮT BUỘC chạy trước khi viết apiMocks cho một SP đã tồn
|
|
407
|
+
tại — tự bịa cấu trúc là sai, module_simulate sẽ cảnh báo
|
|
408
|
+
nếu phát hiện mock khớp SP thật mà chưa được verify.
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Chọn kết nối trong cả 7 bước:** bỏ trống `db` thì MCP **suy từ chính tên SP** — dò tên đó trong
|
|
412
|
+
schema và trong file `.sql` local của từng kết nối. Ra đúng một kết nối thì đi tiếp im lặng; ra
|
|
413
|
+
**không** kết nối nào hoặc **nhiều hơn một** (SP trùng tên ở hai DB) thì tool trả về **menu các alias**,
|
|
414
|
+
chọn một cái rồi gọi lại — đừng đoán. Riêng `db_sp_save` với SP **mới hoàn toàn** thì chưa có gì để
|
|
415
|
+
suy: project nhiều kết nối phải nêu `db` ngay từ lần lưu đầu.
|
|
416
|
+
|
|
417
|
+
> Cách suy luận của `db_sp_verify` (đọc thân SP thật, không đoán theo tên, lỗi cứng khi không đọc
|
|
418
|
+
> được thân SP) và cách kiểm tra API ghi bằng module_simulate: xem
|
|
419
|
+
> [tools-registry.md](tools-registry.md#quy-tắc-bắt-buộc-cho-db-tools). Các guard `DROP`/`ALTER
|
|
420
|
+
> TABLE`, `DELETE`/`UPDATE` không `WHERE` đều enforce ở code level, không bypass được; lệnh ghi qua
|
|
421
|
+
> `db_sql_execute`/`db_sql_execute_nonquery` cần `confirmWrite: true`.
|
|
422
|
+
|
|
423
|
+
**Rollback SP:**
|
|
424
|
+
1. Đọc file trong `{FUI_MCP_WORKDIR}/_history/{projectId}/_db/{alias}/{name}_{thời điểm}.sql`
|
|
425
|
+
2. `db_sp_save(name, oldSql)` — ghi lại bản cũ vào local
|
|
426
|
+
3. `db_sp_deploy(name)` — deploy lại
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## 8a. Linked Server — quy ước `spIO_IN_` / `spIO_OUT_`
|
|
431
|
+
|
|
432
|
+
SQL Server Linked Server đã có sẵn trong hệ thống FUI. Tuy nhiên, **vì lý do bảo mật, không thể truy vấn trực tiếp table của DB khác qua linked server** — chỉ được phép gọi một stored procedure đã định nghĩa sẵn phía kia.
|
|
433
|
+
|
|
434
|
+
**Cú pháp gọi qua Linked Server trong thân SP:**
|
|
435
|
+
```sql
|
|
436
|
+
EXEC XDATA23.Calendar2015.dbo.spIO_IN_DeleteMonHocInfo @MonHocID, @ErrorDKMH OUTPUT
|
|
437
|
+
-- ^^^^^^^^^^^^^^^^^^^^^^^^^ ← 4 phần: linked-server.database.schema.tên-SP
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Quy ước đặt tên SP vượt ranh giới Linked Server
|
|
441
|
+
|
|
442
|
+
| Tiền tố | Hướng dữ liệu | Khi nào dùng |
|
|
443
|
+
|---|---|---|
|
|
444
|
+
| `spIO_IN_` | Dữ liệu đi **vào** DB hiện tại (nhận/import từ DB kia) | SP nhận lệnh từ DB ngoài, tác động lên DB hiện tại |
|
|
445
|
+
| `spIO_OUT_` | Dữ liệu đi **ra** khỏi DB hiện tại (xuất/cung cấp cho DB kia) | SP cung cấp dữ liệu cho DB ngoài gọi vào |
|
|
446
|
+
|
|
447
|
+
**Hệ quả quan trọng:**
|
|
448
|
+
|
|
449
|
+
- Khi thấy một SP trong `db_sp_list` / schema có tiền tố `spIO_IN_` hoặc `spIO_OUT_` → đó là SP biên giới Linked Server, **không phải tAPI endpoint** (dù tên có `spIO_`). Không wire vào `"API":` trong module.json.
|
|
450
|
+
- Tiền tố giúp nhận ra ranh giới ngay trong `db_sp_list` mà không cần đọc thân SP — tên nói lên bản chất.
|
|
451
|
+
- Khi tạo SP mới có tác dụng qua Linked Server: **bắt buộc đặt tiền tố** `spIO_IN_` hoặc `spIO_OUT_` theo đúng hướng dữ liệu, không đặt tên tự do.
|
|
452
|
+
- SP loại này **không deploy được bằng `db_sp_deploy` sang DB kia** — nó chạy **trên DB hiện tại** nhưng bên trong `EXEC` gọi sang DB kia. Deploy bình thường vào alias của DB sở hữu SP đó.
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## 9. Kiểm tra nhanh các kết nối hiện có
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
db_config_read(projectId)
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Liệt kê **mọi kết nối** của project: alias (`db`), `apiDomain`, `sqlServer`, `database`, thư mục
|
|
463
|
+
`_db/{alias}/`, trạng thái `dbToken`/`userToken` (có/không — **không trả giá trị token**), và alias
|
|
464
|
+
nào đang là `default`.
|
|
465
|
+
|
|
466
|
+
Gọi tool này **trước tiên** khi chưa chắc phải truyền `db` nào cho tool khác. Nó cũng nêu ra các thư
|
|
467
|
+
mục trong `_db/` không thuộc kết nối nào (rác của bản cũ, hoặc kết nối đã bỏ) — không tool nào đọc
|
|
468
|
+
chúng nữa, tự xóa tay khi chắc chắn không cần.
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## 10. Cheat sheet — Tool theo tình huống
|
|
473
|
+
|
|
474
|
+
| Tình huống | Tool cần dùng |
|
|
475
|
+
|---|---|
|
|
476
|
+
| **Database chưa tồn tại** — dựng dự án từ số 0 | [project-provisioning.md](project-provisioning.md) (kiểm 2 cổng quyền → CREATE DATABASE → 2 tài khoản → `db_alias_new`) |
|
|
477
|
+
| Kiểm mình có quyền `CREATE DATABASE`/`CREATE LOGIN` trên máy chủ không | [project-provisioning.md](project-provisioning.md) §Bước 0 — `IS_SRVROLEMEMBER` + `HAS_PERMS_BY_NAME`, chỉ đọc |
|
|
478
|
+
| Database đã có nhưng **chưa được đăng ký alias** trong `acc` | `db_alias_new` (xem [project-provisioning.md](project-provisioning.md) §Bước 5) |
|
|
479
|
+
| Kết nối DB lần đầu, **đã có sẵn dbToken** | `db_connect` |
|
|
480
|
+
| Kết nối DB nhưng **chỉ biết alias, không có dbToken** | `db_alias_list` → `db_connect_by_name` |
|
|
481
|
+
| Xem mình được cấp quyền quản lý những database nào | `db_alias_list()` (không tham số) |
|
|
482
|
+
| Project gọi thêm một database nữa (alias khác) | `db_connect_by_name` lần nữa với `apiName` mới |
|
|
483
|
+
| Xem project đang có những kết nối nào, alias nào default | `db_config_read` |
|
|
484
|
+
| Đổi UID/PWD, server/database giữ nguyên | `db_token_rebuild` (nêu `db` nếu nhiều kết nối) |
|
|
485
|
+
| Set userToken sau khi connect | `db_user_token_set` (nêu `db` nếu nhiều kết nối) |
|
|
486
|
+
| Xem danh sách tables/SPs | `db_sp_list` |
|
|
487
|
+
| Lấy nội dung SP từ DB | `db_sp_get` |
|
|
488
|
+
| Refresh schema từ DB thực | `db_schema_get` |
|
|
489
|
+
| Đọc schema đã cache | `db_schema_read` |
|
|
490
|
+
| Lưu SQL chỉnh sửa vào local | `db_sp_save` |
|
|
491
|
+
| Deploy SP lên DB (tự xoá cache tAPI nếu là spAPI_*/spAPIFILE_*) | `db_sp_deploy` |
|
|
492
|
+
| Xem tham số đầu vào của API đã deploy (trước khi wire vào module.json) | `db_sp_help` |
|
|
493
|
+
| Xoá cache tAPI thủ công sau khi ALTER qua `db_sql_execute_nonquery` (đường đó không có auto-clear) | `db_sp_help` **(bắt buộc)** |
|
|
494
|
+
| Kiểm chứng tĩnh API vừa deploy (trước khi wire vào UI, không gọi sống) | `db_sp_verify` |
|
|
495
|
+
| Xóa SP khỏi DB | `db_sp_delete` |
|
|
496
|
+
| Đổi tên SP | `db_sp_rename` |
|
|
497
|
+
| Tìm SQLServer + DatabaseName của alias | `db_alias_list({ search })` (xem mục 2b) |
|
|
498
|
+
| Build dbToken bằng cách mượn UID/PWD của kết nối sẵn có | `db_connect_by_name` làm luôn (xem mục 2b) — **đừng decode tay bằng PowerShell** |
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## 11. Quy tắc thiết kế bảng
|
|
503
|
+
|
|
504
|
+
Quy ước đặt tên bảng, khóa chính, trường timestamp và cột audit đã tách sang **[db-table-design.md](db-table-design.md)** — load file đó khi thiết kế schema mới.
|