@fui-org/fui-cli 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -3
- package/dist/fui.js +160 -43
- package/package.json +4 -2
- package/skills/fui/SKILL.md +131 -66
- package/skills/fui-skill/README.md +112 -0
- package/skills/fui-skill/SKILL.md +269 -0
- package/skills/fui-skill/assets/projectdefaultstyle.css +518 -0
- package/skills/fui-skill/design-md/airbnb/DESIGN.md +545 -0
- package/skills/fui-skill/design-md/airbnb/README.md +5 -0
- package/skills/fui-skill/design-md/airtable/DESIGN.md +554 -0
- package/skills/fui-skill/design-md/airtable/README.md +5 -0
- package/skills/fui-skill/design-md/apple/DESIGN.md +562 -0
- package/skills/fui-skill/design-md/apple/README.md +5 -0
- package/skills/fui-skill/design-md/asu/DESIGN.md +179 -0
- package/skills/fui-skill/design-md/asu/README.md +107 -0
- package/skills/fui-skill/design-md/binance/DESIGN.md +634 -0
- package/skills/fui-skill/design-md/binance/README.md +5 -0
- package/skills/fui-skill/design-md/bmw/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/bmw/README.md +5 -0
- package/skills/fui-skill/design-md/bmw-m/DESIGN.md +503 -0
- package/skills/fui-skill/design-md/bmw-m/README.md +5 -0
- package/skills/fui-skill/design-md/bugatti/DESIGN.md +454 -0
- package/skills/fui-skill/design-md/bugatti/README.md +5 -0
- package/skills/fui-skill/design-md/cal/DESIGN.md +542 -0
- package/skills/fui-skill/design-md/cal/README.md +5 -0
- package/skills/fui-skill/design-md/claude/DESIGN.md +589 -0
- package/skills/fui-skill/design-md/claude/README.md +5 -0
- package/skills/fui-skill/design-md/clay/DESIGN.md +541 -0
- package/skills/fui-skill/design-md/clay/README.md +5 -0
- package/skills/fui-skill/design-md/clickhouse/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/clickhouse/README.md +5 -0
- package/skills/fui-skill/design-md/cohere/DESIGN.md +451 -0
- package/skills/fui-skill/design-md/cohere/README.md +5 -0
- package/skills/fui-skill/design-md/coinbase/DESIGN.md +570 -0
- package/skills/fui-skill/design-md/coinbase/README.md +5 -0
- package/skills/fui-skill/design-md/composio/DESIGN.md +506 -0
- package/skills/fui-skill/design-md/composio/README.md +5 -0
- package/skills/fui-skill/design-md/cursor/DESIGN.md +537 -0
- package/skills/fui-skill/design-md/cursor/README.md +5 -0
- package/skills/fui-skill/design-md/elevenlabs/DESIGN.md +504 -0
- package/skills/fui-skill/design-md/elevenlabs/README.md +5 -0
- package/skills/fui-skill/design-md/expo/DESIGN.md +526 -0
- package/skills/fui-skill/design-md/expo/README.md +5 -0
- package/skills/fui-skill/design-md/ferrari/DESIGN.md +531 -0
- package/skills/fui-skill/design-md/ferrari/README.md +5 -0
- package/skills/fui-skill/design-md/figma/DESIGN.md +578 -0
- package/skills/fui-skill/design-md/figma/README.md +5 -0
- package/skills/fui-skill/design-md/framer/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/framer/README.md +5 -0
- package/skills/fui-skill/design-md/fui/DESIGN.md +532 -0
- package/skills/fui-skill/design-md/hashicorp/DESIGN.md +575 -0
- package/skills/fui-skill/design-md/hashicorp/README.md +5 -0
- package/skills/fui-skill/design-md/ibm/DESIGN.md +550 -0
- package/skills/fui-skill/design-md/ibm/README.md +5 -0
- package/skills/fui-skill/design-md/intercom/DESIGN.md +546 -0
- package/skills/fui-skill/design-md/intercom/README.md +5 -0
- package/skills/fui-skill/design-md/kraken/DESIGN.md +125 -0
- package/skills/fui-skill/design-md/kraken/README.md +5 -0
- package/skills/fui-skill/design-md/lamborghini/DESIGN.md +288 -0
- package/skills/fui-skill/design-md/lamborghini/README.md +5 -0
- package/skills/fui-skill/design-md/linear.app/DESIGN.md +548 -0
- package/skills/fui-skill/design-md/linear.app/README.md +5 -0
- package/skills/fui-skill/design-md/lovable/DESIGN.md +298 -0
- package/skills/fui-skill/design-md/lovable/README.md +5 -0
- package/skills/fui-skill/design-md/mastercard/DESIGN.md +365 -0
- package/skills/fui-skill/design-md/mastercard/README.md +5 -0
- package/skills/fui-skill/design-md/meta/DESIGN.md +683 -0
- package/skills/fui-skill/design-md/meta/README.md +5 -0
- package/skills/fui-skill/design-md/minimax/DESIGN.md +746 -0
- package/skills/fui-skill/design-md/minimax/README.md +5 -0
- package/skills/fui-skill/design-md/mintlify/DESIGN.md +852 -0
- package/skills/fui-skill/design-md/mintlify/README.md +5 -0
- package/skills/fui-skill/design-md/miro/DESIGN.md +825 -0
- package/skills/fui-skill/design-md/miro/README.md +5 -0
- package/skills/fui-skill/design-md/mistral.ai/DESIGN.md +773 -0
- package/skills/fui-skill/design-md/mistral.ai/README.md +5 -0
- package/skills/fui-skill/design-md/mongodb/DESIGN.md +767 -0
- package/skills/fui-skill/design-md/mongodb/README.md +5 -0
- package/skills/fui-skill/design-md/nike/DESIGN.md +575 -0
- package/skills/fui-skill/design-md/nike/README.md +5 -0
- package/skills/fui-skill/design-md/notion/DESIGN.md +821 -0
- package/skills/fui-skill/design-md/notion/README.md +5 -0
- package/skills/fui-skill/design-md/nvidia/DESIGN.md +640 -0
- package/skills/fui-skill/design-md/nvidia/README.md +5 -0
- package/skills/fui-skill/design-md/ollama/DESIGN.md +539 -0
- package/skills/fui-skill/design-md/ollama/README.md +5 -0
- package/skills/fui-skill/design-md/opencode.ai/DESIGN.md +521 -0
- package/skills/fui-skill/design-md/opencode.ai/README.md +5 -0
- package/skills/fui-skill/design-md/pinterest/DESIGN.md +597 -0
- package/skills/fui-skill/design-md/pinterest/README.md +5 -0
- package/skills/fui-skill/design-md/playstation/DESIGN.md +661 -0
- package/skills/fui-skill/design-md/playstation/README.md +5 -0
- package/skills/fui-skill/design-md/posthog/DESIGN.md +690 -0
- package/skills/fui-skill/design-md/posthog/README.md +5 -0
- package/skills/fui-skill/design-md/raycast/DESIGN.md +669 -0
- package/skills/fui-skill/design-md/raycast/README.md +5 -0
- package/skills/fui-skill/design-md/renault/DESIGN.md +589 -0
- package/skills/fui-skill/design-md/renault/README.md +5 -0
- package/skills/fui-skill/design-md/replicate/DESIGN.md +616 -0
- package/skills/fui-skill/design-md/replicate/README.md +5 -0
- package/skills/fui-skill/design-md/resend/DESIGN.md +585 -0
- package/skills/fui-skill/design-md/resend/README.md +5 -0
- package/skills/fui-skill/design-md/revolut/DESIGN.md +636 -0
- package/skills/fui-skill/design-md/revolut/README.md +5 -0
- package/skills/fui-skill/design-md/runwayml/DESIGN.md +244 -0
- package/skills/fui-skill/design-md/runwayml/README.md +5 -0
- package/skills/fui-skill/design-md/sanity/DESIGN.md +357 -0
- package/skills/fui-skill/design-md/sanity/README.md +5 -0
- package/skills/fui-skill/design-md/sentry/DESIGN.md +551 -0
- package/skills/fui-skill/design-md/sentry/README.md +5 -0
- package/skills/fui-skill/design-md/shopify/DESIGN.md +516 -0
- package/skills/fui-skill/design-md/shopify/README.md +5 -0
- package/skills/fui-skill/design-md/slack/DESIGN.md +482 -0
- package/skills/fui-skill/design-md/spacex/DESIGN.md +363 -0
- package/skills/fui-skill/design-md/spacex/README.md +5 -0
- package/skills/fui-skill/design-md/spotify/DESIGN.md +246 -0
- package/skills/fui-skill/design-md/spotify/README.md +5 -0
- package/skills/fui-skill/design-md/starbucks/DESIGN.md +580 -0
- package/skills/fui-skill/design-md/starbucks/README.md +5 -0
- package/skills/fui-skill/design-md/stripe/DESIGN.md +487 -0
- package/skills/fui-skill/design-md/stripe/README.md +5 -0
- package/skills/fui-skill/design-md/supabase/DESIGN.md +462 -0
- package/skills/fui-skill/design-md/supabase/README.md +5 -0
- package/skills/fui-skill/design-md/superhuman/DESIGN.md +448 -0
- package/skills/fui-skill/design-md/superhuman/README.md +5 -0
- package/skills/fui-skill/design-md/tesla/DESIGN.md +286 -0
- package/skills/fui-skill/design-md/tesla/README.md +5 -0
- package/skills/fui-skill/design-md/theverge/DESIGN.md +339 -0
- package/skills/fui-skill/design-md/theverge/README.md +5 -0
- package/skills/fui-skill/design-md/together.ai/DESIGN.md +633 -0
- package/skills/fui-skill/design-md/together.ai/README.md +5 -0
- package/skills/fui-skill/design-md/uber/DESIGN.md +636 -0
- package/skills/fui-skill/design-md/uber/README.md +5 -0
- package/skills/fui-skill/design-md/vercel/DESIGN.md +736 -0
- package/skills/fui-skill/design-md/vercel/README.md +5 -0
- package/skills/fui-skill/design-md/vodafone/DESIGN.md +538 -0
- package/skills/fui-skill/design-md/vodafone/README.md +5 -0
- package/skills/fui-skill/design-md/voltagent/DESIGN.md +521 -0
- package/skills/fui-skill/design-md/voltagent/README.md +5 -0
- package/skills/fui-skill/design-md/warp/DESIGN.md +526 -0
- package/skills/fui-skill/design-md/warp/README.md +5 -0
- package/skills/fui-skill/design-md/webflow/DESIGN.md +588 -0
- package/skills/fui-skill/design-md/webflow/README.md +5 -0
- package/skills/fui-skill/design-md/wired/DESIGN.md +497 -0
- package/skills/fui-skill/design-md/wired/README.md +5 -0
- package/skills/fui-skill/design-md/wise/DESIGN.md +544 -0
- package/skills/fui-skill/design-md/wise/README.md +5 -0
- package/skills/fui-skill/design-md/x.ai/DESIGN.md +465 -0
- package/skills/fui-skill/design-md/x.ai/README.md +5 -0
- package/skills/fui-skill/design-md/zapier/DESIGN.md +537 -0
- package/skills/fui-skill/design-md/zapier/README.md +5 -0
- package/skills/fui-skill/examples/component.vue +162 -0
- package/skills/fui-skill/examples/f-table-patterns.json +331 -0
- package/skills/fui-skill/examples/module-patterns.json +973 -0
- package/skills/fui-skill/examples/project-patterns.json +222 -0
- package/skills/fui-skill/metadata.json +75 -0
- package/skills/fui-skill/references/INDEX.md +144 -0
- package/skills/fui-skill/references/advanced-techniques.md +160 -0
- package/skills/fui-skill/references/coding-standards.md +112 -0
- package/skills/fui-skill/references/component-design.md +455 -0
- package/skills/fui-skill/references/component-quickref.md +77 -0
- package/skills/fui-skill/references/component-table.md +276 -0
- package/skills/fui-skill/references/components-dialog.md +192 -0
- package/skills/fui-skill/references/components-display.md +147 -0
- package/skills/fui-skill/references/components-echart.md +391 -0
- package/skills/fui-skill/references/components-input.md +359 -0
- package/skills/fui-skill/references/controls-patterns.md +847 -0
- package/skills/fui-skill/references/controls-styling-vocabulary.md +140 -0
- package/skills/fui-skill/references/db-table-design.md +77 -0
- package/skills/fui-skill/references/db-workflow.md +504 -0
- package/skills/fui-skill/references/default-function.md +415 -0
- package/skills/fui-skill/references/design-modes.md +85 -0
- package/skills/fui-skill/references/echart-templates.md +481 -0
- package/skills/fui-skill/references/fastproject.md +97 -0
- package/skills/fui-skill/references/fsheet.md +218 -0
- package/skills/fui-skill/references/fullstack-workflow.md +351 -0
- package/skills/fui-skill/references/module-data-patterns.md +126 -0
- package/skills/fui-skill/references/module-json-anatomy.md +137 -0
- package/skills/fui-skill/references/module-structure.md +260 -0
- package/skills/fui-skill/references/new-session.md +108 -0
- package/skills/fui-skill/references/pdfmake.md +60 -0
- package/skills/fui-skill/references/permission-system.md +169 -0
- package/skills/fui-skill/references/platform-architecture.md +294 -0
- package/skills/fui-skill/references/project-config.md +335 -0
- package/skills/fui-skill/references/project-provisioning.md +383 -0
- package/skills/fui-skill/references/script-map.md +296 -0
- package/skills/fui-skill/references/sql-clr-functions.md +224 -0
- package/skills/fui-skill/references/system-design.md +116 -0
- package/skills/fui-skill/references/tapi-file-api.md +191 -0
- package/skills/fui-skill/references/tapi-permission-patterns.md +158 -0
- package/skills/fui-skill/references/tapi-reference.md +549 -0
- package/skills/fui-skill/references/tools-registry.md +460 -0
- package/skills/fui-skill/references/ui-crosswindow-patterns.md +317 -0
- package/skills/fui-skill/references/ui-dialog-patterns.md +229 -0
- package/skills/fui-skill/references/ui-layout-patterns.md +176 -0
- package/skills/fui-skill/references/ui-patterns.md +315 -0
- package/skills/fui-skill/references/ui-screenshot-review.md +94 -0
- package/skills/fui-skill/references/ui-table-cell-patterns.md +316 -0
- package/skills/fui-skill/references/ui-templates.md +29 -0
- package/skills/fui-skill/references/verification.md +246 -0
- package/skills/fui-skill/references/watcher-patterns.md +196 -0
- package/skills/fui-skill/references/websocket-realtime.md +254 -0
- package/skills/fui-skill/scripts/component-3.0.js +2549 -0
- package/skills/fui-skill/scripts/component.js +3142 -0
- package/skills/fui-skill/scripts/componentTable-3.0.js +909 -0
- package/skills/fui-skill/scripts/componentTable.js +769 -0
- package/skills/fui-skill/scripts/defaultfunction-3.0.js +781 -0
- package/skills/fui-skill/scripts/defaultfunction.js +966 -0
- package/skills/fui-skill/scripts/fastproject-3.0.js +870 -0
- package/skills/fui-skill/scripts/fastproject.js +828 -0
- package/skills/fui-skill/scripts/fechart.js +890 -0
- package/skills/fui-skill/scripts/fsheet.js +1330 -0
|
@@ -0,0 +1,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.
|