@fui-org/fui-cli 1.3.1 → 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 +7 -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-x7kph78r.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,37 +1,25 @@
1
- # Dựng hạ tầng cho một dự án FUI mới
1
+ # Provisioning a New FUI Project
2
2
 
3
- > File này sở hữu: **quy trình dựng hạ tầng cho một dự án FUI mới — tạo database SQL Server, hai loại tài khoản SQL (ứng dụng execute-only vs dev db_owner), đăng ký alias vào `acc`, tạo project FUI, cấp quyền quản trị project**. Kết nối/schema/SP xem [db-workflow.md](db-workflow.md); thiết kế app nhiều module xem [system-design.md](system-design.md); quy ước đặt tên bảng xem [db-table-design.md](db-table-design.md); khái niệm quyền người dùng cuối xem [permission-system.md](permission-system.md).
3
+ > Owns: **new-project infrastructure — SQL Server database, two SQL accounts (execute-only app vs db_owner dev), `acc` alias, FUI project, project admin rights**. Connection/schema/SP: [db-workflow.md](db-workflow.md); multi-module design: [system-design.md](system-design.md); table naming: [db-table-design.md](db-table-design.md); end-user rights: [permission-system.md](permission-system.md). Flags: `fui <command> --help`.
4
4
 
5
- Dùng file này khi user nói **"tạo dự án mới"**, **"tạo DB + project FUI mới"**, hoặc khi
6
- [system-design.md](system-design.md) Bước 0 rơi vào nhánh *"dựng mới hoàn toàn"*. Đây là bước **trước**
7
- mọi thứ trong `system-design.md`: chưa có database và chưa có project thì không có gì để thiết kế lên.
5
+ Use for **"new project"** / **"new DB + FUI project"**, or [system-design.md](system-design.md) Step 0 → _"build from scratch"_. Comes **before** all of `system-design.md`.
8
6
 
9
- **Làm Bước 0 rồi đủ 6 bước theo đúng thứ tự, không bỏ bước nào** trừ khi user yêu cầu khác đi. Thứ tự
10
- này **không tuỳ tiện**: có **hai** cổng quyền ở hai hệ thống khác nhau và cả hai phải được kiểm trước
11
- khi tạo bất cứ thứ gì — xem mục ngay dưới.
7
+ **Do Step 0, then Steps 1–6 in order, skipping none** unless the user says otherwise. Two permission gates in two systems must both pass before anything is created.
12
8
 
13
9
  ---
14
10
 
15
- ## 🛑 Bước 0 — Kiểm HAI cổng quyền trước khi tạo bất cứ thứ gì
11
+ ## 🛑 Step 0 — Check BOTH gates first
16
12
 
17
- Dựng một dự án mới đụng vào **hai hệ thống độc lập**, mỗi hệ thống một cổng quyền riêng. Thiếu bất kỳ
18
- cổng nào thì quy trình không đi hết được — nhưng nếu phát hiện muộn thì đã kịp để lại rác không dọn
19
- được bằng tool:
13
+ | Gate | Needs | Check with |
14
+ | ------------------------ | --------------------------------- | -------------------------- |
15
+ | **A. FUI platform** | `SystemRight >= 3` on system `fp` | `fui project new` (Step 1) |
16
+ | **B. Target SQL Server** | create database + create login | read-only `SELECT` below |
20
17
 
21
- | Cổng | Cần gì | Kiểm bằng |
22
- |---|---|---|
23
- | **A. Nền tảng FUI** | `SystemRight >= 3` trên hệ thống `fp` | gọi `project_new` (Bước 1) |
24
- | **B. SQL Server đích** | quyền tạo database + tạo login | một câu `SELECT` chỉ đọc (ngay dưới) |
18
+ **Check B BEFORE A.** B has no side effects; A only answers by actually creating the project. Reverse order ⇒ orphan FUI project if the DB can't be created. Late discovery leaves garbage fui can't clean up.
25
19
 
26
- **Kiểm cổng B TRƯỚC cổng A.** Cổng B là câu truy vấn chỉ đọc, hoàn toàn không có tác dụng phụ; cổng A
27
- thì phải *thật sự tạo project* mới biết. Làm ngược lại là tạo ra một project FUI mồ côi khi hoá ra
28
- không có quyền tạo database.
20
+ ### Gate B — target SQL Server
29
21
 
30
- ### Cổng B — quyền trên SQL Server đích
31
-
32
- `CREATE DATABASE` và `CREATE LOGIN` là thao tác **cấp máy chủ**, tài khoản dev thường ngày (chỉ
33
- `db_owner` trên vài database) **không làm được**. Chạy câu này qua `db_sql_execute` trên một kết nối
34
- **tới đúng SQL Server sắp tạo DB** (`db_alias_list()` cho biết alias nào nằm trên server nào):
22
+ `CREATE DATABASE`/`CREATE LOGIN` are server-level; a normal dev account (`db_owner` on some DBs) can't. Run with `fui query` on a connection **to the exact target server** (`fui db alias list` shows alias → server):
35
23
 
36
24
  ```sql
37
25
  SELECT SUSER_NAME() AS LoginHienTai,
@@ -42,30 +30,19 @@ SELECT SUSER_NAME() AS LoginHienTai,
42
30
  HAS_PERMS_BY_NAME(NULL, NULL, 'ALTER ANY LOGIN') AS taoDuocLogin;
43
31
  ```
44
32
 
45
- Đủ quyền khi: `sysadmin = 1`, **hoặc** `taoDuocDB = 1` **và** `taoDuocLogin = 1`.
33
+ Pass: `sysadmin = 1`, **or** `taoDuocDB = 1` **and** `taoDuocLogin = 1`.
46
34
 
47
- - **Không phải cứ "phải là `sa`".** `sa` là đường thường gặp nhất vì nó là `sysadmin`, nhưng
48
- `dbcreator` + `securityadmin` cũng đủ. Nói "cần `sa`" rồi dừng là chặn oan một cấu hình hợp lệ.
49
- - **Cần CẢ HAI quyền, không chỉ quyền tạo DB.** Bước 3 tạo login cho tài khoản ứng dụng — `dbcreator`
50
- đơn thuần tạo được database nhưng **không** tạo được login, và ca đó hỏng ở giữa chừng: database đã
51
- có, login thì không.
52
- - `IS_SRVROLEMEMBER` trả `NULL` (không phải `0`) khi tên role sai hoặc login không hợp lệ — đọc `NULL`
53
- thành "không có quyền" là kết luận sai; coi như **không biết** và hỏi user.
54
- - **Kết nối phải đúng SQL Server đích.** Kết nối mượn của project anh em có thể trỏ sang máy chủ khác;
55
- lúc đó câu trên trả quyền của máy chủ *sai*, và kết quả xanh là xanh nhầm chỗ.
56
- - ⚠️ Ở đây `HAS_PERMS_BY_NAME` là **đúng công cụ** vì ta đang hỏi về *chính principal đang kết nối*.
57
- Đừng đem cách này đi hỏi quyền của principal **khác** (ví dụ `[public]` có `EXECUTE` không) — nó trả
58
- lời cho người đang hỏi, nên sẽ luôn xanh với login `db_owner` và cho một câu trả lời sai. Ca đó phải
59
- đọc `sys.database_permissions`.
35
+ - `sa` not required — `dbcreator` + `securityadmin` also pass. Don't block a valid setup with "needs `sa`".
36
+ - Need BOTH: `dbcreator` alone creates the DB but not the Step 3 login ⇒ fails midway.
37
+ - `IS_SRVROLEMEMBER` returns `NULL` (not `0`) for a bad role name/invalid login ⇒ **unknown**, ask the user.
38
+ - A connection borrowed from a sibling project may point at another server ⇒ green for the wrong server.
39
+ - ⚠️ `HAS_PERMS_BY_NAME` answers only for the _connected principal_ — correct here. Never use it for another principal (e.g. `[public]` `EXECUTE`): always green for `db_owner`. Read `sys.database_permissions` instead.
60
40
 
61
- Thiếu quyền ⇒ **dừng, báo user xin cấp quyền trên máy chủ đó**. Đừng chạy `CREATE DATABASE` để "xem nó
62
- có lỗi không" — và cũng đừng tự hạ tiêu chuẩn xuống bằng cách cấp `db_datareader` cho tài khoản ứng
63
- dụng để né bước tạo login.
41
+ Fails ⇒ **stop; user must request rights on that server**. Never run `CREATE DATABASE` to probe; never grant `db_datareader` to the app account to avoid creating a login.
64
42
 
65
- ### Cổng A — quyền trên nền tảng FUI
43
+ ### Gate A — FUI platform
66
44
 
67
- Tạo project FUI đòi **`SystemRight >= 3`** trên hệ thống `fp`. Đây là cổng **server-side**, nằm trong
68
- thân `spAPI_FUI_Dev_ProjectEdit` — không né được bằng cách gọi API trực tiếp:
45
+ Creating a project needs **`SystemRight >= 3`** on `fp`, enforced server-side in `spAPI_FUI_Dev_ProjectEdit` (direct API calls can't bypass):
69
46
 
70
47
  ```sql
71
48
  if @jID is null -- không có id ⇒ đang TẠO MỚI
@@ -74,137 +51,98 @@ Begin
74
51
  RAISERROR(N'Bạn không có quyền tạo dự án', 16, 1)
75
52
  ```
76
53
 
77
- **Không có quyền thì DỪNG NGAY và báo lại user — đừng làm bước nào khác.** Lý do đặt `project_new`
78
- trước phần database: `INSERT INTO tblA_Project` chỉ cần
79
- `ProjectID`/`ProjectName`/`GroupName`/`Framework`/`pDomain`/`Router`, **không tham chiếu database nào**
80
- — nên tạo project *không* cần DB có trước. Làm DB trước rồi mới phát hiện bị từ chối là để lại một
81
- database thật, hai SQL login và một bản ghi `acc` mồ côi; mà `DROP DATABASE` thì MCP chặn cứng, dọn
82
- phải làm tay.
83
-
84
- Gọi `project_new` chính là phép kiểm cổng A — vừa là việc phải làm, không tốn thêm lượt gọi nào. Bị từ
85
- chối thì chưa có gì được tạo ra (với điều kiện cổng B đã kiểm trước, đúng thứ tự ở trên).
86
-
87
- Ba mức quyền khác nhau cho ba thao tác, đừng nhầm (đọc từ thân SP thật):
54
+ **No right ⇒ STOP, report, do nothing else.** Project goes before the DB because `INSERT INTO tblA_Project` needs only `ProjectID`/`ProjectName`/`GroupName`/`Framework`/`pDomain`/`Router` — no database. DB-first then rejection leaves a real DB, two SQL logins and an orphan `acc` record; fui hard-blocks `DROP DATABASE`, so cleanup is manual. `fui project new` _is_ the gate A check (no extra call); rejected ⇒ nothing created (if B was checked first).
88
55
 
89
- | Thao tác | SP | Điều kiện |
90
- |---|---|---|
91
- | **Tạo** project (`project_new`) | `spAPI_FUI_Dev_ProjectEdit` (`id` rỗng) | `SystemRight >= 3` |
92
- | **Sửa** project (`project_update`) | `spAPI_FUI_Dev_ProjectEdit` (có `id`) | `SystemRight >= 2` **và** `UserRight > 2` |
93
- | Publish `project.json` (`project_publish_config`) | `spAPI_FUI_Dev_EditorUpdate` (`Mode = 'project'`) | `SystemRight >= 2` **và** `UserRight > 2` |
94
- | Tạo/sửa **module**, publish module | `spAPI_FUI_Dev_ModuleEdit` · `spAPI_FUI_Dev_EditorUpdate` | `SystemRight >= 2` **và** `UserRight > 1` |
56
+ | Operation | SP | Condition |
57
+ | ----------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------ |
58
+ | **Create** project (`fui project new`) | `spAPI_FUI_Dev_ProjectEdit` (empty `id`) | `SystemRight >= 3` |
59
+ | **Edit** project (`fui project update`) | `spAPI_FUI_Dev_ProjectEdit` (with `id`) | `SystemRight >= 2` **and** `UserRight > 2` |
60
+ | Publish `project.json` (`fui project publish-config`) | `spAPI_FUI_Dev_EditorUpdate` (`Mode = 'project'`) | `SystemRight >= 2` **and** `UserRight > 2` |
61
+ | Create/edit/publish **module** | `spAPI_FUI_Dev_ModuleEdit` · `spAPI_FUI_Dev_EditorUpdate` | `SystemRight >= 2` **and** `UserRight > 1` |
95
62
 
96
- `SystemRight` là điều kiện **cần** (toàn hệ thống `fp`), `UserRight` là điều kiện **đủ** (trên đúng
97
- project đó) — hai trục AND với nhau. Chỉ riêng *tạo* project là không xét `UserRight`, vì project chưa
98
- tồn tại thì chưa có dòng nào trong `tblA_UserProject`.
63
+ `SystemRight` (whole `fp`) is necessary, `UserRight` (that project) sufficient — ANDed. Only _create_ skips `UserRight` (no `tblA_UserProject` row yet).
99
64
 
100
- > ⚠️ `pDomain` được kiểm **trước** cổng quyền (`CheckDomain`): domain trùng với project khác sẽ báo lỗi
101
- > domain chứ không phải lỗi quyền. Đừng đọc nhầm thông báo rồi đi xin cấp quyền.
65
+ > ⚠️ `pDomain` is checked **before** the permission gate (`CheckDomain`): a duplicate domain gives a domain error, not a permission error. Don't go request rights over it.
102
66
 
103
67
  ---
104
68
 
105
- ## Chỉ mục — dựng một dự án mới đi qua những file nào
69
+ ## Index — phases of a new project
106
70
 
107
- File này **chỉ sở hữu giai đoạn 1**. Một dự án mới đi trọn 7 giai đoạn; đây là bản đồ để biết đang ở đâu
108
- và đọc tiếp file nào, thay vì mò từng file một.
71
+ This file owns **phase 1 only**.
109
72
 
110
- | # | Giai đoạn | File sở hữu | Làm gì |
111
- |---|---|---|---|
112
- | 0 | Làm rõ phạm vi với user | [system-design.md](system-design.md) §Bước 0 | Ai dùng, quy trình nghiệp vụ, entity chính |
113
- | **1** | **Hạ tầng ← BẠN ĐANG Ở ĐÂY** | **file này** (Bước 0 + 1–6) | Kiểm 2 cổng quyền → `project_new` → DB + 2 tài khoản SQL + alias `acc` → quyền quản trị |
114
- | 2 | Phân rã hệ thống thành module | [system-design.md](system-design.md) §Bước 1 | Danh sách module, vai trò truy cập |
115
- | 3 | Thiết kế schema | [db-table-design.md](db-table-design.md) | Quy ước đặt tên bảng, PK/FK, cột audit. `CREATE TABLE` do **lập trình viên tự chạy** |
116
- | 4 | Định nghĩa quyền **người dùng cuối** | [permission-system.md](permission-system.md) + [system-design.md](system-design.md) §Bước 4b | `right_system_new` / `right_function_new` — làm TRƯỚC khi module nào gác quyền |
117
- | 5 | Cấu hình runtime project | [project-config.md](project-config.md) | `project.json`: menu, menuLeft, điều hướng, domainSetting |
118
- | 6 | Module đầu tiên | `module_new` → [fullstack-workflow.md](fullstack-workflow.md) | Quy trình 6 bước UI-first cho từng module |
73
+ | # | Phase | Owner | Do |
74
+ | ----- | ------------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
75
+ | 0 | Clarify scope | [system-design.md](system-design.md) §Step 0 | Users, process, main entities |
76
+ | **1** | **Infrastructure ← HERE** | **this file** | 2 gates → `fui project new` → DB + 2 SQL accounts + `acc` alias → admin rights |
77
+ | 2 | Split into modules | [system-design.md](system-design.md) §Step 1 | Module list, access roles |
78
+ | 3 | Schema | [db-table-design.md](db-table-design.md) | Naming, PK/FK, audit columns. `CREATE TABLE` **run by the developer** |
79
+ | 4 | **End-user** rights | [permission-system.md](permission-system.md) + [system-design.md](system-design.md) §Step 4b | `fui right system new` / `fui right function new` — BEFORE any module gates on them |
80
+ | 5 | Runtime config | [project-config.md](project-config.md) | `project.json`: menu, menuLeft, navigation, domainSetting |
81
+ | 6 | First module | `fui module new` → [fullstack-workflow.md](fullstack-workflow.md) | 6-step UI-first flow |
119
82
 
120
- **Không thuộc file này** (đừng tìm ở đây, cũng đừng chép vào đây): quy ước đặt tên bảng · cấu hình menu ·
121
- quyền người dùng cuối · cách viết SP ([tapi-reference.md](tapi-reference.md)) · kết nối/schema/SP hằng
122
- ngày ([db-workflow.md](db-workflow.md)).
83
+ Not in this file (don't look or copy here): table naming · menus · end-user rights · SP writing ([tapi-reference.md](tapi-reference.md)) · daily connection/schema/SP ([db-workflow.md](db-workflow.md)).
123
84
 
124
85
  ---
125
86
 
126
- ## 🔴 Hai mặt phẳng quyền — đọc trước, đừng trộn
87
+ ## 🔴 Two permission planes — don't mix
127
88
 
128
- Từ "quyền" trong FUI trỏ tới **hai hệ thống khác hẳn nhau**. Nhầm hai cái này là đi cấp quyền ở sai nơi
129
- rồi kết luận "cấp rồi mà không có tác dụng".
89
+ | | `SystemRight` (system `acc`) | `UserRight` (`tblA_UserProject`) |
90
+ | --------- | ------------------------------------------------------------------ | --------------------------------------- |
91
+ | Scope | **System-wide** — one number per user per app | **Per project** |
92
+ | Answers | "What can this user do in app X?" | "What can this user do with project Y?" |
93
+ | Stored in | `acc.tblSysRight` + `acc` assignment tables | `tblA_UserProject` (FUI platform DB) |
94
+ | Commands | `fui right ...` (8) → [permission-system.md](permission-system.md) | `fui user ...` (5) → Step 6 |
130
95
 
131
- | | `SystemRight` (hệ thống `acc`) | `UserRight` (bảng `tblA_UserProject`) |
132
- |---|---|---|
133
- | Phạm vi | **Toàn hệ thống** — một con số cho một user trên một app | **Từng project một** |
134
- | Trả lời câu hỏi | "User này làm được gì trong app X nói chung?" | "User này làm được gì với project Y cụ thể?" |
135
- | Lưu ở | `acc.tblSysRight` + bảng gán quyền của `acc` | `tblA_UserProject` (DB nền tảng FUI) |
136
- | Tool | 8 tool `right_*` → [permission-system.md](permission-system.md) | 5 tool `project_user_*` → Bước 6 dưới đây |
96
+ Mixing them = granting in the wrong place, then "granted but no effect". The FUI IDE _is itself a FUI app_ (project `fp`), so `SystemRight` on `fp` = developer rights in the IDE:
137
97
 
138
- **Chỗ dễ nhầm nhất:** IDE FUI *bản thân nó cũng là một app FUI* (project `fp`). Nên `SystemRight` trên
139
- `fp` chính là "quyền của lập trình viên trong IDE" — **không** phải chỉ là quyền của người dùng cuối
140
- như bảng cũ của file này từng ghi. Cụ thể:
98
+ - `SystemRight` on `fp` = **may create projects** (≥ 3); necessary for every edit.
99
+ - `UserRight` on project Y = **may touch Y**; sufficient.
100
+ - ANDed: `UserRight = 3` with `SystemRight = 1` can't edit anything.
141
101
 
142
- - `SystemRight` trên `fp` = **được phép tạo project hay không** (≥ 3), và là điều kiện *cần* cho mọi
143
- thao tác sửa.
144
- - `UserRight` trên project Y = **được đụng vào project Y hay không**. Điều kiện *đủ*.
145
- - Hai cái **AND với nhau**, xem bảng ba thao tác ở mục trên. Có `UserRight = 3` mà `SystemRight = 1`
146
- thì vẫn không sửa được gì.
147
-
148
- Còn `SystemRight`/`FunctionRight` trên **project nghiệp vụ của bạn** (không phải `fp`) thì đúng là quyền
149
- của người dùng cuối trong app đó — việc đó làm sau, theo
150
- [system-design.md](system-design.md) Bước 4b.
102
+ `SystemRight`/`FunctionRight` on **your business project** (not `fp`) are end-user rights — later, per [system-design.md](system-design.md) Step 4b.
151
103
 
152
104
  ---
153
105
 
154
- ## Bước 1 — Tạo project FUI (đây là cổng A, làm trước phần database)
106
+ ## Step 1 — Create the FUI project (gate A, before the DB)
155
107
 
156
108
  ```
157
- project_new({ projectData: {
158
- ProjectID: "...", ProjectName: "...", GroupName: "...", Framework: "V3",
159
- pDomain: "...", // tuỳ chọn — tên miền riêng của project
160
- Router: "" // tuỳ chọn, mặc định ""
161
- }})
109
+ fui project new --data '{
110
+ "ProjectID": "...", "ProjectName": "...", "GroupName": "...", "Framework": "V3",
111
+ "pDomain": "...",
112
+ "Router": ""
113
+ }'
162
114
  ```
163
115
 
164
- - **Chỉ SÁU field này được đọc.** `spAPI_FUI_Dev_ProjectEdit` lấy đúng `ProjectID`, `ProjectName`,
165
- `GroupName`, `Framework`, `pDomain`, `Router` từ `@jsonObj` rồi thôi.
166
- - 🔴 **Đừng truyền `title`/`login`/`apiDomain`/`fileDomain`/`userInfo`/`menu` vào đây — chúng bị BỎ
167
- QUA, lặng lẽ.** SP ghi cột `projectData` bằng một hằng số rỗng cứng trong thân nó:
116
+ `pDomain` optional (project's own domain); `Router` optional, default `""`.
117
+
118
+ - **Only these SIX fields are read** from `@jsonObj`.
119
+ - 🔴 **`title`/`login`/`apiDomain`/`fileDomain`/`userInfo`/`menu` are silently IGNORED.** The SP writes `projectData` from a hardcoded constant:
168
120
  ```sql
169
121
  declare @jProjectdata nvarchar(1000) = '{"title":"","apiDomain":"","login":"","menu":[]}'
170
122
  ```
171
- Truyền vào thì API vẫn trả ✅ và project vẫn được tạo — chỉ là cấu hình runtime **không nằm ở đâu
172
- cả**. Đúng kiểu hỏng-âm-thầm: mọi dấu hiệu đều nói thành công.
173
- - **Cấu hình runtime đặt ở bước riêng sau đó**: sửa file `{projectId}/project.json` rồi
174
- `project_publish_config` — xem [project-config.md](project-config.md). Đây là đường **duy nhất** để
175
- `menu`/`apiDomain`/`login` thật sự vào được project.
176
- - **`projectData` phải PHẲNG** — không bọc trong `"data"`.
177
- - **`Framework`: dự án mới dùng `V3`** trừ khi user nêu lý do khác (thường là phải đồng bộ với các
178
- project cũ cùng nhóm). Đây là lựa chọn **không đổi lại được rẻ**: nó quyết định tầng import, vocabulary
179
- component, và cả cú pháp grid — V2 là Vuetify 1.5 (`v-container/v-layout/v-flex`), V3 là Vuetify 3
180
- (`v-container/v-row/v-col`), và ngưỡng `w` px của hai bên cũng khác nhau. Phần lớn ví dụ module.json
181
- trong skill viết theo cú pháp **V2** (đa số project đang chạy), nên khi làm project V3 phải đối chiếu
182
- → [controls-patterns.md](controls-patterns.md) §6 (chủ sở hữu bảng khác biệt V2/V3). Nêu rõ lựa chọn
183
- này với user, đừng để nó thành mặc định ngầm.
184
-
185
- **Người gọi API tự động được `UserRight = 3` trên project vừa tạo — chỉ người tạo, không ai khác**
186
- (SP tự `INSERT INTO tblA_UserProject ... 3`). Đó chính là lý do phải có Bước 6.
187
-
188
- Bị từ chối `Bạn không có quyền tạo dự án` → **dừng hẳn, báo user xin `SystemRight >= 3` trên hệ thống
189
- `fp`**. Không đi tiếp các bước dưới: lúc này chưa có gì được tạo ra, và nên giữ nguyên như vậy.
123
+ API still returns ✅, project created, config lost — silent failure.
124
+ - Runtime config: edit `{projectId}/project.json` then `fui project publish-config` ([project-config.md](project-config.md)) — the **only** way `menu`/`apiDomain`/`login` get in.
125
+ - `projectData` must be **FLAT** — not wrapped in `"data"`.
126
+ - **`Framework`: `V3` for new projects** unless the user has a reason (usually matching older projects in the group). Expensive to change: decides import layer, component vocabulary, grid syntax — V2 = Vuetify 1.5 (`v-container/v-layout/v-flex`), V3 = Vuetify 3 (`v-container/v-row/v-col`), and `w` px thresholds differ. Most skill module.json examples are **V2**; for V3 cross-check [controls-patterns.md](controls-patterns.md) §6 (V2/V3 diff owner). State the choice to the user; never implicit.
127
+
128
+ **Only the caller gets `UserRight = 3` on the new project** (SP `INSERT INTO tblA_UserProject ... 3`) — hence Step 6.
129
+
130
+ Rejected with `Bạn không có quyền tạo dự án` ⇒ **stop; user must request `SystemRight >= 3` on `fp`**. Nothing created yet — keep it so.
190
131
 
191
132
  ---
192
133
 
193
- ## Bước 2 — Tạo database
134
+ ## Step 2 — Create the database
194
135
 
195
- > Điều kiện tiên quyết: **cổng B ở Bước 0 đã xanh** (`sysadmin`, hoặc `CREATE ANY DATABASE` +
196
- > `ALTER ANY LOGIN`). Chưa kiểm thì quay lại kiểm — đừng chạy `CREATE DATABASE` để dò xem có lỗi không.
136
+ > Prerequisite: gate B green. Never run `CREATE DATABASE` to probe.
197
137
 
198
- **Hỏi server đích xem các database khác đang để file vật lý ở đâu trước, không hardcode đường dẫn.**
199
- Mỗi server một quy ước; đoán sai thì file nằm lạc chỗ và không ai phát hiện cho tới lúc backup.
138
+ **Query where the server keeps other DB files; never hardcode paths** (wrong guess = misplaced files, unnoticed until backup):
200
139
 
201
140
  ```sql
202
141
  SELECT DISTINCT LEFT(physical_name, LEN(physical_name) - CHARINDEX('\', REVERSE(physical_name)))
203
142
  FROM sys.master_files WHERE database_id > 4;
204
143
  ```
205
144
 
206
- `database_id > 4` để bỏ `master`/`model`/`msdb`/`tempdb` — chúng nằm ở thư mục cài đặt SQL Server, không
207
- phải nơi đặt DB nghiệp vụ. Kết quả thường gặp: `C:\Application\Database\`. Dùng chung đúng quy ước đó:
145
+ `database_id > 4` skips `master`/`model`/`msdb`/`tempdb` (install dir). Typical: `C:\Application\Database\`. Follow it:
208
146
 
209
147
  ```sql
210
148
  CREATE DATABASE [TenDB]
@@ -212,15 +150,13 @@ ON PRIMARY (NAME = N'TenDB', FILENAME = N'C:\Application\Database\TenDB.mdf'
212
150
  LOG ON (NAME = N'TenDB_log', FILENAME = N'C:\Application\Database\TenDB_log.ldf');
213
151
  ```
214
152
 
215
- **Vòng gà–trứng:** DB mới chưa có kết nối nào, nên chạy câu này qua một kết nối **đã có sẵn tới chính
216
- SQL Server đó** — `db_alias_list()` cho biết alias nào đang nằm trên server nào, rồi
217
- `db_connect_by_name({ apiName })` để mượn. Chạy bằng `db_sql_execute_nonquery({ confirmWrite: true })`.
153
+ Chicken-and-egg: the new DB has no connection — borrow an **existing connection on the same server** (`fui db alias list`, then `fui db add-by-name <apiName>`) and run `fui exec "<sql>" --confirm-write`.
218
154
 
219
155
  ---
220
156
 
221
- ## Bước 3 — Tài khoản ỨNG DỤNG (execute-only)
157
+ ## Step 3 — APP account (execute-only)
222
158
 
223
- Login **trùng tên database**, mật khẩu ngẫu nhiên mạnh:
159
+ Login **named after the DB**, strong random password:
224
160
 
225
161
  ```bash
226
162
  openssl rand -base64 12
@@ -234,19 +170,15 @@ CREATE USER [TenDB] FOR LOGIN [TenDB];
234
170
  GRANT EXECUTE ON SCHEMA::dbo TO [TenDB];
235
171
  ```
236
172
 
237
- **Chỉ `public` + `GRANT EXECUTE ON SCHEMA::dbo`. KHÔNG `db_datareader`, KHÔNG `db_datawriter`, KHÔNG
238
- cấp quyền trên bảng.** Lý do: tAPI chỉ chạy stored procedure, nên execute-only là **đủ dùng** — mọi SP
239
- mới tự động nằm trong `SCHEMA::dbo` nên không phải cấp lại từng cái. Cấp thêm quyền bảng không giúp app
240
- chạy tốt hơn, nó chỉ mở sẵn đường đọc/ghi thẳng bảng cho một lời gọi bị lợi dụng.
173
+ **Only `public` + `GRANT EXECUTE ON SCHEMA::dbo`. NO `db_datareader`, NO `db_datawriter`, NO table grants.** tAPI only runs SPs; new SPs fall under `SCHEMA::dbo` automatically. Table rights don't help the app — they only open direct table access to an abused call.
241
174
 
242
- Đây là tài khoản **duy nhất** được nạp vào `acc` ở Bước 5.
175
+ The **only** account loaded into `acc` (Step 5).
243
176
 
244
177
  ---
245
178
 
246
- ## Bước 4 — Tài khoản DEV (db_owner)
179
+ ## Step 4 — DEV account (db_owner)
247
180
 
248
- **Mặc định dùng lại tài khoản dev dùng chung sẵn có** của hệ thống (ví dụ `xTuan`) — login đã tồn tại ở
249
- cấp server, chỉ cần cấp vào database mới:
181
+ **Default: reuse the existing shared dev login** (e.g. `xTuan`) — just add it to the new DB:
250
182
 
251
183
  ```sql
252
184
  USE [TenDB];
@@ -254,130 +186,93 @@ CREATE USER [xTuan] FOR LOGIN [xTuan];
254
186
  ALTER ROLE db_owner ADD MEMBER [xTuan];
255
187
  ```
256
188
 
257
- **KHÔNG `CREATE LOGIN` mới** trừ khi user yêu cầu riêng một tài khoản dev cho project đó. Đẻ thêm login
258
- cho mỗi project là đẻ thêm một mật khẩu phải quản lý, trong khi tài khoản dùng chung đã có sẵn ở mọi
259
- DB anh em — và chính nó là cặp credential mà `db_connect_by_name` mượn được.
189
+ **NO new `CREATE LOGIN`** unless the user asks for a project-specific dev account — each extra login is another password, while the shared one exists in every sibling DB and is the credential `fui db add-by-name` borrows.
260
190
 
261
191
  ---
262
192
 
263
- ## Bước 5 — Đăng ký database thành alias tAPI
193
+ ## Step 5 — Register the DB as a tAPI alias
264
194
 
265
195
  ```
266
- db_alias_new({
267
- apiName: "tenalias", // module.json sẽ gọi "/tenalias/TenAPI"
268
- moduleName: "Tên hệ thống",
269
- sqlServer: "...",
270
- database: "TenDB",
271
- username: "TenDB", // tài khoản ỨNG DỤNG ở Bước 3
272
- password: "<mật khẩu Bước 3>"
273
- })
196
+ fui db alias new tenalias \
197
+ --module-name "Tên hệ thống" \
198
+ --sql-server "..." \
199
+ --database TenDB \
200
+ --username TenDB \
201
+ --password-env TENDB_PASSWORD
274
202
  ```
275
203
 
276
- Tool gọi `SM_Modules_Update` **hai lượt** — `UpdateConnection=0` tạo record, rồi `UpdateConnection=1`
277
- nạp connection — vì record phải tồn tại trước khi set connection lên nó.
204
+ `tenalias` → module.json calls `"/tenalias/TenAPI"`. Password only via `--password -` (stdin) or `--password-env VAR`.
205
+
206
+ fui calls `SM_Modules_Update` **twice**: `UpdateConnection=0` creates the record, `UpdateConnection=1` sets the connection (record must exist first).
278
207
 
279
- - 🔴 **`username`/`password` phải là tài khoản ỨNG DỤNG ở Bước 3, KHÔNG phải tài khoản dev.** Nạp nhầm tài khoản dev
280
- thì **mọi lời gọi API của app chạy dưới quyền `db_owner`** — không có lỗi nào nổi lên, app chạy y hệt,
281
- chỉ là hàng rào bảo mật biến mất.
282
- - `ModuleID` mới = `MAX(ModuleID)+1` trên **toàn bộ** `acc.tblModules` → tool cần một kết nối alias
283
- `acc` (`db_connect_by_name({ apiName: "acc" })`). Không suy từ `db_alias_list` được: danh sách đó chỉ
284
- gồm module **bạn** được quản lý, MAX trên tập con có thể đụng ID của người khác.
285
- - Alias đã tồn tại ⇒ lỗi cứng, tool không ghi đè.
208
+ - 🔴 **Credentials = Step 3 APP account, NEVER the dev account.** Dev account ⇒ every app API call runs as `db_owner`, with no visible error.
209
+ - New `ModuleID` = `MAX(ModuleID)+1` over **all** `acc.tblModules` ⇒ needs an `acc` connection (`fui db add-by-name acc`). Can't use `fui db alias list`: it lists only modules **you** manage; MAX over a subset may collide.
210
+ - Existing alias ⇒ hard error, no overwrite.
286
211
 
287
- Xong bước này thì `db_connect_by_name({ apiName: "tenalias" })` dùng được — xem
288
- [db-workflow.md](db-workflow.md) §2b.
212
+ Then `fui db add-by-name tenalias` works — [db-workflow.md](db-workflow.md) §2b.
289
213
 
290
214
  ---
291
215
 
292
- ## Bước 6 — Cấp quyền quản trị project cho người khác
216
+ ## Step 6 — Grant project admin to others
293
217
 
294
- Ba bước, và thứ tự này là **bắt buộc** chứ không phải gợi ý:
218
+ Order is **mandatory**:
295
219
 
296
220
  ```
297
- 1. project_user_list() → ai đang có quyền
298
- 2. project_user_search({ textsearch: "tên/username" }) → lấy UserID thật
299
- 3. project_user_new({ userId: "DEV001", userRight: 3 })
221
+ 1. fui user list → who has rights now
222
+ 2. fui user search "tên/username" → get the real UserID
223
+ 3. fui user add DEV001 --right 3
300
224
  ```
301
225
 
302
- **`project_user_new`/`_update`/`_delete` chỉ nhận `userId` chính xác, không nhận tên hay username.**
303
- Đây là cổng xác nhận danh tính, và nó là **cấu trúc chứ không phải lời nhắc**: muốn có `userId` thì bắt
304
- buộc đã chạy `project_user_search` và đã nhìn thấy bảng `UserID | Username | Họ tên`. **Đưa bảng đó cho
305
- user xem và để họ chọn trước khi cấp** — trùng tên là chuyện thường, và cấp nhầm quyền Admin cho người
306
- khác không lùi được bằng một lượt gọi.
307
-
308
- | Mức | Nghĩa |
309
- |---|---|
310
- | 1 | View only |
311
- | 2 | Develop |
312
- | 3 | **Admin** — mức cần cho người quản lý project (`project_publish_config` đòi `> 2`) |
313
-
314
- - Tạo và sửa là **hai tool riêng**: `project_user_new` lỗi cứng nếu user đã có quyền (trỏ sang
315
- `_update`), và ngược lại. Không tool nào upsert ngầm.
316
- - Thu hồi quyền là `project_user_delete` — 🔴 tóm tắt hậu quả và chờ user xác nhận trước khi gọi.
317
- - **Quyền LUÔN là cấp project — không có biến thể cấp module.** Chữ ký SP có `@MID` và module
318
- `fp/permission` của IDE cũng truyền nó, nhưng **thân SP không bao giờ đọc**: mọi thao tác lọc theo
319
- `ProjectID`. Đừng suy ra một phạm vi không tồn tại từ việc thấy tham số đó.
320
-
321
- **Điều kiện để CHÍNH BẠN cấp được quyền** (thân `spAPI_FUI_Dev_UserRight_Update`):
322
- `SystemRight >= 2` **và** (`UserRight > 2` trên project đó **hoặc** `SystemRight >= 4`) — tức
323
- `SystemRight >= 4` cấp được cho mọi project mà không cần là admin của project đó. Chỉ *xem* danh sách
324
- (`project_user_list`) thì nhẹ hơn: `SystemRight >= 2` và (`UserRight > 1` hoặc `SystemRight >= 3`).
226
+ **`fui user add`/`set`/`remove` take only the exact `UserID`** — a structural identity gate: you must run `fui user search` and see `UserID | Username | Họ tên`. **Show that table and let the user pick before granting** — duplicate names are common; a wrong Admin grant can't be undone in one call.
227
+
228
+ | Level | Meaning |
229
+ | ----- | -------------------------------------------------------------------------------------- |
230
+ | 1 | View only |
231
+ | 2 | Develop |
232
+ | 3 | **Admin** — needed to manage the project (`fui project publish-config` requires `> 2`) |
233
+
234
+ - Create and edit are separate: `fui user add` hard-errors if rights exist (use `set`), and vice versa. No silent upsert.
235
+ - Revoke = `fui user remove` — 🔴 summarize the consequence, wait for confirmation.
236
+ - **Rights are ALWAYS project-level.** The SP has `@MID` and IDE module `fp/permission` passes it, but the body never reads it — all filtering is by `ProjectID`. Don't infer a module scope.
237
+
238
+ **For YOU to grant** (`spAPI_FUI_Dev_UserRight_Update`): `SystemRight >= 2` **and** (`UserRight > 2` on the project **or** `SystemRight >= 4`) — `SystemRight >= 4` grants on any project. _Listing_ (`fui user list`): `SystemRight >= 2` and (`UserRight > 1` or `SystemRight >= 3`).
325
239
 
326
240
  ---
327
241
 
328
- ## Xong hạ tầng rồi — đi tiếp đâu
242
+ ## After infrastructure
329
243
 
330
- Hết Bước 6 là **hết giai đoạn 1**, chưa phải hết việc: lúc này project **rỗng và chưa có cấu hình
331
- runtime** (`menu`/`apiDomain`/`login` vẫn là chuỗi rỗng do SP seed — xem Bước 1), chưa có bảng, chưa có
332
- module, chưa có mã quyền nào. Đừng dừng ở đây và báo "đã xong", cũng đừng nhảy thẳng vào viết module.json.
244
+ Step 6 ends **phase 1 only**: the project is empty, runtime config still SP-seeded empty strings, no tables/modules/right codes. Don't report "done"; don't jump into module.json.
333
245
 
334
- 1. `project_sync({ projectId })` + `session_set({ projectId })` — kéo project về workspace và đặt làm
335
- target, khỏi phải truyền `projectId` ở mọi bước sau.
336
- 2. **Cấu hình runtime**: sửa `{projectId}/project.json` (`apiDomain` trỏ vào alias vừa tạo, `title`,
337
- `login`, `menu`) rồi `project_publish_config` — [project-config.md](project-config.md). **Bắt buộc**,
338
- không phải tuỳ chọn: `project_new` không nhận những khoá này, nên bỏ qua bước này là project không
339
- gọi được API nào.
340
- 3. `db_connect_by_name({ apiName })` — kết nối alias vừa tạo ([db-workflow.md](db-workflow.md) §2c).
341
- Đây cũng là **phép thử** rằng Bước 3 và Bước 5 đã đúng: nó chạy được nghĩa là tài khoản ứng dụng và
342
- bản ghi `acc` đều ổn.
343
- 4. Quay lại [system-design.md](system-design.md) từ **Bước 1** và đi tiếp theo thứ tự ở chỉ mục trên.
246
+ 1. `fui project sync` + `fui use <projectId>` — pull into workspace, set as target.
247
+ 2. **Runtime config (mandatory)**: edit `{projectId}/project.json` (`apiDomain` → new alias, `title`, `login`, `menu`), then `fui project publish-config` — [project-config.md](project-config.md). Skipped ⇒ the project can't call any API.
248
+ 3. `fui db add-by-name <apiName>` ([db-workflow.md](db-workflow.md) §2c) — also **proves** Steps 3 and 5 (app account + `acc` record OK).
249
+ 4. Resume [system-design.md](system-design.md) at **Step 1**, following the index.
344
250
 
345
- Dự án chỉ có **một** module đơn giản (không cần phân rã hệ thống) thì bỏ qua `system-design.md`, đi
346
- thẳng `module_new` → [fullstack-workflow.md](fullstack-workflow.md).
251
+ Only **one** simple module ⇒ skip `system-design.md`: `fui module new` → [fullstack-workflow.md](fullstack-workflow.md).
347
252
 
348
253
  ---
349
254
 
350
255
  ## Checklist
351
256
 
352
- - [ ] **Cổng B (SQL Server)** đã kiểm bằng `SELECT` chỉ đọc, trên kết nối tới **đúng máy chủ đích**: `sysadmin = 1`, hoặc `CREATE ANY DATABASE` **và** `ALTER ANY LOGIN` đều `= 1`
353
- - [ ] **Cổng A (FUI)**: `project_new` chạy TRƯỚC phần database và đã thành công — không có `SystemRight >= 3` thì dừng ở đây, chưa tạo database nào
354
- - [ ] `projectData` PHẲNG, **chỉ 6 field** (`ProjectID`/`ProjectName`/`GroupName`/`Framework`/`pDomain`/`Router`); `Framework` đã chốt với user
355
- - [ ] Đã query `sys.master_files` để lấy đúng thư mục file vật lý, không hardcode
356
- - [ ] `CREATE DATABASE` với `FILENAME` theo đúng quy ước của server đó
357
- - [ ] Tài khoản ứng dụng: login trùng tên DB, mật khẩu ngẫu nhiên, **chỉ** `GRANT EXECUTE ON SCHEMA::dbo`
358
- - [ ] Tài khoản dev: **dùng lại login dùng chung**, chỉ `CREATE USER` + `db_owner`
359
- - [ ] `db_alias_new` đã nạp **tài khoản ứng dụng** (không phải tài khoản dev)
360
- - [ ] `db_connect_by_name({ apiName })` chạy được — chứng minh alias + credential đều đúng
361
- - [ ] `project_user_list` cho thấy đúng những người cần có quyền
362
- - [ ] Đã `project_sync` + `session_set` + **`project_publish_config`** (cấu hình runtime — `project_new` KHÔNG nhận `menu`/`apiDomain`/`login`)
363
- - [ ] Đã nói cho user biết **giai đoạn 2 là gì** (xem chỉ mục ở đầu file) — không dừng lại ở "hạ tầng xong"
364
- - [ ] **Kiểm chứng bảo mật**: đăng nhập bằng tài khoản ứng dụng, `SELECT TOP 1 * FROM <bảng bất kỳ>` phải
365
- **bị từ chối** còn `EXEC` một SP thì chạy. Không kiểm thì không biết `GRANT` đã đúng hay chưa
257
+ - [ ] Gate B: read-only `SELECT` on the **target server** — `sysadmin = 1`, or `CREATE ANY DATABASE` **and** `ALTER ANY LOGIN` = 1
258
+ - [ ] Gate A: `fui project new` ran BEFORE the DB and succeeded — else stop, no DB
259
+ - [ ] `projectData` FLAT, **only 6 fields**; `Framework` agreed with user
260
+ - [ ] Physical folder from `sys.master_files`, not hardcoded; `CREATE DATABASE` `FILENAME` follows it
261
+ - [ ] App account: login = DB name, random password, **only** `GRANT EXECUTE ON SCHEMA::dbo`
262
+ - [ ] Dev account: **shared login reused**, only `CREATE USER` + `db_owner`
263
+ - [ ] `fui db alias new` loaded the **app account**
264
+ - [ ] `fui db add-by-name <apiName>` works
265
+ - [ ] `fui user list` shows exactly the right people
266
+ - [ ] `fui project sync` + `fui use` + **`fui project publish-config`** done
267
+ - [ ] Told the user **what phase 2 is**
268
+ - [ ] **Security check**: as the app account, `SELECT TOP 1 * FROM <any table>` is **denied** and `EXEC` of an SP works — otherwise the `GRANT` is unverified
366
269
 
367
270
  ---
368
271
 
369
- ## Ba điều dễ hiểu sai
272
+ ## Three common misreadings
370
273
 
371
- **DDL ở đây khác luật DDL của schema nghiệp vụ.** `CREATE DATABASE` / `CREATE LOGIN` / `GRANT` là DDL
372
- **cấp server và đăng nhập**, chạy qua `db_sql_execute_nonquery({ confirmWrite: true })` như mô tả ở
373
- trên. Luật *"`CREATE TABLE`/`ALTER TABLE` do lập trình viên tự chạy"* của
374
- [system-design.md](system-design.md) Bước 2 vẫn nguyên vẹn và chỉ nói về **schema nghiệp vụ** — hai luật
375
- không mâu thuẫn, chúng nói về hai loại đối tượng khác nhau.
274
+ **Server-level DDL ≠ business-schema DDL.** `CREATE DATABASE` / `CREATE LOGIN` / `GRANT` run via `fui exec "<sql>" --confirm-write`. The _"`CREATE TABLE`/`ALTER TABLE` run by the developer"_ rule ([system-design.md](system-design.md) Step 2) still stands for the **business schema** only — no conflict.
376
275
 
377
- **Mật khẩu tài khoản ứng dụng nằm nguyên văn trong transcript** ở Bước 3, vì câu `CREATE LOGIN` đi qua
378
- tham số tool. Đó là sự thật của quy trình, không giấu được: coi transcript của phiên dựng hạ tầng là
379
- nhạy cảm. Từ Bước 5 trở đi không tool nào in lại mật khẩu đó nữa (`db_alias_new` và `db_alias_list` đều cố
380
- ý không in trường `Password`).
276
+ **The app password is in the transcript verbatim** (Step 3 `CREATE LOGIN` goes through the fui command) — treat the provisioning transcript as sensitive. From Step 5 on nothing prints it (`fui db alias new`/`fui db alias list` omit `Password`).
381
277
 
382
- **`UserRight` ở Bước 6 không phải `SystemRight` trên project nghiệp vụ.** Xem lại bảng hai mặt phẳng ở đầu file. Cấp `UserRight=3`
383
- cho một người **không** cho họ quyền gì trong app đang chạy, và ngược lại.
278
+ **Step 6 `UserRight` ≠ `SystemRight` on the business project.** `UserRight=3` grants **nothing** in the running app, and vice versa.