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
|
* _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
|
* Table: _api_keys
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -53,7 +53,7 @@ use App\RSpade\Core\Models\User_Model;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Api_Key_Model extends Rsx_System_Model_Abstract
|
class Api_Key_Model extends Rsx_System_Model_Abstract
|
||||||
{
|
{
|
||||||
protected $table = '_api_keys';
|
protected $table = '_api_keys';
|
||||||
|
|
||||||
public static $enums = [];
|
public static $enums = [];
|
||||||
|
|||||||
@@ -30,42 +30,50 @@ use App\RSpade\Core\Files\File_Storage_Model;
|
|||||||
* provides the basic structure for categorizing uploaded files.
|
* provides the basic structure for categorizing uploaded files.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_
|
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||||
* @property integer $id
|
* Generated on: 2026-01-29 08:25:50
|
||||||
* @property string $key
|
* Table: _file_attachments
|
||||||
* @property integer $file_storage_id
|
*
|
||||||
* @property string $file_name
|
* @property int $id
|
||||||
* @property string $file_extension
|
* @property mixed $key
|
||||||
* @property integer $file_type_id
|
* @property int $file_storage_id
|
||||||
* @property integer $width
|
* @property mixed $file_name
|
||||||
* @property integer $height
|
* @property mixed $file_extension
|
||||||
* @property integer $duration
|
* @property int $file_type_id
|
||||||
* @property boolean $is_animated
|
* @property int $width
|
||||||
* @property integer $frame_count
|
* @property int $height
|
||||||
* @property integer $fileable_type
|
* @property int $duration
|
||||||
* @property integer $fileable_id
|
* @property bool $is_animated
|
||||||
* @property string $fileable_category
|
* @property int $frame_count
|
||||||
* @property string $fileable_type_meta
|
* @property int $fileable_type
|
||||||
* @property integer $fileable_order
|
* @property int $fileable_id
|
||||||
|
* @property mixed $fileable_category
|
||||||
|
* @property mixed $fileable_type_meta
|
||||||
|
* @property int $fileable_order
|
||||||
* @property string $fileable_meta
|
* @property string $fileable_meta
|
||||||
* @property integer $site_id
|
* @property int $site_id
|
||||||
* @property string $session_id
|
* @property mixed $session_id
|
||||||
* @property \Carbon\Carbon $created_at
|
* @property string $created_at
|
||||||
* @property \Carbon\Carbon $updated_at
|
* @property string $updated_at
|
||||||
* @property integer $created_by
|
* @property int $created_by
|
||||||
* @property integer $updated_by
|
* @property int $updated_by
|
||||||
* @method static mixed file_type_id_enum()
|
*
|
||||||
* @method static mixed file_type_id_enum_select()
|
* @property-read string $file_type_id__label
|
||||||
* @method static mixed file_type_id_enum_ids()
|
* @property-read string $file_type_id__constant
|
||||||
* @property-read mixed $file_type_id_constant
|
*
|
||||||
* @property-read mixed $file_type_id_label
|
* @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
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class File_Attachment_Model extends Rsx_Site_Model_Abstract
|
class File_Attachment_Model extends Rsx_Site_Model_Abstract
|
||||||
{
|
{
|
||||||
/** __AUTO_GENERATED: */
|
/**
|
||||||
|
* _AUTO_GENERATED_ Enum constants
|
||||||
|
*/
|
||||||
const FILE_TYPE_IMAGE = 1;
|
const FILE_TYPE_IMAGE = 1;
|
||||||
const FILE_TYPE_ANIMATED_IMAGE = 2;
|
const FILE_TYPE_ANIMATED_IMAGE = 2;
|
||||||
const FILE_TYPE_VIDEO = 3;
|
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_TEXT = 5;
|
||||||
const FILE_TYPE_DOCUMENT = 6;
|
const FILE_TYPE_DOCUMENT = 6;
|
||||||
const FILE_TYPE_OTHER = 7;
|
const FILE_TYPE_OTHER = 7;
|
||||||
|
|
||||||
|
/** __AUTO_GENERATED: */
|
||||||
|
|
||||||
/** __/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
|
* _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
|
* Table: _file_storage
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -30,7 +30,7 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class File_Storage_Model extends Rsx_Model_Abstract
|
class File_Storage_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
// Required static properties from parent abstract class
|
// Required static properties from parent abstract class
|
||||||
public static $enums = [];
|
public static $enums = [];
|
||||||
public static $rel = [];
|
public static $rel = [];
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use App\RSpade\Core\Models\Region_Model;
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
* _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
|
* Table: countries
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -32,7 +32,7 @@ use App\RSpade\Core\Models\Region_Model;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Country_Model extends Rsx_Model_Abstract
|
class Country_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
public static $enums = [];
|
public static $enums = [];
|
||||||
|
|
||||||
protected $table = 'countries';
|
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
|
* _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
|
* Table: ip_addresses
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -30,7 +30,7 @@ use App\RSpade\Core\Database\Models\Rsx_System_Model_Abstract;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Ip_Address_Model extends Rsx_System_Model_Abstract
|
class Ip_Address_Model extends Rsx_System_Model_Abstract
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Enum field definitions
|
* Enum field definitions
|
||||||
* @var array
|
* @var array
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use App\RSpade\Core\Session\Session;
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
* _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
|
* Table: login_users
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -61,7 +61,7 @@ class Login_User_Model extends Rsx_Model_Abstract implements
|
|||||||
\Illuminate\Contracts\Auth\Authenticatable,
|
\Illuminate\Contracts\Auth\Authenticatable,
|
||||||
\Illuminate\Contracts\Auth\Access\Authorizable,
|
\Illuminate\Contracts\Auth\Access\Authorizable,
|
||||||
\Illuminate\Contracts\Auth\CanResetPassword
|
\Illuminate\Contracts\Auth\CanResetPassword
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_ Enum constants
|
* _AUTO_GENERATED_ Enum constants
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use App\RSpade\Core\Models\Country_Model;
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
* _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
|
* Table: regions
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -31,7 +31,7 @@ use App\RSpade\Core\Models\Country_Model;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Region_Model extends Rsx_Model_Abstract
|
class Region_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
public static $enums = [];
|
public static $enums = [];
|
||||||
|
|
||||||
protected $table = 'regions';
|
protected $table = 'regions';
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ use App\RSpade\Core\Models\User_Model;
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
* _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
|
* Table: sites
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property mixed $slug
|
* @property mixed $slug
|
||||||
* @property mixed $name
|
* @property mixed $name
|
||||||
|
* @property mixed $timezone
|
||||||
* @property bool $is_enabled
|
* @property bool $is_enabled
|
||||||
* @property string $deleted_at
|
* @property string $deleted_at
|
||||||
* @property string $created_at
|
* @property string $created_at
|
||||||
@@ -31,7 +32,7 @@ use App\RSpade\Core\Models\User_Model;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Site_Model extends Rsx_Model_Abstract
|
class Site_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
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
|
* _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
|
* Table: user_invites
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -28,7 +28,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class User_Invite_Model extends Rsx_Site_Model_Abstract
|
class User_Invite_Model extends Rsx_Site_Model_Abstract
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Enum field definitions
|
* Enum field definitions
|
||||||
* @var array
|
* @var array
|
||||||
|
|||||||
@@ -23,41 +23,46 @@ use App\RSpade\Core\Models\User_Profile_Model;
|
|||||||
* See: php artisan rsx:man acls
|
* See: php artisan rsx:man acls
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_
|
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||||
* @property integer $id
|
* Generated on: 2026-01-29 08:25:50
|
||||||
* @property integer $login_user_id
|
* Table: users
|
||||||
* @property integer $site_id
|
*
|
||||||
* @property string $first_name
|
* @property int $id
|
||||||
* @property string $last_name
|
* @property int $login_user_id
|
||||||
* @property string $phone
|
* @property int $site_id
|
||||||
* @property integer $role_id
|
* @property mixed $first_name
|
||||||
* @property boolean $is_enabled
|
* @property mixed $last_name
|
||||||
* @property integer $user_role_id
|
* @property mixed $phone
|
||||||
* @property string $email
|
* @property int $role_id
|
||||||
* @property \Carbon\Carbon $deleted_at
|
* @property bool $is_enabled
|
||||||
* @property \Carbon\Carbon $created_at
|
* @property int $user_role_id
|
||||||
* @property \Carbon\Carbon $updated_at
|
* @property mixed $email
|
||||||
* @property integer $created_by
|
* @property string $deleted_at
|
||||||
* @property integer $updated_by
|
* @property string $created_at
|
||||||
* @property integer $deleted_by
|
* @property string $updated_at
|
||||||
* @property string $invite_code
|
* @property int $created_by
|
||||||
* @property \Carbon\Carbon $invite_accepted_at
|
* @property int $updated_by
|
||||||
* @property \Carbon\Carbon $invite_expires_at
|
* @property int $deleted_by
|
||||||
* @method static mixed role_id_enum()
|
* @property mixed $invite_code
|
||||||
* @method static mixed role_id_enum_select()
|
* @property string $invite_accepted_at
|
||||||
* @method static mixed role_id_enum_ids()
|
* @property string $invite_expires_at
|
||||||
* @property-read mixed $role_id_constant
|
*
|
||||||
* @property-read mixed $role_id_label
|
* @property-read string $role_id__label
|
||||||
* @property-read mixed $role_id_permissions
|
* @property-read string $role_id__constant
|
||||||
* @property-read mixed $role_id_can_admin_roles
|
*
|
||||||
* @property-read mixed $role_id_selectable
|
* @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
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class User_Model extends Rsx_Site_Model_Abstract
|
class User_Model extends Rsx_Site_Model_Abstract
|
||||||
{
|
{
|
||||||
/** __AUTO_GENERATED: */
|
/**
|
||||||
|
* _AUTO_GENERATED_ Enum constants
|
||||||
|
*/
|
||||||
const ROLE_DEVELOPER = 100;
|
const ROLE_DEVELOPER = 100;
|
||||||
const ROLE_ROOT_ADMIN = 200;
|
const ROLE_ROOT_ADMIN = 200;
|
||||||
const ROLE_SITE_OWNER = 300;
|
const ROLE_SITE_OWNER = 300;
|
||||||
@@ -66,6 +71,9 @@ class User_Model extends Rsx_Site_Model_Abstract
|
|||||||
const ROLE_USER = 600;
|
const ROLE_USER = 600;
|
||||||
const ROLE_VIEWER = 700;
|
const ROLE_VIEWER = 700;
|
||||||
const ROLE_DISABLED = 800;
|
const ROLE_DISABLED = 800;
|
||||||
|
|
||||||
|
/** __AUTO_GENERATED: */
|
||||||
|
|
||||||
/** __/AUTO_GENERATED */
|
/** __/AUTO_GENERATED */
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use App\RSpade\Core\Models\User_Model;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
* _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
|
* Table: user_permissions
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -22,7 +22,7 @@ use App\RSpade\Core\Models\User_Model;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class User_Permission_Model extends Rsx_Model_Abstract
|
class User_Permission_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
protected $table = 'user_permissions';
|
protected $table = 'user_permissions';
|
||||||
protected $fillable = []; // No mass assignment - always explicit
|
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
|
* _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
|
* Table: user_profiles
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -51,7 +51,7 @@ use App\RSpade\Core\Models\User_Model;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class User_Profile_Model extends Rsx_Model_Abstract
|
class User_Profile_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The table associated with the model
|
* 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.
|
* and two-factor authentication via email or SMS.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_
|
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||||
* @property integer $id
|
* Generated on: 2026-01-29 08:25:50
|
||||||
* @property string $email
|
* Table: user_verifications
|
||||||
* @property string $verification_code
|
*
|
||||||
* @property integer $verification_type_id
|
* @property int $id
|
||||||
* @property \Carbon\Carbon $verified_at
|
* @property mixed $email
|
||||||
* @property \Carbon\Carbon $expires_at
|
* @property mixed $verification_code
|
||||||
* @property \Carbon\Carbon $created_at
|
* @property int $verification_type_id
|
||||||
* @property \Carbon\Carbon $updated_at
|
* @property string $verified_at
|
||||||
* @property integer $created_by
|
* @property string $expires_at
|
||||||
* @property integer $updated_by
|
* @property string $created_at
|
||||||
* @method static mixed verification_type_id_enum()
|
* @property string $updated_at
|
||||||
* @method static mixed verification_type_id_enum_select()
|
* @property int $created_by
|
||||||
* @method static mixed verification_type_id_enum_ids()
|
* @property int $updated_by
|
||||||
* @property-read mixed $verification_type_id_constant
|
*
|
||||||
* @property-read mixed $verification_type_id_label
|
* @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
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class User_Verification_Model extends Rsx_Model_Abstract
|
class User_Verification_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
/** __AUTO_GENERATED: */
|
/**
|
||||||
|
* _AUTO_GENERATED_ Enum constants
|
||||||
|
*/
|
||||||
const VERIFICATION_TYPE_EMAIL = 1;
|
const VERIFICATION_TYPE_EMAIL = 1;
|
||||||
const VERIFICATION_TYPE_SMS = 2;
|
const VERIFICATION_TYPE_SMS = 2;
|
||||||
const VERIFICATION_TYPE_EMAIL_RECOVERY = 3;
|
const VERIFICATION_TYPE_EMAIL_RECOVERY = 3;
|
||||||
const VERIFICATION_TYPE_SMS_RECOVERY = 4;
|
const VERIFICATION_TYPE_SMS_RECOVERY = 4;
|
||||||
|
|
||||||
|
/** __AUTO_GENERATED: */
|
||||||
|
|
||||||
/** __/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
|
* _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
|
* Table: _search_indexes
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property mixed $indexable_type
|
* @property int $indexable_type
|
||||||
* @property int $indexable_id
|
* @property int $indexable_id
|
||||||
* @property string $content
|
* @property string $content
|
||||||
* @property array $metadata
|
* @property array $metadata
|
||||||
@@ -37,7 +37,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Search_Index_Model extends Rsx_Site_Model_Abstract
|
class Search_Index_Model extends Rsx_Site_Model_Abstract
|
||||||
{
|
{
|
||||||
// Required static properties from parent abstract class
|
// Required static properties from parent abstract class
|
||||||
public static $enums = [];
|
public static $enums = [];
|
||||||
public static $rel = [];
|
public static $rel = [];
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ use App\RSpade\Core\Session\User_Agent;
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
* _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
|
* Table: _sessions
|
||||||
*
|
*
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -63,7 +63,7 @@ use App\RSpade\Core\Session\User_Agent;
|
|||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Session extends Rsx_System_Model_Abstract
|
class Session extends Rsx_System_Model_Abstract
|
||||||
{
|
{
|
||||||
// Enum definitions (required by abstract parent)
|
// Enum definitions (required by abstract parent)
|
||||||
public static $enums = [];
|
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;
|
use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _AUTO_GENERATED_
|
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||||
* @property integer $id
|
* Generated on: 2026-01-29 08:25:50
|
||||||
* @property integer $session_id
|
* Table: _flash_alerts
|
||||||
* @property integer $type_id
|
*
|
||||||
|
* @property int $id
|
||||||
|
* @property int $session_id
|
||||||
|
* @property int $type_id
|
||||||
* @property string $message
|
* @property string $message
|
||||||
* @property \Carbon\Carbon $created_at
|
* @property string $created_at
|
||||||
* @property integer $created_by
|
* @property int $created_by
|
||||||
* @property integer $updated_by
|
* @property int $updated_by
|
||||||
* @property \Carbon\Carbon $updated_at
|
* @property string $updated_at
|
||||||
* @method static mixed type_id_enum()
|
*
|
||||||
* @method static mixed type_id_enum_select()
|
* @property-read string $type_id__label
|
||||||
* @method static mixed type_id_enum_ids()
|
* @property-read string $type_id__constant
|
||||||
* @property-read mixed $type_id_constant
|
*
|
||||||
* @property-read mixed $type_id_label
|
* @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
|
* @mixin \Eloquent
|
||||||
*/
|
*/
|
||||||
class Flash_Alert_Model extends Rsx_Model_Abstract
|
class Flash_Alert_Model extends Rsx_Model_Abstract
|
||||||
{
|
{
|
||||||
/** __AUTO_GENERATED: */
|
/**
|
||||||
|
* _AUTO_GENERATED_ Enum constants
|
||||||
|
*/
|
||||||
const TYPE_SUCCESS = 1;
|
const TYPE_SUCCESS = 1;
|
||||||
const TYPE_ERROR = 2;
|
const TYPE_ERROR = 2;
|
||||||
const TYPE_INFO = 3;
|
const TYPE_INFO = 3;
|
||||||
const TYPE_WARNING = 4;
|
const TYPE_WARNING = 4;
|
||||||
|
|
||||||
|
/** __AUTO_GENERATED: */
|
||||||
|
|
||||||
/** __/AUTO_GENERATED */
|
/** __/AUTO_GENERATED */
|
||||||
|
|
||||||
// Enum constants (auto-generated by rsx:migrate:document_models)
|
// Enum constants (auto-generated by rsx:migrate:document_models)
|
||||||
|
|||||||
@@ -381,6 +381,21 @@
|
|||||||
"created_at": "2026-01-12T07:36:24+00:00",
|
"created_at": "2026-01-12T07:36:24+00:00",
|
||||||
"created_by": "root",
|
"created_by": "root",
|
||||||
"command": "php artisan make:migration:safe add_timezone_to_sites_table"
|
"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