@fui-org/fui-cli 0.2.0 → 0.4.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 (212) hide show
  1. package/README.md +20 -2
  2. package/dist/fui.js +347 -44
  3. package/package.json +15 -3
  4. package/skills/fui/SKILL.md +149 -66
  5. package/skills/fui-skill/README.md +112 -0
  6. package/skills/fui-skill/SKILL.md +269 -0
  7. package/skills/fui-skill/assets/projectdefaultstyle.css +518 -0
  8. package/skills/fui-skill/design-md/airbnb/DESIGN.md +545 -0
  9. package/skills/fui-skill/design-md/airbnb/README.md +5 -0
  10. package/skills/fui-skill/design-md/airtable/DESIGN.md +554 -0
  11. package/skills/fui-skill/design-md/airtable/README.md +5 -0
  12. package/skills/fui-skill/design-md/apple/DESIGN.md +562 -0
  13. package/skills/fui-skill/design-md/apple/README.md +5 -0
  14. package/skills/fui-skill/design-md/asu/DESIGN.md +179 -0
  15. package/skills/fui-skill/design-md/asu/README.md +107 -0
  16. package/skills/fui-skill/design-md/binance/DESIGN.md +634 -0
  17. package/skills/fui-skill/design-md/binance/README.md +5 -0
  18. package/skills/fui-skill/design-md/bmw/DESIGN.md +544 -0
  19. package/skills/fui-skill/design-md/bmw/README.md +5 -0
  20. package/skills/fui-skill/design-md/bmw-m/DESIGN.md +503 -0
  21. package/skills/fui-skill/design-md/bmw-m/README.md +5 -0
  22. package/skills/fui-skill/design-md/bugatti/DESIGN.md +454 -0
  23. package/skills/fui-skill/design-md/bugatti/README.md +5 -0
  24. package/skills/fui-skill/design-md/cal/DESIGN.md +542 -0
  25. package/skills/fui-skill/design-md/cal/README.md +5 -0
  26. package/skills/fui-skill/design-md/claude/DESIGN.md +589 -0
  27. package/skills/fui-skill/design-md/claude/README.md +5 -0
  28. package/skills/fui-skill/design-md/clay/DESIGN.md +541 -0
  29. package/skills/fui-skill/design-md/clay/README.md +5 -0
  30. package/skills/fui-skill/design-md/clickhouse/DESIGN.md +544 -0
  31. package/skills/fui-skill/design-md/clickhouse/README.md +5 -0
  32. package/skills/fui-skill/design-md/cohere/DESIGN.md +451 -0
  33. package/skills/fui-skill/design-md/cohere/README.md +5 -0
  34. package/skills/fui-skill/design-md/coinbase/DESIGN.md +570 -0
  35. package/skills/fui-skill/design-md/coinbase/README.md +5 -0
  36. package/skills/fui-skill/design-md/composio/DESIGN.md +506 -0
  37. package/skills/fui-skill/design-md/composio/README.md +5 -0
  38. package/skills/fui-skill/design-md/cursor/DESIGN.md +537 -0
  39. package/skills/fui-skill/design-md/cursor/README.md +5 -0
  40. package/skills/fui-skill/design-md/elevenlabs/DESIGN.md +504 -0
  41. package/skills/fui-skill/design-md/elevenlabs/README.md +5 -0
  42. package/skills/fui-skill/design-md/expo/DESIGN.md +526 -0
  43. package/skills/fui-skill/design-md/expo/README.md +5 -0
  44. package/skills/fui-skill/design-md/ferrari/DESIGN.md +531 -0
  45. package/skills/fui-skill/design-md/ferrari/README.md +5 -0
  46. package/skills/fui-skill/design-md/figma/DESIGN.md +578 -0
  47. package/skills/fui-skill/design-md/figma/README.md +5 -0
  48. package/skills/fui-skill/design-md/framer/DESIGN.md +544 -0
  49. package/skills/fui-skill/design-md/framer/README.md +5 -0
  50. package/skills/fui-skill/design-md/fui/DESIGN.md +532 -0
  51. package/skills/fui-skill/design-md/hashicorp/DESIGN.md +575 -0
  52. package/skills/fui-skill/design-md/hashicorp/README.md +5 -0
  53. package/skills/fui-skill/design-md/ibm/DESIGN.md +550 -0
  54. package/skills/fui-skill/design-md/ibm/README.md +5 -0
  55. package/skills/fui-skill/design-md/intercom/DESIGN.md +546 -0
  56. package/skills/fui-skill/design-md/intercom/README.md +5 -0
  57. package/skills/fui-skill/design-md/kraken/DESIGN.md +125 -0
  58. package/skills/fui-skill/design-md/kraken/README.md +5 -0
  59. package/skills/fui-skill/design-md/lamborghini/DESIGN.md +288 -0
  60. package/skills/fui-skill/design-md/lamborghini/README.md +5 -0
  61. package/skills/fui-skill/design-md/linear.app/DESIGN.md +548 -0
  62. package/skills/fui-skill/design-md/linear.app/README.md +5 -0
  63. package/skills/fui-skill/design-md/lovable/DESIGN.md +298 -0
  64. package/skills/fui-skill/design-md/lovable/README.md +5 -0
  65. package/skills/fui-skill/design-md/mastercard/DESIGN.md +365 -0
  66. package/skills/fui-skill/design-md/mastercard/README.md +5 -0
  67. package/skills/fui-skill/design-md/meta/DESIGN.md +683 -0
  68. package/skills/fui-skill/design-md/meta/README.md +5 -0
  69. package/skills/fui-skill/design-md/minimax/DESIGN.md +746 -0
  70. package/skills/fui-skill/design-md/minimax/README.md +5 -0
  71. package/skills/fui-skill/design-md/mintlify/DESIGN.md +852 -0
  72. package/skills/fui-skill/design-md/mintlify/README.md +5 -0
  73. package/skills/fui-skill/design-md/miro/DESIGN.md +825 -0
  74. package/skills/fui-skill/design-md/miro/README.md +5 -0
  75. package/skills/fui-skill/design-md/mistral.ai/DESIGN.md +773 -0
  76. package/skills/fui-skill/design-md/mistral.ai/README.md +5 -0
  77. package/skills/fui-skill/design-md/mongodb/DESIGN.md +767 -0
  78. package/skills/fui-skill/design-md/mongodb/README.md +5 -0
  79. package/skills/fui-skill/design-md/nike/DESIGN.md +575 -0
  80. package/skills/fui-skill/design-md/nike/README.md +5 -0
  81. package/skills/fui-skill/design-md/notion/DESIGN.md +821 -0
  82. package/skills/fui-skill/design-md/notion/README.md +5 -0
  83. package/skills/fui-skill/design-md/nvidia/DESIGN.md +640 -0
  84. package/skills/fui-skill/design-md/nvidia/README.md +5 -0
  85. package/skills/fui-skill/design-md/ollama/DESIGN.md +539 -0
  86. package/skills/fui-skill/design-md/ollama/README.md +5 -0
  87. package/skills/fui-skill/design-md/opencode.ai/DESIGN.md +521 -0
  88. package/skills/fui-skill/design-md/opencode.ai/README.md +5 -0
  89. package/skills/fui-skill/design-md/pinterest/DESIGN.md +597 -0
  90. package/skills/fui-skill/design-md/pinterest/README.md +5 -0
  91. package/skills/fui-skill/design-md/playstation/DESIGN.md +661 -0
  92. package/skills/fui-skill/design-md/playstation/README.md +5 -0
  93. package/skills/fui-skill/design-md/posthog/DESIGN.md +690 -0
  94. package/skills/fui-skill/design-md/posthog/README.md +5 -0
  95. package/skills/fui-skill/design-md/raycast/DESIGN.md +669 -0
  96. package/skills/fui-skill/design-md/raycast/README.md +5 -0
  97. package/skills/fui-skill/design-md/renault/DESIGN.md +589 -0
  98. package/skills/fui-skill/design-md/renault/README.md +5 -0
  99. package/skills/fui-skill/design-md/replicate/DESIGN.md +616 -0
  100. package/skills/fui-skill/design-md/replicate/README.md +5 -0
  101. package/skills/fui-skill/design-md/resend/DESIGN.md +585 -0
  102. package/skills/fui-skill/design-md/resend/README.md +5 -0
  103. package/skills/fui-skill/design-md/revolut/DESIGN.md +636 -0
  104. package/skills/fui-skill/design-md/revolut/README.md +5 -0
  105. package/skills/fui-skill/design-md/runwayml/DESIGN.md +244 -0
  106. package/skills/fui-skill/design-md/runwayml/README.md +5 -0
  107. package/skills/fui-skill/design-md/sanity/DESIGN.md +357 -0
  108. package/skills/fui-skill/design-md/sanity/README.md +5 -0
  109. package/skills/fui-skill/design-md/sentry/DESIGN.md +551 -0
  110. package/skills/fui-skill/design-md/sentry/README.md +5 -0
  111. package/skills/fui-skill/design-md/shopify/DESIGN.md +516 -0
  112. package/skills/fui-skill/design-md/shopify/README.md +5 -0
  113. package/skills/fui-skill/design-md/slack/DESIGN.md +482 -0
  114. package/skills/fui-skill/design-md/spacex/DESIGN.md +363 -0
  115. package/skills/fui-skill/design-md/spacex/README.md +5 -0
  116. package/skills/fui-skill/design-md/spotify/DESIGN.md +246 -0
  117. package/skills/fui-skill/design-md/spotify/README.md +5 -0
  118. package/skills/fui-skill/design-md/starbucks/DESIGN.md +580 -0
  119. package/skills/fui-skill/design-md/starbucks/README.md +5 -0
  120. package/skills/fui-skill/design-md/stripe/DESIGN.md +487 -0
  121. package/skills/fui-skill/design-md/stripe/README.md +5 -0
  122. package/skills/fui-skill/design-md/supabase/DESIGN.md +462 -0
  123. package/skills/fui-skill/design-md/supabase/README.md +5 -0
  124. package/skills/fui-skill/design-md/superhuman/DESIGN.md +448 -0
  125. package/skills/fui-skill/design-md/superhuman/README.md +5 -0
  126. package/skills/fui-skill/design-md/tesla/DESIGN.md +286 -0
  127. package/skills/fui-skill/design-md/tesla/README.md +5 -0
  128. package/skills/fui-skill/design-md/theverge/DESIGN.md +339 -0
  129. package/skills/fui-skill/design-md/theverge/README.md +5 -0
  130. package/skills/fui-skill/design-md/together.ai/DESIGN.md +633 -0
  131. package/skills/fui-skill/design-md/together.ai/README.md +5 -0
  132. package/skills/fui-skill/design-md/uber/DESIGN.md +636 -0
  133. package/skills/fui-skill/design-md/uber/README.md +5 -0
  134. package/skills/fui-skill/design-md/vercel/DESIGN.md +736 -0
  135. package/skills/fui-skill/design-md/vercel/README.md +5 -0
  136. package/skills/fui-skill/design-md/vodafone/DESIGN.md +538 -0
  137. package/skills/fui-skill/design-md/vodafone/README.md +5 -0
  138. package/skills/fui-skill/design-md/voltagent/DESIGN.md +521 -0
  139. package/skills/fui-skill/design-md/voltagent/README.md +5 -0
  140. package/skills/fui-skill/design-md/warp/DESIGN.md +526 -0
  141. package/skills/fui-skill/design-md/warp/README.md +5 -0
  142. package/skills/fui-skill/design-md/webflow/DESIGN.md +588 -0
  143. package/skills/fui-skill/design-md/webflow/README.md +5 -0
  144. package/skills/fui-skill/design-md/wired/DESIGN.md +497 -0
  145. package/skills/fui-skill/design-md/wired/README.md +5 -0
  146. package/skills/fui-skill/design-md/wise/DESIGN.md +544 -0
  147. package/skills/fui-skill/design-md/wise/README.md +5 -0
  148. package/skills/fui-skill/design-md/x.ai/DESIGN.md +465 -0
  149. package/skills/fui-skill/design-md/x.ai/README.md +5 -0
  150. package/skills/fui-skill/design-md/zapier/DESIGN.md +537 -0
  151. package/skills/fui-skill/design-md/zapier/README.md +5 -0
  152. package/skills/fui-skill/examples/component.vue +162 -0
  153. package/skills/fui-skill/examples/f-table-patterns.json +331 -0
  154. package/skills/fui-skill/examples/module-patterns.json +973 -0
  155. package/skills/fui-skill/examples/project-patterns.json +222 -0
  156. package/skills/fui-skill/metadata.json +75 -0
  157. package/skills/fui-skill/references/INDEX.md +144 -0
  158. package/skills/fui-skill/references/advanced-techniques.md +160 -0
  159. package/skills/fui-skill/references/coding-standards.md +112 -0
  160. package/skills/fui-skill/references/component-design.md +455 -0
  161. package/skills/fui-skill/references/component-quickref.md +77 -0
  162. package/skills/fui-skill/references/component-table.md +276 -0
  163. package/skills/fui-skill/references/components-dialog.md +192 -0
  164. package/skills/fui-skill/references/components-display.md +147 -0
  165. package/skills/fui-skill/references/components-echart.md +391 -0
  166. package/skills/fui-skill/references/components-input.md +359 -0
  167. package/skills/fui-skill/references/controls-patterns.md +847 -0
  168. package/skills/fui-skill/references/controls-styling-vocabulary.md +140 -0
  169. package/skills/fui-skill/references/db-table-design.md +77 -0
  170. package/skills/fui-skill/references/db-workflow.md +504 -0
  171. package/skills/fui-skill/references/default-function.md +415 -0
  172. package/skills/fui-skill/references/design-modes.md +85 -0
  173. package/skills/fui-skill/references/echart-templates.md +481 -0
  174. package/skills/fui-skill/references/fastproject.md +97 -0
  175. package/skills/fui-skill/references/fsheet.md +218 -0
  176. package/skills/fui-skill/references/fullstack-workflow.md +351 -0
  177. package/skills/fui-skill/references/module-data-patterns.md +126 -0
  178. package/skills/fui-skill/references/module-json-anatomy.md +137 -0
  179. package/skills/fui-skill/references/module-structure.md +260 -0
  180. package/skills/fui-skill/references/new-session.md +108 -0
  181. package/skills/fui-skill/references/pdfmake.md +60 -0
  182. package/skills/fui-skill/references/permission-system.md +169 -0
  183. package/skills/fui-skill/references/platform-architecture.md +294 -0
  184. package/skills/fui-skill/references/project-config.md +335 -0
  185. package/skills/fui-skill/references/project-provisioning.md +383 -0
  186. package/skills/fui-skill/references/script-map.md +296 -0
  187. package/skills/fui-skill/references/sql-clr-functions.md +224 -0
  188. package/skills/fui-skill/references/system-design.md +116 -0
  189. package/skills/fui-skill/references/tapi-file-api.md +191 -0
  190. package/skills/fui-skill/references/tapi-permission-patterns.md +158 -0
  191. package/skills/fui-skill/references/tapi-reference.md +549 -0
  192. package/skills/fui-skill/references/tools-registry.md +460 -0
  193. package/skills/fui-skill/references/ui-crosswindow-patterns.md +317 -0
  194. package/skills/fui-skill/references/ui-dialog-patterns.md +229 -0
  195. package/skills/fui-skill/references/ui-layout-patterns.md +176 -0
  196. package/skills/fui-skill/references/ui-patterns.md +315 -0
  197. package/skills/fui-skill/references/ui-screenshot-review.md +94 -0
  198. package/skills/fui-skill/references/ui-table-cell-patterns.md +316 -0
  199. package/skills/fui-skill/references/ui-templates.md +29 -0
  200. package/skills/fui-skill/references/verification.md +246 -0
  201. package/skills/fui-skill/references/watcher-patterns.md +196 -0
  202. package/skills/fui-skill/references/websocket-realtime.md +254 -0
  203. package/skills/fui-skill/scripts/component-3.0.js +2549 -0
  204. package/skills/fui-skill/scripts/component.js +3142 -0
  205. package/skills/fui-skill/scripts/componentTable-3.0.js +909 -0
  206. package/skills/fui-skill/scripts/componentTable.js +769 -0
  207. package/skills/fui-skill/scripts/defaultfunction-3.0.js +781 -0
  208. package/skills/fui-skill/scripts/defaultfunction.js +966 -0
  209. package/skills/fui-skill/scripts/fastproject-3.0.js +870 -0
  210. package/skills/fui-skill/scripts/fastproject.js +828 -0
  211. package/skills/fui-skill/scripts/fechart.js +890 -0
  212. package/skills/fui-skill/scripts/fsheet.js +1330 -0
@@ -0,0 +1,383 @@
1
+ # Dựng hạ tầng cho một dự án FUI mới
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).
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.
8
+
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.
12
+
13
+ ---
14
+
15
+ ## 🛑 Bước 0 — Kiểm HAI cổng quyền trước khi tạo bất cứ thứ gì
16
+
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:
20
+
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) |
25
+
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.
29
+
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):
35
+
36
+ ```sql
37
+ SELECT SUSER_NAME() AS LoginHienTai,
38
+ IS_SRVROLEMEMBER('sysadmin') AS sysadmin,
39
+ IS_SRVROLEMEMBER('dbcreator') AS dbcreator,
40
+ IS_SRVROLEMEMBER('securityadmin') AS securityadmin,
41
+ HAS_PERMS_BY_NAME(NULL, NULL, 'CREATE ANY DATABASE') AS taoDuocDB,
42
+ HAS_PERMS_BY_NAME(NULL, NULL, 'ALTER ANY LOGIN') AS taoDuocLogin;
43
+ ```
44
+
45
+ Đủ quyền khi: `sysadmin = 1`, **hoặc** `taoDuocDB = 1` **và** `taoDuocLogin = 1`.
46
+
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`.
60
+
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.
64
+
65
+ ### Cổng A — quyền trên nền tảng FUI
66
+
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:
69
+
70
+ ```sql
71
+ if @jID is null -- không có id ⇒ đang TẠO MỚI
72
+ Begin
73
+ if @sys_SystemRight < 3
74
+ RAISERROR(N'Bạn không có quyền tạo dự án', 16, 1)
75
+ ```
76
+
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):
88
+
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` |
95
+
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`.
99
+
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.
102
+
103
+ ---
104
+
105
+ ## Chỉ mục — dựng một dự án mới đi qua những file nào
106
+
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.
109
+
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 |
119
+
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)).
123
+
124
+ ---
125
+
126
+ ## 🔴 Hai mặt phẳng quyền — đọc trước, đừng trộn
127
+
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".
130
+
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 |
137
+
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ể:
141
+
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.
151
+
152
+ ---
153
+
154
+ ## Bước 1 — Tạo project FUI (đây là cổng A, làm trước phần database)
155
+
156
+ ```
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
+ }})
162
+ ```
163
+
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ó:
168
+ ```sql
169
+ declare @jProjectdata nvarchar(1000) = '{"title":"","apiDomain":"","login":"","menu":[]}'
170
+ ```
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.
190
+
191
+ ---
192
+
193
+ ## Bước 2 — Tạo database
194
+
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.
197
+
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.
200
+
201
+ ```sql
202
+ SELECT DISTINCT LEFT(physical_name, LEN(physical_name) - CHARINDEX('\', REVERSE(physical_name)))
203
+ FROM sys.master_files WHERE database_id > 4;
204
+ ```
205
+
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 đó:
208
+
209
+ ```sql
210
+ CREATE DATABASE [TenDB]
211
+ ON PRIMARY (NAME = N'TenDB', FILENAME = N'C:\Application\Database\TenDB.mdf')
212
+ LOG ON (NAME = N'TenDB_log', FILENAME = N'C:\Application\Database\TenDB_log.ldf');
213
+ ```
214
+
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 })`.
218
+
219
+ ---
220
+
221
+ ## Bước 3 — Tài khoản ỨNG DỤNG (execute-only)
222
+
223
+ Login **trùng tên database**, mật khẩu ngẫu nhiên mạnh:
224
+
225
+ ```bash
226
+ openssl rand -base64 12
227
+ ```
228
+
229
+ ```sql
230
+ CREATE LOGIN [TenDB] WITH PASSWORD = N'<mật khẩu vừa sinh>',
231
+ DEFAULT_DATABASE = [TenDB], CHECK_POLICY = ON;
232
+ USE [TenDB];
233
+ CREATE USER [TenDB] FOR LOGIN [TenDB];
234
+ GRANT EXECUTE ON SCHEMA::dbo TO [TenDB];
235
+ ```
236
+
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.
241
+
242
+ Đây là tài khoản **duy nhất** được nạp vào `acc` ở Bước 5.
243
+
244
+ ---
245
+
246
+ ## Bước 4 — Tài khoản DEV (db_owner)
247
+
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:
250
+
251
+ ```sql
252
+ USE [TenDB];
253
+ CREATE USER [xTuan] FOR LOGIN [xTuan];
254
+ ALTER ROLE db_owner ADD MEMBER [xTuan];
255
+ ```
256
+
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.
260
+
261
+ ---
262
+
263
+ ## Bước 5 — Đăng ký database thành alias tAPI
264
+
265
+ ```
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
+ })
274
+ ```
275
+
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ó.
278
+
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 đè.
286
+
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.
289
+
290
+ ---
291
+
292
+ ## Bước 6 — Cấp quyền quản trị project cho người khác
293
+
294
+ Ba bước, và thứ tự này là **bắt buộc** chứ không phải gợi ý:
295
+
296
+ ```
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 })
300
+ ```
301
+
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`).
325
+
326
+ ---
327
+
328
+ ## Xong hạ tầng rồi — đi tiếp đâu
329
+
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.
333
+
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.
344
+
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).
347
+
348
+ ---
349
+
350
+ ## Checklist
351
+
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
366
+
367
+ ---
368
+
369
+ ## Ba điều dễ hiểu sai
370
+
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.
376
+
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`).
381
+
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.