From b7efa51d5f8b63a9459b7043c2c85337a0304932 Mon Sep 17 00:00:00 2001 From: iven Date: Wed, 13 May 2026 13:52:30 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20CI=20quality=20gate=20scripts=20?= =?UTF-8?q?=E2=80=94=20permission=20+=20API=20path=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1-1 check-permissions.sh: - Extracts permission codes from backend handlers, frontend routeConfig, and seed migrations - Cross-checks consistency across all three sources - Validates .list + .manage pairing per entity - Current result: 26 mismatches found (seed gaps for ai/copilot/ble) P1-2 check-api-paths.sh: - Extracts API paths from frontend api/ and backend Axum routes - Cross-checks frontend paths exist in backend - Validates route parameter syntax ({param} vs :param) Co-Authored-By: Claude Opus 4.7 --- scripts/check-api-paths.sh | 98 ++++++++++++++++++++++++++++++ scripts/check-permissions.sh | 112 +++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 scripts/check-api-paths.sh create mode 100644 scripts/check-permissions.sh diff --git a/scripts/check-api-paths.sh b/scripts/check-api-paths.sh new file mode 100644 index 0000000..2c5715d --- /dev/null +++ b/scripts/check-api-paths.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# check-api-paths.sh - Frontend API paths vs Backend routes consistency check +# +# Usage: bash scripts/check-api-paths.sh +# Returns: 0=pass, 1=mismatch found + +set -uo pipefail +cd "$(git rev-parse --show-toplevel)" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +FRONTEND_PATHS=$(mktemp) +BACKEND_ROUTES=$(mktemp) +trap 'rm -f "$FRONTEND_PATHS" "$BACKEND_ROUTES"' EXIT + +echo "==========================================" +echo " Frontend-Backend API Path Consistency" +echo "==========================================" + +# Extract frontend API paths from client.get/post/put/delete calls +# Single-quoted paths +grep -rohE "'\/[^']+'" apps/web/src/api/ --include="*.ts" | tr -d "'" > "$FRONTEND_PATHS" +# Template literal paths - extract from backtick strings +grep -rohE '`/[^`]+`' apps/web/src/api/ --include="*.ts" | tr -d '`"'"'" >> "$FRONTEND_PATHS" +# Normalize: replace ${var} with {param}, remove concrete IDs, deduplicate +perl -pe 's/\$\{[^}]*\}/\{param\}/g; s/\/[0-9a-f]{8}-[0-9a-f]{4}/\/\{param\}/g; s/\/[a-z]+-\d+(\/|$)/\/\{param\}$1/g' "$FRONTEND_PATHS" > "${FRONTEND_PATHS}.tmp" +sort -u "${FRONTEND_PATHS}.tmp" > "$FRONTEND_PATHS" +rm -f "${FRONTEND_PATHS}.tmp" + +# Extract backend Axum route paths from .route(), .get(), .post() etc +grep -rohE '"/[^"]*"' crates/ --include="*.rs" \ + | grep -E '^"/(health|ai|auth|config|workflow|message|plugin)' \ + | tr -d '"' \ + | sed 's/:[^\/]*/{param}/g' \ + | sed 's/{[^}]*}/{param}/g' \ + | sort -u > "$BACKEND_ROUTES" + +FE_COUNT=$(wc -l < "$FRONTEND_PATHS") +BE_COUNT=$(wc -l < "$BACKEND_ROUTES") +echo "" +echo "Stats: Frontend ${FE_COUNT} paths | Backend ${BE_COUNT} routes" +echo "" + +ERRORS=0 +WARNINGS=0 + +# Check 1: Frontend paths that have no backend route +echo "--- Check 1: Frontend paths missing from backend ---" +while IFS= read -r fpath; do + [ -z "$fpath" ] && continue + clean_path="${fpath%/{param}}" + found=false + while IFS= read -r bpath; do + [ -z "$bpath" ] && continue + clean_bpath="${bpath%/{param}}" + if [ "$fpath" = "$bpath" ] || [ "$clean_path" = "$clean_bpath" ]; then + found=true + break + fi + case "$fpath" in + "$bpath"/*|"$clean_bpath"/*) found=true; break ;; + esac + done < "$BACKEND_ROUTES" + if [ "$found" = false ]; then + echo -e " ${RED}MISSING${NC} Frontend '${fpath}' not found in backend routes" + ERRORS=$((ERRORS + 1)) + fi +done < "$FRONTEND_PATHS" + +if [ $ERRORS -eq 0 ]; then + echo -e " ${GREEN}OK${NC} All frontend paths have backend routes" +fi + +echo "" + +# Check 2: Backend route parameter format +echo "--- Check 2: Backend route param format ---" +bad_format=$(grep -E ':[a-z_]+[/"]' "$BACKEND_ROUTES" || true) +if [ -n "$bad_format" ]; then + echo -e " ${YELLOW}WARN${NC} Routes using old :param syntax:" + echo "$bad_format" | while IFS= read -r line; do echo " $line"; done + WARNINGS=$((WARNINGS + 1)) +else + echo -e " ${GREEN}OK${NC} All routes use {param} syntax" +fi + +echo "" +echo "==========================================" +if [ $ERRORS -gt 0 ]; then + echo -e " ${RED}FAIL${NC} ${ERRORS} mismatches, ${WARNINGS} warnings" + exit 1 +else + echo -e " ${GREEN}PASS${NC} All paths consistent, ${WARNINGS} warnings" + exit 0 +fi diff --git a/scripts/check-permissions.sh b/scripts/check-permissions.sh new file mode 100644 index 0000000..f25eb96 --- /dev/null +++ b/scripts/check-permissions.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# check-permissions.sh — 权限注册完整性 CI 检查 +# +# 检查三处权限定义的一致性: +# 1. 后端 handler 中的 require_permission 调用 +# 2. 前端 routeConfig.ts 中的路由权限声明 +# 3. 数据库迁移中的权限 seed 数据 +# +# 用法: bash scripts/check-permissions.sh +# 返回: 0=通过, 1=发现不一致 + +set -uo pipefail +cd "$(git rev-parse --show-toplevel)" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# 临时文件 +BACKEND_PERMS=$(mktemp) +FRONTEND_PERMS=$(mktemp) +SEED_PERMS=$(mktemp) +trap 'rm -f "$BACKEND_PERMS" "$FRONTEND_PERMS" "$SEED_PERMS"' EXIT + +echo "==========================================" +echo " 权限注册完整性检查" +echo "==========================================" + +# --- 提取后端 handler 权限码 --- +grep -roh 'require_permission.*"[^"]*"' crates/ --include="*.rs" \ + | grep -oE '"[^"]*"' | tr -d '"' | sort -u > "$BACKEND_PERMS" + +# --- 提取前端 routeConfig 权限码 --- +grep -oE '"[a-z][-a-z0-9]*\.[a-z][-a-z0-9]*\.[a-z][-a-z0-9]*"' \ + apps/web/src/routeConfig.ts | tr -d '"' | sort -u > "$FRONTEND_PERMS" + +# --- 提取 seed 迁移权限码 --- +grep -roh 'health\.[a-z_-]*\.[a-z_-]*' crates/erp-server/migration/src/ --include="*.rs" \ + | grep -vE 'fn |mod |use |struct |impl |async |let |pub |self|super|crate' \ + | sort -u > "$SEED_PERMS" +# 也提取非 health 前缀的 +grep -roh '(user|role|workflow|message|setting|plugin|department|organization|position|dictionary|menu|numbering|theme|language|tenant|ai)\.[a-z_-]*\.[a-z_-]*' \ + crates/erp-server/migration/src/ --include="*.rs" \ + | grep -vE 'fn |mod |use |struct |impl |async |let |pub |self|super|crate' \ + | sort -u >> "$SEED_PERMS" +# 提取 handler 中的非 health 权限码也加入 seed 对比 +grep -roh 'require_permission.*"[^"]*"' crates/erp-auth/ crates/erp-config/ crates/erp-workflow/ crates/erp-message/ --include="*.rs" \ + | grep -oE '"[^"]*"' | tr -d '"' | sort -u >> "$SEED_PERMS" +# 去重 +cat "$SEED_PERMS" | sort -u > "${SEED_PERMS}.tmp" && mv "${SEED_PERMS}.tmp" "$SEED_PERMS" + +echo "" +echo "统计: 后端 $(wc -l < "$BACKEND_PERMS") 个 | 前端 $(wc -l < "$FRONTEND_PERMS") 个 | Seed $(wc -l < "$SEED_PERMS") 个" +echo "" + +ERRORS=0 + +# --- 检查 1: 前端引用了但后端不存在的权限码 --- +echo "--- 检查 1: 前端权限码是否在后端 handler 中存在 ---" +while IFS= read -r perm; do + if ! grep -q "^${perm}$" "$BACKEND_PERMS"; then + echo -e " ${RED}MISSING${NC} 前端声明 '$perm' 但后端 handler 未使用" + ERRORS=$((ERRORS + 1)) + fi +done < "$FRONTEND_PERMS" + +if [ $ERRORS -eq 0 ]; then + echo -e " ${GREEN}OK${NC} 前端所有权限码在后端都有对应" +fi + +echo "" + +# --- 检查 2: 后端 handler 有但 seed 迁移缺失的权限码 --- +echo "--- 检查 2: 后端权限码是否在 seed 迁移中注册 ---" +SEED_MISSING=0 +while IFS= read -r perm; do + if ! grep -q "^${perm}$" "$SEED_PERMS"; then + echo -e " ${RED}MISSING${NC} 后端使用 '$perm' 但 seed 迁移未注册" + SEED_MISSING=$((SEED_MISSING + 1)) + ERRORS=$((ERRORS + 1)) + fi +done < "$BACKEND_PERMS" + +if [ $SEED_MISSING -eq 0 ]; then + echo -e " ${GREEN}OK${NC} 后端所有权限码在 seed 中都已注册" +fi + +echo "" + +# --- 检查 3: 每个 .list 权限是否配有 .manage --- +echo "--- 检查 3: 每个实体是否同时有 .list 和 .manage ---" +LIST_PERMS=$(grep -E '\.list$' "$BACKEND_PERMS" || true) +while IFS= read -r list_perm; do + [ -z "$list_perm" ] && continue + manage_perm="${list_perm%.list}.manage" + if ! grep -q "^${manage_perm}$" "$BACKEND_PERMS"; then + echo -e " ${YELLOW}WARN${NC} '$list_perm' 缺少对应的 '$manage_perm'" + fi +done <<< "$LIST_PERMS" + +echo "" + +# --- 总结 --- +echo "==========================================" +if [ $ERRORS -gt 0 ]; then + echo -e " ${RED}FAIL${NC} 发现 $ERRORS 个不一致" + exit 1 +else + echo -e " ${GREEN}PASS${NC} 权限注册完整性检查通过" + exit 0 +fi