# Full API test using /login and database menu queries $companyUrl = "http://localhost:8006" # Step 1: Login via /login (which is in SecurityConfig anonymous whitelist) Write-Host "=== Step 1: Login ===" $loginBody = '{"tenantCode":"T202605253515","username":"admin","password":"admin123"}' $loginResp = Invoke-WebRequest -Uri "$companyUrl/login" -Method POST -ContentType "application/json" -Body $loginBody -UseBasicParsing $loginJson = $loginResp.Content | ConvertFrom-Json $token = $loginJson.token Write-Host "Token OK, length: $($token.Length)" $headers = @{ Authorization = "Bearer $token" } # Step 2: Get menu routes via getRouters Write-Host "`n=== Step 2: Get Routers ===" $routesResp = Invoke-WebRequest -Uri "$companyUrl/getRouters" -Method GET -Headers $headers -UseBasicParsing $routesJson = $routesResp.Content | ConvertFrom-Json Write-Host "getRouters code: $($routesJson.code)" # Recursive extract perms from menu tree function Extract-Perms { param($menuList) $results = @() foreach ($menu in $menuList) { if ($menu.perms -and $menu.perms.Trim() -ne "") { $results += $menu.perms } if ($menu.children -and $menu.children.Count -gt 0) { $results += Extract-Perms -menuList $menu.children } } return $results } $allPerms = Extract-Perms -menuList $routesJson.data Write-Host "Perms from getRouters: $($allPerms.Count)" # Step 3: Also get menu list from system/menu/list (has more detail including buttons) Write-Host "`n=== Step 3: Get Menu List ===" try { $menuResp = Invoke-WebRequest -Uri "$companyUrl/system/menu/list" -Method GET -Headers $headers -UseBasicParsing $menuJson = $menuResp.Content | ConvertFrom-Json Write-Host "system/menu/list code: $($menuJson.code), total: $($menuJson.total)" # Extract perms from menu list $menuPerms = @() foreach ($row in $menuJson.rows) { if ($row.perms -and $row.perms.Trim() -ne "") { $menuPerms += $row.perms } } Write-Host "Perms from system/menu/list: $($menuPerms.Count)" } catch { Write-Host "system/menu/list error: $($_.Exception.Message)" $menuPerms = @() } # Merge all perms $allPerms = $allPerms + $menuPerms | Sort-Object -Unique Write-Host "`nTotal unique perms: $($allPerms.Count)" # Step 4: Convert perms to API paths # perms format: module:entity:action -> /module/entity/action $apiPaths = @{} foreach ($perm in $allPerms) { if ($perm -match "^\s*$") { continue } $parts = $perm -split ":" if ($parts.Count -ge 3) { $mod = $parts[0] $ent = $parts[1] $act = $parts[2] $p = "/$mod/$ent/$act" if (-not $apiPaths.ContainsKey($p)) { $apiPaths[$p] = $perm } } elseif ($parts.Count -eq 2) { $mod = $parts[0] $ent = $parts[1] $p = "/$mod/$ent/list" if (-not $apiPaths.ContainsKey($p)) { $apiPaths[$p] = $perm } } } Write-Host "Total unique API paths from perms: $($apiPaths.Count)" # Step 5: Also extract component paths from menus (page routes) Write-Host "`n=== Step 5: Extract page routes ===" $pagePaths = @{} function Extract-PageRoutes { param($menuList) foreach ($menu in $menuList) { if ($menu.path -and $menu.path -ne "" -and $menu.component -and $menu.component -ne "") { $pagePaths[$menu.path] = $menu.component } if ($menu.children -and $menu.children.Count -gt 0) { Extract-PageRoutes -menuList $menu.children } } } Extract-PageRoutes -menuList $routesJson.data Write-Host "Page routes: $($pagePaths.Count)" # Step 6: Now scan saasadminui src/api for ALL API URLs Write-Host "`n=== Step 6: Scan saasadminui src/api for URLs ===" $apiFiles = Get-ChildItem -Path "d:\ylrz\saasadminui\src\api" -Recurse -Filter "*.js" $frontendApis = @{} foreach ($file in $apiFiles) { $content = Get-Content $file.FullName -Raw -Encoding UTF8 # Match url patterns: url: '/xxx/yyy/zzz' or url: "/xxx/yyy/zzz" $matches = [regex]::Matches($content, "url:\s*['`"]([^'`"]+)['`"]") foreach ($m in $matches) { $url = $m.Groups[1].Value # Remove template variables like ${xxx} $cleanUrl = $url -replace '\$\{[^}]+\}', '1' if (-not $frontendApis.ContainsKey($cleanUrl)) { $frontendApis[$cleanUrl] = $file.Name } } } Write-Host "Frontend API URLs found: $($frontendApis.Count)" # Step 7: Test ALL unique API paths (from perms + frontend) Write-Host "`n=== Step 7: Test all API endpoints ===" # Merge all paths $testPaths = @{} foreach ($p in $apiPaths.Keys) { if (-not $testPaths.ContainsKey($p)) { $testPaths[$p] = "perms:$($apiPaths[$p])" } } foreach ($p in $frontendApis.Keys) { if (-not $testPaths.ContainsKey($p)) { $testPaths[$p] = "frontend:$($frontendApis[$p])" } } Write-Host "Total unique paths to test: $($testPaths.Count)" $ok = [System.Collections.ArrayList]::new() $notFound = [System.Collections.ArrayList]::new() $serverErr = [System.Collections.ArrayList]::new() $forbidden = [System.Collections.ArrayList]::new() $timeout = [System.Collections.ArrayList]::new() $other = [System.Collections.ArrayList]::new() $i = 0 $total = $testPaths.Count foreach ($path in ($testPaths.Keys | Sort-Object)) { $i++ $source = $testPaths[$path] if ($i % 100 -eq 0) { Write-Host " Progress: $i / $total" } # Determine method - GET for list/query, POST for add/edit/remove $method = "GET" if ($path -match "/add|/edit|/remove|/delete|/save|/update|/import|/export|/upload") { $method = "POST" } try { if ($method -eq "GET") { $resp = Invoke-WebRequest -Uri "$companyUrl$path" -Method GET -Headers $headers -UseBasicParsing -TimeoutSec 8 } else { $resp = Invoke-WebRequest -Uri "$companyUrl$path" -Method POST -Headers $headers -ContentType "application/json" -Body "{}" -UseBasicParsing -TimeoutSec 8 } $body = $resp.Content | ConvertFrom-Json if ($body.code -and $body.code -ne 200) { $code = "$($body.code)" if ($code -eq "404") { [void]$notFound.Add("$path | $source") } elseif ($code -eq "500") { [void]$serverErr.Add("$path | $source") } elseif ($code -eq "403") { [void]$forbidden.Add("$path | $source") } else { [void]$other.Add("$path | $source | code=$code") } } else { [void]$ok.Add("$path | $source") } } catch { $err = "$($_.Exception.Message)" if ($err -match "404") { [void]$notFound.Add("$path | $source") } elseif ($err -match "500") { [void]$serverErr.Add("$path | $source") } elseif ($err -match "403") { [void]$forbidden.Add("$path | $source") } elseif ($err -match "timeout|timed out") { [void]$timeout.Add("$path | $source") } else { [void]$other.Add("$path | $source | err") } } } # Step 8: Summary Write-Host "`n========================================" Write-Host "=== API Test Results ===" Write-Host "========================================" Write-Host "200 OK: $($ok.Count)" Write-Host "404 Not Found: $($notFound.Count)" Write-Host "500 Server Error: $($serverErr.Count)" Write-Host "403 Forbidden: $($forbidden.Count)" Write-Host "TIMEOUT: $($timeout.Count)" Write-Host "OTHER: $($other.Count)" if ($notFound.Count -gt 0) { Write-Host "`n--- 404 Details ---" $notFound | Sort-Object | ForEach-Object { Write-Host " $_" } } if ($serverErr.Count -gt 0) { Write-Host "`n--- 500 Details ---" $serverErr | Sort-Object | ForEach-Object { Write-Host " $_" } } if ($forbidden.Count -gt 0) { Write-Host "`n--- 403 Details ---" $forbidden | Sort-Object | ForEach-Object { Write-Host " $_" } } if ($timeout.Count -gt 0) { Write-Host "`n--- TIMEOUT Details ---" $timeout | Sort-Object | ForEach-Object { Write-Host " $_" } } if ($other.Count -gt 0) { Write-Host "`n--- OTHER Details ---" $other | Sort-Object | ForEach-Object { Write-Host " $_" } } # Save results $out = [System.Collections.ArrayList]::new() [void]$out.Add("API Test Results - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')") [void]$out.Add("=" * 60) [void]$out.Add("200 OK: $($ok.Count)") [void]$out.Add("404 Not Found: $($notFound.Count)") [void]$out.Add("500 Server Error: $($serverErr.Count)") [void]$out.Add("403 Forbidden: $($forbidden.Count)") [void]$out.Add("TIMEOUT: $($timeout.Count)") [void]$out.Add("OTHER: $($other.Count)") [void]$out.Add("") [void]$out.Add("=== 200 OK ===") $ok | Sort-Object | ForEach-Object { [void]$out.Add($_) } [void]$out.Add("") [void]$out.Add("=== 404 Not Found ===") $notFound | Sort-Object | ForEach-Object { [void]$out.Add($_) } [void]$out.Add("") [void]$out.Add("=== 500 Server Error ===") $serverErr | Sort-Object | ForEach-Object { [void]$out.Add($_) } [void]$out.Add("") [void]$out.Add("=== 403 Forbidden ===") $forbidden | Sort-Object | ForEach-Object { [void]$out.Add($_) } [void]$out.Add("") [void]$out.Add("=== TIMEOUT ===") $timeout | Sort-Object | ForEach-Object { [void]$out.Add($_) } [void]$out.Add("") [void]$out.Add("=== OTHER ===") $other | Sort-Object | ForEach-Object { [void]$out.Add($_) } [System.IO.File]::WriteAllLines("d:\ylrz\saasadminui\api_test_results.txt", $out, (New-Object System.Text.UTF8Encoding($false))) Write-Host "`nResults saved to d:\ylrz\saasadminui\api_test_results.txt"