Files
portal/app/app/Models/ApiKey.php
T
Дмитрий a5e2bbbbe8 feat(api): api_keys model + GET/regenerate endpoints (closes J5 part 1)
Audit J5/D3: the api_keys table existed in schema but had zero code.
Adds the ApiKey model + factory, and ApiKeyController with GET
/api/api-keys (list active keys, key_hash hidden) and POST
/api/api-keys/regenerate (deactivate prior + create new, full key
returned once, bcrypt-hashed in DB). Tenant-scoped via auth:sanctum +
tenant middleware (RLS on api_keys). phpstan-baseline.neon updated for
Pest PendingCalls false-positives in the new test file; also removes
8 pre-existing stale ignore.unmatched entries (properties now resolved
by existing @mixin IdeHelper* docblocks — confirmed pre-existing via
git stash test before Task 3 changes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 21:53:35 +03:00

67 lines
1.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Models;
use Database\Factories\ApiKeyFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* API-ключ тенанта (таблица api_keys). Tenant-aware, RLS на уровне БД.
*
* key_hash — bcrypt-хэш; оригинал ключа показывается ОДИН раз при генерации
* (ApiKeyController::regenerate). key_prefix (10 символов) — для отображения
* в UI. Таблица имеет только created_at (без updated_at).
*
* @mixin IdeHelperApiKey
*/
class ApiKey extends Model
{
/** @use HasFactory<ApiKeyFactory> */
use HasFactory;
public $timestamps = false;
protected $fillable = [
'tenant_id',
'user_id',
'name',
'key_hash',
'key_prefix',
'scopes',
'last_used_at',
'last_used_ip',
'expires_at',
'is_active',
'created_at',
];
protected $hidden = ['key_hash'];
protected function casts(): array
{
return [
'scopes' => 'array',
'is_active' => 'boolean',
'last_used_at' => 'datetime',
'expires_at' => 'datetime',
'created_at' => 'datetime',
];
}
/** @return BelongsTo<Tenant, $this> */
public function tenant(): BelongsTo
{
return $this->belongsTo(Tenant::class);
}
/** @return BelongsTo<User, $this> */
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}