Add NAS file manager integration and all pending platform changes

- Installed p7h/nas-file-manager package via private VCS repo
- Published config/nas-file-manager.php with super_admin middleware restriction
- Added NAS env vars to .env.example
- Created admin/nas-storage page with connection info panel and file browser widget
- Added NAS Storage link to admin sidebar (super_admin only)
- Added SuperAdminController@nasStorage method and admin.nas-storage route
- Includes all accumulated branch changes: profile wall, 2FA, audit logs,
  settings panel, country/phone/timezone components, posts, slideshow,
  playlist shares, video downloads/shares, comment likes, notifications,
  social links, and more

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
ghassanandClaude Sonnet 4.6 committed 2026-05-13 13:24:32 +03:00
1 parent d44490dfe0
commit 0b2e95ea65
161 files changed
+27496 -7250

No files matched your search

+105 -20
View File
@@ -2,13 +2,15 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Notifications\VerifyEmail;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, HasFactory, Notifiable;
@@ -29,6 +31,18 @@ class User extends Authenticatable
'tiktok',
'birthday',
'location',
'gender',
'nationality',
'phone_code',
'phone_number',
'timezone',
'whatsapp',
'google_location',
'social_phone',
'social_email',
'two_factor_secret',
'two_factor_enabled',
'banner',
];
protected $hidden = [
@@ -37,10 +51,47 @@ class User extends Authenticatable
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'email_verified_at' => 'datetime',
'password' => 'hashed',
'two_factor_enabled' => 'boolean',
];
protected $appends = ['avatar_url', 'banner_url'];
// Auto-generate a unique slug-based username when creating a user without one
protected static function boot(): void
{
parent::boot();
static::creating(function (User $user) {
if (empty($user->username)) {
$user->username = static::generateUniqueUsername($user->name);
}
});
}
public static function generateUniqueUsername(string $name): string
{
$base = substr(Str::slug(trim($name)), 0, 18);
if ($base === '') {
$base = 'user';
}
do {
$slug = $base . '-' . Str::lower(Str::random(6));
} while (static::where('username', $slug)->exists());
return $slug;
}
// Returns the channel URL slug — the username, auto-generated and saved if missing
public function getChannelAttribute(): string
{
if (empty($this->username)) {
$this->username = static::generateUniqueUsername($this->name);
$this->saveQuietly();
}
return $this->username;
}
// Relationships
public function videos()
{
@@ -67,6 +118,11 @@ class User extends Authenticatable
return $this->hasMany(Playlist::class);
}
public function posts()
{
return $this->hasMany(\App\Models\Post::class);
}
public function getAvatarUrlAttribute()
{
if ($this->avatar) {
@@ -76,6 +132,19 @@ class User extends Authenticatable
return 'https://i.pravatar.cc/150?u='.$this->id;
}
public function getBannerUrlAttribute(): ?string
{
if ($this->banner) {
return asset('storage/banners/'.$this->banner);
}
return null;
}
public function sendEmailVerificationNotification(): void
{
$this->notify(new VerifyEmail);
}
// Role helper methods
public function isSuperAdmin()
{
@@ -92,31 +161,47 @@ class User extends Authenticatable
return $this->role === 'user' || $this->role === null;
}
// Placeholder for subscriber count (would need a separate table in full implementation)
public function getSubscriberCountAttribute()
// Users who subscribe TO this channel
public function subscribers()
{
// For now, return a placeholder - in production this would come from a subscriptions table
return rand(100, 10000);
return $this->belongsToMany(
User::class,
'user_subscriptions',
'channel_id',
'subscriber_id'
)->withPivot('created_at');
}
// Get social links as an array
public function getSocialLinksAttribute()
// Channels this user subscribes to
public function subscriptions()
{
return [
'twitter' => $this->twitter,
'instagram' => $this->instagram,
'facebook' => $this->facebook,
'youtube' => $this->youtube,
'linkedin' => $this->linkedin,
'tiktok' => $this->tiktok,
];
return $this->belongsToMany(
User::class,
'user_subscriptions',
'subscriber_id',
'channel_id'
)->withPivot('created_at');
}
public function isSubscribedTo(User $channel): bool
{
return $this->subscriptions()->where('channel_id', $channel->id)->exists();
}
public function getSubscriberCountAttribute(): int
{
return $this->subscribers()->count();
}
// Check if user has any social links
public function socialLinks()
{
return $this->hasMany(UserSocialLink::class)->orderBy('sort_order');
}
public function hasSocialLinks()
{
return $this->twitter || $this->instagram || $this->facebook ||
$this->youtube || $this->linkedin || $this->tiktok;
return $this->socialLinks()->exists();
}
// Get formatted website URL