feat: add app and database modules
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Models\AI;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
|
||||
class AiUsageLog extends Model
|
||||
{
|
||||
use Prunable;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'provider',
|
||||
'model',
|
||||
'prompt',
|
||||
'response',
|
||||
'prompt_tokens',
|
||||
'completion_tokens',
|
||||
'total_tokens',
|
||||
'estimated_cost',
|
||||
'status',
|
||||
'error_message',
|
||||
'metadata',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'metadata' => 'array',
|
||||
'estimated_cost' => 'decimal:6',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the prunable model query.
|
||||
*/
|
||||
public function prunable()
|
||||
{
|
||||
return static::where('created_at', '<=', now()->subMonths(3));
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
|
||||
class AiHealingLog extends Model
|
||||
{
|
||||
use \Illuminate\Database\Eloquent\Factories\HasFactory, Prunable;
|
||||
|
||||
protected $fillable = [
|
||||
'error_type',
|
||||
'error_message',
|
||||
'stack_trace',
|
||||
'ai_diagnosis',
|
||||
'original_code',
|
||||
'fixed_code',
|
||||
'action_taken',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'stack_trace' => 'encrypted',
|
||||
'original_code' => 'encrypted',
|
||||
'fixed_code' => 'encrypted',
|
||||
];
|
||||
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return self::query()->where('created_at', '<=', now()->subDays(90));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class DashboardWidgetPreference extends Model
|
||||
{
|
||||
protected $fillable = ['user_id', 'widget_key', 'visible', 'sort_order'];
|
||||
|
||||
protected $casts = ['visible' => 'boolean'];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return merged widget config: defaults overlaid with user prefs.
|
||||
*
|
||||
* @return array<string, array{label: string, visible: bool, sort_order: int, permission: string|null}>
|
||||
*/
|
||||
public static function forUser(int $userId): array
|
||||
{
|
||||
$defaults = self::defaults();
|
||||
|
||||
$prefs = self::where('user_id', $userId)
|
||||
->get()
|
||||
->keyBy('widget_key');
|
||||
|
||||
foreach ($defaults as $key => &$widget) {
|
||||
if ($prefs->has($key)) {
|
||||
$widget['visible'] = $prefs[$key]->visible;
|
||||
$widget['sort_order'] = $prefs[$key]->sort_order;
|
||||
}
|
||||
}
|
||||
|
||||
uasort($defaults, fn ($a, $b) => $a['sort_order'] <=> $b['sort_order']);
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* All available widgets with their defaults.
|
||||
*/
|
||||
public static function defaults(): array
|
||||
{
|
||||
return [
|
||||
'cpu' => ['label' => 'CPU Load', 'visible' => true, 'sort_order' => 1, 'permission' => null],
|
||||
'ram' => ['label' => 'Memory', 'visible' => true, 'sort_order' => 2, 'permission' => null],
|
||||
'disk' => ['label' => 'Storage', 'visible' => true, 'sort_order' => 3, 'permission' => null],
|
||||
'live_users' => ['label' => 'Live Users', 'visible' => true, 'sort_order' => 4, 'permission' => null],
|
||||
'queues' => ['label' => 'Queue Stats', 'visible' => true, 'sort_order' => 5, 'permission' => null],
|
||||
'activity_feed' => ['label' => 'Activity Feed', 'visible' => true, 'sort_order' => 6, 'permission' => 'view health and logs'],
|
||||
'ai_insight' => ['label' => 'AI Security Insight','visible' => true, 'sort_order' => 7, 'permission' => 'view ai log analysis'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class DeviceToken extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'token',
|
||||
'platform',
|
||||
'device_name',
|
||||
'app_version',
|
||||
'last_used_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'last_used_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
|
||||
class MobileErrorLog extends Model
|
||||
{
|
||||
use Prunable;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* Get the prunable model query.
|
||||
*/
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return self::query()->where('occurred_at', '<=', now()->subDays(90));
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'error_type',
|
||||
'message',
|
||||
'stack_trace',
|
||||
'platform',
|
||||
'app_version',
|
||||
'device_info',
|
||||
'occurred_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'device_info' => 'array',
|
||||
'occurred_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file is strictly prohibited without prior written
|
||||
* permission from the author.
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Services\MobileConfig\MobileConfigService;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
|
||||
class MobileSetting extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
|
||||
protected $fillable = [
|
||||
'key',
|
||||
'value',
|
||||
'group',
|
||||
'type',
|
||||
];
|
||||
|
||||
/**
|
||||
* Spatie Activitylog configuration
|
||||
*/
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->useLogName('mobile-config')
|
||||
->logOnly(['key', 'value', 'group', 'type'])
|
||||
->logOnlyDirty()
|
||||
->dontSubmitEmptyLogs();
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::saved(fn () => MobileConfigService::clearCacheStatic());
|
||||
static::deleted(fn () => MobileConfigService::clearCacheStatic());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
|
||||
class MobileSyncLog extends Model
|
||||
{
|
||||
use Prunable;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* Get the prunable model query.
|
||||
*/
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return self::query()->where('synced_at', '<=', now()->subDays(30));
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'platform',
|
||||
'device_model',
|
||||
'app_version',
|
||||
'ip_address',
|
||||
'synced_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'synced_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Notification extends Model
|
||||
{
|
||||
use Prunable, SoftDeletes;
|
||||
|
||||
protected $table = 'system_notifications';
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'message',
|
||||
'recipient',
|
||||
'type',
|
||||
'read_at',
|
||||
'created_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'read_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user who created the notification.
|
||||
*/
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Users who have interacted with this notification (read/deleted).
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'notification_user')
|
||||
->withPivot('read_at', 'deleted_at')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prunable model query.
|
||||
* Auto-delete notifications older than 30 days.
|
||||
*/
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return self::query()->where('created_at', '<=', now()->subDays(30));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
|
||||
class OtpCode extends Model
|
||||
{
|
||||
use Prunable;
|
||||
|
||||
protected $fillable = [
|
||||
'identifier',
|
||||
'code',
|
||||
'expires_at',
|
||||
'verified_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'expires_at' => 'datetime',
|
||||
'verified_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return self::query()->where('expires_at', '<', now());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
|
||||
class PasswordHistory extends Model
|
||||
{
|
||||
use Prunable;
|
||||
|
||||
protected $fillable = ['user_id', 'password'];
|
||||
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return self::query()->where('created_at', '<=', now()->subDays(365));
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\Permission\Models\Permission as SpatiePermission;
|
||||
|
||||
class Permission extends SpatiePermission
|
||||
{
|
||||
use HasFactory, LogsActivity, SoftDeletes;
|
||||
|
||||
/**
|
||||
* Activity log configuration
|
||||
*/
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->useLogName('permission-management')
|
||||
->logOnly(['name', 'guard_name', 'is_active'])
|
||||
->logOnlyDirty()
|
||||
->dontSubmitEmptyLogs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fillable attributes
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'scope',
|
||||
'guard_name',
|
||||
'is_active',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
/**
|
||||
* Casting
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot model to automatically set created_by & updated_by
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit trail relations
|
||||
*/
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function updater()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'updated_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\Permission\Models\Permission as SpatiePermission;
|
||||
|
||||
class Permission extends SpatiePermission
|
||||
{
|
||||
use HasFactory, LogsActivity, SoftDeletes;
|
||||
|
||||
/**
|
||||
* Activity log configuration
|
||||
*/
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->useLogName('permission-management')
|
||||
->logOnly(['name', 'guard_name', 'is_active'])
|
||||
->logOnlyDirty()
|
||||
->dontSubmitEmptyLogs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ketika permission diupdate
|
||||
*/
|
||||
public function updated(\App\Models\Permission $permission)
|
||||
{
|
||||
\Illuminate\Support\Facades\Cache::forget("permission_status:{$permission->name}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ketika permission didelete (termasuk soft delete)
|
||||
*/
|
||||
public function deleted(\App\Models\Permission $permission)
|
||||
{
|
||||
\Illuminate\Support\Facades\Cache::forget("permission_status:{$permission->name}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fillable attributes
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'guard_name',
|
||||
'is_active',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
/**
|
||||
* Casting
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot model to automatically set created_by & updated_by
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit trail relations
|
||||
*/
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function updater()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'updated_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\Permission\Models\Role as SpatieRole;
|
||||
|
||||
class Role extends SpatieRole
|
||||
{
|
||||
use HasFactory, LogsActivity, SoftDeletes;
|
||||
|
||||
/**
|
||||
* Activity log configuration for Role
|
||||
*/
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->useLogName('role-management') // module log category
|
||||
->logOnly(['name', 'guard_name', 'is_active']) // fields to monitor
|
||||
->logOnlyDirty() // only log if changed
|
||||
->dontSubmitEmptyLogs(); // skip if no changes
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'guard_name',
|
||||
'is_active',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot model to automatically set created_by & updated_by
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
// Automatically set created_by
|
||||
static::creating(function ($model) {
|
||||
if (Auth::check()) {
|
||||
$model->created_by = Auth::id();
|
||||
}
|
||||
});
|
||||
|
||||
// Automatically set updated_by (including soft deletes)
|
||||
static::updating(function ($model) {
|
||||
if (Auth::check()) {
|
||||
$model->updated_by = Auth::id();
|
||||
}
|
||||
});
|
||||
|
||||
static::deleting(function ($model) {
|
||||
if (Auth::check()) {
|
||||
$model->updated_by = Auth::id();
|
||||
$model->saveQuietly(); // save without triggering infinite update log
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Audit Trail Relationships
|
||||
*/
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function updater()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'updated_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file is strictly prohibited without prior written
|
||||
* permission from the author.
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
|
||||
class SystemSetting extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
|
||||
/**
|
||||
* Spatie Activitylog configuration
|
||||
*/
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->useLogName('system-config')
|
||||
->logOnly(['key', 'value', 'group'])
|
||||
->logOnlyDirty()
|
||||
->dontSubmitEmptyLogs();
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'key',
|
||||
'value',
|
||||
'type',
|
||||
'group',
|
||||
'is_public',
|
||||
'description',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_public' => 'boolean',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SystemSettingRevision extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'system_setting_id',
|
||||
'key',
|
||||
'old_value',
|
||||
'new_value',
|
||||
'changed_by',
|
||||
'changed_ip',
|
||||
'changed_agent',
|
||||
'created_at',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
// MOBILE APPS
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Transaction extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'title',
|
||||
'amount',
|
||||
'type',
|
||||
'category',
|
||||
'icon',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file is strictly prohibited without prior written
|
||||
* permission from the author.
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Notifications\Auth\ResetPasswordNotification;
|
||||
use App\Notifications\Auth\VerifyEmailNotification;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable as WebAuthnContract;
|
||||
use Laragear\WebAuthn\WebAuthnAuthentication;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
class User extends Authenticatable implements HasMedia, WebAuthnContract
|
||||
{
|
||||
use HasApiTokens, HasFactory, HasRoles, InteractsWithMedia, LogsActivity, Notifiable, SoftDeletes, WebAuthnAuthentication;
|
||||
|
||||
protected string $guard_name = 'web';
|
||||
|
||||
/**
|
||||
* Spatie Activitylog configuration
|
||||
*/
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->useLogName('user-management')
|
||||
->logOnly(['name', 'username', 'email', 'phone_number', 'is_active'])
|
||||
->logOnlyDirty()
|
||||
->dontSubmitEmptyLogs();
|
||||
}
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'phone_number',
|
||||
'password',
|
||||
'google_id',
|
||||
'facebook_id',
|
||||
'github_id',
|
||||
'is_active',
|
||||
'password_changed_at',
|
||||
'last_session_id',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
/**
|
||||
* Hidden fields
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
'media', // Hide raw media relation
|
||||
];
|
||||
|
||||
/**
|
||||
* Appended attributes
|
||||
*/
|
||||
protected $appends = ['avatar'];
|
||||
|
||||
/**
|
||||
* Avatar Accessor (using Media Library)
|
||||
*/
|
||||
public function getAvatarAttribute(): ?string
|
||||
{
|
||||
return $this->getFirstMediaUrl('avatar') ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*/
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'is_active' => 'boolean',
|
||||
'password_changed_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Password History Relation
|
||||
*/
|
||||
public function passwordHistories()
|
||||
{
|
||||
return $this->hasMany(PasswordHistory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trusted Devices Relation
|
||||
*/
|
||||
public function trustedDevices()
|
||||
{
|
||||
return $this->hasMany(UserTrustedDevice::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* ==========================================
|
||||
* DISABLE RESET PASSWORD FOR SOCIAL USERS
|
||||
* ==========================================
|
||||
*/
|
||||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
// ❌ OAuth user → BLOCK reset password
|
||||
if ($this->google_id || $this->facebook_id || $this->github_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ User manual → normal
|
||||
$this->notify(new ResetPasswordNotification($token));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use our branded email verification template.
|
||||
*/
|
||||
public function sendEmailVerificationNotification()
|
||||
{
|
||||
$this->notify(new VerifyEmailNotification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
public function isSocialUser(): bool
|
||||
{
|
||||
return ! is_null($this->google_id) || ! is_null($this->facebook_id) || ! is_null($this->github_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consents Relation
|
||||
*/
|
||||
public function consents()
|
||||
{
|
||||
return $this->hasMany(UserConsent::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has agreed to the current version of a document.
|
||||
*/
|
||||
public function hasAgreedToCurrentLegal(string $type): bool
|
||||
{
|
||||
// Settings use 'pdp' prefix for Privacy Policy, while code uses 'privacy' type
|
||||
$keyPrefix = ($type === 'privacy' || $type === 'pdp') ? 'pdp' : $type;
|
||||
$currentVersion = (int) get_setting("{$keyPrefix}_document_version", 1);
|
||||
|
||||
return $this->consents()
|
||||
->where(function ($q) use ($type) {
|
||||
$q->where('consent_type', $type)
|
||||
->orWhere('consent_type', 'privacy')
|
||||
->orWhere('consent_type', 'pdp');
|
||||
})
|
||||
->where('version_id', '>=', $currentVersion)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifications this user has interacted with (including personal read/deleted status).
|
||||
*/
|
||||
public function broadcastNotifications()
|
||||
{
|
||||
return $this->belongsToMany(Notification::class, 'system_notification_user')
|
||||
->withPivot('read_at', 'deleted_at')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who created this record.
|
||||
*/
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who last updated this record.
|
||||
*/
|
||||
public function updater()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'updated_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserConsent extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Disable standard timestamps since we only use created_at.
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'consent_type',
|
||||
'version_id',
|
||||
'ip_address',
|
||||
'user_agent',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relationship: User
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
|
||||
class UserTrustedDevice extends Model
|
||||
{
|
||||
use Prunable;
|
||||
|
||||
protected $fillable = ['user_id', 'device_id', 'token', 'expires_at'];
|
||||
|
||||
protected $casts = [
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function prunable(): Builder
|
||||
{
|
||||
return self::query()->where('expires_at', '<', now());
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user