mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
docs(api): document audit events and fix the private key update path
- Add the OpenAPI attribute for GET /audit-events
- Document PATCH /security/keys/{uuid} with its uuid parameter instead
of /security/keys
This commit is contained in:
@@ -8,9 +8,80 @@ use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
class AuditEventsController extends Controller
|
||||
{
|
||||
#[OA\Get(
|
||||
summary: 'List',
|
||||
description: 'List audit events of the team. Only team admins and owners can view audit events. Actor e-mail, token, metadata, changes, IP address, and user agent are returned only for tokens with the read:sensitive or root permission.',
|
||||
path: '/audit-events',
|
||||
operationId: 'list-audit-events',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Audit Events'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'per_page', in: 'query', description: 'Items per page.', required: false, schema: new OA\Schema(type: 'integer', default: 25, minimum: 1, maximum: 100)),
|
||||
new OA\Parameter(name: 'page', in: 'query', description: 'Page number.', required: false, schema: new OA\Schema(type: 'integer', default: 1, minimum: 1)),
|
||||
new OA\Parameter(name: 'search', in: 'query', description: 'Search text.', required: false, schema: new OA\Schema(type: 'string', maxLength: 255)),
|
||||
new OA\Parameter(name: 'action', in: 'query', description: 'Filter by action. Use all for every action.', required: false, schema: new OA\Schema(type: 'string', maxLength: 255)),
|
||||
new OA\Parameter(name: 'source', in: 'query', description: 'Filter by source.', required: false, schema: new OA\Schema(type: 'string', enum: ['all', 'ui', 'api', 'mcp', 'webhook', 'system', 'scheduler'])),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'A page of audit events, newest first.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'current_page' => ['type' => 'integer'],
|
||||
'data' => [
|
||||
'type' => 'array',
|
||||
'items' => [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
'team_id' => ['type' => 'integer', 'nullable' => true],
|
||||
'event' => ['type' => 'string'],
|
||||
'source' => ['type' => 'string'],
|
||||
'action' => ['type' => 'string'],
|
||||
'level' => ['type' => 'string'],
|
||||
'actor_type' => ['type' => 'string', 'nullable' => true],
|
||||
'actor_id' => ['type' => 'integer', 'nullable' => true],
|
||||
'actor_name' => ['type' => 'string', 'nullable' => true],
|
||||
'resource_type' => ['type' => 'string', 'nullable' => true],
|
||||
'resource_uuid' => ['type' => 'string', 'nullable' => true],
|
||||
'resource_name' => ['type' => 'string', 'nullable' => true],
|
||||
'description' => ['type' => 'string', 'nullable' => true],
|
||||
'created_at' => ['type' => 'string', 'format' => 'date-time'],
|
||||
],
|
||||
],
|
||||
],
|
||||
'per_page' => ['type' => 'integer'],
|
||||
'last_page' => ['type' => 'integer'],
|
||||
'total' => ['type' => 'integer'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 403,
|
||||
description: 'Only team admins and owners can view audit logs.',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 422,
|
||||
ref: '#/components/responses/422',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$teamId = getTeamIdFromToken();
|
||||
|
||||
@@ -253,12 +253,15 @@ class SecurityController extends Controller
|
||||
#[OA\Patch(
|
||||
summary: 'Update',
|
||||
description: 'Update a private key.',
|
||||
path: '/security/keys',
|
||||
path: '/security/keys/{uuid}',
|
||||
operationId: 'update-private-key',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Private Keys'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key UUID', schema: new OA\Schema(type: 'string')),
|
||||
],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: [
|
||||
@@ -300,6 +303,10 @@ class SecurityController extends Controller
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
description: 'Private Key not found.',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 422,
|
||||
ref: '#/components/responses/422',
|
||||
|
||||
+254
-64
@@ -5732,6 +5732,178 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/audit-events": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Audit Events"
|
||||
],
|
||||
"summary": "List",
|
||||
"description": "List audit events of the team. Only team admins and owners can view audit events. Actor e-mail, token, metadata, changes, IP address, and user agent are returned only for tokens with the read:sensitive or root permission.",
|
||||
"operationId": "list-audit-events",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "per_page",
|
||||
"in": "query",
|
||||
"description": "Items per page.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 25,
|
||||
"maximum": 100,
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"description": "Page number.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"in": "query",
|
||||
"description": "Search text.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "action",
|
||||
"in": "query",
|
||||
"description": "Filter by action. Use all for every action.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"in": "query",
|
||||
"description": "Filter by source.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"all",
|
||||
"ui",
|
||||
"api",
|
||||
"mcp",
|
||||
"webhook",
|
||||
"system",
|
||||
"scheduler"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A page of audit events, newest first.",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"current_page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"team_id": {
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
},
|
||||
"event": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"type": "string"
|
||||
},
|
||||
"actor_type": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"actor_id": {
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
},
|
||||
"actor_name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"resource_type": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"resource_uuid": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"resource_name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last_page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#\/components\/responses\/401"
|
||||
},
|
||||
"403": {
|
||||
"description": "Only team admins and owners can view audit logs."
|
||||
},
|
||||
"422": {
|
||||
"$ref": "#\/components\/responses\/422"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/cloud-init-scripts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -15231,70 +15403,6 @@
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"Private Keys"
|
||||
],
|
||||
"summary": "Update",
|
||||
"description": "Update a private key.",
|
||||
"operationId": "update-private-key",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"required": [
|
||||
"private_key"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"private_key": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "The updated private key's UUID.",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#\/components\/responses\/401"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#\/components\/responses\/400"
|
||||
},
|
||||
"422": {
|
||||
"$ref": "#\/components\/responses\/422"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/security\/keys\/{uuid}": {
|
||||
@@ -15409,6 +15517,84 @@
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"Private Keys"
|
||||
],
|
||||
"summary": "Update",
|
||||
"description": "Update a private key.",
|
||||
"operationId": "update-private-key",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "uuid",
|
||||
"in": "path",
|
||||
"description": "Private Key UUID",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"required": [
|
||||
"private_key"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"private_key": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "The updated private key's UUID.",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#\/components\/responses\/401"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#\/components\/responses\/400"
|
||||
},
|
||||
"404": {
|
||||
"description": "Private Key not found."
|
||||
},
|
||||
"422": {
|
||||
"$ref": "#\/components\/responses\/422"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/servers\/{uuid}\/cloudflare-tunnel": {
|
||||
@@ -24606,6 +24792,10 @@
|
||||
"name": "Secret Managers",
|
||||
"description": "Secret Managers"
|
||||
},
|
||||
{
|
||||
"name": "Audit Events",
|
||||
"description": "Audit Events"
|
||||
},
|
||||
{
|
||||
"name": "Cloud-init Scripts",
|
||||
"description": "Cloud-init Scripts"
|
||||
|
||||
+133
-40
@@ -3707,6 +3707,86 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
/audit-events:
|
||||
get:
|
||||
tags:
|
||||
- 'Audit Events'
|
||||
summary: List
|
||||
description: 'List audit events of the team. Only team admins and owners can view audit events. Actor e-mail, token, metadata, changes, IP address, and user agent are returned only for tokens with the read:sensitive or root permission.'
|
||||
operationId: list-audit-events
|
||||
parameters:
|
||||
-
|
||||
name: per_page
|
||||
in: query
|
||||
description: 'Items per page.'
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 25
|
||||
maximum: 100
|
||||
minimum: 1
|
||||
-
|
||||
name: page
|
||||
in: query
|
||||
description: 'Page number.'
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
minimum: 1
|
||||
-
|
||||
name: search
|
||||
in: query
|
||||
description: 'Search text.'
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
maxLength: 255
|
||||
-
|
||||
name: action
|
||||
in: query
|
||||
description: 'Filter by action. Use all for every action.'
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
maxLength: 255
|
||||
-
|
||||
name: source
|
||||
in: query
|
||||
description: 'Filter by source.'
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- ui
|
||||
- api
|
||||
- mcp
|
||||
- webhook
|
||||
- system
|
||||
- scheduler
|
||||
responses:
|
||||
'200':
|
||||
description: 'A page of audit events, newest first.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
current_page: { type: integer }
|
||||
data: { type: array, items: { type: object, properties: { id: { type: integer }, team_id: { type: integer, nullable: true }, event: { type: string }, source: { type: string }, action: { type: string }, level: { type: string }, actor_type: { type: string, nullable: true }, actor_id: { type: integer, nullable: true }, actor_name: { type: string, nullable: true }, resource_type: { type: string, nullable: true }, resource_uuid: { type: string, nullable: true }, resource_name: { type: string, nullable: true }, description: { type: string, nullable: true }, created_at: { type: string, format: date-time } } } }
|
||||
per_page: { type: integer }
|
||||
last_page: { type: integer }
|
||||
total: { type: integer }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'403':
|
||||
description: 'Only team admins and owners can view audit logs.'
|
||||
'422':
|
||||
$ref: '#/components/responses/422'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
/cloud-init-scripts:
|
||||
get:
|
||||
tags:
|
||||
@@ -9763,46 +9843,6 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
patch:
|
||||
tags:
|
||||
- 'Private Keys'
|
||||
summary: Update
|
||||
description: 'Update a private key.'
|
||||
operationId: update-private-key
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- private_key
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
private_key:
|
||||
type: string
|
||||
type: object
|
||||
additionalProperties: false
|
||||
responses:
|
||||
'201':
|
||||
description: "The updated private key's UUID."
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
uuid: { type: string }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'422':
|
||||
$ref: '#/components/responses/422'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/security/keys/{uuid}':
|
||||
get:
|
||||
tags:
|
||||
@@ -9874,6 +9914,56 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
patch:
|
||||
tags:
|
||||
- 'Private Keys'
|
||||
summary: Update
|
||||
description: 'Update a private key.'
|
||||
operationId: update-private-key
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'Private Key UUID'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- private_key
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
private_key:
|
||||
type: string
|
||||
type: object
|
||||
additionalProperties: false
|
||||
responses:
|
||||
'201':
|
||||
description: "The updated private key's UUID."
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
uuid: { type: string }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
description: 'Private Key not found.'
|
||||
'422':
|
||||
$ref: '#/components/responses/422'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/servers/{uuid}/cloudflare-tunnel':
|
||||
get:
|
||||
tags:
|
||||
@@ -15779,6 +15869,9 @@ tags:
|
||||
-
|
||||
name: 'Secret Managers'
|
||||
description: 'Secret Managers'
|
||||
-
|
||||
name: 'Audit Events'
|
||||
description: 'Audit Events'
|
||||
-
|
||||
name: 'Cloud-init Scripts'
|
||||
description: 'Cloud-init Scripts'
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
function auditAndPrivateKeyOpenApiDocument(): array
|
||||
{
|
||||
return json_decode((string) file_get_contents(__DIR__.'/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
test('documents the audit events endpoint', function () {
|
||||
$operation = auditAndPrivateKeyOpenApiDocument()['paths']['/audit-events']['get'] ?? null;
|
||||
|
||||
expect($operation)->not->toBeNull()
|
||||
->and($operation['operationId'])->toBe('list-audit-events')
|
||||
->and($operation['security'])->toBe([['bearerAuth' => []]])
|
||||
->and(collect($operation['parameters'])->pluck('name')->all())->toBe(['per_page', 'page', 'search', 'action', 'source'])
|
||||
->and($operation['responses'])->toHaveKeys(['200', '401', '403', '422']);
|
||||
});
|
||||
|
||||
test('documents the private key update on the uuid path', function () {
|
||||
$paths = auditAndPrivateKeyOpenApiDocument()['paths'];
|
||||
|
||||
expect($paths['/security/keys'])->not->toHaveKey('patch')
|
||||
->and($paths['/security/keys/{uuid}']['patch']['operationId'])->toBe('update-private-key')
|
||||
->and($paths['/security/keys/{uuid}']['patch']['parameters'][0])->toMatchArray(['name' => 'uuid', 'in' => 'path', 'required' => true]);
|
||||
});
|
||||
Reference in New Issue
Block a user