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:
1 parent
d44490dfe0
commit
0b2e95ea65
161 files changed
+27496
-7250
No files matched your search
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('video_views', function (Blueprint $table) {
|
||||
$table->string('ip_address', 45)->nullable()->after('video_id');
|
||||
$table->string('country', 2)->nullable()->after('ip_address');
|
||||
$table->string('country_name', 100)->nullable()->after('country');
|
||||
// Allow null so guest (unauthenticated) views can be recorded
|
||||
$table->unsignedBigInteger('user_id')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('video_views', function (Blueprint $table) {
|
||||
$table->dropColumn(['ip_address', 'country', 'country_name']);
|
||||
$table->unsignedBigInteger('user_id')->nullable(false)->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('gender', 20)->nullable()->after('birthday');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('gender');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('nationality', 2)->nullable()->after('gender'); // ISO2 e.g. "BH"
|
||||
$table->string('phone_code', 20)->nullable()->after('nationality'); // e.g. "+973|BH"
|
||||
$table->string('phone_number', 30)->nullable()->after('phone_code');
|
||||
$table->string('timezone', 60)->nullable()->after('phone_number'); // IANA e.g. "Asia/Bahrain"
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn(['nationality', 'phone_code', 'phone_number', 'timezone']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('whatsapp', 30)->nullable()->after('tiktok');
|
||||
$table->string('google_location', 500)->nullable()->after('whatsapp');
|
||||
$table->string('social_phone', 30)->nullable()->after('google_location');
|
||||
$table->string('social_email', 100)->nullable()->after('social_phone');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn(['whatsapp', 'google_location', 'social_phone', 'social_email']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_social_links', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('platform', 30);
|
||||
$table->string('value', 500);
|
||||
$table->unsignedSmallInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
$table->index(['user_id', 'sort_order']);
|
||||
});
|
||||
|
||||
// Migrate existing individual social columns into the new table
|
||||
$cols = ['twitter','instagram','facebook','youtube','linkedin','tiktok',
|
||||
'website','whatsapp','google_location','social_phone','social_email'];
|
||||
foreach (\DB::table('users')->get() as $user) {
|
||||
$order = 0;
|
||||
foreach ($cols as $col) {
|
||||
$val = $user->$col ?? null;
|
||||
if ($val) {
|
||||
\DB::table('user_social_links')->insert([
|
||||
'user_id' => $user->id,
|
||||
'platform' => $col,
|
||||
'value' => $val,
|
||||
'sort_order' => $order++,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_social_links');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('user_social_links', function (Blueprint $table) {
|
||||
$table->enum('visibility', ['public', 'registered', 'subscribers', 'only_me'])
|
||||
->default('public')
|
||||
->after('value');
|
||||
});
|
||||
|
||||
// Default sensitive platforms to 'subscribers'
|
||||
\DB::table('user_social_links')
|
||||
->whereIn('platform', ['whatsapp', 'social_phone', 'social_email'])
|
||||
->update(['visibility' => 'subscribers']);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('user_social_links', function (Blueprint $table) {
|
||||
$table->dropColumn('visibility');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->boolean('allow_download')->default(false)->after('is_shorts');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn('allow_download');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_subscriptions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('subscriber_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('channel_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->unique(['subscriber_id', 'channel_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_subscriptions');
|
||||
}
|
||||
};
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->string('download_access')->default('disabled')->after('allow_download');
|
||||
});
|
||||
|
||||
// Migrate existing data
|
||||
DB::table('videos')->where('allow_download', true)->update(['download_access' => 'everyone']);
|
||||
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn('allow_download');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->boolean('allow_download')->default(false)->after('download_access');
|
||||
});
|
||||
|
||||
DB::table('videos')->where('download_access', 'everyone')->update(['allow_download' => true]);
|
||||
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn('download_access');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('notifications', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('type');
|
||||
$table->morphs('notifiable');
|
||||
$table->text('data');
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('notifications');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('posts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->text('body')->nullable();
|
||||
$table->foreignId('video_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('image')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('posts');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('post_reactions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('type', 20)->default('like');
|
||||
$table->timestamps();
|
||||
$table->unique(['user_id', 'post_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('post_reactions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->unsignedInteger('download_count')->default(0)->after('has_hls');
|
||||
$table->unsignedInteger('share_count')->default(0)->after('download_count');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn(['download_count', 'share_count']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('video_downloads', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('video_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('user_id')->nullable()->constrained()->onDelete('set null');
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->string('country', 2)->nullable();
|
||||
$table->string('country_name', 100)->nullable();
|
||||
$table->string('type', 10)->default('video'); // 'video' or 'mp3'
|
||||
$table->timestamp('downloaded_at')->useCurrent();
|
||||
|
||||
$table->index(['video_id', 'downloaded_at']);
|
||||
$table->index(['video_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('video_downloads');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('video_shares', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('video_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('token', 12)->unique();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
});
|
||||
|
||||
Schema::create('share_accesses', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('share_id')->constrained('video_shares')->cascadeOnDelete();
|
||||
$table->string('device_id', 64);
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->string('country', 2)->nullable();
|
||||
$table->string('country_name')->nullable();
|
||||
$table->timestamp('accessed_at')->useCurrent();
|
||||
$table->unique(['share_id', 'device_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('share_accesses');
|
||||
Schema::dropIfExists('video_shares');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('comment_likes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('comment_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('created_at')->nullable();
|
||||
$table->unique(['comment_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('comment_likes');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('two_factor_secret')->nullable()->after('password');
|
||||
$table->boolean('two_factor_enabled')->default(false)->after('two_factor_secret');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn(['two_factor_secret', 'two_factor_enabled']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('video_slides', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('video_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('filename');
|
||||
$table->unsignedSmallInteger('position')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->string('slideshow_video_path')->nullable()->after('thumbnail');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('video_slides');
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn('slideshow_video_path');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique();
|
||||
$table->text('value')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Seed defaults
|
||||
$defaults = [
|
||||
['key' => 'gpu_enabled', 'value' => 'true'],
|
||||
['key' => 'gpu_device', 'value' => '0'],
|
||||
['key' => 'gpu_encoder', 'value' => 'h264_nvenc'],
|
||||
['key' => 'gpu_hwaccel', 'value' => 'cuda'],
|
||||
['key' => 'gpu_preset', 'value' => 'p4'],
|
||||
];
|
||||
foreach ($defaults as $row) {
|
||||
DB::table('settings')->insert(array_merge($row, [
|
||||
'created_at' => now(), 'updated_at' => now(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('settings');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (DB::getDriverName() === 'mysql') {
|
||||
DB::statement("ALTER TABLE playlists MODIFY COLUMN visibility ENUM('public', 'private', 'unlisted') NOT NULL DEFAULT 'private'");
|
||||
} else {
|
||||
// SQLite stores enums as CHECK constraints; use table rebuild to update the constraint.
|
||||
$this->rebuildSqliteEnum(['public', 'private', 'unlisted']);
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (DB::getDriverName() === 'mysql') {
|
||||
DB::table('playlists')->where('visibility', 'unlisted')->update(['visibility' => 'private']);
|
||||
DB::statement("ALTER TABLE playlists MODIFY COLUMN visibility ENUM('public', 'private') NOT NULL DEFAULT 'private'");
|
||||
} else {
|
||||
DB::table('playlists')->where('visibility', 'unlisted')->update(['visibility' => 'private']);
|
||||
$this->rebuildSqliteEnum(['public', 'private']);
|
||||
}
|
||||
}
|
||||
|
||||
private function rebuildSqliteEnum(array $values): void
|
||||
{
|
||||
$inList = implode(', ', array_map(fn($v) => "'$v'", $values));
|
||||
|
||||
DB::statement('PRAGMA foreign_keys = OFF');
|
||||
|
||||
DB::statement("
|
||||
CREATE TABLE playlists_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
thumbnail VARCHAR(255),
|
||||
visibility VARCHAR(255) CHECK (visibility IN ({$inList})) NOT NULL DEFAULT 'private',
|
||||
is_default TINYINT(1) NOT NULL DEFAULT 0,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
|
||||
DB::statement('INSERT INTO playlists_new SELECT * FROM playlists');
|
||||
DB::statement('DROP TABLE playlists');
|
||||
DB::statement('ALTER TABLE playlists_new RENAME TO playlists');
|
||||
|
||||
// Recreate indexes
|
||||
DB::statement('CREATE INDEX playlists_user_id_visibility_index ON playlists (user_id, visibility)');
|
||||
|
||||
DB::statement('PRAGMA foreign_keys = ON');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Playlist;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('playlists', function (Blueprint $table) {
|
||||
$table->string('share_token', 64)->nullable()->unique()->after('is_default');
|
||||
});
|
||||
|
||||
// Backfill tokens for existing playlists
|
||||
Playlist::whereNull('share_token')->each(function ($playlist) {
|
||||
$playlist->updateQuietly(['share_token' => Str::random(32)]);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('playlists', function (Blueprint $table) {
|
||||
$table->dropColumn('share_token');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('playlist_shares', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('playlist_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('token', 12)->unique();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
});
|
||||
|
||||
Schema::create('playlist_share_accesses', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('share_id')->constrained('playlist_shares')->cascadeOnDelete();
|
||||
$table->string('device_id', 64);
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->string('country', 2)->nullable();
|
||||
$table->string('country_name')->nullable();
|
||||
$table->timestamp('accessed_at')->useCurrent();
|
||||
$table->unique(['share_id', 'device_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('playlist_share_accesses');
|
||||
Schema::dropIfExists('playlist_shares');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Video;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->string('share_token', 64)->nullable()->unique()->after('share_count');
|
||||
});
|
||||
|
||||
// Backfill existing videos
|
||||
Video::whereNull('share_token')->each(function ($video) {
|
||||
$video->updateQuietly(['share_token' => Str::random(32)]);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn('share_token');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('post_images', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('filename');
|
||||
$table->unsignedTinyInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('post_images');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('post_videos', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('video_id')->nullable()->nullOnDelete();
|
||||
$table->unsignedTinyInteger('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('post_videos');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('username', 50)->nullable()->unique()->after('name');
|
||||
});
|
||||
|
||||
// Back-fill existing users with a unique slug
|
||||
DB::table('users')->orderBy('id')->each(function ($user) {
|
||||
$base = substr(Str::slug(trim($user->name)), 0, 18);
|
||||
if ($base === '') $base = 'user';
|
||||
do {
|
||||
$slug = $base . '-' . Str::lower(Str::random(6));
|
||||
} while (DB::table('users')->where('username', $slug)->exists());
|
||||
DB::table('users')->where('id', $user->id)->update(['username' => $slug]);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('username');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('banner')->nullable()->after('avatar');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('banner');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('audit_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('user_id')->nullable();
|
||||
$table->string('user_name')->nullable();
|
||||
$table->string('action', 64);
|
||||
$table->string('subject_type', 64)->nullable();
|
||||
$table->string('subject_id', 64)->nullable();
|
||||
$table->string('subject_label')->nullable();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->string('user_agent')->nullable();
|
||||
$table->json('details')->nullable();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->index('user_id');
|
||||
$table->index('action');
|
||||
$table->index('created_at');
|
||||
$table->index('ip_address');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('audit_logs');
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user