From ca1c41aa9dd4425222c5d1373952c3acc8778628 Mon Sep 17 00:00:00 2001 From: Huarch Date: Wed, 4 Nov 2026 13:35:29 +0800 Subject: [PATCH] feat(admin): sync and authorize Keycloak users --- contracts/manifest.json | 2 +- contracts/server-v1.openapi.json | 292 +++++++- e2e/authenticated.spec.ts | 34 + e2e/support/mockBackend.ts | 19 +- src/app/RefineContext.test.tsx | 23 + src/app/RefineContext.tsx | 16 +- .../admin/SystemAdminPanel.test.tsx | 87 +++ src/components/admin/SystemAdminPanel.tsx | 81 ++- src/components/auth/RoutePermissionGuard.tsx | 17 + src/generated/serverApi.ts | 649 ++++++++++++++---- 10 files changed, 1077 insertions(+), 143 deletions(-) create mode 100644 src/components/admin/SystemAdminPanel.test.tsx diff --git a/contracts/manifest.json b/contracts/manifest.json index b94ab11..8e3b31c 100644 --- a/contracts/manifest.json +++ b/contracts/manifest.json @@ -7,7 +7,7 @@ }, "server": { "file": "server-v1.openapi.json", - "sha256": "b565d841061c9091f48ff3118bcc0cbb1b918b0cb8c2316d177570e8b2d8ba29" + "sha256": "19961572fad04f8545a4f607d4bf7276de64a84b266daecbb21d74e0975f97ef" } } } diff --git a/contracts/server-v1.openapi.json b/contracts/server-v1.openapi.json index 5b68dce..c4efa32 100644 --- a/contracts/server-v1.openapi.json +++ b/contracts/server-v1.openapi.json @@ -89,6 +89,7 @@ "map_extent": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -151,6 +152,7 @@ "map_extent": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -232,6 +234,7 @@ "map_extent": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -378,6 +381,7 @@ "request_data": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -570,6 +574,7 @@ "link_result_list": { "description": "管道模拟结果列表", "items": { + "additionalProperties": true, "type": "object" }, "title": "Link Result List", @@ -578,6 +583,7 @@ "node_result_list": { "description": "节点模拟结果列表", "items": { + "additionalProperties": true, "type": "object" }, "title": "Node Result List", @@ -602,6 +608,7 @@ "iforest_params": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -628,6 +635,7 @@ }, { "items": { + "additionalProperties": true, "type": "object" }, "type": "array" @@ -767,6 +775,7 @@ }, { "items": { + "additionalProperties": true, "type": "object" }, "type": "array" @@ -793,6 +802,7 @@ }, { "items": { + "additionalProperties": true, "type": "object" }, "type": "array" @@ -845,6 +855,7 @@ }, { "items": { + "additionalProperties": true, "type": "object" }, "type": "array" @@ -866,6 +877,7 @@ }, { "items": { + "additionalProperties": true, "type": "object" }, "type": "array" @@ -1201,6 +1213,59 @@ "type": "string" }, "JsonValue": {}, + "KeycloakUserSyncIssue": { + "properties": { + "reason": { + "title": "Reason", + "type": "string" + }, + "username": { + "title": "Username", + "type": "string" + } + }, + "required": [ + "username", + "reason" + ], + "title": "KeycloakUserSyncIssue", + "type": "object" + }, + "KeycloakUserSyncResponse": { + "properties": { + "created": { + "title": "Created", + "type": "integer" + }, + "issues": { + "items": { + "$ref": "#/components/schemas/KeycloakUserSyncIssue" + }, + "title": "Issues", + "type": "array" + }, + "scanned": { + "title": "Scanned", + "type": "integer" + }, + "skipped": { + "title": "Skipped", + "type": "integer" + }, + "updated": { + "title": "Updated", + "type": "integer" + } + }, + "required": [ + "scanned", + "created", + "updated", + "skipped" + ], + "title": "KeycloakUserSyncResponse", + "type": "object" + }, "LeakageIdentifyRequestRest": { "properties": { "dma_count": { @@ -1251,6 +1316,7 @@ }, { "items": { + "additionalProperties": true, "type": "object" }, "type": "array" @@ -1845,6 +1911,7 @@ "properties": { "items": { "items": { + "additionalProperties": true, "type": "object" }, "title": "Items", @@ -1919,6 +1986,7 @@ "title": "Duration" }, "pump_control": { + "additionalProperties": true, "description": "泵控制策略", "title": "Pump Control", "type": "object" @@ -1937,6 +2005,7 @@ "tank_init_level": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -1968,6 +2037,7 @@ }, "errors": { "items": { + "additionalProperties": true, "type": "object" }, "title": "Errors", @@ -2254,6 +2324,7 @@ "map_extent": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -3201,6 +3272,92 @@ ] } }, + "/api/v1/admin/keycloak-user-syncs": { + "post": { + "operationId": "post_admin_keycloak_user_syncs", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeycloakUserSyncResponse" + } + } + }, + "description": "Successful Response" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + }, + "description": "Authentication required" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + }, + "description": "Insufficient permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + }, + "description": "Resource not found" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + }, + "description": "Resource conflict" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + }, + "description": "Validation error" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + }, + "description": "Dependency unavailable" + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "summary": "Sync Keycloak User Directory", + "tags": [ + "Metadata Admin" + ] + } + }, "/api/v1/admin/project-provisions": { "post": { "operationId": "post_admin_project_provisions", @@ -4544,6 +4701,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Patch Admin Projects Project Id Model Imports", "type": "object" } @@ -4651,6 +4809,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Post Admin Projects Project Id Model Imports", "type": "object" } @@ -6459,6 +6618,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Audit Logs Count", "type": "object" } @@ -6730,6 +6890,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Backdrops Properties", "type": "object" } @@ -6826,6 +6987,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -7111,6 +7273,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Post Burst Detections", "type": "object" } @@ -7221,6 +7384,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Post Burst Locations", "type": "object" } @@ -7491,6 +7655,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Controls Properties", "type": "object" } @@ -7587,6 +7752,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -7926,6 +8092,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -8157,6 +8324,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Curves Properties", "type": "object" } @@ -8264,6 +8432,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -8385,6 +8554,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Demands Properties", "type": "object" } @@ -8492,6 +8662,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -8594,7 +8765,7 @@ "required": true, "schema": { "description": "总需水量(m³/h)", - "exclusiveMinimum": 0.0, + "exclusiveMinimum": 0, "title": "Demand", "type": "number" } @@ -8715,6 +8886,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -8828,6 +9000,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -8953,6 +9126,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Element Properties", "type": "object" } @@ -9074,6 +9248,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Element Properties With Types", "type": "object" } @@ -9404,6 +9579,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Emitters Properties", "type": "object" } @@ -9511,6 +9687,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -9620,6 +9797,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -9990,6 +10168,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Post Geocoding Requests", "type": "object" } @@ -11497,6 +11676,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Junctions Properties", "type": "object" } @@ -11604,6 +11784,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -12169,6 +12350,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -12269,6 +12451,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -12401,6 +12584,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Labels Properties", "type": "object" } @@ -12497,6 +12681,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -12618,6 +12803,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Post Leakage Identifications", "type": "object" } @@ -12728,6 +12914,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Link Properties", "type": "object" } @@ -13505,6 +13692,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -13605,6 +13793,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -13712,6 +13901,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -13833,6 +14023,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Mixing Configurations Detail", "type": "object" } @@ -13976,6 +14167,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Network In Extents", "type": "object" } @@ -14184,6 +14376,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Network Options", "type": "object" } @@ -14280,6 +14473,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -14390,6 +14584,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Network Options Energy", "type": "object" } @@ -14500,6 +14695,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Network Options Pump Energy", "type": "object" } @@ -14607,6 +14803,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -14717,6 +14914,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Network Options Time", "type": "object" } @@ -14817,6 +15015,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Backdrop", @@ -14919,6 +15118,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Control", @@ -15021,6 +15221,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Curve", @@ -15123,6 +15324,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Demand", @@ -15225,6 +15427,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Emitter", @@ -15327,6 +15530,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Energy", @@ -15429,6 +15633,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Junction", @@ -15531,6 +15736,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Label", @@ -15633,6 +15839,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Mixing", @@ -15735,6 +15942,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Option", @@ -15837,6 +16045,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Pattern", @@ -15939,6 +16148,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Pipe", @@ -16041,6 +16251,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Pipe Reaction", @@ -16143,6 +16354,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Pump", @@ -16245,6 +16457,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Pump Energy", @@ -16347,6 +16560,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Quality", @@ -16449,6 +16663,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Reaction", @@ -16550,6 +16765,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Region", @@ -16652,6 +16868,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Reservoir", @@ -16753,6 +16970,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Scada Device", @@ -16855,6 +17073,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Source", @@ -16957,6 +17176,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Tag", @@ -17059,6 +17279,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Tank", @@ -17161,6 +17382,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Tank Reaction", @@ -17263,6 +17485,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Time", @@ -17365,6 +17588,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Valve", @@ -17467,6 +17691,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Network Schemas Vertex", @@ -17831,6 +18056,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Node Properties", "type": "object" } @@ -18600,6 +18826,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -18831,6 +19058,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Patterns Properties", "type": "object" } @@ -18938,6 +19166,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -19047,6 +19276,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -19168,6 +19398,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Pipe Reactions Detail", "type": "object" } @@ -21083,6 +21314,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Pipes Properties", "type": "object" } @@ -21190,6 +21422,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -21880,6 +22113,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Post Project Return Dict Runs", "type": "object" } @@ -23545,6 +23779,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Pumps Properties", "type": "object" } @@ -23652,6 +23887,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -23773,6 +24009,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Quality Configurations Properties", "type": "object" } @@ -23869,6 +24106,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -23978,6 +24216,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -24088,6 +24327,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Reactions Detail", "type": "object" } @@ -24185,6 +24425,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -24402,6 +24643,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -24508,6 +24750,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -24628,6 +24871,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Regions Detail", "type": "object" } @@ -26059,6 +26303,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Reservoirs Properties", "type": "object" } @@ -26166,6 +26411,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -26752,6 +26998,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Rule Properties", "type": "object" } @@ -26848,6 +27095,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -26959,6 +27207,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Rule Schemas", @@ -27297,6 +27546,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Scada Properties", "type": "object" } @@ -28280,6 +28530,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -28387,6 +28638,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -28508,6 +28760,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Sources Detail", "type": "object" } @@ -28618,6 +28871,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Status", "type": "object" } @@ -28727,6 +28981,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -28838,6 +29093,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Status Schemas", @@ -29057,6 +29313,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -29189,6 +29446,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Tags Detail", "type": "object" } @@ -29287,6 +29545,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -29408,6 +29667,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Tank Reactions Detail", "type": "object" } @@ -31936,6 +32196,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Tanks Properties", "type": "object" } @@ -32043,6 +32304,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -32843,6 +33105,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -35497,6 +35760,7 @@ "application/json": { "schema": { "additionalProperties": { + "additionalProperties": true, "type": "object" }, "title": "Response Get Title Schemas", @@ -35598,6 +35862,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Titles", "type": "object" } @@ -35694,6 +35959,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -35969,6 +36235,23 @@ "type": "array" } }, + { + "description": "事故要素类型;auto优先管段再节点;分析同名节点须指定node,纯管段分析可指定link", + "in": "query", + "name": "accident_element_type", + "required": false, + "schema": { + "default": "auto", + "description": "事故要素类型;auto优先管段再节点;分析同名节点须指定node,纯管段分析可指定link", + "enum": [ + "auto", + "link", + "node" + ], + "title": "Accident Element Type", + "type": "string" + } + }, { "in": "header", "name": "X-Project-Id", @@ -37423,6 +37706,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Valves Properties", "type": "object" } @@ -37530,6 +37814,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -38109,6 +38394,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -38209,6 +38495,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -38451,6 +38738,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Get Visual Elements Properties", "type": "object" } @@ -38547,6 +38835,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Payload", "type": "object" } @@ -38667,6 +38956,7 @@ "content": { "application/json": { "schema": { + "additionalProperties": true, "title": "Response Post Web Searches", "type": "object" } diff --git a/e2e/authenticated.spec.ts b/e2e/authenticated.spec.ts index 505f573..7f40c11 100644 --- a/e2e/authenticated.spec.ts +++ b/e2e/authenticated.spec.ts @@ -15,3 +15,37 @@ test("已登录管理员可以打开审计日志页面", async ({ page }) => { await expect(page.getByText("系统管理员权限已验证")).toBeVisible(); await expect(page.getByRole("heading", { name: "查询结果" })).toBeVisible(); }); + +test("已认证但未授权的账号会看到明确说明", async ({ page }) => { + await page.route("**/api/v1/access-context", (route) => + route.fulfill({ + status: 403, + contentType: "application/json", + body: JSON.stringify({ detail: "User authorization pending" }), + }), + ); + + await page.goto("/"); + + await expect(page.getByText("账号尚未获得访问权限")).toBeVisible(); + await expect( + page.getByText("身份认证已成功,但当前账号尚未由系统管理员启用"), + ).toBeVisible(); +}); + +test("系统管理员可以手动同步 Keycloak 用户目录", async ({ page }) => { + await page.goto("/system-admin"); + await page.getByRole("tab", { name: "系统用户" }).click(); + + await page.getByRole("button", { name: "同步 Keycloak 用户" }).click(); + await expect(page.getByRole("button", { name: "正在同步" })).toBeDisabled(); + + await expect( + page.getByText("已扫描 3 个 Keycloak 身份,新增 1 个,更新 1 个,跳过 1 个。"), + ).toBeVisible(); + await expect(page.getByText("跳过项包括服务账号、缺少邮箱或身份冲突")).toBeVisible(); + + await page.setViewportSize({ width: 390, height: 844 }); + await expect(page.getByRole("button", { name: "同步 Keycloak 用户" })).toBeVisible(); + await expect(page.getByRole("button", { name: "刷新", exact: true })).toBeVisible(); +}); diff --git a/e2e/support/mockBackend.ts b/e2e/support/mockBackend.ts index 0723df0..847449f 100644 --- a/e2e/support/mockBackend.ts +++ b/e2e/support/mockBackend.ts @@ -50,10 +50,27 @@ export const mockBackend = async (page: Page) => { return json(route, { count: 0 }); } + if ( + url.pathname === "/api/v1/admin/keycloak-user-syncs" && + route.request().method() === "POST" + ) { + await new Promise((resolve) => setTimeout(resolve, 150)); + return json(route, { + scanned: 3, + created: 1, + updated: 1, + skipped: 1, + issues: [{ username: "no-email", reason: "missing_email" }], + }); + } + if ( url.pathname === "/api/v1/audit-logs" || url.pathname === "/api/v1/admin/users" || - url.pathname === "/api/v1/admin/projects" + url.pathname === "/api/v1/admin/projects" || + /^\/api\/v1\/admin\/projects\/[^/]+\/(members|databases)$/.test( + url.pathname, + ) ) { return json(route, []); } diff --git a/src/app/RefineContext.test.tsx b/src/app/RefineContext.test.tsx index 8b80725..84b540f 100644 --- a/src/app/RefineContext.test.tsx +++ b/src/app/RefineContext.test.tsx @@ -115,6 +115,29 @@ describe("RefineContext access authentication", () => { }); }); + it("shows an authorization message when access-context rejects the account", async () => { + global.fetch = jest.fn().mockResolvedValue({ + ok: false, + status: 403, + headers: new Headers(), + } as Response); + + render( + +
应用内容
+
, + ); + + expect( + await screen.findByText("账号尚未获得访问权限"), + ).toBeInTheDocument(); + expect(screen.queryByText("应用内容")).not.toBeInTheDocument(); + expect(useAuthStore.getState()).toMatchObject({ + sessionExpired: false, + sessionExpiryReason: null, + }); + }); + it("prioritizes an unauthenticated session over route permissions", async () => { mockSessionState = { data: null, status: "unauthenticated" }; diff --git a/src/app/RefineContext.tsx b/src/app/RefineContext.tsx index 00135d1..1133663 100644 --- a/src/app/RefineContext.tsx +++ b/src/app/RefineContext.tsx @@ -9,7 +9,7 @@ import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar"; import { RefineSnackbarProvider } from "@refinedev/mui"; import { SessionProvider, signIn, useSession } from "next-auth/react"; import { usePathname } from "next/navigation"; -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import routerProvider from "@refinedev/nextjs-router"; import { NavigationLink } from "@components/loading/NavigationLink"; @@ -75,6 +75,7 @@ export const App = (props: React.PropsWithChildren) => { const setAccessContext = useAccessStore((state) => state.setContext); const setAccessLoading = useAccessStore((state) => state.setLoading); const resetAccess = useAccessStore((state) => state.reset); + const [accessDenied, setAccessDenied] = useState(false); const can = (permission: string) => permissions.includes(permission); useEffect(() => { @@ -103,21 +104,29 @@ export const App = (props: React.PropsWithChildren) => { useEffect(() => { if (status !== "authenticated") { + setAccessDenied(false); resetAccess(); return; } let cancelled = false; + setAccessDenied(false); setAccessLoading(true); apiFetch(`${config.BACKEND_URL}/api/v1/access-context`, { projectHeaderMode: currentProjectId ? "include" : "omit", }) .then(async (response) => { if (cancelled) return; + if (response.status === 403) { + setAccessDenied(true); + resetAccess(); + return; + } if (!response.ok) { resetAccess(); return; } + setAccessDenied(false); setAccessContext(await response.json()); }) .catch(() => { @@ -392,7 +401,10 @@ export const App = (props: React.PropsWithChildren) => { }} > - + {props.children} diff --git a/src/components/admin/SystemAdminPanel.test.tsx b/src/components/admin/SystemAdminPanel.test.tsx new file mode 100644 index 0000000..673fa4a --- /dev/null +++ b/src/components/admin/SystemAdminPanel.test.tsx @@ -0,0 +1,87 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; + +import { apiFetch } from "@/lib/apiFetch"; +import { SystemAdminPanel } from "./SystemAdminPanel"; + +const openNotification = jest.fn(); + +jest.mock("@refinedev/core", () => ({ + useNotification: () => ({ open: openNotification }), +})); + +jest.mock("@config/config", () => ({ + config: { BACKEND_URL: "https://server.example.com" }, +})); + +jest.mock("@/lib/apiFetch", () => ({ + apiFetch: jest.fn(), +})); + +jest.mock("@/lib/sessionRecoveryDraft", () => ({ + useSessionRecoveryDraft: jest.fn(), +})); + +jest.mock("@/store/projectStore", () => ({ + useProjectStore: (selector: (state: { currentProjectId: null }) => unknown) => + selector({ currentProjectId: null }), +})); + +const jsonResponse = (body: unknown) => + ({ + ok: true, + status: 200, + json: async () => body, + text: async () => JSON.stringify(body), + }) as Response; + +describe("SystemAdminPanel Keycloak synchronization", () => { + beforeEach(() => { + jest.clearAllMocks(); + (apiFetch as jest.Mock).mockImplementation((input: string) => { + if (input.endsWith("/api/v1/admin/users/me")) { + return Promise.resolve( + jsonResponse({ + id: "admin-id", + keycloak_id: "admin-keycloak-id", + username: "admin", + email: "admin@example.com", + role: "admin", + is_active: true, + is_superuser: true, + }), + ); + } + if (input.endsWith("/api/v1/admin/keycloak-user-syncs")) { + return Promise.resolve( + jsonResponse({ + scanned: 3, + created: 1, + updated: 1, + skipped: 1, + issues: [{ username: "no-email", reason: "missing_email" }], + }), + ); + } + return Promise.resolve(jsonResponse([])); + }); + }); + + it("syncs the Keycloak directory and shows the result", async () => { + render(); + + fireEvent.click(await screen.findByRole("tab", { name: "系统用户" })); + fireEvent.click( + screen.getByRole("button", { name: "同步 Keycloak 用户" }), + ); + + await waitFor(() => { + expect(apiFetch).toHaveBeenCalledWith( + "https://server.example.com/api/v1/admin/keycloak-user-syncs", + expect.objectContaining({ method: "POST" }), + ); + }); + expect(await screen.findByRole("alert")).toHaveTextContent( + "已扫描 3 个 Keycloak 身份,新增 1 个,更新 1 个,跳过 1 个。", + ); + }); +}); diff --git a/src/components/admin/SystemAdminPanel.tsx b/src/components/admin/SystemAdminPanel.tsx index 1288c50..29e688f 100644 --- a/src/components/admin/SystemAdminPanel.tsx +++ b/src/components/admin/SystemAdminPanel.tsx @@ -53,6 +53,7 @@ import { Save as SaveIcon, Security as SecurityIcon, Storage as StorageIcon, + Sync as SyncIcon, } from "@mui/icons-material"; import { useNotification } from "@refinedev/core"; import { config } from "@config/config"; @@ -70,6 +71,17 @@ type MetadataUser = { is_superuser: boolean; }; +type KeycloakUserSyncResult = { + scanned: number; + created: number; + updated: number; + skipped: number; + issues: Array<{ + username: string; + reason: "missing_email" | "service_account" | "identity_conflict" | string; + }>; +}; + type ProjectMember = { id: string; user_id: string; @@ -469,6 +481,9 @@ export const SystemAdminPanel = () => { const [adminChecked, setAdminChecked] = useState(false); const [isAuthorized, setIsAuthorized] = useState(false); const [metadataConfigAvailable, setMetadataConfigAvailable] = useState(true); + const [syncingKeycloakUsers, setSyncingKeycloakUsers] = useState(false); + const [keycloakSyncResult, setKeycloakSyncResult] = + useState(null); const [projectId, setProjectId] = useState(currentProjectId ?? ""); const [memberForm, setMemberForm] = useState({ user_id: "", @@ -564,6 +579,33 @@ export const SystemAdminPanel = () => { setUsers(await response.json()); }, []); + const syncKeycloakUsers = useCallback(async () => { + if (syncingKeycloakUsers) return; + setSyncingKeycloakUsers(true); + try { + const response = await apiFetch( + `${config.BACKEND_URL}/api/v1/admin/keycloak-user-syncs`, + { + method: "POST", + projectHeaderMode: "omit", + }, + ); + if (!response.ok) { + throw new Error(await readErrorText(response)); + } + const result = (await response.json()) as KeycloakUserSyncResult; + setKeycloakSyncResult(result); + await loadUsers(); + notifySuccess( + `Keycloak 用户同步完成:新增 ${result.created},更新 ${result.updated},跳过 ${result.skipped}`, + ); + } catch (error) { + notifyError(error instanceof Error ? error.message : String(error)); + } finally { + setSyncingKeycloakUsers(false); + } + }, [loadUsers, notifyError, notifySuccess, syncingKeycloakUsers]); + const applyProjectForm = useCallback((project: AdminProject | null) => { if (!project) { setProjectForm(defaultProjectForm); @@ -1238,13 +1280,44 @@ export const SystemAdminPanel = () => { } variant="outlined" onClick={loadUsers}> - 刷新 - + + + + } /> + {keycloakSyncResult && ( + 0 ? "warning" : "success"} + sx={{ borderRadius: 2 }} + > + {`已扫描 ${keycloakSyncResult.scanned} 个 Keycloak 身份,新增 ${keycloakSyncResult.created} 个,更新 ${keycloakSyncResult.updated} 个,跳过 ${keycloakSyncResult.skipped} 个。`} + {keycloakSyncResult.skipped > 0 && + " 跳过项包括服务账号、缺少邮箱或身份冲突,请检查 Keycloak 用户资料。"} + + )} diff --git a/src/components/auth/RoutePermissionGuard.tsx b/src/components/auth/RoutePermissionGuard.tsx index fa90f8b..3a6fcb1 100644 --- a/src/components/auth/RoutePermissionGuard.tsx +++ b/src/components/auth/RoutePermissionGuard.tsx @@ -12,9 +12,11 @@ import { useAuthStore } from "@/store/authStore"; export const RoutePermissionGuard = ({ children, authenticated, + accessDenied = false, }: { children: ReactNode; authenticated: boolean; + accessDenied?: boolean; }) => { const pathname = usePathname(); const permissions = useAccessStore((state) => state.permissions); @@ -37,6 +39,21 @@ export const RoutePermissionGuard = ({ ); } + if (accessDenied) { + return ( + + }> + + 账号尚未获得访问权限 + + 身份认证已成功,但当前账号尚未由系统管理员启用,或无权访问所选项目。请联系系统管理员完成授权后重试。 + + + + + ); + } + if (requiredPermission && loading) { return ( diff --git a/src/generated/serverApi.ts b/src/generated/serverApi.ts index 542698a..f7bb585 100644 --- a/src/generated/serverApi.ts +++ b/src/generated/serverApi.ts @@ -21,6 +21,23 @@ export interface paths { patch?: never; trace?: never; }; + "/api/v1/admin/keycloak-user-syncs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Sync Keycloak User Directory */ + post: operations["post_admin_keycloak_user_syncs"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/v1/admin/project-provisions": { parameters: { query?: never; @@ -4723,7 +4740,9 @@ export interface components { /** Gs Workspace */ gs_workspace: string; /** Map Extent */ - map_extent?: Record | null; + map_extent?: { + [key: string]: unknown; + } | null; /** Name */ name: string; /** @@ -4747,7 +4766,9 @@ export interface components { /** Gs Workspace */ gs_workspace: string; /** Map Extent */ - map_extent?: Record | null; + map_extent?: { + [key: string]: unknown; + } | null; /** Name */ name: string; /** @@ -4772,7 +4793,9 @@ export interface components { /** Gs Workspace */ gs_workspace?: string | null; /** Map Extent */ - map_extent?: Record | null; + map_extent?: { + [key: string]: unknown; + } | null; /** Name */ name?: string | null; /** Status */ @@ -4818,7 +4841,9 @@ export interface components { /** Project Id */ project_id: string | null; /** Request Data */ - request_data: Record | null; + request_data: { + [key: string]: unknown; + } | null; /** Request Method */ request_method: string | null; /** Request Path */ @@ -4883,12 +4908,16 @@ export interface components { * Link Result List * @description 管道模拟结果列表 */ - link_result_list: Record[]; + link_result_list: { + [key: string]: unknown; + }[]; /** * Node Result List * @description 节点模拟结果列表 */ - node_result_list: Record[]; + node_result_list: { + [key: string]: unknown; + }[]; }; /** BurstDetectionRequestRest */ BurstDetectionRequestRest: { @@ -4902,7 +4931,9 @@ export interface components { * Iforest Params * @description 隔离森林算法参数 */ - iforest_params?: Record | null; + iforest_params?: { + [key: string]: unknown; + } | null; /** * Mu * @description 异常值检测的参数 @@ -4915,7 +4946,9 @@ export interface components { */ observed_pressure_data?: { [key: string]: unknown[]; - } | Record[] | unknown[][] | null; + } | { + [key: string]: unknown; + }[] | unknown[][] | null; /** * Points Per Day * @description 每天的数据点数 @@ -4972,7 +5005,9 @@ export interface components { */ burst_flow?: { [key: string]: number; - } | Record[] | null; + } | { + [key: string]: unknown; + }[] | null; /** * Burst Leakage * @description 爆管时的漏水量 @@ -4984,7 +5019,9 @@ export interface components { */ burst_pressure?: { [key: string]: number; - } | Record[] | null; + } | { + [key: string]: unknown; + }[] | null; /** * Data Source * @description 数据来源:monitoring(监测)或simulation(模拟) @@ -5009,14 +5046,18 @@ export interface components { */ normal_flow?: { [key: string]: number; - } | Record[] | null; + } | { + [key: string]: unknown; + }[] | null; /** * Normal Pressure * @description 正常时的压力数据 */ normal_pressure?: { [key: string]: number; - } | Record[] | null; + } | { + [key: string]: unknown; + }[] | null; /** * Pressure Scada Ids * @description 压力SCADA传感器ID列表 @@ -5149,6 +5190,26 @@ export interface components { */ HistorySource: "scada_raw" | "scada_cleaned" | "realtime_simulation" | "analysis_simulation"; JsonValue: unknown; + /** KeycloakUserSyncIssue */ + KeycloakUserSyncIssue: { + /** Reason */ + reason: string; + /** Username */ + username: string; + }; + /** KeycloakUserSyncResponse */ + KeycloakUserSyncResponse: { + /** Created */ + created: number; + /** Issues */ + issues?: components["schemas"]["KeycloakUserSyncIssue"][]; + /** Scanned */ + scanned: number; + /** Skipped */ + skipped: number; + /** Updated */ + updated: number; + }; /** LeakageIdentifyRequestRest */ LeakageIdentifyRequestRest: { /** @@ -5180,7 +5241,9 @@ export interface components { */ observed_pressure_data?: { [key: string]: unknown[]; - } | Record[] | null; + } | { + [key: string]: unknown; + }[] | null; /** * Output Flow Unit * @description 输出流量单位 @@ -5423,7 +5486,9 @@ export interface components { /** Page[dict[str, Any]] */ Page_dict_str__Any__: { /** Items */ - items: Record[]; + items: { + [key: string]: unknown; + }[]; /** Limit */ limit: number; /** Offset */ @@ -5454,7 +5519,9 @@ export interface components { * Pump Control * @description 泵控制策略 */ - pump_control: Record; + pump_control: { + [key: string]: unknown; + }; /** * Scheme Name * @description 方案名称 @@ -5469,7 +5536,9 @@ export interface components { * Tank Init Level * @description 水箱初始水位 */ - tank_init_level?: Record | null; + tank_init_level?: { + [key: string]: unknown; + } | null; }; /** * ProblemDetails @@ -5481,7 +5550,9 @@ export interface components { /** Detail */ detail: string; /** Errors */ - errors?: Record[]; + errors?: { + [key: string]: unknown; + }[]; /** Instance */ instance: string; /** Status */ @@ -5614,7 +5685,9 @@ export interface components { /** Gs Workspace */ gs_workspace: string; /** Map Extent */ - map_extent?: Record | null; + map_extent?: { + [key: string]: unknown; + } | null; /** Name */ name: string; /** @@ -5995,6 +6068,80 @@ export interface operations { }; }; }; + post_admin_keycloak_user_syncs: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["KeycloakUserSyncResponse"]; + }; + }; + /** @description Authentication required */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProblemDetails"]; + }; + }; + /** @description Insufficient permission */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProblemDetails"]; + }; + }; + /** @description Resource not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProblemDetails"]; + }; + }; + /** @description Resource conflict */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProblemDetails"]; + }; + }; + /** @description Validation error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProblemDetails"]; + }; + }; + /** @description Dependency unavailable */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProblemDetails"]; + }; + }; + }; + }; post_admin_project_provisions: { parameters: { query?: never; @@ -6959,7 +7106,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -7039,7 +7188,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -8228,7 +8379,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -8389,7 +8542,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -8459,7 +8614,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -8636,7 +8793,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -8716,7 +8875,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -8881,7 +9042,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -8951,7 +9114,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -9113,7 +9278,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -9358,7 +9525,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -9431,7 +9600,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -9520,7 +9691,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -9593,7 +9766,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -9754,7 +9929,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -9836,7 +10013,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -9927,7 +10106,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -10008,7 +10189,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -10245,7 +10428,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -10318,7 +10503,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -10398,7 +10585,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -10583,7 +10772,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -11626,7 +11817,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -11699,7 +11892,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -12099,7 +12294,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -12179,7 +12376,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -12268,7 +12467,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -12338,7 +12539,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -12428,7 +12631,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -12507,7 +12712,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -13053,7 +13260,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -13133,7 +13342,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -13211,7 +13422,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -13300,7 +13513,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -13385,7 +13600,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -13537,7 +13754,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -13607,7 +13826,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -13693,7 +13914,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -13772,7 +13995,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -13845,7 +14070,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -13931,7 +14158,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -14008,7 +14237,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14086,7 +14317,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14164,7 +14397,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14242,7 +14477,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14320,7 +14557,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14398,7 +14637,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14476,7 +14717,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14554,7 +14797,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14632,7 +14877,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14710,7 +14957,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14788,7 +15037,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14866,7 +15117,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -14944,7 +15197,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15022,7 +15277,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15100,7 +15357,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15178,7 +15437,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15256,7 +15517,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15334,7 +15597,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15412,7 +15677,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15490,7 +15757,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15568,7 +15837,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15646,7 +15917,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15724,7 +15997,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15802,7 +16077,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15880,7 +16157,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -15958,7 +16237,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -16036,7 +16317,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -16278,7 +16561,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -16744,7 +17029,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -16989,7 +17276,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -17062,7 +17351,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -17142,7 +17433,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -17231,7 +17524,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -18517,7 +18812,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -18590,7 +18887,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -19076,7 +19375,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -20255,7 +20556,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -20328,7 +20631,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -20417,7 +20722,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -20487,7 +20794,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -20567,7 +20876,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -20653,7 +20964,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -20802,7 +21115,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -20882,7 +21197,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -20960,7 +21277,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -21049,7 +21368,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -22013,7 +22334,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -22086,7 +22409,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -22496,7 +22821,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -22566,7 +22893,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -22653,7 +22982,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -22890,7 +23221,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -23521,7 +23854,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -23678,7 +24013,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -23767,7 +24104,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -23846,7 +24185,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -23919,7 +24260,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -24006,7 +24349,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -24156,7 +24501,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -24247,7 +24594,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -24317,7 +24666,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -24406,7 +24757,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -26099,7 +26452,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -26172,7 +26527,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -26732,7 +27089,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -28460,7 +28819,9 @@ export interface operations { }; content: { "application/json": { - [key: string]: Record; + [key: string]: { + [key: string]: unknown; + }; }; }; }; @@ -28537,7 +28898,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -28607,7 +28970,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -28768,6 +29133,8 @@ export interface operations { accident_element: string[]; /** @description 已故障的阀门ID列表(可选) */ disabled_valves?: string[]; + /** @description 事故要素类型;auto优先管段再节点;分析同名节点须指定node,纯管段分析可指定link */ + accident_element_type?: "auto" | "link" | "node"; }; header: { "X-Project-Id": string; @@ -29747,7 +30114,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -29820,7 +30189,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -30220,7 +30591,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -30300,7 +30673,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -30466,7 +30841,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */ @@ -30536,7 +30913,9 @@ export interface operations { }; requestBody: { content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; responses: { @@ -30626,7 +31005,9 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": Record; + "application/json": { + [key: string]: unknown; + }; }; }; /** @description Authentication required */