Add notification system with Rsx_Throttle utility
Add action log feature, fix Bootstrap variables and DataGrid styling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: _api_keys
|
||||
*
|
||||
* @property int $id
|
||||
@@ -53,7 +53,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Api_Key_Model extends Rsx_System_Model_Abstract
|
||||
{
|
||||
{
|
||||
protected $table = '_api_keys';
|
||||
|
||||
public static $enums = [];
|
||||
|
||||
@@ -30,42 +30,50 @@ use App\RSpade\Core\Files\File_Storage_Model;
|
||||
* provides the basic structure for categorizing uploaded files.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_
|
||||
* @property integer $id
|
||||
* @property string $key
|
||||
* @property integer $file_storage_id
|
||||
* @property string $file_name
|
||||
* @property string $file_extension
|
||||
* @property integer $file_type_id
|
||||
* @property integer $width
|
||||
* @property integer $height
|
||||
* @property integer $duration
|
||||
* @property boolean $is_animated
|
||||
* @property integer $frame_count
|
||||
* @property integer $fileable_type
|
||||
* @property integer $fileable_id
|
||||
* @property string $fileable_category
|
||||
* @property string $fileable_type_meta
|
||||
* @property integer $fileable_order
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: _file_attachments
|
||||
*
|
||||
* @property int $id
|
||||
* @property mixed $key
|
||||
* @property int $file_storage_id
|
||||
* @property mixed $file_name
|
||||
* @property mixed $file_extension
|
||||
* @property int $file_type_id
|
||||
* @property int $width
|
||||
* @property int $height
|
||||
* @property int $duration
|
||||
* @property bool $is_animated
|
||||
* @property int $frame_count
|
||||
* @property int $fileable_type
|
||||
* @property int $fileable_id
|
||||
* @property mixed $fileable_category
|
||||
* @property mixed $fileable_type_meta
|
||||
* @property int $fileable_order
|
||||
* @property string $fileable_meta
|
||||
* @property integer $site_id
|
||||
* @property string $session_id
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property integer $created_by
|
||||
* @property integer $updated_by
|
||||
* @method static mixed file_type_id_enum()
|
||||
* @method static mixed file_type_id_enum_select()
|
||||
* @method static mixed file_type_id_enum_ids()
|
||||
* @property-read mixed $file_type_id_constant
|
||||
* @property-read mixed $file_type_id_label
|
||||
* @property int $site_id
|
||||
* @property mixed $session_id
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
*
|
||||
* @property-read string $file_type_id__label
|
||||
* @property-read string $file_type_id__constant
|
||||
*
|
||||
* @method static array file_type_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array file_type_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array file_type_id__enum_labels() Get simple id => label map
|
||||
* @method static array file_type_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class File_Attachment_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
/** __AUTO_GENERATED: */
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
const FILE_TYPE_IMAGE = 1;
|
||||
const FILE_TYPE_ANIMATED_IMAGE = 2;
|
||||
const FILE_TYPE_VIDEO = 3;
|
||||
@@ -73,6 +81,9 @@ class File_Attachment_Model extends Rsx_Site_Model_Abstract
|
||||
const FILE_TYPE_TEXT = 5;
|
||||
const FILE_TYPE_DOCUMENT = 6;
|
||||
const FILE_TYPE_OTHER = 7;
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,7 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: _file_storage
|
||||
*
|
||||
* @property int $id
|
||||
@@ -30,7 +30,7 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class File_Storage_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
// Required static properties from parent abstract class
|
||||
public static $enums = [];
|
||||
public static $rel = [];
|
||||
|
||||
@@ -14,7 +14,7 @@ use App\RSpade\Core\Models\Region_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: countries
|
||||
*
|
||||
* @property int $id
|
||||
@@ -32,7 +32,7 @@ use App\RSpade\Core\Models\Region_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Country_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
public static $enums = [];
|
||||
|
||||
protected $table = 'countries';
|
||||
|
||||
@@ -12,7 +12,7 @@ use App\RSpade\Core\Database\Models\Rsx_System_Model_Abstract;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: ip_addresses
|
||||
*
|
||||
* @property int $id
|
||||
@@ -30,7 +30,7 @@ use App\RSpade\Core\Database\Models\Rsx_System_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Ip_Address_Model extends Rsx_System_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* Enum field definitions
|
||||
* @var array
|
||||
|
||||
@@ -24,7 +24,7 @@ use App\RSpade\Core\Session\Session;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: login_users
|
||||
*
|
||||
* @property int $id
|
||||
@@ -61,7 +61,7 @@ class Login_User_Model extends Rsx_Model_Abstract implements
|
||||
\Illuminate\Contracts\Auth\Authenticatable,
|
||||
\Illuminate\Contracts\Auth\Access\Authorizable,
|
||||
\Illuminate\Contracts\Auth\CanResetPassword
|
||||
{
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,7 @@ use App\RSpade\Core\Models\Country_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: regions
|
||||
*
|
||||
* @property int $id
|
||||
@@ -31,7 +31,7 @@ use App\RSpade\Core\Models\Country_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Region_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
public static $enums = [];
|
||||
|
||||
protected $table = 'regions';
|
||||
|
||||
@@ -14,12 +14,13 @@ use App\RSpade\Core\Models\User_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: sites
|
||||
*
|
||||
* @property int $id
|
||||
* @property mixed $slug
|
||||
* @property mixed $name
|
||||
* @property mixed $timezone
|
||||
* @property bool $is_enabled
|
||||
* @property string $deleted_at
|
||||
* @property string $created_at
|
||||
@@ -31,7 +32,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Site_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: user_invites
|
||||
*
|
||||
* @property int $id
|
||||
@@ -28,7 +28,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Invite_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* Enum field definitions
|
||||
* @var array
|
||||
|
||||
@@ -23,41 +23,46 @@ use App\RSpade\Core\Models\User_Profile_Model;
|
||||
* See: php artisan rsx:man acls
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_
|
||||
* @property integer $id
|
||||
* @property integer $login_user_id
|
||||
* @property integer $site_id
|
||||
* @property string $first_name
|
||||
* @property string $last_name
|
||||
* @property string $phone
|
||||
* @property integer $role_id
|
||||
* @property boolean $is_enabled
|
||||
* @property integer $user_role_id
|
||||
* @property string $email
|
||||
* @property \Carbon\Carbon $deleted_at
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property integer $created_by
|
||||
* @property integer $updated_by
|
||||
* @property integer $deleted_by
|
||||
* @property string $invite_code
|
||||
* @property \Carbon\Carbon $invite_accepted_at
|
||||
* @property \Carbon\Carbon $invite_expires_at
|
||||
* @method static mixed role_id_enum()
|
||||
* @method static mixed role_id_enum_select()
|
||||
* @method static mixed role_id_enum_ids()
|
||||
* @property-read mixed $role_id_constant
|
||||
* @property-read mixed $role_id_label
|
||||
* @property-read mixed $role_id_permissions
|
||||
* @property-read mixed $role_id_can_admin_roles
|
||||
* @property-read mixed $role_id_selectable
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: users
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $login_user_id
|
||||
* @property int $site_id
|
||||
* @property mixed $first_name
|
||||
* @property mixed $last_name
|
||||
* @property mixed $phone
|
||||
* @property int $role_id
|
||||
* @property bool $is_enabled
|
||||
* @property int $user_role_id
|
||||
* @property mixed $email
|
||||
* @property string $deleted_at
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
* @property int $deleted_by
|
||||
* @property mixed $invite_code
|
||||
* @property string $invite_accepted_at
|
||||
* @property string $invite_expires_at
|
||||
*
|
||||
* @property-read string $role_id__label
|
||||
* @property-read string $role_id__constant
|
||||
*
|
||||
* @method static array role_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array role_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array role_id__enum_labels() Get simple id => label map
|
||||
* @method static array role_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
/** __AUTO_GENERATED: */
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
const ROLE_DEVELOPER = 100;
|
||||
const ROLE_ROOT_ADMIN = 200;
|
||||
const ROLE_SITE_OWNER = 300;
|
||||
@@ -66,6 +71,9 @@ class User_Model extends Rsx_Site_Model_Abstract
|
||||
const ROLE_USER = 600;
|
||||
const ROLE_VIEWER = 700;
|
||||
const ROLE_DISABLED = 800;
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
// =========================================================================
|
||||
|
||||
@@ -7,7 +7,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: user_permissions
|
||||
*
|
||||
* @property int $id
|
||||
@@ -22,7 +22,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Permission_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
protected $table = 'user_permissions';
|
||||
protected $fillable = []; // No mass assignment - always explicit
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: user_profiles
|
||||
*
|
||||
* @property int $id
|
||||
@@ -51,7 +51,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Profile_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* The table associated with the model
|
||||
*
|
||||
|
||||
@@ -11,33 +11,44 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
* and two-factor authentication via email or SMS.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_
|
||||
* @property integer $id
|
||||
* @property string $email
|
||||
* @property string $verification_code
|
||||
* @property integer $verification_type_id
|
||||
* @property \Carbon\Carbon $verified_at
|
||||
* @property \Carbon\Carbon $expires_at
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property integer $created_by
|
||||
* @property integer $updated_by
|
||||
* @method static mixed verification_type_id_enum()
|
||||
* @method static mixed verification_type_id_enum_select()
|
||||
* @method static mixed verification_type_id_enum_ids()
|
||||
* @property-read mixed $verification_type_id_constant
|
||||
* @property-read mixed $verification_type_id_label
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: user_verifications
|
||||
*
|
||||
* @property int $id
|
||||
* @property mixed $email
|
||||
* @property mixed $verification_code
|
||||
* @property int $verification_type_id
|
||||
* @property string $verified_at
|
||||
* @property string $expires_at
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
*
|
||||
* @property-read string $verification_type_id__label
|
||||
* @property-read string $verification_type_id__constant
|
||||
*
|
||||
* @method static array verification_type_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array verification_type_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array verification_type_id__enum_labels() Get simple id => label map
|
||||
* @method static array verification_type_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Verification_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
/** __AUTO_GENERATED: */
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
const VERIFICATION_TYPE_EMAIL = 1;
|
||||
const VERIFICATION_TYPE_SMS = 2;
|
||||
const VERIFICATION_TYPE_EMAIL_RECOVERY = 3;
|
||||
const VERIFICATION_TYPE_SMS_RECOVERY = 4;
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,11 +17,11 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: _search_indexes
|
||||
*
|
||||
* @property int $id
|
||||
* @property mixed $indexable_type
|
||||
* @property int $indexable_type
|
||||
* @property int $indexable_id
|
||||
* @property string $content
|
||||
* @property array $metadata
|
||||
@@ -37,7 +37,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Search_Index_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
{
|
||||
// Required static properties from parent abstract class
|
||||
public static $enums = [];
|
||||
public static $rel = [];
|
||||
|
||||
@@ -41,7 +41,7 @@ use App\RSpade\Core\Session\User_Agent;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-26 02:43:29
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: _sessions
|
||||
*
|
||||
* @property int $id
|
||||
@@ -63,7 +63,7 @@ use App\RSpade\Core\Session\User_Agent;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Session extends Rsx_System_Model_Abstract
|
||||
{
|
||||
{
|
||||
// Enum definitions (required by abstract parent)
|
||||
public static $enums = [];
|
||||
|
||||
|
||||
105
app/RSpade/Core/Throttle/Rsx_Throttle.php
Executable file
105
app/RSpade/Core/Throttle/Rsx_Throttle.php
Executable file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\RSpade\Core\Throttle;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\RSpade\Core\Session\Session;
|
||||
|
||||
/**
|
||||
* Rsx_Throttle - Rate limiting utility for periodic per-user actions
|
||||
*
|
||||
* Provides atomic check-and-execute functionality for throttling operations.
|
||||
* Database implementation is hidden - API exposes only check().
|
||||
*
|
||||
* Use cases:
|
||||
* - Periodic maintenance tasks (notification expiry, cache refresh)
|
||||
* - Rate-limited background calculations
|
||||
* - Any operation that should run at most once per interval per user
|
||||
*
|
||||
* @example
|
||||
* if (Rsx_Throttle::check('NOTIFICATION_EXPIRE_CHECK', $user_id, minutes: 30)) {
|
||||
* Notification::expire_old();
|
||||
* }
|
||||
*/
|
||||
class Rsx_Throttle
|
||||
{
|
||||
/**
|
||||
* Check if action should run and atomically mark as executed
|
||||
*
|
||||
* First-time calls return true and create record.
|
||||
* Subsequent calls return true only after interval has passed.
|
||||
*
|
||||
* @param string $action_key Unique action identifier (use SCREAMING_SNAKE_CASE)
|
||||
* @param int $user_id Login user ID (authentication identity)
|
||||
* @param int $minutes Throttle interval in minutes (default 30)
|
||||
* @return bool True if action should execute now, false if throttled
|
||||
*/
|
||||
public static function check(string $action_key, int $user_id, int $minutes = 30): bool
|
||||
{
|
||||
$site_id = Session::get_site_id();
|
||||
$threshold = now()->subMinutes($minutes);
|
||||
|
||||
// TODO: Future optimization - use row-level locking with SELECT ... FOR UPDATE
|
||||
// instead of table lock for better concurrency per user/site
|
||||
|
||||
// Atomic check-and-update using table lock
|
||||
DB::statement('LOCK TABLES _throttle WRITE');
|
||||
|
||||
try {
|
||||
$record = DB::table('_throttle')
|
||||
->where('site_id', $site_id)
|
||||
->where('user_id', $user_id)
|
||||
->where('action_key', $action_key)
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
// First time - create record and return true
|
||||
DB::table('_throttle')->insert([
|
||||
'site_id' => $site_id,
|
||||
'user_id' => $user_id,
|
||||
'action_key' => $action_key,
|
||||
'last_executed_at' => now(),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($record->last_executed_at < $threshold) {
|
||||
// Interval passed - update and return true
|
||||
DB::table('_throttle')
|
||||
->where('id', $record->id)
|
||||
->update([
|
||||
'last_executed_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Still within throttle interval
|
||||
return false;
|
||||
|
||||
} finally {
|
||||
DB::statement('UNLOCK TABLES');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force reset throttle for an action
|
||||
*
|
||||
* Useful for testing or forcing immediate re-execution.
|
||||
*
|
||||
* @param string $action_key Action identifier to reset
|
||||
* @param int $user_id Login user ID
|
||||
*/
|
||||
public static function reset(string $action_key, int $user_id): void
|
||||
{
|
||||
$site_id = Session::get_site_id();
|
||||
|
||||
DB::table('_throttle')
|
||||
->where('site_id', $site_id)
|
||||
->where('user_id', $user_id)
|
||||
->where('action_key', $action_key)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
@@ -5,29 +5,41 @@ namespace App\RSpade\Lib\Flash;
|
||||
use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_
|
||||
* @property integer $id
|
||||
* @property integer $session_id
|
||||
* @property integer $type_id
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2026-01-29 08:25:50
|
||||
* Table: _flash_alerts
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $session_id
|
||||
* @property int $type_id
|
||||
* @property string $message
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property integer $created_by
|
||||
* @property integer $updated_by
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @method static mixed type_id_enum()
|
||||
* @method static mixed type_id_enum_select()
|
||||
* @method static mixed type_id_enum_ids()
|
||||
* @property-read mixed $type_id_constant
|
||||
* @property-read mixed $type_id_label
|
||||
* @property string $created_at
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
* @property string $updated_at
|
||||
*
|
||||
* @property-read string $type_id__label
|
||||
* @property-read string $type_id__constant
|
||||
*
|
||||
* @method static array type_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array type_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array type_id__enum_labels() Get simple id => label map
|
||||
* @method static array type_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Flash_Alert_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
/** __AUTO_GENERATED: */
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
const TYPE_SUCCESS = 1;
|
||||
const TYPE_ERROR = 2;
|
||||
const TYPE_INFO = 3;
|
||||
const TYPE_WARNING = 4;
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
// Enum constants (auto-generated by rsx:migrate:document_models)
|
||||
|
||||
@@ -381,6 +381,21 @@
|
||||
"created_at": "2026-01-12T07:36:24+00:00",
|
||||
"created_by": "root",
|
||||
"command": "php artisan make:migration:safe add_timezone_to_sites_table"
|
||||
},
|
||||
"2026_01_29_070242_create_action_logs_tables.php": {
|
||||
"created_at": "2026-01-29T07:02:42+00:00",
|
||||
"created_by": "root",
|
||||
"command": "php artisan make:migration:safe create_action_logs_tables"
|
||||
},
|
||||
"2026_01_29_081808_create_throttle_table.php": {
|
||||
"created_at": "2026-01-29T08:18:08+00:00",
|
||||
"created_by": "root",
|
||||
"command": "php artisan make:migration:safe create_throttle_table"
|
||||
},
|
||||
"2026_01_29_081902_create_notifications_table.php": {
|
||||
"created_at": "2026-01-29T08:19:02+00:00",
|
||||
"created_by": "root",
|
||||
"command": "php artisan make:migration:safe create_notifications_table"
|
||||
}
|
||||
}
|
||||
}
|
||||
66
database/migrations/2026_01_29_070242_create_action_logs_tables.php
Executable file
66
database/migrations/2026_01_29_070242_create_action_logs_tables.php
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* IMPORTANT: Use raw MySQL queries for clarity and auditability
|
||||
* ✅ DB::statement() with raw SQL
|
||||
* ❌ Schema::create() with Blueprint
|
||||
*
|
||||
* REQUIRED: ALL tables MUST have: id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
|
||||
* No exceptions - every table needs this exact ID column (SIGNED for easier migrations)
|
||||
*
|
||||
* Integer types: Use BIGINT for all integers, TINYINT(1) for booleans only
|
||||
* Never use unsigned - all integers should be signed
|
||||
*
|
||||
* Migrations must be self-contained - no Model/Service references
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// Main action log table
|
||||
DB::statement("
|
||||
CREATE TABLE action_logs (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
type_id BIGINT NOT NULL,
|
||||
actor_type BIGINT NULL,
|
||||
actor_id BIGINT NULL,
|
||||
subject_type BIGINT NOT NULL,
|
||||
subject_id BIGINT NOT NULL,
|
||||
metadata JSON NULL,
|
||||
created_by BIGINT NULL,
|
||||
created_at TIMESTAMP(3) NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at TIMESTAMP(3) NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
updated_by BIGINT NULL,
|
||||
|
||||
INDEX idx_action_logs_site_id (site_id),
|
||||
INDEX idx_action_logs_type_id (type_id),
|
||||
INDEX idx_action_logs_actor (actor_type, actor_id),
|
||||
INDEX idx_action_logs_subject (subject_type, subject_id),
|
||||
INDEX idx_action_logs_created_at (created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
");
|
||||
|
||||
// Related entities table (one-to-many from action_logs)
|
||||
DB::statement("
|
||||
CREATE TABLE action_log_related (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
action_log_id BIGINT NOT NULL,
|
||||
role_id BIGINT NOT NULL,
|
||||
related_type BIGINT NOT NULL,
|
||||
related_id BIGINT NOT NULL,
|
||||
|
||||
INDEX idx_alr_action_log_id (action_log_id),
|
||||
INDEX idx_alr_related (related_type, related_id),
|
||||
FOREIGN KEY (action_log_id) REFERENCES action_logs(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
");
|
||||
}
|
||||
};
|
||||
42
database/migrations/2026_01_29_081808_create_throttle_table.php
Executable file
42
database/migrations/2026_01_29_081808_create_throttle_table.php
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* IMPORTANT: Use raw MySQL queries for clarity and auditability
|
||||
* ✅ DB::statement() with raw SQL
|
||||
* ❌ Schema::create() with Blueprint
|
||||
*
|
||||
* REQUIRED: ALL tables MUST have: id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
|
||||
* No exceptions - every table needs this exact ID column (SIGNED for easier migrations)
|
||||
*
|
||||
* Integer types: Use BIGINT for all integers, TINYINT(1) for booleans only
|
||||
* Never use unsigned - all integers should be signed
|
||||
*
|
||||
* Migrations must be self-contained - no Model/Service references
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
DB::statement("
|
||||
CREATE TABLE _throttle (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
action_key VARCHAR(255) NOT NULL,
|
||||
last_executed_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
created_at TIMESTAMP(3) NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at TIMESTAMP(3) NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
|
||||
UNIQUE KEY uk_throttle_site_user_action (site_id, user_id, action_key),
|
||||
INDEX idx_throttle_last_executed (last_executed_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
");
|
||||
}
|
||||
};
|
||||
52
database/migrations/2026_01_29_081902_create_notifications_table.php
Executable file
52
database/migrations/2026_01_29_081902_create_notifications_table.php
Executable file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* IMPORTANT: Use raw MySQL queries for clarity and auditability
|
||||
* ✅ DB::statement() with raw SQL
|
||||
* ❌ Schema::create() with Blueprint
|
||||
*
|
||||
* REQUIRED: ALL tables MUST have: id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
|
||||
* No exceptions - every table needs this exact ID column (SIGNED for easier migrations)
|
||||
*
|
||||
* Integer types: Use BIGINT for all integers, TINYINT(1) for booleans only
|
||||
* Never use unsigned - all integers should be signed
|
||||
*
|
||||
* Migrations must be self-contained - no Model/Service references
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
DB::statement("
|
||||
CREATE TABLE notifications (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
type_id BIGINT NOT NULL,
|
||||
entity_type BIGINT NULL,
|
||||
entity_id BIGINT NULL,
|
||||
metadata JSON NULL,
|
||||
read_at TIMESTAMP(3) NULL,
|
||||
expires_at TIMESTAMP(3) NOT NULL,
|
||||
created_by BIGINT NULL,
|
||||
created_at TIMESTAMP(3) NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at TIMESTAMP(3) NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
updated_by BIGINT NULL,
|
||||
|
||||
INDEX idx_notifications_site_user (site_id, user_id),
|
||||
INDEX idx_notifications_user_unread (site_id, user_id, read_at),
|
||||
INDEX idx_notifications_entity (entity_type, entity_id),
|
||||
INDEX idx_notifications_expires (expires_at),
|
||||
INDEX idx_notifications_type (type_id),
|
||||
INDEX idx_notifications_created (created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
");
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user