@fui-org/fui-cli 1.3.2 → 2.0.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.
Files changed (111) hide show
  1. package/README.md +19 -3
  2. package/dist/fui-bmg5pnmq.js +379 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +3 -6
  5. package/skills/fui/SKILL.md +9 -41
  6. package/skills/fui-skill/SKILL.md +95 -225
  7. package/skills/fui-skill/assets/projectdefaultstyle-3.0.css +555 -0
  8. package/skills/fui-skill/assets/projectdefaultstyle.css +207 -235
  9. package/skills/fui-skill/references/INDEX.md +105 -137
  10. package/skills/fui-skill/references/advanced-techniques.md +76 -68
  11. package/skills/fui-skill/references/coding-standards.md +56 -56
  12. package/skills/fui-skill/references/component-design.md +166 -173
  13. package/skills/fui-skill/references/component-quickref.md +61 -60
  14. package/skills/fui-skill/references/component-table.md +128 -117
  15. package/skills/fui-skill/references/components-dialog.md +55 -56
  16. package/skills/fui-skill/references/components-display.md +24 -30
  17. package/skills/fui-skill/references/components-echart.md +186 -261
  18. package/skills/fui-skill/references/components-input.md +72 -96
  19. package/skills/fui-skill/references/controls-patterns.md +196 -342
  20. package/skills/fui-skill/references/controls-styling-vocabulary.md +130 -97
  21. package/skills/fui-skill/references/db-table-design.md +24 -28
  22. package/skills/fui-skill/references/db-workflow.md +191 -390
  23. package/skills/fui-skill/references/default-function.md +169 -128
  24. package/skills/fui-skill/references/design-modes.md +35 -63
  25. package/skills/fui-skill/references/echart-templates.md +204 -196
  26. package/skills/fui-skill/references/fastproject.md +62 -60
  27. package/skills/fui-skill/references/fsheet.md +109 -124
  28. package/skills/fui-skill/references/fullstack-workflow.md +90 -128
  29. package/skills/fui-skill/references/module-data-patterns.md +31 -40
  30. package/skills/fui-skill/references/module-json-anatomy.md +47 -52
  31. package/skills/fui-skill/references/module-structure.md +80 -196
  32. package/skills/fui-skill/references/new-session.md +49 -51
  33. package/skills/fui-skill/references/pdfmake.md +17 -17
  34. package/skills/fui-skill/references/permission-system.md +89 -108
  35. package/skills/fui-skill/references/platform-architecture.md +128 -153
  36. package/skills/fui-skill/references/project-config.md +102 -134
  37. package/skills/fui-skill/references/project-provisioning.md +139 -244
  38. package/skills/fui-skill/references/script-map.md +208 -242
  39. package/skills/fui-skill/references/sql-clr-functions.md +98 -97
  40. package/skills/fui-skill/references/system-design.md +63 -88
  41. package/skills/fui-skill/references/tapi-file-api.md +46 -52
  42. package/skills/fui-skill/references/tapi-permission-patterns.md +51 -53
  43. package/skills/fui-skill/references/tapi-reference.md +132 -207
  44. package/skills/fui-skill/references/tools-registry.md +84 -460
  45. package/skills/fui-skill/references/ui-crosswindow-patterns.md +79 -75
  46. package/skills/fui-skill/references/ui-dialog-patterns.md +98 -72
  47. package/skills/fui-skill/references/ui-layout-patterns.md +26 -26
  48. package/skills/fui-skill/references/ui-patterns.md +71 -83
  49. package/skills/fui-skill/references/ui-screenshot-review.md +63 -62
  50. package/skills/fui-skill/references/ui-table-cell-patterns.md +61 -59
  51. package/skills/fui-skill/references/ui-templates.md +16 -23
  52. package/skills/fui-skill/references/verification.md +236 -246
  53. package/skills/fui-skill/references/watcher-patterns.md +30 -63
  54. package/skills/fui-skill/references/websocket-realtime.md +83 -66
  55. package/skills/fui-skill/scripts/component-3.0.js +298 -131
  56. package/skills/fui-skill/scripts/component.js +277 -271
  57. package/skills/fui-skill/scripts/componentTable-3.0.js +182 -53
  58. package/skills/fui-skill/scripts/componentTable.js +171 -49
  59. package/skills/fui-skill/scripts/defaultfunction-3.0.js +88 -3
  60. package/skills/fui-skill/scripts/defaultfunction.js +88 -3
  61. package/skills/fui-skill/scripts/fsheet.js +38 -0
  62. package/dist/fui-y8an39cn.js +0 -420
  63. package/skills/fui-skill/README.md +0 -112
  64. package/skills/fui-skill/metadata.json +0 -75
  65. package/skills/fui-skill-cli/SKILL.md +0 -139
  66. package/skills/fui-skill-cli/references/INDEX.md +0 -110
  67. package/skills/fui-skill-cli/references/advanced-techniques.md +0 -168
  68. package/skills/fui-skill-cli/references/coding-standards.md +0 -112
  69. package/skills/fui-skill-cli/references/component-design.md +0 -448
  70. package/skills/fui-skill-cli/references/component-quickref.md +0 -78
  71. package/skills/fui-skill-cli/references/component-table.md +0 -248
  72. package/skills/fui-skill-cli/references/components-dialog.md +0 -191
  73. package/skills/fui-skill-cli/references/components-display.md +0 -141
  74. package/skills/fui-skill-cli/references/components-echart.md +0 -316
  75. package/skills/fui-skill-cli/references/components-input.md +0 -335
  76. package/skills/fui-skill-cli/references/controls-patterns.md +0 -701
  77. package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +0 -137
  78. package/skills/fui-skill-cli/references/db-table-design.md +0 -73
  79. package/skills/fui-skill-cli/references/db-workflow.md +0 -288
  80. package/skills/fui-skill-cli/references/default-function.md +0 -425
  81. package/skills/fui-skill-cli/references/design-modes.md +0 -57
  82. package/skills/fui-skill-cli/references/echart-templates.md +0 -489
  83. package/skills/fui-skill-cli/references/fastproject.md +0 -99
  84. package/skills/fui-skill-cli/references/fsheet.md +0 -203
  85. package/skills/fui-skill-cli/references/fullstack-workflow.md +0 -313
  86. package/skills/fui-skill-cli/references/module-data-patterns.md +0 -117
  87. package/skills/fui-skill-cli/references/module-json-anatomy.md +0 -132
  88. package/skills/fui-skill-cli/references/module-structure.md +0 -141
  89. package/skills/fui-skill-cli/references/new-session.md +0 -85
  90. package/skills/fui-skill-cli/references/pdfmake.md +0 -60
  91. package/skills/fui-skill-cli/references/permission-system.md +0 -150
  92. package/skills/fui-skill-cli/references/platform-architecture.md +0 -269
  93. package/skills/fui-skill-cli/references/project-config.md +0 -303
  94. package/skills/fui-skill-cli/references/project-provisioning.md +0 -278
  95. package/skills/fui-skill-cli/references/script-map.md +0 -262
  96. package/skills/fui-skill-cli/references/sql-clr-functions.md +0 -225
  97. package/skills/fui-skill-cli/references/system-design.md +0 -89
  98. package/skills/fui-skill-cli/references/tapi-file-api.md +0 -185
  99. package/skills/fui-skill-cli/references/tapi-permission-patterns.md +0 -156
  100. package/skills/fui-skill-cli/references/tapi-reference.md +0 -474
  101. package/skills/fui-skill-cli/references/tools-registry.md +0 -84
  102. package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +0 -321
  103. package/skills/fui-skill-cli/references/ui-dialog-patterns.md +0 -255
  104. package/skills/fui-skill-cli/references/ui-layout-patterns.md +0 -176
  105. package/skills/fui-skill-cli/references/ui-patterns.md +0 -303
  106. package/skills/fui-skill-cli/references/ui-screenshot-review.md +0 -95
  107. package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +0 -318
  108. package/skills/fui-skill-cli/references/ui-templates.md +0 -22
  109. package/skills/fui-skill-cli/references/verification.md +0 -236
  110. package/skills/fui-skill-cli/references/watcher-patterns.md +0 -163
  111. package/skills/fui-skill-cli/references/websocket-realtime.md +0 -271
@@ -1,91 +1,84 @@
1
1
  # DB Connection Workflow
2
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).
3
+ > Owns: DB connections, dbToken, schema cache, SP workflow, rollback. Per-command risk/guards: [tools-registry.md](tools-registry.md); SP checklist: [verification.md](verification.md). Flags: `fui <command> --help`.
4
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.
5
+ ## 1. Storage layout
6
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:
7
+ One `_db/` subfolder PER connection. The folder name is a short, readable `connectionName` chosen by the user/AI; `apiName` is only the tAPI route alias and **may repeat across zones**.
13
8
 
14
9
  ```
15
- {FUI_MCP_WORKDIR}/{projectId}/
10
+ {workspace}/{projectId}/
16
11
  └── _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}/…
12
+ ├── _connections.json ← all connections + default
13
+ ├── rights.json ← rights cache from "acc", keyed by alias
14
+ ├── {connectionName}/ ← key in "connections" = folder name
15
+ │ ├── schema.json ← schema cache
16
+ │ └── {name}.sql ← SPs (NO "sp/" level)
17
+ └── {other-connectionName}/…
23
18
  ```
24
19
 
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:
20
+ - Three identifiers, three jobs:
21
+ - `apiName`: the alias in `/{apiName}/...` URLs; may repeat across zones.
22
+ - `connectionName`: the key in `connections` and the folder name; pick something memorable (`docs`, `docs_lhbs`).
23
+ - `sqlServer + database`: the physical database; fui uses it to recognize a connection it already has, never as a folder name.
24
+ - Every DB command takes `--db <connectionName>` or `--db <project>/<connectionName>`; a unique apiName also works. Omitted → inferred from the object; if not inferable, or an alias exists in several zones, fui returns a MENU of connectionName + apiName + SQLServer/database — pick one, re-run. Never guess.
25
+ - Overwritten/deleted files go to `{workspace}/_history/`, same path shape; all versions of a file sit together. Never auto-cleaned (user deletes manually):
34
26
 
35
27
  ```
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
28
+ {workspace}/_history/{projectId}/_db/{connectionName}/{name}_{timestamp}.sql
29
+ {workspace}/_history/{projectId}/_db/{connectionName}/{name}_{timestamp}_deleted.sql ← DROPped SP
38
30
  ```
39
31
 
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`:**
32
+ `_db/_connections.json`:
45
33
 
46
34
  ```json
47
35
  {
48
- "default": "acc",
36
+ "version": 2,
37
+ "default": "docs",
49
38
  "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>"
39
+ "docs": {
40
+ "apiName": "docs",
41
+ "apiDomain": "tapi.lhu.edu.vn",
42
+ "sqlServer": "172.0.0.24",
43
+ "database": "QLCongVan",
44
+ "dbToken": "<base64-encoded connection string>",
45
+ "userToken": "<bearer token — optional>"
56
46
  },
57
- "Dashboard": { "apiDomain": "api.example3.vn", "…": "…" }
47
+ "docs_lhbs": {
48
+ "apiName": "docs",
49
+ "apiDomain": "tapi.lhbs.vn",
50
+ "sqlServer": "192.168.100.23",
51
+ "database": "QLCongVan",
52
+ "dbToken": "<base64>"
53
+ }
58
54
  }
59
55
  }
60
56
  ```
61
57
 
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`.
58
+ `default` applies only to reads without `--db`. Writes (`fui query` with a write, `fui exec`, `fui db token-rebuild`) never use it — `--db` required.
64
59
 
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 |
60
+ | Field | Req | Meaning |
61
+ | ----------- | --- | ------------------------------------------------------------------ |
62
+ | `version` | ✅ | Always `2` for this layout |
63
+ | key | ✅ | `connectionName`, also the folder name (`docs`, `docs_lhbs`) |
64
+ | `apiName` | ✅ | tAPI alias in `"API": "/{apiName}/Ten"`; may repeat across zones |
65
+ | `apiDomain` | ✅ | Host only, NO alias, NO trailing `/` (`api.example3.vn`) |
66
+ | `sqlServer` | ✅ | SQL Server host; with `database`, identifies the physical database |
67
+ | `database` | ✅ | Database name; with `sqlServer`, prevents duplicate connections |
68
+ | `dbToken` | ✅ | Base64 SQL Server connection string |
69
+ | `userToken` | ❌ | Bearer to test `spAPI_*` after deploy |
73
70
 
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.
71
+ Without `--sql-server`/`--database`, `fui db add` reads both from the connection string in the token.
75
72
 
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`.
73
+ **Naming:** pass `--name <connectionName>` for a specific name. Omitted, fui suggests `apiName` first; if taken, adds the zone from `apiDomain` (`docs` → `docs_lhbs`); still taken, adds the database and a number. Names take letters, digits, `.`, `_`, `-` and are lowercased. The same server + database added again keeps its existing name.
81
74
 
82
- ---
75
+ **v1 compatibility, no silent moves:** an old record without `apiName` reads as `apiName = connectionName = key`. fui never renames old SQL folders. A new connection can take its own name, so an alias repeated across zones can sit next to the old record.
83
76
 
84
- ## 2. Tìm thông tin kết nối của một project
77
+ > **CRITICAL — `apiDomain` has two meanings:** `_connections.json` → bare host (`api.example3.vn`); `project.json` `data.apiDomain` → full base URL with apiName + trailing `/` (`https://tapi.example.vn/acc/`). Top bug: bare host + apiName without `/` → `tapi.lhu.edu.vnts` instead of `tapi.lhu.edu.vn/ts`. See [tapi-reference.md](tapi-reference.md) §3. Don't build URLs; copy the `Wiring URL` line from `fui sp verify`/`fui sp help`.
85
78
 
86
- ### 2a. Đọc `apiDomain` từ project.json
79
+ ## 2. Finding connection info
87
80
 
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:
81
+ ### 2a. From project.json
89
82
 
90
83
  ```json
91
84
  "data": {
@@ -94,11 +87,7 @@ dọn** — user tự xoá tay khi thấy đủ.
94
87
  }
95
88
  ```
96
89
 
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:
90
+ → `apiDomain` = `tapi.example.vn`, `apiName` = `acc` (first path segment). Multi-domain projects:
102
91
 
103
92
  ```json
104
93
  "domainSetting": {
@@ -111,394 +100,206 @@ Một số project có nhiều domain, mỗi domain dùng một tAPI server riê
111
100
  }
112
101
  ```
113
102
 
114
- → Hỏi user đang làm việc với domain nào để chọn `apiDomain` đúng.
103
+ → Ask the user which domain to use.
115
104
 
116
- ### 2b. Chỉ biết alias, chưa có dbToken → `db_alias_list` + `db_connect_by_name`
105
+ ### 2b. Alias known, no dbToken → `fui db alias list` + `fui db add-by-name`
117
106
 
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):
107
+ Never extract UID/PWD by hand (PowerShell etc.) — exposes credentials.
121
108
 
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).
109
+ 1. `fui db alias list` — no args: tAPI injects `@sys_UserID` from the Bearer token, so `SM_Modules_ManagerSelect` returns only DBs you're granted. Columns `APIName | SQLServer | DatabaseName | ModuleName | Enable`; `--search` filters client-side (APIName/DatabaseName/ModuleName, case-insensitive).
110
+ 2. `fui db add-by-name <apiName>` — looks up SQLServer/DatabaseName on acc → borrows the UID/PWD pair of an existing connection → builds dbToken → tests `SELECT 1` before saving anything → saves + fetches schema to `_db/{connectionName}/`. Never prints dbToken/UID/PWD.
130
111
 
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.**
112
+ - Several different logins in workspace → menu; pass `--borrow-from <projectId>/<connectionName>`. Same login across one group's connections is not ambiguous.
113
+ - "apiName not found" = alias doesn't exist OR you're not granted it. Don't assume a typo; run `fui db alias list`.
114
+ - Project with no connections works: apiDomain from local `project.json`, userToken borrowed from another project on the same domain (auto only if exactly one candidate, else menu). Else pass `--api-domain` + `--user-token-env VAR`.
138
115
 
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.)
116
+ > ⚠️ `Password` in `SM_Modules_ManagerSelect` is a tAPI internal hash, not the SQL password — unusable for dbToken. fui never prints it.
143
117
 
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ì.
118
+ ### 2c. First connection for a new project
146
119
 
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.
120
+ > Requires the DB to exist and be registered as an alias in `acc`. Otherwise do [project-provisioning.md](project-provisioning.md) first (DB, two SQL accounts, `fui db alias new`).
159
121
 
160
122
  ```
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`.
123
+ 1. fui project sync -p <projectId> → local project.json (apiDomain source)
124
+ 2. fui use -p <projectId> → project implied afterwards
125
+ 3. fui db alias list → aliases you may manage
126
+ 4. fui db add-by-name <apiName> → lookup + token + test + save + schema (do NOT run fui schema pull after)
127
+ 5. Module uses other aliases? Repeat 4 per alias. Added connections do NOT become default (use --default).
168
128
  ```
169
129
 
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
130
+ Already have a dbToken → skip 3–4, use `fui db add`.
175
131
 
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.
132
+ ## 3. Building a dbToken
178
133
 
179
- `dbToken` là chuỗi kết nối SQL Server được **base64-encode**. Chuỗi gốc:
134
+ Only if you hold UID/PWD and want `fui db add`; to borrow credentials use `fui db add-by-name` (no plain text). dbToken = base64 of:
180
135
 
181
136
  ```
182
137
  Server={sqlServer};Database={database};UID={uid};PWD={pwd};Encrypt=True;TrustServerCertificate=True;
183
138
  ```
184
139
 
185
- **Build bằng JavaScript:**
186
140
  ```js
187
- Buffer.from("Server=192.0.2.101;Database=AccountUser;UID=sa;PWD=MyPass;Encrypt=True;TrustServerCertificate=True;").toString("base64")
141
+ Buffer.from(
142
+ "Server=192.0.2.101;Database=AccountUser;UID=sa;PWD=MyPass;Encrypt=True;TrustServerCertificate=True;",
143
+ ).toString("base64");
188
144
  ```
189
145
 
190
- **Build bằng PowerShell:**
191
146
  ```powershell
192
147
  [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("Server=192.0.2.101;Database=AccountUser;UID=sa;PWD=MyPass;Encrypt=True;TrustServerCertificate=True;"))
193
148
  ```
194
149
 
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
- ---
150
+ Pass secrets only via stdin (`-`) or `--*-env VAR`.
268
151
 
269
- ## 6. userToken — Mục đích và chia sẻ
152
+ ## 4. `fui db add`
270
153
 
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.
154
+ - Saved to `{workspace}/{projectId}/_db/_connections.json` under `apiName`. projectId ≠ apiName; a project can have many apiNames — never derive one from the other.
155
+ - **Mandatory:** projectId not stated, or alias's project unclear → ask _"Which project should this DB connection be saved to? (projectId)"_ before `fui db add`. Wrong project misplaces schema/SPs.
274
156
 
275
157
  ```
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_*)
158
+ fui db add <apiName> -p <project> --token - --api-domain host [--name connectionName] [--sql-server host --database name] [--user-token-env VAR] [--default]
159
+ → schema auto-fetched to _db/{connectionName}/schema.json; do NOT run fui schema pull after
278
160
  ```
279
161
 
280
- ### Chia sẻ userToken giữa các project
162
+ - Always pass `--sql-server` + `--database` (enables `fui db token-rebuild`).
163
+ - Multi-DB: scan every `"/{alias}/…"` in `module.json` and match it against the current zone/apiDomain. One alias can map to databases in several zones; each physical database gets its own `fui db add` and its own `_db/{connectionName}/`; later adds don't touch existing ones. A missing alias gets no checks: `fui module validate` can't match SP names, `fui sp verify`/`fui sp get` can't find SPs, `fui sp deploy` can't reach the DB.
164
+ - First connection stays `default`; change with `--default`.
281
165
 
282
- **userToken có thể dùng chung** cho các project trong cùng một **GroupName** (xem trong `_projectInfo.json`):
166
+ ## 5. Password change — `fui db token-rebuild`
283
167
 
284
- ```json
285
- { "GroupName": "GroupA" }
286
- ```
168
+ UID/PWD changed, Server/Database unchanged:
287
169
 
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:
170
+ 1. `fui db list -p <projectId>` — confirm the connection has `sqlServer` and `database` (missing → full `fui db add` instead).
171
+ 2. `fui db token-rebuild --db <connectionName> --uid <login> --pwd -` — rebuilds, tests, saves.
289
172
 
290
- ```
291
- db_user_token_set({ projectId, db, userToken })
292
- ```
173
+ `--db` REQUIRED with >1 connection (overwrites credentials; wrong guess clobbers another DB's token).
293
174
 
294
- Hoặc bỏ qua `userToken` khi gọi tool — MCP sẽ tự scan workspace tìm token từ project cùng group.
175
+ ## 6. userToken
295
176
 
296
- ### userToken lưu THEO TỪNG KẾT NỐI
177
+ - `userToken` = user's Bearer token to call `spAPI_*` after deploy as a real user; `dbToken` = direct SQL Server connection.
297
178
 
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
179
  ```
310
- db_user_token_set({ projectId, db, userToken })
180
+ dbToken → SQL Server connection to run schema/SP
181
+ userToken → call tAPI REST API as an end-user (test spAPI_*)
311
182
  ```
312
183
 
313
- ---
184
+ - Shareable across projects with the same `GroupName` in `_projectInfo.json` (`{ "GroupName": "GroupA" }`): `fui db user-token --db <connectionName> --from <project/connectionName>` — no re-login. `fui db user-token` with no source lists workspace tokens.
185
+ - Stored PER CONNECTION in `_connections.json`. Multi-DB → one token per connection; pass `--db <connectionName>` (omitted → default).
186
+ - `fui right …` calls `acc` with the userToken of the connection matching the current apiName (else default); missing there → rights commands fail even if other connections have tokens.
187
+ - Not set at `fui db add` → `fui db user-token --db <connectionName> --token -`.
314
188
 
315
- ## 6a. SSO theo tên miền — cùng domain cha thì token DÙNG ĐƯỢC, đừng hỏi lại
189
+ ## 6a. Domain SSO — same parent domain: token works, don't ask
316
190
 
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`**.
191
+ SSO is by parent domain: all APIs under e.g. `*.lhu.edu.vn` (`tapi.lhu.edu.vn`, `api.lhu.edu.vn`, `capnhatluong.lhu.edu.vn`…) accept the same `userToken`.
320
192
 
321
- Hệ quả trực tiếp — đây là điểm hay bị làm sai:
193
+ - Have a token for that parent domain → use it for other projects/aliases there. Do NOT ask the user, do NOT test-call to "verify", do NOT make them log in.
194
+ - Scanning the workspace for tokens (`fui db user-token` without a token) is valid and recommended.
322
195
 
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.
196
+ | Case | Action |
197
+ | ------------------------------------------------- | -------------------------------------------- |
198
+ | Same parent domain | Use now, no asking/testing |
199
+ | Different parent domain (`*.dhlh.vn`, `*.fap.vn`) | Token invalid — need that domain's token |
200
+ | `domainSetting` hosts on different parent domains | Each parent domain is its own SSO zone (§2a) |
328
201
 
329
- Ranh giới của luật (chỗ vẫn phải hỏi):
202
+ > `GroupName` is admin grouping; the parent domain decides acceptance. If they differ, trust the domain.
330
203
 
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 |
204
+ ## 7. Schema refresh
336
205
 
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.
206
+ | When | Command |
207
+ | --------------------------------------------------------------- | --------------------------------------- |
208
+ | After `fui db add` | Not needed |
209
+ | User asks / table or SP created, renamed, dropped / cache stale | `fui schema pull --db <connectionName>` |
340
210
 
341
- ---
211
+ Read cache (no DB call): `fui schema [--db <connectionName>] [--type Table|View|API|"API File"|Procedure|Function] [--search text]`.
342
212
 
343
- ## 7. Refresh schema
213
+ - Each connection has its own `schema.json`; both commands act on one connection. Pulling one connection doesn't refresh others — check `fui db list`, pull each. Object missing in `fui schema` usually = wrong connection.
214
+ - `fui schema pull` also reloads every `_db/{connectionName}/{name}.sql`; local copies differing from server go to `_history/` first. Don't trust local files as latest before a pull.
344
215
 
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 })` |
216
+ Orphans: `fui db add`/`fui schema pull` scan actual `.sql` files in that connection's `_db/{connectionName}/` only:
351
217
 
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 })` |
218
+ | Group | Condition | Behavior |
219
+ | ----------------- | --------------- | -------------------------------------------------------------------------------------------------------------- |
220
+ | Alive | in NEW schema | keep, reload body |
221
+ | Dropped on server | in OLD, not NEW | archive to `_history/{projectId}/_db/{connectionName}/{name}_{timestamp}_deleted.sql`, remove locally |
222
+ | Local only | in neither | listed, NOT deleted (likely unsaved draft from `fui sp save`); clean with `fui schema pull --prune-local-only` |
375
223
 
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).
224
+ Prints `📋 Archived orphaned local SP file(s)...` when archiving. Name match is case-insensitive.
378
225
 
379
- ---
380
-
381
- ## 8. Workflow làm việc với SP
226
+ ## 8. SP workflow
382
227
 
383
228
  ```
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.
229
+ 1. fui sp list → all connections, SPs tagged by connection
230
+ 2. fui sp get <name> → fetch definition → _db/{connectionName}/{name}.sql
231
+ 3. edit _db/{connectionName}/{name}.sql
232
+ 4. fui sp save <name> -f file.sql → local only, not deployed
233
+ 5. fui sp deploy <name> → server's RUNNING version saved to _history/ first (rollback copy);
234
+ spAPI_*/spAPIFILE_* → auto /help clears tAPI cache once
235
+ (failure ignored, doesn't block, don't retry)
236
+ 6. fui sp help <name> → REQUIRED if the SP was ALTERed outside fui sp deploy
237
+ (e.g. ALTER PROCEDURE via fui exec — no auto-clear).
238
+ Also shows input params before wiring IN/OUT in module.json.
239
+ Calls ONCE on the standard URL; failure = one info line.
240
+ Do NOT try URL variants.
241
+ 7. fui sp verify <name> [--params …] → STATIC contract check (read or write API); calls NO endpoint.
242
+ Reads real SP body → response shape, error contract, read/write,
243
+ apiMocks snippet for fui module simulate. REQUIRED before writing
244
+ apiMocks for an existing SP — never invent structure;
245
+ fui module simulate warns on unverified mocks matching real SPs.
409
246
  ```
410
247
 
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`.
248
+ - `--db` omitted → inferred from SP name (schema + local `.sql` per connection). One match → silent; zero or several (same name in two DBs) → connection menu, pick and re-run, don't guess.
249
+ - `fui sp save` for a brand-new SP can't infer: multi-connection projects must pass `--db` on first save.
422
250
 
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
251
+ > `fui sp verify` reads the real body, not the name; hard error if unreadable. Write-API testing with `fui module simulate`: [tools-registry.md](tools-registry.md). Guards on `DROP`/`ALTER TABLE`, `DELETE`/`UPDATE` without `WHERE` are enforced in code, no bypass. Writes via `fui query`/`fui exec` need `--confirm-write`.
427
252
 
428
- ---
253
+ **Rollback:** read `{workspace}/_history/{projectId}/_db/{connectionName}/{name}_{timestamp}.sql` → `fui sp save <name> -f <old file>` → `fui sp deploy <name>`.
429
254
 
430
- ## 8a. Linked Server — quy ước `spIO_IN_` / `spIO_OUT_`
255
+ ## 8a. Linked Server — `spIO_IN_` / `spIO_OUT_`
431
256
 
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.
257
+ Linked servers exist, but for security you can NOT query another DB's tables through them — only call a predefined SP on the other side:
433
258
 
434
- **Cú pháp gọi qua Linked Server trong thân SP:**
435
259
  ```sql
436
260
  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.
261
+ -- ^^^^^^^^^^^^^^^^^^^^^^^^^ ← 4 parts: linked-server.database.schema.sp-name
262
+ ```
263
+
264
+ | Prefix | Direction | Use |
265
+ | ----------- | ----------------- | --------------------------------------------------------- |
266
+ | `spIO_IN_` | Into current DB | Receives commands from an external DB, acts on current DB |
267
+ | `spIO_OUT_` | Out of current DB | Provides data for an external DB to call |
268
+
269
+ - These are boundary SPs, NOT tAPI endpoints (despite `spIO_`). Never wire into `"API":` in module.json.
270
+ - New SPs acting across a linked server MUST use the prefix matching data direction.
271
+ - `fui sp deploy` can't deploy them to the other DB; they run on the current DB and `EXEC` across. Deploy to the owning DB's connection.
272
+
273
+ ## 9. List connections
274
+
275
+ `fui db list -p <projectId>` — every connection: connectionName (`db`), `apiName`, `apiDomain`, `sqlServer`, `database`, `_db/{connectionName}/`, dbToken/userToken present or not (never values), which is `default`. Run first when unsure which `--db` to use. Also lists `_db/` folders with no connection (leftovers, nothing reads them) — delete manually when sure.
276
+
277
+ ## 10. Cheat sheet
278
+
279
+ | Situation | Command |
280
+ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
281
+ | DB doesn't exist (from scratch) | [project-provisioning.md](project-provisioning.md): 2 permission gates → CREATE DATABASE → 2 accounts → `fui db alias new` |
282
+ | Check `CREATE DATABASE`/`CREATE LOGIN` rights | [project-provisioning.md](project-provisioning.md) §Step 0 — `IS_SRVROLEMEMBER` + `HAS_PERMS_BY_NAME`, read-only |
283
+ | DB exists, no alias in `acc` | `fui db alias new` ([project-provisioning.md](project-provisioning.md) §Step 5) |
284
+ | First connect, have dbToken | `fui db add` |
285
+ | Alias only, no dbToken | `fui db alias list` → `fui db add-by-name` |
286
+ | Which DBs am I granted | `fui db alias list` |
287
+ | Another DB (new alias) | `fui db add-by-name` again |
288
+ | Connections + default | `fui db list` |
289
+ | UID/PWD changed | `fui db token-rebuild` (`--db` if several) |
290
+ | Set userToken | `fui db user-token` (`--db` if several) |
291
+ | List tables/SPs | `fui sp list` |
292
+ | Get SP body | `fui sp get` |
293
+ | Refresh / read schema | `fui schema pull` / `fui schema` |
294
+ | Save SQL locally | `fui sp save` |
295
+ | Deploy (auto cache clear for spAPI__/spAPIFILE__) | `fui sp deploy` |
296
+ | API input params before wiring | `fui sp help` |
297
+ | Clear tAPI cache after ALTER via `fui exec` | `fui sp help` (required) |
298
+ | Static check of deployed API | `fui sp verify` |
299
+ | Drop / rename SP | `fui sp delete` / `fui sp rename` |
300
+ | SQLServer + DatabaseName of an alias | `fui db alias list --search <text>` |
301
+ | dbToken via borrowed UID/PWD | `fui db add-by-name` — never decode by hand in PowerShell |
302
+
303
+ ## 11. Table design
304
+
305
+ Naming, primary keys, timestamps, audit columns: [db-table-design.md](db-table-design.md) — load when designing a schema.