docs: add database schema snapshot and validation index links

This commit is contained in:
Chris
2026-03-30 00:42:48 +08:00
parent ddaaadfe5b
commit 5170787d43
3 changed files with 218 additions and 0 deletions

View File

@@ -25,6 +25,8 @@
- 後端任務進度與驗收條件
- `docs/API_CLIENTS_SQL.sql`
- `api_clients` 白名單表與初始資料 SQL
- `docs/DB_SCHEMA_SNAPSHOT.md`
- 目前資料庫 schema 快照(欄位/索引/約束)
## 目前狀態2026-03-29
- 後端骨架已建立FastAPI + SQLAlchemy

215
docs/DB_SCHEMA_SNAPSHOT.md Normal file
View File

@@ -0,0 +1,215 @@
# DB Schema Snapshot
- Date: 2026-03-30
- Database: `member.ose.tw`
- Schema: `public`
## Tables
- `api_clients`
- `auth_sync_state`
- `companies`
- `modules`
- `permissions`
- `sites`
- `user_scope_permissions`
- `users`
## `api_clients`
### Columns
- `id`: `uuid` (not null: `true`)
- `client_key`: `text` (not null: `true`)
- `name`: `text` (not null: `true`)
- `status`: `client_status` (not null: `true`)
- `api_key_hash`: `text` (not null: `true`)
- `allowed_origins`: `jsonb` (not null: `true`)
- `allowed_ips`: `jsonb` (not null: `true`)
- `allowed_paths`: `jsonb` (not null: `true`)
- `rate_limit_per_min`: `integer` (not null: `false`)
- `expires_at`: `timestamp with time zone` (not null: `false`)
- `last_used_at`: `timestamp with time zone` (not null: `false`)
- `created_at`: `timestamp with time zone` (not null: `true`)
- `updated_at`: `timestamp with time zone` (not null: `true`)
### Constraints
- `api_clients_client_key_key` (`u`): UNIQUE (client_key)
- `api_clients_pkey` (`p`): PRIMARY KEY (id)
### Indexes
- `api_clients_client_key_key`: `CREATE UNIQUE INDEX api_clients_client_key_key ON public.api_clients USING btree (client_key)`
- `api_clients_pkey`: `CREATE UNIQUE INDEX api_clients_pkey ON public.api_clients USING btree (id)`
- `idx_api_clients_expires_at`: `CREATE INDEX idx_api_clients_expires_at ON public.api_clients USING btree (expires_at)`
- `idx_api_clients_status`: `CREATE INDEX idx_api_clients_status ON public.api_clients USING btree (status)`
## `auth_sync_state`
### Columns
- `user_id`: `uuid` (not null: `true`)
- `last_synced_at`: `timestamp with time zone` (not null: `false`)
- `source_version`: `text` (not null: `false`)
- `last_error`: `text` (not null: `false`)
- `updated_at`: `timestamp with time zone` (not null: `true`)
### Constraints
- `auth_sync_state_pkey` (`p`): PRIMARY KEY (user_id)
- `auth_sync_state_user_id_fkey` (`f`): FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
### Indexes
- `auth_sync_state_pkey`: `CREATE UNIQUE INDEX auth_sync_state_pkey ON public.auth_sync_state USING btree (user_id)`
## `companies`
### Columns
- `id`: `uuid` (not null: `true`)
- `company_key`: `text` (not null: `true`)
- `name`: `text` (not null: `true`)
- `status`: `record_status` (not null: `true`)
- `created_at`: `timestamp with time zone` (not null: `true`)
- `updated_at`: `timestamp with time zone` (not null: `true`)
### Constraints
- `companies_company_key_key` (`u`): UNIQUE (company_key)
- `companies_pkey` (`p`): PRIMARY KEY (id)
### Indexes
- `companies_company_key_key`: `CREATE UNIQUE INDEX companies_company_key_key ON public.companies USING btree (company_key)`
- `companies_pkey`: `CREATE UNIQUE INDEX companies_pkey ON public.companies USING btree (id)`
## `modules`
### Columns
- `id`: `uuid` (not null: `true`)
- `module_key`: `text` (not null: `true`)
- `name`: `text` (not null: `true`)
- `status`: `record_status` (not null: `true`)
- `created_at`: `timestamp with time zone` (not null: `true`)
- `updated_at`: `timestamp with time zone` (not null: `true`)
### Constraints
- `modules_module_key_key` (`u`): UNIQUE (module_key)
- `modules_pkey` (`p`): PRIMARY KEY (id)
### Indexes
- `modules_module_key_key`: `CREATE UNIQUE INDEX modules_module_key_key ON public.modules USING btree (module_key)`
- `modules_pkey`: `CREATE UNIQUE INDEX modules_pkey ON public.modules USING btree (id)`
## `permissions`
### Columns
- `id`: `uuid` (not null: `true`)
- `user_id`: `uuid` (not null: `true`)
- `scope_type`: `character varying(32)` (not null: `true`)
- `scope_id`: `character varying(128)` (not null: `true`)
- `module`: `character varying(128)` (not null: `true`)
- `action`: `character varying(32)` (not null: `true`)
- `created_at`: `timestamp with time zone` (not null: `true`)
### Constraints
- `permissions_pkey` (`p`): PRIMARY KEY (id)
- `permissions_user_id_fkey` (`f`): FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
- `uq_permissions_user_scope_module_action` (`u`): UNIQUE (user_id, scope_type, scope_id, module, action)
### Indexes
- `idx_permissions_user_id`: `CREATE INDEX idx_permissions_user_id ON public.permissions USING btree (user_id)`
- `permissions_pkey`: `CREATE UNIQUE INDEX permissions_pkey ON public.permissions USING btree (id)`
- `uq_permissions_user_scope_module_action`: `CREATE UNIQUE INDEX uq_permissions_user_scope_module_action ON public.permissions USING btree (user_id, scope_type, scope_id, module, action)`
## `sites`
### Columns
- `id`: `uuid` (not null: `true`)
- `site_key`: `text` (not null: `true`)
- `company_id`: `uuid` (not null: `true`)
- `name`: `text` (not null: `true`)
- `status`: `record_status` (not null: `true`)
- `created_at`: `timestamp with time zone` (not null: `true`)
- `updated_at`: `timestamp with time zone` (not null: `true`)
### Constraints
- `sites_company_id_fkey` (`f`): FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
- `sites_pkey` (`p`): PRIMARY KEY (id)
- `sites_site_key_key` (`u`): UNIQUE (site_key)
### Indexes
- `idx_sites_company_id`: `CREATE INDEX idx_sites_company_id ON public.sites USING btree (company_id)`
- `sites_pkey`: `CREATE UNIQUE INDEX sites_pkey ON public.sites USING btree (id)`
- `sites_site_key_key`: `CREATE UNIQUE INDEX sites_site_key_key ON public.sites USING btree (site_key)`
## `user_scope_permissions`
### Columns
- `id`: `uuid` (not null: `true`)
- `user_id`: `uuid` (not null: `true`)
- `module_id`: `uuid` (not null: `true`)
- `action`: `permission_action` (not null: `true`)
- `scope_type`: `scope_type` (not null: `true`)
- `company_id`: `uuid` (not null: `false`)
- `site_id`: `uuid` (not null: `false`)
- `created_at`: `timestamp with time zone` (not null: `true`)
- `updated_at`: `timestamp with time zone` (not null: `true`)
### Constraints
- `user_scope_permissions_check` (`c`): CHECK ((((scope_type = 'company'::scope_type) AND (company_id IS NOT NULL) AND (site_id IS NULL)) OR ((scope_type = 'site'::scope_type) AND (site_id IS NOT NULL) AND (company_id IS NULL))))
- `user_scope_permissions_company_id_fkey` (`f`): FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
- `user_scope_permissions_module_id_fkey` (`f`): FOREIGN KEY (module_id) REFERENCES modules(id) ON DELETE CASCADE
- `user_scope_permissions_pkey` (`p`): PRIMARY KEY (id)
- `user_scope_permissions_site_id_fkey` (`f`): FOREIGN KEY (site_id) REFERENCES sites(id) ON DELETE CASCADE
- `user_scope_permissions_user_id_fkey` (`f`): FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
### Indexes
- `idx_usp_company_id`: `CREATE INDEX idx_usp_company_id ON public.user_scope_permissions USING btree (company_id)`
- `idx_usp_module_id`: `CREATE INDEX idx_usp_module_id ON public.user_scope_permissions USING btree (module_id)`
- `idx_usp_site_id`: `CREATE INDEX idx_usp_site_id ON public.user_scope_permissions USING btree (site_id)`
- `idx_usp_user_id`: `CREATE INDEX idx_usp_user_id ON public.user_scope_permissions USING btree (user_id)`
- `uq_usp_company`: `CREATE UNIQUE INDEX uq_usp_company ON public.user_scope_permissions USING btree (user_id, module_id, action, scope_type, company_id) WHERE (scope_type = 'company'::scope_type)`
- `uq_usp_site`: `CREATE UNIQUE INDEX uq_usp_site ON public.user_scope_permissions USING btree (user_id, module_id, action, scope_type, site_id) WHERE (scope_type = 'site'::scope_type)`
- `user_scope_permissions_pkey`: `CREATE UNIQUE INDEX user_scope_permissions_pkey ON public.user_scope_permissions USING btree (id)`
## `users`
### Columns
- `id`: `uuid` (not null: `true`)
- `authentik_sub`: `text` (not null: `true`)
- `email`: `text` (not null: `false`)
- `display_name`: `text` (not null: `false`)
- `status`: `record_status` (not null: `true`)
- `created_at`: `timestamp with time zone` (not null: `true`)
- `updated_at`: `timestamp with time zone` (not null: `true`)
- `authentik_user_id`: `integer` (not null: `false`)
- `is_active`: `boolean` (not null: `true`)
### Constraints
- `users_authentik_sub_key` (`u`): UNIQUE (authentik_sub)
- `users_email_key` (`u`): UNIQUE (email)
- `users_pkey` (`p`): PRIMARY KEY (id)
### Indexes
- `idx_users_authentik_sub`: `CREATE INDEX idx_users_authentik_sub ON public.users USING btree (authentik_sub)`
- `users_authentik_sub_key`: `CREATE UNIQUE INDEX users_authentik_sub_key ON public.users USING btree (authentik_sub)`
- `users_email_key`: `CREATE UNIQUE INDEX users_email_key ON public.users USING btree (email)`
- `users_pkey`: `CREATE UNIQUE INDEX users_pkey ON public.users USING btree (id)`

View File

@@ -16,6 +16,7 @@
## SQL 與配置
- `docs/API_CLIENTS_SQL.sql`
- `docs/DB_SCHEMA_SNAPSHOT.md`
## 給前端 AI 的一句話交接
請先完成 `/me``/me/permissions/snapshot``/admin/permissions/grant|revoke` 三組 API 對接,並依 `FRONTEND_IMPLEMENTATION_CHECKLIST.md` 逐項完成。