feat: activity logging for name operations

NameActivity model with log() static method. Stores event, name,
properties (JSON), and hashed IP (GDPR). SubmitClaim action now
logs 'claimed' event. Ready for dns_updated, registered, etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-04 11:46:31 +01:00
parent 113b228fee
commit 14f4d1fdc0
No known key found for this signature in database
GPG key ID: AF404715446AEB41
3 changed files with 89 additions and 1 deletions

View file

@ -6,6 +6,7 @@ namespace Mod\Names\Actions;
use Illuminate\Validation\ValidationException;
use Mod\Chain\Services\DaemonRpc;
use Mod\Names\Models\NameActivity;
use Mod\Names\Models\NameClaim;
/**
@ -27,10 +28,17 @@ class SubmitClaim
$this->validate($name, $email);
return NameClaim::create([
$claim = NameClaim::create([
'name' => $name,
'email' => $email,
]);
NameActivity::log($name, 'claimed', [
'claim_id' => $claim->claim_id,
'email' => $email,
], request()?->ip());
return $claim;
}
private function validate(string $name, string $email): void

View file

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('name_activity', function (Blueprint $table) {
$table->id();
$table->string('name')->index();
$table->string('event', 50)->index();
$table->json('properties')->nullable();
$table->string('ip_hash', 64)->nullable();
$table->timestamp('created_at')->useCurrent();
});
}
public function down(): void
{
Schema::dropIfExists('name_activity');
}
};

View file

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Mod\Names\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Activity log for name operations.
*
* NameActivity::log('mybrand', 'registered', ['address' => '...']);
* NameActivity::log('mybrand', 'dns_updated', ['records' => [...]]);
* $recent = NameActivity::forName('mybrand')->latest()->get();
*/
class NameActivity extends Model
{
public $timestamps = false;
protected $table = 'name_activity';
protected $fillable = [
'name',
'event',
'properties',
'ip_hash',
'created_at',
];
protected $casts = [
'properties' => 'array',
'created_at' => 'datetime',
];
public function scopeForName($query, string $name)
{
return $query->where('name', $name);
}
/**
* Log a name activity event.
*
* NameActivity::log('mybrand', 'claimed', ['email' => 'me@example.com']);
*/
public static function log(string $name, string $event, array $properties = [], ?string $ip = null): self
{
return static::create([
'name' => $name,
'event' => $event,
'properties' => $properties,
'ip_hash' => $ip ? hash('sha256', $ip) : null,
'created_at' => now(),
]);
}
}