First commit

This commit is contained in:
dyzulk
2025-12-30 12:11:01 +07:00
commit f68f34980a
150 changed files with 22717 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Helpers\UuidHelper;
class ActivityLog extends Model
{
protected $primaryKey = 'id';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'id',
'user_id',
'action',
'description',
'ip_address',
'user_agent'
];
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->id)) {
$model->id = UuidHelper::generate();
}
});
}
public function user()
{
return $this->belongsTo(User::class);
}
}

55
app/Models/ApiKey.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class ApiKey extends Model
{
use HasFactory;
protected $primaryKey = 'id';
protected $keyType = 'string';
public $incrementing = false;
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->id)) {
$model->id = \App\Helpers\UuidHelper::generate();
}
});
}
protected $fillable = [
'user_id',
'name',
'key',
'last_used_at',
'is_active',
];
protected $casts = [
'last_used_at' => 'datetime',
'is_active' => 'boolean',
];
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Generate a unique API Key.
*/
public static function generate()
{
do {
$key = 'dvp_' . Str::random(56); // Prefix (4) + Random (56) = 60 chars
} while (static::where('key', $key)->exists());
return $key;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Helpers\UuidHelper;
class CaCertificate extends Model
{
protected $primaryKey = 'uuid';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'uuid',
'ca_type',
'cert_content',
'key_content',
'serial_number',
'common_name',
'organization',
'valid_from',
'valid_to',
'download_count',
'last_downloaded_at'
];
protected $casts = [
'valid_from' => 'datetime',
'valid_to' => 'datetime',
'last_downloaded_at' => 'datetime',
];
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->uuid)) {
$model->uuid = UuidHelper::generate();
}
});
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Helpers\UuidHelper;
class Certificate extends Model
{
protected $primaryKey = 'uuid';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'uuid', 'user_id', 'common_name', 'organization', 'locality',
'state', 'country', 'san', 'key_bits', 'serial_number',
'cert_content', 'key_content', 'csr_content',
'valid_from', 'valid_to', 'expired_notification_sent_at'
];
protected $casts = [
'valid_from' => 'datetime',
'valid_to' => 'datetime',
'expired_notification_sent_at' => 'datetime',
];
protected $hidden = [
'expired_notification_sent_at',
];
protected $appends = [
'ssl_status',
];
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->uuid)) {
$model->uuid = UuidHelper::generate();
}
});
}
public function user()
{
return $this->belongsTo(User::class);
}
public function getSslStatusAttribute()
{
if ($this->valid_to && $this->valid_to->isPast()) {
return 'EXPIRED';
}
return 'ACTIVE';
}
public function toArray()
{
$array = parent::toArray();
$ordered = [];
// Define the preferred order of keys
$paramOrder = [
'uuid', 'user_id', 'common_name', 'organization', 'locality',
'state', 'country', 'san', 'status', 'key_bits', 'serial_number',
'ssl_status', // <--- Inserted here (before content)
'cert_content', 'key_content', 'csr_content',
'valid_from', 'valid_to', 'created_at', 'updated_at'
];
// Reconstruct query based on paramOrder
foreach ($paramOrder as $key) {
if (array_key_exists($key, $array)) {
$ordered[$key] = $array[$key];
unset($array[$key]);
}
}
// Append any remaining keys (that weren't in our explicit list)
return array_merge($ordered, $array);
}
}

38
app/Models/Inquiry.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Inquiry extends Model
{
use HasFactory;
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'name',
'email',
'category',
'subject',
'message',
'status',
'replied_at',
];
protected $casts = [
'replied_at' => 'datetime',
];
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$model->{$model->getKeyName()} = \App\Helpers\UuidHelper::generate();
}
});
}
}

32
app/Models/LegalPage.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class LegalPage extends Model
{
use HasFactory, HasUuids;
protected $fillable = ['title', 'slug', 'is_active'];
public $incrementing = false;
protected $keyType = 'string';
public function revisions()
{
return $this->hasMany(LegalPageRevision::class);
}
public function latestRevision()
{
return $this->hasOne(LegalPageRevision::class)->latestOfMany('created_at');
}
public function currentRevision()
{
return $this->hasOne(LegalPageRevision::class)->where('is_active', true)->latest();
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class LegalPageRevision extends Model
{
use HasFactory, HasUuids;
protected $fillable = [
'legal_page_id',
'content',
'major', 'minor', 'patch',
'status', 'published_at',
'change_log', 'is_active', 'created_by'
];
public $incrementing = false;
protected $keyType = 'string';
protected $appends = ['version'];
public function getVersionAttribute()
{
return "{$this->major}.{$this->minor}.{$this->patch}";
}
public function legalPage()
{
return $this->belongsTo(LegalPage::class);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class LoginHistory extends Model
{
use HasFactory;
protected $primaryKey = 'id';
protected $keyType = 'string';
public $incrementing = false;
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->id)) {
$model->id = \App\Helpers\UuidHelper::generate();
}
});
}
protected $fillable = [
'user_id',
'ip_address',
'user_agent',
'device_type',
'os',
'browser',
'city',
'country',
'country_code',
];
public function user()
{
return $this->belongsTo(User::class);
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class SocialAccount extends Model
{
protected $primaryKey = 'id';
protected $keyType = 'string';
public $incrementing = false;
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->id)) {
$model->id = \App\Helpers\UuidHelper::generate();
}
});
}
protected $fillable = [
'user_id',
'provider',
'provider_id',
'provider_email',
'avatar',
'token',
'refresh_token',
'expires_at',
];
protected $casts = [
'expires_at' => 'datetime',
];
public function user()
{
return $this->belongsTo(User::class);
}
}

45
app/Models/Ticket.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class Ticket extends Model
{
use HasFactory, HasUuids;
protected $fillable = [
'user_id',
'ticket_number',
'subject',
'category',
'priority',
'status',
];
/**
* Get the user that owns the ticket.
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Get the replies for the ticket.
*/
public function replies()
{
return $this->hasMany(TicketReply::class);
}
/**
* Helper to generate a unique ticket number.
*/
public static function generateTicketNumber()
{
return 'TKT-' . strtoupper(bin2hex(random_bytes(3)));
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class TicketAttachment extends Model
{
use HasFactory, HasUuids;
protected $fillable = [
'ticket_reply_id',
'file_name',
'file_path',
'file_type',
'file_size',
];
public function reply()
{
return $this->belongsTo(TicketReply::class, 'ticket_reply_id');
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class TicketReply extends Model
{
use HasFactory, HasUuids;
protected $fillable = [
'ticket_id',
'user_id',
'message',
'attachment_path',
];
/**
* Get the ticket that owns the reply.
*/
public function ticket()
{
return $this->belongsTo(Ticket::class);
}
/**
* Get the user that wrote the reply.
*/
public function user()
{
return $this->belongsTo(User::class);
}
public function attachments()
{
return $this->hasMany(TicketAttachment::class);
}
}

189
app/Models/User.php Normal file
View File

@@ -0,0 +1,189 @@
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable implements MustVerifyEmail
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasApiTokens, HasFactory, Notifiable;
public const ROLE_OWNER = 'owner';
public const ROLE_ADMIN = 'admin';
public const ROLE_CUSTOMER = 'customer';
/**
* The channels the user receives notification broadcasts on.
*/
public function receivesBroadcastNotificationsOn(): string
{
return 'App.Models.User.' . $this->id;
}
/**
* Get the API keys for the user.
*/
public function apiKeys()
{
return $this->hasMany(ApiKey::class);
}
/**
* Get the login history for the user.
*/
public function loginHistories()
{
return $this->hasMany(LoginHistory::class);
}
/**
* Get the tickets for the user.
*/
public function tickets()
{
return $this->hasMany(Ticket::class);
}
public $incrementing = false;
protected $keyType = 'string';
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$model->{$model->getKeyName()} = \App\Helpers\UuidHelper::generate();
}
});
}
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'first_name',
'last_name',
'email',
'pending_email',
'password',
'avatar',
'role',
'phone',
'bio',
'job_title',
'location',
'country',
'city_state',
'postal_code',
'tax_id',
'settings_email_alerts',
'settings_certificate_renewal',
'default_landing_page',
'theme',
'language',
'email_verified_at',
];
/**
* Get the social accounts for the user.
*/
public function socialAccounts()
{
return $this->hasMany(SocialAccount::class);
}
/**
* Check if user is owner.
*/
public function isOwner(): bool
{
return $this->role === self::ROLE_OWNER;
}
/**
* Check if user is admin.
*/
public function isAdmin(): bool
{
return $this->role === self::ROLE_ADMIN;
}
/**
* Check if user is admin or owner.
*/
public function isAdminOrOwner(): bool
{
return in_array($this->role, [self::ROLE_OWNER, self::ROLE_ADMIN]);
}
/**
* Get the avatar URL with cache busting timestamp.
*/
public function getAvatarAttribute($value)
{
if (!$value) return $value;
// If it's already a full R2 URL, append timestamp
if (str_contains($value, 'cdn.trustlab.dyzulk.com')) {
$separator = str_contains($value, '?') ? '&' : '?';
return $value . $separator . 't=' . ($this->updated_at ? $this->updated_at->timestamp : time());
}
return $value;
}
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'settings_email_alerts' => 'boolean',
'settings_certificate_renewal' => 'boolean',
];
}
/**
* Send the email verification notification.
*
* @return void
*/
public function sendEmailVerificationNotification()
{
$this->notify(new \App\Notifications\VerifyEmailNotification);
}
/**
* Route notifications for the mail channel.
*
* @param \Illuminate\Notifications\Notification $notification
* @return array|string
*/
public function routeNotificationForMail($notification)
{
if ($notification instanceof \App\Notifications\PendingEmailVerificationNotification) {
return $this->pending_email;
}
return $this->email;
}
}