docs: add database schema snapshot and validation index links
This commit is contained in:
@@ -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
215
docs/DB_SCHEMA_SNAPSHOT.md
Normal 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)`
|
||||
@@ -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` 逐項完成。
|
||||
|
||||
Reference in New Issue
Block a user