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

+99 -4
View File
@@ -25,6 +25,11 @@ class Video extends Model
'is_shorts',
'has_hls',
'hls_path',
'download_access',
'download_count',
'share_count',
'share_token',
'slideshow_video_path',
];
protected $casts = [
@@ -54,6 +59,16 @@ class Video extends Model
->withTimestamps();
}
public function slides()
{
return $this->hasMany(VideoSlide::class)->orderBy('position');
}
public function hasSlideshow(): bool
{
return $this->slides()->count() > 1;
}
// Accessors
public function getUrlAttribute()
{
@@ -92,10 +107,84 @@ class Video extends Model
return \DB::table('video_views')->where('video_id', $this->id)->count();
}
// Get shareable URL for the video
// ── Short opaque URL encoding ─────────────────────────────────────
// 3-round Feistel cipher over 30-bit space → base62.
// Consecutive IDs produce completely different output. Pure PHP, no extensions.
private const URL_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
private static function feistelRound(int $n, int $round): int
{
// Deterministic pseudo-random function per round (no extension needed)
$keys = [0x2D4F8A1B, 0x7C3E6912, 0xA5B2D083];
return (int)(($n * $keys[$round % 3] + ($round * 0x9E3779B9)) & 0x7FFF);
}
public static function encodeId(int $id): string
{
// Split 30-bit id into two 15-bit halves
$L = ($id >> 15) & 0x7FFF;
$R = $id & 0x7FFF;
// 3 Feistel rounds
for ($i = 0; $i < 3; $i++) {
$tmp = $L ^ self::feistelRound($R, $i);
$L = $R;
$R = $tmp;
}
$n = (($L & 0x7FFF) << 15) | ($R & 0x7FFF);
$base = strlen(self::URL_ALPHABET);
$out = '';
do {
$out = self::URL_ALPHABET[$n % $base] . $out;
$n = intdiv($n, $base);
} while ($n > 0);
// Pad to fixed length so all URLs look uniform
return str_pad($out, 5, '0', STR_PAD_LEFT);
}
public static function decodeId(string $encoded): ?int
{
$base = strlen(self::URL_ALPHABET);
$n = 0;
for ($i = 0, $len = strlen($encoded); $i < $len; $i++) {
$pos = strpos(self::URL_ALPHABET, $encoded[$i]);
if ($pos === false) return null;
$n = $n * $base + $pos;
}
// Reverse Feistel
$L = ($n >> 15) & 0x7FFF;
$R = $n & 0x7FFF;
for ($i = 2; $i >= 0; $i--) {
$tmp = $R ^ self::feistelRound($L, $i);
$R = $L;
$L = $tmp;
}
return (($L & 0x7FFF) << 15) | ($R & 0x7FFF);
}
// Route model binding — use encoded ID in URLs
public function getRouteKey()
{
return self::encodeId($this->id);
}
public function getRouteKeyName()
{
return 'id';
}
public function resolveRouteBinding($value, $field = null)
{
$realId = self::decodeId((string) $value);
if ($realId === null || $realId <= 0) {
abort(404);
}
return $this->where('id', $realId)->firstOrFail();
}
// All share URLs use the unguessable token route
public function getShareUrlAttribute()
{
return route('videos.show', $this->id);
return route('videos.showByToken', $this->share_token);
}
// Get formatted duration (e.g., "1:30" or "0:45" for shorts)
@@ -213,6 +302,12 @@ class Video extends Model
return $this->type === 'match';
}
public function isAudioOnly(): bool
{
$ext = strtolower(pathinfo($this->filename ?? '', PATHINFO_EXTENSION));
return in_array($ext, ['mp3', 'm4a', 'aac', 'flac', 'wav']);
}
// Shorts helpers
public function isShorts()
{
@@ -331,13 +426,13 @@ class Video extends Model
// Get video stream URL for Open Graph
public function getStreamUrlAttribute()
{
return route('videos.stream', $this->id);
return route('videos.stream', $this);
}
// Get secure share URL
public function getSecureShareUrlAttribute()
{
return secure_url(route('videos.show', $this->id));
return secure_url(route('videos.show', $this));
}
// Get secure thumbnail URL