add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
<?php
|
|
|
|
|
|
refactor(audit): extract audit domain into self-contained module
Move the entire audit subsystem (system audit, API audit, import audit,
user lifecycle audit, frontend telemetry) from core into modules/audit/.
Core decoupling via interface-based injection:
- AuditRecorderInterface replaces SystemAuditService in 10+ core services
- UserLifecycleAuditInterface / ImportAuditInterface for specialized flows
- NullAuditRecorder fallback when audit module is disabled
- ApiBootstrap/ApiResponse use null-safe callable resolvers
Module structure (modules/audit/):
- Manifest with routes, permissions, scheduler jobs, authorization policy
- 9 services, 8 repositories, 6 domain enums, 4 job handlers
- 33 page files, 4 JS files, 8 test files, migration scripts, i18n
Core cleanup:
- OperationsAuthorizationPolicy, UiCapabilityMap, PermissionService
surgically cleaned of audit-specific constants
- Sidebar template cleared of hardcoded audit navigation
- AuditModuleIsolationContractTest ensures no future core→module coupling
All quality gates pass: 1346 tests (19,276 assertions), PHPStan level 5
clean, architecture contracts verified.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 21:12:49 +01:00
|
|
|
namespace MintyPHP\Module\Audit\Repository;
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
|
|
|
|
|
use MintyPHP\DB;
|
|
|
|
|
use MintyPHP\Repository\Support\RepoQuery;
|
2026-03-25 18:46:49 +01:00
|
|
|
use MintyPHP\Repository\Support\RepositoryArrayHelper;
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
|
2026-03-13 21:58:51 +01:00
|
|
|
/** Records API request/response audit entries with status codes, timing, and error details. */
|
2026-03-26 12:33:59 +01:00
|
|
|
class ApiAuditLogRepository
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
{
|
2026-03-04 15:56:58 +01:00
|
|
|
private const FILTER_OPTIONS_LIMIT_MAX = 200;
|
|
|
|
|
|
2026-02-23 12:58:19 +01:00
|
|
|
public function create(array $data): int|false
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
{
|
|
|
|
|
$id = DB::insert(
|
|
|
|
|
'insert into api_audit_log (
|
|
|
|
|
request_id, method, path, query_json, status_code, duration_ms, error_code,
|
|
|
|
|
user_id, tenant_id, api_token_id, token_tenant_id, ip, user_agent, created_at
|
|
|
|
|
) values (?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())',
|
|
|
|
|
(string) ($data['request_id'] ?? ''),
|
|
|
|
|
(string) ($data['method'] ?? ''),
|
|
|
|
|
(string) ($data['path'] ?? ''),
|
|
|
|
|
$data['query_json'] ?? null,
|
|
|
|
|
(string) ((int) ($data['status_code'] ?? 0)),
|
|
|
|
|
$data['duration_ms'] !== null ? (string) ((int) $data['duration_ms']) : null,
|
|
|
|
|
$data['error_code'] ?? null,
|
|
|
|
|
$data['user_id'] !== null ? (string) ((int) $data['user_id']) : null,
|
|
|
|
|
$data['tenant_id'] !== null ? (string) ((int) $data['tenant_id']) : null,
|
|
|
|
|
$data['api_token_id'] !== null ? (string) ((int) $data['api_token_id']) : null,
|
|
|
|
|
$data['token_tenant_id'] !== null ? (string) ((int) $data['token_tenant_id']) : null,
|
|
|
|
|
$data['ip'] ?? null,
|
|
|
|
|
$data['user_agent'] ?? null
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return $id ? (int) $id : false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-23 12:58:19 +01:00
|
|
|
public function listPaged(array $filters): array
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
{
|
|
|
|
|
$search = trim((string) ($filters['search'] ?? ''));
|
|
|
|
|
$status = strtolower(trim((string) ($filters['status'] ?? '')));
|
|
|
|
|
$method = strtoupper(trim((string) ($filters['method'] ?? '')));
|
|
|
|
|
$createdFrom = trim((string) ($filters['created_from'] ?? ''));
|
|
|
|
|
$createdTo = trim((string) ($filters['created_to'] ?? ''));
|
2026-03-04 15:56:58 +01:00
|
|
|
$tenantIds = array_slice(
|
|
|
|
|
RepoQuery::normalizeIdList($filters['tenant_ids'] ?? ''),
|
|
|
|
|
0,
|
|
|
|
|
self::FILTER_OPTIONS_LIMIT_MAX
|
|
|
|
|
);
|
|
|
|
|
$userIds = array_slice(
|
|
|
|
|
RepoQuery::normalizeIdList($filters['user_ids'] ?? ''),
|
|
|
|
|
0,
|
|
|
|
|
self::FILTER_OPTIONS_LIMIT_MAX
|
|
|
|
|
);
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
|
|
|
|
|
[$limit, $offset] = RepoQuery::sanitizeLimitOffset($filters, 20, 1, 200, 0);
|
|
|
|
|
[$order, $dir] = RepoQuery::sanitizeOrder(
|
|
|
|
|
$filters,
|
|
|
|
|
['id', 'created_at', 'status_code', 'duration_ms', 'method', 'path'],
|
|
|
|
|
'created_at',
|
|
|
|
|
'desc'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$where = [];
|
|
|
|
|
$params = [];
|
|
|
|
|
|
|
|
|
|
RepoQuery::addLikeFilter(
|
|
|
|
|
$where,
|
|
|
|
|
$params,
|
2026-04-05 23:20:55 +02:00
|
|
|
[
|
|
|
|
|
'api_audit_log.path',
|
|
|
|
|
'api_audit_log.request_id',
|
|
|
|
|
'api_audit_log.error_code',
|
|
|
|
|
'api_audit_log.ip',
|
|
|
|
|
'api_audit_log.user_agent',
|
|
|
|
|
],
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
$search
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (in_array($status, ['2xx', '4xx', '5xx'], true)) {
|
|
|
|
|
$rangeStart = (int) $status[0] * 100;
|
|
|
|
|
$where[] = 'api_audit_log.status_code between ? and ?';
|
|
|
|
|
$params[] = (string) $rangeStart;
|
|
|
|
|
$params[] = (string) ($rangeStart + 99);
|
|
|
|
|
} elseif ($status !== '' && ctype_digit($status)) {
|
|
|
|
|
$statusCode = (int) $status;
|
|
|
|
|
if ($statusCode >= 100 && $statusCode <= 599) {
|
|
|
|
|
$where[] = 'api_audit_log.status_code = ?';
|
|
|
|
|
$params[] = (string) $statusCode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (in_array($method, ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'], true)) {
|
|
|
|
|
$where[] = 'api_audit_log.method = ?';
|
|
|
|
|
$params[] = $method;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $createdFrom)) {
|
|
|
|
|
$where[] = 'api_audit_log.created_at >= ?';
|
|
|
|
|
$params[] = $createdFrom . ' 00:00:00';
|
|
|
|
|
}
|
|
|
|
|
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $createdTo)) {
|
|
|
|
|
$where[] = 'api_audit_log.created_at <= ?';
|
|
|
|
|
$params[] = $createdTo . ' 23:59:59';
|
|
|
|
|
}
|
2026-03-04 15:56:58 +01:00
|
|
|
if ($tenantIds !== []) {
|
|
|
|
|
$where[] = 'api_audit_log.tenant_id in (???)';
|
|
|
|
|
$params[] = $tenantIds;
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
}
|
2026-03-04 15:56:58 +01:00
|
|
|
if ($userIds !== []) {
|
|
|
|
|
$where[] = 'api_audit_log.user_id in (???)';
|
|
|
|
|
$params[] = $userIds;
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
|
|
|
|
|
$fromSql = ' from api_audit_log ' .
|
|
|
|
|
'left join users on users.id = api_audit_log.user_id ' .
|
|
|
|
|
'left join tenants on tenants.id = api_audit_log.tenant_id ';
|
|
|
|
|
|
|
|
|
|
$total = (int) (DB::selectValue('select count(*)' . $fromSql . $whereSql, ...$params) ?? 0);
|
|
|
|
|
|
|
|
|
|
$rows = DB::select(
|
|
|
|
|
'select
|
|
|
|
|
api_audit_log.id,
|
|
|
|
|
api_audit_log.request_id,
|
|
|
|
|
api_audit_log.method,
|
|
|
|
|
api_audit_log.path,
|
|
|
|
|
api_audit_log.query_json,
|
|
|
|
|
api_audit_log.status_code,
|
|
|
|
|
api_audit_log.duration_ms,
|
|
|
|
|
api_audit_log.error_code,
|
|
|
|
|
api_audit_log.user_id,
|
|
|
|
|
api_audit_log.tenant_id,
|
|
|
|
|
api_audit_log.api_token_id,
|
|
|
|
|
api_audit_log.token_tenant_id,
|
|
|
|
|
api_audit_log.ip,
|
|
|
|
|
api_audit_log.user_agent,
|
|
|
|
|
api_audit_log.created_at,
|
|
|
|
|
users.id,
|
|
|
|
|
users.uuid,
|
|
|
|
|
users.display_name,
|
|
|
|
|
users.email,
|
|
|
|
|
tenants.id,
|
|
|
|
|
tenants.uuid,
|
|
|
|
|
tenants.description
|
|
|
|
|
' . $fromSql . $whereSql .
|
|
|
|
|
sprintf(' order by api_audit_log.`%s` %s limit ? offset ?', $order, $dir),
|
|
|
|
|
...array_merge($params, [(string) $limit, (string) $offset])
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$normalized = [];
|
|
|
|
|
if (is_array($rows)) {
|
|
|
|
|
foreach ($rows as $row) {
|
2026-02-23 12:58:19 +01:00
|
|
|
$item = $this->normalizeRow($row);
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
if ($item !== null) {
|
|
|
|
|
$normalized[] = $item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'total' => $total,
|
|
|
|
|
'rows' => $normalized,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 15:56:58 +01:00
|
|
|
public function listFilterOptions(int $limit = self::FILTER_OPTIONS_LIMIT_MAX): array
|
|
|
|
|
{
|
|
|
|
|
$limit = max(1, min(self::FILTER_OPTIONS_LIMIT_MAX, $limit));
|
|
|
|
|
|
|
|
|
|
$methodRows = DB::select(
|
|
|
|
|
'select
|
|
|
|
|
api_audit_log.method,
|
|
|
|
|
max(api_audit_log.created_at) as last_used_at
|
|
|
|
|
from api_audit_log
|
|
|
|
|
where api_audit_log.method is not null
|
|
|
|
|
and api_audit_log.method <> \'\'
|
|
|
|
|
group by api_audit_log.method
|
|
|
|
|
order by last_used_at desc
|
|
|
|
|
limit ?',
|
|
|
|
|
(string) $limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$methods = [];
|
|
|
|
|
if (is_array($methodRows)) {
|
|
|
|
|
foreach ($methodRows as $row) {
|
2026-03-25 18:46:49 +01:00
|
|
|
$flat = RepositoryArrayHelper::flattenRow($row);
|
2026-03-04 15:56:58 +01:00
|
|
|
$method = strtoupper(trim((string) ($flat['method'] ?? '')));
|
|
|
|
|
if ($method === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$methods[] = $method;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$methods = array_values(array_unique($methods));
|
|
|
|
|
|
|
|
|
|
$userRows = DB::select(
|
|
|
|
|
'select
|
|
|
|
|
api_audit_log.user_id,
|
|
|
|
|
max(api_audit_log.created_at) as last_used_at,
|
|
|
|
|
max(users.id) as user_exists_id,
|
|
|
|
|
max(users.display_name) as user_display_name,
|
|
|
|
|
max(users.email) as user_email
|
|
|
|
|
from api_audit_log
|
|
|
|
|
left join users on users.id = api_audit_log.user_id
|
|
|
|
|
where api_audit_log.user_id is not null
|
|
|
|
|
and api_audit_log.user_id > 0
|
|
|
|
|
group by api_audit_log.user_id
|
|
|
|
|
order by last_used_at desc
|
|
|
|
|
limit ?',
|
|
|
|
|
(string) $limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$users = [];
|
|
|
|
|
if (is_array($userRows)) {
|
|
|
|
|
foreach ($userRows as $row) {
|
2026-03-25 18:46:49 +01:00
|
|
|
$flat = RepositoryArrayHelper::flattenRow($row);
|
2026-03-04 15:56:58 +01:00
|
|
|
$id = (int) ($flat['user_id'] ?? 0);
|
|
|
|
|
if ($id <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$users[] = [
|
|
|
|
|
'id' => $id,
|
|
|
|
|
'display_name' => trim((string) ($flat['user_display_name'] ?? '')),
|
|
|
|
|
'email' => trim((string) ($flat['user_email'] ?? '')),
|
|
|
|
|
'exists' => (int) ($flat['user_exists_id'] ?? 0) > 0,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tenantRows = DB::select(
|
|
|
|
|
'select
|
|
|
|
|
api_audit_log.tenant_id,
|
|
|
|
|
max(api_audit_log.created_at) as last_used_at,
|
|
|
|
|
max(tenants.id) as tenant_exists_id,
|
|
|
|
|
max(tenants.description) as tenant_description
|
|
|
|
|
from api_audit_log
|
|
|
|
|
left join tenants on tenants.id = api_audit_log.tenant_id
|
|
|
|
|
where api_audit_log.tenant_id is not null
|
|
|
|
|
and api_audit_log.tenant_id > 0
|
|
|
|
|
group by api_audit_log.tenant_id
|
|
|
|
|
order by last_used_at desc
|
|
|
|
|
limit ?',
|
|
|
|
|
(string) $limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$tenants = [];
|
|
|
|
|
if (is_array($tenantRows)) {
|
|
|
|
|
foreach ($tenantRows as $row) {
|
2026-03-25 18:46:49 +01:00
|
|
|
$flat = RepositoryArrayHelper::flattenRow($row);
|
2026-03-04 15:56:58 +01:00
|
|
|
$id = (int) ($flat['tenant_id'] ?? 0);
|
|
|
|
|
if ($id <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$tenants[] = [
|
|
|
|
|
'id' => $id,
|
|
|
|
|
'description' => trim((string) ($flat['tenant_description'] ?? '')),
|
|
|
|
|
'exists' => (int) ($flat['tenant_exists_id'] ?? 0) > 0,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'methods' => $methods,
|
|
|
|
|
'users' => $users,
|
|
|
|
|
'tenants' => $tenants,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-23 12:58:19 +01:00
|
|
|
public function find(int $id): ?array
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
{
|
|
|
|
|
$row = DB::selectOne(
|
|
|
|
|
'select
|
|
|
|
|
api_audit_log.id,
|
|
|
|
|
api_audit_log.request_id,
|
|
|
|
|
api_audit_log.method,
|
|
|
|
|
api_audit_log.path,
|
|
|
|
|
api_audit_log.query_json,
|
|
|
|
|
api_audit_log.status_code,
|
|
|
|
|
api_audit_log.duration_ms,
|
|
|
|
|
api_audit_log.error_code,
|
|
|
|
|
api_audit_log.user_id,
|
|
|
|
|
api_audit_log.tenant_id,
|
|
|
|
|
api_audit_log.api_token_id,
|
|
|
|
|
api_audit_log.token_tenant_id,
|
|
|
|
|
api_audit_log.ip,
|
|
|
|
|
api_audit_log.user_agent,
|
|
|
|
|
api_audit_log.created_at,
|
|
|
|
|
users.id,
|
|
|
|
|
users.uuid,
|
|
|
|
|
users.display_name,
|
|
|
|
|
users.email,
|
|
|
|
|
tenants.id,
|
|
|
|
|
tenants.uuid,
|
|
|
|
|
tenants.description
|
|
|
|
|
from api_audit_log
|
|
|
|
|
left join users on users.id = api_audit_log.user_id
|
|
|
|
|
left join tenants on tenants.id = api_audit_log.tenant_id
|
|
|
|
|
where api_audit_log.id = ?
|
|
|
|
|
limit 1',
|
|
|
|
|
(string) $id
|
|
|
|
|
);
|
|
|
|
|
|
2026-02-23 12:58:19 +01:00
|
|
|
return $this->normalizeRow($row);
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-23 12:58:19 +01:00
|
|
|
public function purgeOlderThanDays(int $days): int
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
{
|
|
|
|
|
if ($days <= 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
$cutoff = (new \DateTimeImmutable('now', new \DateTimeZone('UTC')))
|
|
|
|
|
->modify('-' . $days . ' days')
|
|
|
|
|
->format('Y-m-d H:i:s');
|
|
|
|
|
|
|
|
|
|
$deleted = DB::delete('delete from api_audit_log where created_at < ?', $cutoff);
|
|
|
|
|
return is_int($deleted) ? $deleted : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-23 12:58:19 +01:00
|
|
|
private function normalizeRow(mixed $row): ?array
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
{
|
|
|
|
|
if (!is_array($row)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$log = $row['api_audit_log'] ?? [];
|
|
|
|
|
if (!is_array($log) || !isset($log['id'])) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = is_array($row['users'] ?? null) ? $row['users'] : [];
|
|
|
|
|
$tenant = is_array($row['tenants'] ?? null) ? $row['tenants'] : [];
|
|
|
|
|
|
|
|
|
|
$log['user_uuid'] = (string) ($user['uuid'] ?? '');
|
|
|
|
|
$log['user_display_name'] = (string) ($user['display_name'] ?? '');
|
|
|
|
|
$log['user_email'] = (string) ($user['email'] ?? '');
|
|
|
|
|
$log['tenant_uuid'] = (string) ($tenant['uuid'] ?? '');
|
|
|
|
|
$log['tenant_description'] = (string) ($tenant['description'] ?? '');
|
2026-04-05 23:20:55 +02:00
|
|
|
$log['ip_hash'] = trim((string) ($log['ip'] ?? ''));
|
|
|
|
|
$log['user_agent_hash'] = trim((string) ($log['user_agent'] ?? ''));
|
|
|
|
|
$log['query_keys'] = $this->extractQueryKeys($log['query_json'] ?? null);
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
|
|
|
|
|
return $log;
|
|
|
|
|
}
|
2026-03-04 15:56:58 +01:00
|
|
|
|
2026-04-05 23:20:55 +02:00
|
|
|
/**
|
|
|
|
|
* @return list<string>
|
|
|
|
|
*/
|
|
|
|
|
private function extractQueryKeys(mixed $queryJson): array
|
|
|
|
|
{
|
|
|
|
|
if (!is_string($queryJson) || trim($queryJson) === '') {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$decoded = json_decode($queryJson, true);
|
|
|
|
|
if (!is_array($decoded)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$keysRaw = $decoded['keys'] ?? null;
|
|
|
|
|
if (!is_array($keysRaw)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$keys = [];
|
|
|
|
|
foreach ($keysRaw as $value) {
|
|
|
|
|
$key = trim((string) $value);
|
|
|
|
|
if ($key === '' || in_array($key, $keys, true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$keys[] = $key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $keys;
|
|
|
|
|
}
|
add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00
|
|
|
}
|