docs: add OpenAPI responses={} to all router endpoints

Add explicit HTTP status code documentation to every endpoint
across 15 router files. Each endpoint now declares all possible
response codes (200/201/204/400/401/404/409/429/502/503) with
descriptions so frontend can distinguish error types.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-03 01:12:08 +02:00
parent 7ad885d276
commit 8f26776bb3
15 changed files with 624 additions and 2 deletions

View File

@@ -213,6 +213,11 @@ _NamePath = Annotated[str, Path(description='Jail name as configured in fail2ban
"",
response_model=JailConfigListResponse,
summary="List configuration for all active jails",
responses={
200: {"description": "Jail configs returned", "model": JailConfigListResponse},
401: {"description": "Session missing, expired, or invalid"},
502: {"description": "fail2ban unreachable"},
},
)
async def get_jail_configs(
request: Request,
@@ -241,6 +246,11 @@ async def get_jail_configs(
"/inactive",
response_model=InactiveJailListResponse,
summary="List all inactive jails discovered in config files",
responses={
200: {"description": "Inactive jail list returned", "model": InactiveJailListResponse},
401: {"description": "Session missing, expired, or invalid"},
502: {"description": "fail2ban unreachable"},
},
)
async def get_inactive_jails(
request: Request,
@@ -268,6 +278,10 @@ async def get_inactive_jails(
"/pending-recovery",
response_model=PendingRecovery | None,
summary="Return active crash-recovery record if one exists",
responses={
200: {"description": "Recovery record or null", "model": PendingRecovery},
401: {"description": "Session missing, expired, or invalid"},
},
)
async def get_pending_recovery(
_auth: AuthDep,
@@ -293,6 +307,12 @@ async def get_pending_recovery(
"/{name}",
response_model=JailConfigResponse,
summary="Return configuration for a single jail",
responses={
200: {"description": "Jail config returned", "model": JailConfigResponse},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found"},
502: {"description": "fail2ban unreachable"},
},
)
async def get_jail_config(
request: Request,
@@ -325,6 +345,15 @@ async def get_jail_config(
status_code=status.HTTP_204_NO_CONTENT,
summary="Update jail configuration",
dependencies=[Depends(_check_jail_update_rate_limit)],
responses={
204: {"description": "Jail config updated successfully"},
400: {"description": "Set command rejected or invalid regex"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found"},
422: {"description": "Regex pattern failed to compile"},
429: {"description": "Rate limit exceeded for jail update operations"},
502: {"description": "fail2ban unreachable"},
},
)
async def update_jail_config(
request: Request,
@@ -365,6 +394,14 @@ async def update_jail_config(
status_code=status.HTTP_204_NO_CONTENT,
summary="Add a log file path to an existing jail",
dependencies=[Depends(_check_jail_create_rate_limit)],
responses={
204: {"description": "Log path added successfully"},
400: {"description": "Command rejected or path invalid"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found"},
429: {"description": "Rate limit exceeded for jail create operations"},
502: {"description": "fail2ban unreachable"},
},
)
async def add_log_path(
request: Request,
@@ -400,6 +437,15 @@ async def add_log_path(
status_code=status.HTTP_204_NO_CONTENT,
summary="Remove a monitored log path from a jail",
dependencies=[Depends(_check_jail_delete_rate_limit)],
responses={
204: {"description": "Log path removed successfully"},
400: {"description": "Command rejected"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found"},
422: {"description": "Log path outside allowed directories"},
429: {"description": "Rate limit exceeded for jail delete operations"},
502: {"description": "fail2ban unreachable"},
},
)
async def delete_log_path(
request: Request,
@@ -440,6 +486,15 @@ async def delete_log_path(
response_model=JailActivationResponse,
summary="Activate an inactive jail",
dependencies=[Depends(_check_jail_activate_rate_limit)],
responses={
200: {"description": "Jail activated", "model": JailActivationResponse},
400: {"description": "Invalid jail name"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found in config files"},
409: {"description": "Jail already active"},
429: {"description": "Rate limit exceeded for jail activate operations"},
502: {"description": "fail2ban unreachable"},
},
)
async def activate_jail(
app: AppDep,
@@ -494,6 +549,15 @@ async def activate_jail(
response_model=JailActivationResponse,
summary="Deactivate an active jail",
dependencies=[Depends(_check_jail_deactivate_rate_limit)],
responses={
200: {"description": "Jail deactivated", "model": JailActivationResponse},
400: {"description": "Invalid jail name"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found in config files"},
409: {"description": "Jail already inactive"},
429: {"description": "Rate limit exceeded for jail deactivate operations"},
502: {"description": "fail2ban unreachable"},
},
)
async def deactivate_jail(
_auth: AuthDep,
@@ -536,6 +600,15 @@ async def deactivate_jail(
"/{name}/local",
status_code=status.HTTP_204_NO_CONTENT,
summary="Delete the jail.d override file for an inactive jail",
responses={
204: {"description": "Override file deleted successfully"},
400: {"description": "Invalid jail name"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found in config files"},
409: {"description": "Jail currently active"},
500: {"description": "File cannot be deleted"},
502: {"description": "fail2ban unreachable"},
},
)
async def delete_jail_local_override(
request: Request,
@@ -578,6 +651,12 @@ async def delete_jail_local_override(
"/{name}/validate",
response_model=JailValidationResult,
summary="Validate jail configuration before activation",
responses={
200: {"description": "Validation result", "model": JailValidationResult},
400: {"description": "Invalid jail name"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found in config files"},
},
)
async def validate_jail(
request: Request,
@@ -609,6 +688,12 @@ async def validate_jail(
"/{name}/rollback",
response_model=RollbackResponse,
summary="Disable a bad jail config and restart fail2ban",
responses={
200: {"description": "Rollback completed", "model": RollbackResponse},
400: {"description": "Invalid jail name"},
401: {"description": "Session missing, expired, or invalid"},
500: {"description": "Failed to write .local override file"},
},
)
async def rollback_jail(
_auth: AuthDep,
@@ -654,6 +739,14 @@ async def rollback_jail(
status_code=status.HTTP_204_NO_CONTENT,
summary="Assign a filter to a jail",
dependencies=[Depends(_check_jail_create_rate_limit)],
responses={
204: {"description": "Filter assigned successfully"},
400: {"description": "Invalid jail name or filter name"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail or filter not found"},
429: {"description": "Rate limit exceeded for jail create operations"},
500: {"description": "Failed to write .local override file"},
},
)
async def assign_filter_to_jail(
request: Request,
@@ -689,6 +782,14 @@ async def assign_filter_to_jail(
status_code=status.HTTP_204_NO_CONTENT,
summary="Add an action to a jail",
dependencies=[Depends(_check_jail_create_rate_limit)],
responses={
204: {"description": "Action added successfully"},
400: {"description": "Invalid jail name or action name"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail or action not found"},
429: {"description": "Rate limit exceeded for jail create operations"},
500: {"description": "Failed to write .local override file"},
},
)
async def assign_action_to_jail(
request: Request,
@@ -725,6 +826,14 @@ async def assign_action_to_jail(
status_code=status.HTTP_204_NO_CONTENT,
summary="Remove an action from a jail",
dependencies=[Depends(_check_jail_delete_rate_limit)],
responses={
204: {"description": "Action removed successfully"},
400: {"description": "Invalid jail name or action name"},
401: {"description": "Session missing, expired, or invalid"},
404: {"description": "Jail not found in config files"},
429: {"description": "Rate limit exceeded for jail delete operations"},
500: {"description": "Failed to write .local override file"},
},
)
async def remove_action_from_jail(
request: Request,