fix(audit): stop duplicate and empty audit rows

- Set env var version and compose parsing version while creating, so a create writes one row
- Record no rows for the auto-created preview copy of an env var
- Record env and application API updates only with the fields that really changed
- Store no change data for new env vars, and null for empty change data
- Store metadata team_id as an integer
- Drop the unused level and resource indexes of audit_events

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-11-05 23:49:17 +01:00
co-authored by Claude Opus 5.5
parent 13298591d8
commit b7d0e6be2a
10 changed files with 111 additions and 39 deletions
+1 -2
View File
@@ -329,6 +329,7 @@ class Application extends BaseModel
$application->manual_webhook_secret_gitlab ??= Str::random(40);
$application->manual_webhook_secret_bitbucket ??= Str::random(40);
$application->manual_webhook_secret_gitea ??= Str::random(40);
$application->compose_parsing_version = self::$parserVersion;
});
static::addGlobalScope('withRelations', function ($builder) {
$builder->withCount([
@@ -426,8 +427,6 @@ class Application extends BaseModel
ApplicationSetting::create([
'application_id' => $application->id,
]);
$application->compose_parsing_version = self::$parserVersion;
$application->save();
// Add default NIXPACKS_NODE_VERSION environment variable for Nixpacks applications
if ($application->build_pack === 'nixpacks') {
+6 -1
View File
@@ -123,7 +123,7 @@ class AuditEvent extends Model
*/
public static function recordModelMutation(string $event, array $context, array $changes): void
{
self::recordWithChanges($event, $context, $changes);
self::recordWithChanges($event, $context, $changes ?: null);
}
/**
@@ -161,6 +161,11 @@ class AuditEvent extends Model
*/
private static function attributesFor(string $event, array $context, ?array $changes, string $level): array
{
// API tokens store team_id as a string. Keep it an integer, so JSON filters on metadata work.
if (is_string($context['team_id'] ?? null) && ctype_digit($context['team_id'])) {
$context['team_id'] = (int) $context['team_id'];
}
$teamId = data_get(auth()->user()?->currentAccessToken(), 'team_id')
?? (array_key_exists('team_id', $context)
? $context['team_id']
+9 -4
View File
@@ -48,6 +48,9 @@ class EnvironmentVariable extends BaseModel
{
use Auditable;
/** The key is in the metadata of the row, and the flags of a new variable have little audit value. */
protected bool $auditCreatedChanges = false;
/**
* Flags that a "# coolify:" line of the developer view can set, with their defaults.
*
@@ -140,6 +143,9 @@ class EnvironmentVariable extends BaseModel
protected static function booted()
{
static::creating(function (ModelsEnvironmentVariable $environment_variable) {
$environment_variable->version = config('constants.coolify.version');
});
static::created(function (ModelsEnvironmentVariable $environment_variable) {
if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) {
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)
@@ -152,7 +158,8 @@ class EnvironmentVariable extends BaseModel
$application = Application::find($environment_variable->resourceable_id);
// A compose preview generates its own magic secrets on the first pull request parse.
if ($application && ! ($application->build_pack === 'dockercompose' && $environment_variable->isGeneratedMagicVariable())) {
ModelsEnvironmentVariable::create([
// The preview copy is part of the same change, so it gets no audit rows of its own.
$preview = new ModelsEnvironmentVariable([
'key' => $environment_variable->key,
'value' => $environment_variable->value,
'is_multiline' => $environment_variable->is_multiline ?? false,
@@ -166,12 +173,10 @@ class EnvironmentVariable extends BaseModel
'resourceable_id' => $environment_variable->resourceable_id,
'is_preview' => true,
]);
$preview->withoutAuditLogging(fn () => $preview->save());
}
}
}
$environment_variable->update([
'version' => config('constants.coolify.version'),
]);
});
static::saving(function (ModelsEnvironmentVariable $environmentVariable) {
-3
View File
@@ -100,10 +100,7 @@ class Service extends BaseModel
if (blank($service->name)) {
$service->name = 'service-'.new_public_id();
}
});
static::created(function ($service) {
$service->compose_parsing_version = self::$parserVersion;
$service->save();
});
static::saving(function ($service) {
if ($service->isDirty('docker_compose_raw')) {