Files
Verde-Web/app/Models/OtpCode.php
admin dc297d4cd7 feat(backend): complete Module 1 auth (register/login/OTP/reset)
Closes Module 1: 9 auth endpoints under /api/v1/auth, OTP via SMS
(Semaphore + log + fake drivers), role middleware, role + admin seeders,
27 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:46:53 +08:00

63 lines
1.3 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class OtpCode extends Model
{
use HasFactory;
public const CHANNEL_SMS = 'sms';
public const CHANNEL_EMAIL = 'email';
public const PURPOSE_REGISTER = 'register';
public const PURPOSE_LOGIN = 'login';
public const PURPOSE_PHONE_VERIFY = 'phone_verify';
public const PURPOSE_PASSWORD_RESET = 'password_reset';
public const MAX_ATTEMPTS = 5;
protected $fillable = [
'user_id',
'channel',
'destination',
'purpose',
'code_hash',
'attempts',
'expires_at',
'consumed_at',
];
protected function casts(): array
{
return [
'expires_at' => 'datetime',
'consumed_at' => 'datetime',
'attempts' => 'integer',
];
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function isExpired(): bool
{
return $this->expires_at->isPast();
}
public function isConsumed(): bool
{
return $this->consumed_at !== null;
}
public function isExhausted(): bool
{
return $this->attempts >= self::MAX_ATTEMPTS;
}
}