diff --git a/app/RSpade/Core/Api/Api_Key_Model.php b/app/RSpade/Core/Api/Api_Key_Model.php index 48c2c6696..2784d2f33 100644 --- a/app/RSpade/Core/Api/Api_Key_Model.php +++ b/app/RSpade/Core/Api/Api_Key_Model.php @@ -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 = []; diff --git a/app/RSpade/Core/Files/File_Attachment_Model.php b/app/RSpade/Core/Files/File_Attachment_Model.php index cf9badb12..0227f7e26 100644 --- a/app/RSpade/Core/Files/File_Attachment_Model.php +++ b/app/RSpade/Core/Files/File_Attachment_Model.php @@ -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 */ /** diff --git a/app/RSpade/Core/Files/File_Storage_Model.php b/app/RSpade/Core/Files/File_Storage_Model.php index e29c2654f..ed37b9f25 100644 --- a/app/RSpade/Core/Files/File_Storage_Model.php +++ b/app/RSpade/Core/Files/File_Storage_Model.php @@ -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 = []; diff --git a/app/RSpade/Core/Models/Country_Model.php b/app/RSpade/Core/Models/Country_Model.php index 56186f84f..b11ebef6a 100644 --- a/app/RSpade/Core/Models/Country_Model.php +++ b/app/RSpade/Core/Models/Country_Model.php @@ -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'; diff --git a/app/RSpade/Core/Models/Ip_Address_Model.php b/app/RSpade/Core/Models/Ip_Address_Model.php index 673c8de5a..5a6585ad4 100644 --- a/app/RSpade/Core/Models/Ip_Address_Model.php +++ b/app/RSpade/Core/Models/Ip_Address_Model.php @@ -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 diff --git a/app/RSpade/Core/Models/Login_User_Model.php b/app/RSpade/Core/Models/Login_User_Model.php index 86aef6ff4..650179aad 100644 --- a/app/RSpade/Core/Models/Login_User_Model.php +++ b/app/RSpade/Core/Models/Login_User_Model.php @@ -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 */ diff --git a/app/RSpade/Core/Models/Region_Model.php b/app/RSpade/Core/Models/Region_Model.php index d46eb5205..cc3cab3f9 100644 --- a/app/RSpade/Core/Models/Region_Model.php +++ b/app/RSpade/Core/Models/Region_Model.php @@ -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'; diff --git a/app/RSpade/Core/Models/Site_Model.php b/app/RSpade/Core/Models/Site_Model.php index 04a02bb0c..14a880bd9 100644 --- a/app/RSpade/Core/Models/Site_Model.php +++ b/app/RSpade/Core/Models/Site_Model.php @@ -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; /** diff --git a/app/RSpade/Core/Models/User_Invite_Model.php b/app/RSpade/Core/Models/User_Invite_Model.php index 15a3ccdf8..00244711c 100644 --- a/app/RSpade/Core/Models/User_Invite_Model.php +++ b/app/RSpade/Core/Models/User_Invite_Model.php @@ -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 diff --git a/app/RSpade/Core/Models/User_Model.php b/app/RSpade/Core/Models/User_Model.php index 87936c8fe..f1522431b 100644 --- a/app/RSpade/Core/Models/User_Model.php +++ b/app/RSpade/Core/Models/User_Model.php @@ -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 */ // ========================================================================= diff --git a/app/RSpade/Core/Models/User_Permission_Model.php b/app/RSpade/Core/Models/User_Permission_Model.php index 73fee230b..fc8c21542 100644 --- a/app/RSpade/Core/Models/User_Permission_Model.php +++ b/app/RSpade/Core/Models/User_Permission_Model.php @@ -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 diff --git a/app/RSpade/Core/Models/User_Profile_Model.php b/app/RSpade/Core/Models/User_Profile_Model.php index c582b16c1..03aa638aa 100644 --- a/app/RSpade/Core/Models/User_Profile_Model.php +++ b/app/RSpade/Core/Models/User_Profile_Model.php @@ -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 * diff --git a/app/RSpade/Core/Models/User_Verification_Model.php b/app/RSpade/Core/Models/User_Verification_Model.php index bdad29a8e..fd56473a0 100644 --- a/app/RSpade/Core/Models/User_Verification_Model.php +++ b/app/RSpade/Core/Models/User_Verification_Model.php @@ -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 */ /** diff --git a/app/RSpade/Core/Search/Search_Index_Model.php b/app/RSpade/Core/Search/Search_Index_Model.php index 1027b99a8..54c10819e 100644 --- a/app/RSpade/Core/Search/Search_Index_Model.php +++ b/app/RSpade/Core/Search/Search_Index_Model.php @@ -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 = []; diff --git a/app/RSpade/Core/Session/Session.php b/app/RSpade/Core/Session/Session.php index a8f224096..4b69b6ef7 100644 --- a/app/RSpade/Core/Session/Session.php +++ b/app/RSpade/Core/Session/Session.php @@ -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 = []; diff --git a/app/RSpade/Core/Throttle/Rsx_Throttle.php b/app/RSpade/Core/Throttle/Rsx_Throttle.php new file mode 100755 index 000000000..c1437b93e --- /dev/null +++ b/app/RSpade/Core/Throttle/Rsx_Throttle.php @@ -0,0 +1,105 @@ +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(); + } +} diff --git a/app/RSpade/Lib/Flash/Flash_Alert_Model.php b/app/RSpade/Lib/Flash/Flash_Alert_Model.php index e6361dada..725df11e9 100644 --- a/app/RSpade/Lib/Flash/Flash_Alert_Model.php +++ b/app/RSpade/Lib/Flash/Flash_Alert_Model.php @@ -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) diff --git a/database/migrations/.migration_whitelist b/database/migrations/.migration_whitelist index 2e8392a48..00a49762a 100755 --- a/database/migrations/.migration_whitelist +++ b/database/migrations/.migration_whitelist @@ -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" } } } \ No newline at end of file diff --git a/database/migrations/2026_01_29_070242_create_action_logs_tables.php b/database/migrations/2026_01_29_070242_create_action_logs_tables.php new file mode 100755 index 000000000..868eae5df --- /dev/null +++ b/database/migrations/2026_01_29_070242_create_action_logs_tables.php @@ -0,0 +1,66 @@ +