fix: finalize unified schema and correct permission snapshot mapping
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import and_, delete, or_, select
|
||||
from sqlalchemy import and_, delete, literal, or_, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.company import Company
|
||||
@@ -16,6 +16,7 @@ class PermissionsRepository:
|
||||
def list_by_user(self, user_id: str, authentik_sub: str) -> list[tuple[str, str, str | None, str, str]]:
|
||||
direct_stmt = (
|
||||
select(
|
||||
literal("direct"),
|
||||
UserScopePermission.scope_type,
|
||||
Company.company_key,
|
||||
Site.site_key,
|
||||
@@ -30,6 +31,7 @@ class PermissionsRepository:
|
||||
)
|
||||
group_stmt = (
|
||||
select(
|
||||
literal("group"),
|
||||
PermissionGroupPermission.scope_type,
|
||||
PermissionGroupPermission.scope_id,
|
||||
PermissionGroupPermission.system,
|
||||
@@ -44,10 +46,11 @@ class PermissionsRepository:
|
||||
result: list[tuple[str, str, str | None, str, str]] = []
|
||||
dedup = set()
|
||||
for row in rows:
|
||||
if len(row) == 5:
|
||||
scope_type, scope_id, system_key, module_key, action = row
|
||||
source = row[0]
|
||||
if source == "group":
|
||||
_, scope_type, scope_id, system_key, module_key, action = row
|
||||
else:
|
||||
scope_type, company_key, site_key, module_key, action = row
|
||||
_, scope_type, company_key, site_key, module_key, action = row
|
||||
scope_id = company_key if scope_type == "company" else site_key
|
||||
system_key = module_key.split(".", 1)[0] if isinstance(module_key, str) and "." in module_key else None
|
||||
key = (scope_type, scope_id or "", system_key, module_key, action)
|
||||
|
||||
@@ -2,30 +2,13 @@ BEGIN;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'record_status') THEN
|
||||
CREATE TYPE record_status AS ENUM ('active','inactive');
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'client_status') THEN
|
||||
CREATE TYPE client_status AS ENUM ('active','inactive');
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'scope_type') THEN
|
||||
CREATE TYPE scope_type AS ENUM ('company','site');
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'permission_action') THEN
|
||||
CREATE TYPE permission_action AS ENUM ('view','create','update','delete','manage');
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
authentik_sub TEXT NOT NULL UNIQUE,
|
||||
authentik_user_id INTEGER,
|
||||
email TEXT UNIQUE,
|
||||
display_name TEXT,
|
||||
status record_status NOT NULL DEFAULT 'active',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
@@ -43,7 +26,7 @@ CREATE TABLE IF NOT EXISTS companies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
status record_status NOT NULL DEFAULT 'active',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
@@ -53,7 +36,7 @@ CREATE TABLE IF NOT EXISTS sites (
|
||||
site_key TEXT NOT NULL UNIQUE,
|
||||
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
status record_status NOT NULL DEFAULT 'active',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
@@ -62,7 +45,7 @@ CREATE TABLE IF NOT EXISTS systems (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
system_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
status record_status NOT NULL DEFAULT 'active',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
@@ -71,7 +54,7 @@ CREATE TABLE IF NOT EXISTS modules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
module_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
status record_status NOT NULL DEFAULT 'active',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
@@ -93,8 +76,8 @@ CREATE TABLE IF NOT EXISTS user_scope_permissions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
module_id UUID NOT NULL REFERENCES modules(id) ON DELETE CASCADE,
|
||||
action permission_action NOT NULL,
|
||||
scope_type scope_type NOT NULL,
|
||||
action VARCHAR(32) NOT NULL,
|
||||
scope_type VARCHAR(16) NOT NULL,
|
||||
company_id UUID REFERENCES companies(id) ON DELETE CASCADE,
|
||||
site_id UUID REFERENCES sites(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
@@ -113,7 +96,7 @@ CREATE TABLE IF NOT EXISTS permission_groups (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
group_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
status record_status NOT NULL DEFAULT 'active',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
@@ -142,7 +125,7 @@ CREATE TABLE IF NOT EXISTS api_clients (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
client_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
status client_status NOT NULL DEFAULT 'active',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
api_key_hash TEXT NOT NULL,
|
||||
allowed_origins JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
allowed_ips JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
|
||||
27
backend/scripts/migrate_enum_to_text.sql
Normal file
27
backend/scripts/migrate_enum_to_text.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
BEGIN;
|
||||
|
||||
-- users / master tables
|
||||
ALTER TABLE users ALTER COLUMN status TYPE VARCHAR(16) USING status::text;
|
||||
ALTER TABLE companies ALTER COLUMN status TYPE VARCHAR(16) USING status::text;
|
||||
ALTER TABLE sites ALTER COLUMN status TYPE VARCHAR(16) USING status::text;
|
||||
ALTER TABLE systems ALTER COLUMN status TYPE VARCHAR(16) USING status::text;
|
||||
ALTER TABLE modules ALTER COLUMN status TYPE VARCHAR(16) USING status::text;
|
||||
ALTER TABLE permission_groups ALTER COLUMN status TYPE VARCHAR(16) USING status::text;
|
||||
|
||||
-- api_clients
|
||||
ALTER TABLE api_clients ALTER COLUMN status TYPE VARCHAR(16) USING status::text;
|
||||
|
||||
-- user scoped permissions
|
||||
ALTER TABLE user_scope_permissions ALTER COLUMN action TYPE VARCHAR(32) USING action::text;
|
||||
ALTER TABLE user_scope_permissions ALTER COLUMN scope_type TYPE VARCHAR(16) USING scope_type::text;
|
||||
|
||||
-- keep check constraint compatible with varchar
|
||||
ALTER TABLE user_scope_permissions DROP CONSTRAINT IF EXISTS user_scope_permissions_check;
|
||||
ALTER TABLE user_scope_permissions
|
||||
ADD CONSTRAINT user_scope_permissions_check
|
||||
CHECK (
|
||||
((scope_type = 'company' AND company_id IS NOT NULL AND site_id IS NULL)
|
||||
OR (scope_type = 'site' AND site_id IS NOT NULL AND company_id IS NULL))
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
87
docs/FRONTEND_HANDOFF_SCHEMA_V2.md
Normal file
87
docs/FRONTEND_HANDOFF_SCHEMA_V2.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Frontend 交辦清單(Schema v2)
|
||||
|
||||
## 目標
|
||||
前端要改成對應後端新模型:
|
||||
- 公司(companies)
|
||||
- 品牌站台(sites)
|
||||
- 會員(users)
|
||||
- 系統/模組(systems/modules)
|
||||
- 權限群組(permission-groups)
|
||||
|
||||
## 既有頁面要調整
|
||||
|
||||
### 1) 權限管理頁 `/admin/permissions`
|
||||
- Grant/Revoke payload 改為:
|
||||
- `scope_type`: `company` 或 `site`
|
||||
- `scope_id`: `company_key` 或 `site_key`
|
||||
- `system`: 必填(例如 `mkt`)
|
||||
- `module`: 選填(空值代表系統層權限)
|
||||
- `action`
|
||||
- 表單新增 `system` 欄位。
|
||||
- `module` 改成可選。
|
||||
|
||||
### 2) 我的權限頁 `/me/permissions`
|
||||
- 顯示欄位改為:
|
||||
- `scope_type`
|
||||
- `scope_id`
|
||||
- `system`
|
||||
- `module`
|
||||
- `action`
|
||||
|
||||
## 新增頁面
|
||||
|
||||
### 3) 系統管理 `/admin/systems`
|
||||
- 列表:`GET /admin/systems`
|
||||
- 新增:`POST /admin/systems`
|
||||
|
||||
### 4) 模組管理 `/admin/modules`
|
||||
- 列表:`GET /admin/modules`
|
||||
- 新增:`POST /admin/modules`
|
||||
- `system_key`
|
||||
- `module_key`
|
||||
- `name`
|
||||
|
||||
### 5) 公司管理 `/admin/companies`
|
||||
- 列表:`GET /admin/companies`
|
||||
- 新增:`POST /admin/companies`
|
||||
|
||||
### 6) 站台管理 `/admin/sites`
|
||||
- 列表:`GET /admin/sites`
|
||||
- 新增:`POST /admin/sites`
|
||||
- `company_key`
|
||||
|
||||
### 7) 會員列表 `/admin/members`
|
||||
- 列表:`GET /admin/members`
|
||||
|
||||
### 8) 權限群組 `/admin/permission-groups`
|
||||
- 群組列表/新增:
|
||||
- `GET /admin/permission-groups`
|
||||
- `POST /admin/permission-groups`
|
||||
- 群組綁會員:
|
||||
- `POST /admin/permission-groups/{group_key}/members/{authentik_sub}`
|
||||
- `DELETE /admin/permission-groups/{group_key}/members/{authentik_sub}`
|
||||
- 群組授權:
|
||||
- `POST /admin/permission-groups/{group_key}/permissions/grant`
|
||||
- `POST /admin/permission-groups/{group_key}/permissions/revoke`
|
||||
|
||||
## 共用資料載入(下拉選單)
|
||||
權限表單應先載入:
|
||||
- systems: `GET /admin/systems`
|
||||
- modules: `GET /admin/modules`
|
||||
- companies: `GET /admin/companies`
|
||||
- sites: `GET /admin/sites`
|
||||
|
||||
## 認證(管理 API)
|
||||
所有 `/admin/*` API 一律帶:
|
||||
- `X-Client-Key`
|
||||
- `X-API-Key`
|
||||
|
||||
本地測試可用:
|
||||
- `X-Client-Key: admin-frontend`
|
||||
- `X-API-Key: dev-admin-key-123`
|
||||
|
||||
## 驗收條件
|
||||
- 可以新增 system/module/company/site。
|
||||
- 可以做 user 直接 grant/revoke。
|
||||
- 可以建立 permission-group、加會員、做群組 grant/revoke。
|
||||
- `/me/permissions/snapshot` 能看到直接權限 + 群組權限(去重後)。
|
||||
@@ -14,6 +14,7 @@
|
||||
- `docs/TASKPLAN_FRONTEND.md`
|
||||
- `docs/TASKPLAN_BACKEND.md`
|
||||
- `docs/ORG_MEMBER_MANAGEMENT_PLAN.md`(公司組織/會員管理規劃)
|
||||
- `docs/FRONTEND_HANDOFF_SCHEMA_V2.md`(前端交辦清單,直接給另一隻 AI)
|
||||
|
||||
## SQL 與配置
|
||||
- `backend/scripts/init_schema.sql`
|
||||
|
||||
Reference in New Issue
Block a user