Initialer Laravel Commit für BetiX
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled

This commit is contained in:
2026-04-04 18:01:50 +02:00
commit 0280278978
374 changed files with 65210 additions and 0 deletions

1
database/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.sqlite*

View File

@@ -0,0 +1,60 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'username' => fake()->unique()->userName(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
'two_factor_secret' => null,
'two_factor_recovery_codes' => null,
'two_factor_confirmed_at' => null,
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
/**
* Indicate that the model has two-factor authentication configured.
*/
public function withTwoFactor(): static
{
return $this->state(fn (array $attributes) => [
'two_factor_secret' => encrypt('secret'),
'two_factor_recovery_codes' => encrypt(json_encode(['recovery-code-1'])),
'two_factor_confirmed_at' => now(),
]);
}
}

View File

@@ -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
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration')->index();
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->integer('expiration')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};

View File

@@ -0,0 +1,57 @@
<?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('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};

View File

@@ -0,0 +1,112 @@
<?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
{
// ---------------------------------------------------------------
// USERS (all columns consolidated from scattered add_* migrations)
// ---------------------------------------------------------------
Schema::create('users', function (Blueprint $table) {
$table->id();
// --- Public Identity ---
$table->string('username')->unique();
$table->string('username_index')->unique(); // Blind index for case-insensitive lookups
$table->string('name');
// --- Private Identity (Encrypted) ---
$table->text('email'); // Encrypted via SafeEncryptedString cast
$table->string('email_index')->unique(); // Blind index for login lookups
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
// --- Role & Status ---
$table->string('role')->default('user'); // admin | mod | streamer | user
$table->string('clan_tag')->nullable();
$table->string('avatar_url')->nullable(); // OAuth / external avatar
$table->string('avatar')->nullable(); // Uploaded avatar
$table->string('banner')->nullable(); // Uploaded banner
$table->text('bio')->nullable();
$table->boolean('is_public')->default(false);
$table->boolean('is_adult')->default(false);
$table->boolean('is_banned')->default(false);
// --- VIP & Balance ---
$table->integer('vip_level')->default(0);
$table->string('preferred_locale', 8)->nullable();
$table->decimal('balance', 16, 4)->default(0); // BTX main balance
$table->string('currency', 3)->default('EUR');
// --- Vault ---
$table->decimal('vault_balance', 16, 4)->default(0); // BTX vault balance (plaintext decimal)
$table->text('vault_balances')->nullable(); // JSON map for multi-currency vault
$table->string('vault_pin_hash')->nullable();
$table->timestamp('vault_pin_set_at')->nullable();
$table->unsignedSmallInteger('vault_pin_attempts')->default(0);
$table->timestamp('vault_pin_locked_until')->nullable();
$table->timestamp('withdraw_cooldown_until')->nullable();
// --- Personal Details (Encrypted PII) ---
$table->text('first_name')->nullable();
$table->text('last_name')->nullable();
$table->text('birthdate')->nullable();
$table->text('gender')->nullable();
$table->text('phone')->nullable();
// --- Address (Encrypted PII) ---
$table->text('country')->nullable();
$table->text('address_line1')->nullable();
$table->text('address_line2')->nullable();
$table->text('city')->nullable();
$table->text('state')->nullable();
$table->text('postal_code')->nullable();
// --- 2FA ---
$table->text('two_factor_secret')->nullable();
$table->text('two_factor_recovery_codes')->nullable();
$table->timestamp('two_factor_confirmed_at')->nullable();
// --- Security & Tracking ---
$table->timestamp('last_login_at')->nullable();
$table->text('last_login_ip')->nullable();
$table->text('last_login_user_agent')->nullable();
$table->string('registration_ip', 45)->nullable();
$table->rememberToken();
$table->timestamps();
});
// ---------------------------------------------------------------
// PASSWORD RESET TOKENS
// ---------------------------------------------------------------
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
// ---------------------------------------------------------------
// SESSIONS
// ---------------------------------------------------------------
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
public function down(): void
{
Schema::dropIfExists('sessions');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('users');
}
};

View File

@@ -0,0 +1,39 @@
<?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('user_stats', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->unsignedBigInteger('total_logins')->default(0);
// Stored as TEXT to support Laravel encryption cast
$table->text('total_wagered')->nullable();
$table->text('total_won')->nullable();
$table->text('total_lost')->nullable();
$table->text('biggest_win')->nullable();
$table->string('biggest_win_game')->nullable();
$table->unsignedInteger('total_wins')->default(0);
$table->unsignedInteger('vip_level')->default(1);
$table->unsignedBigInteger('vip_points')->default(0);
$table->timestamp('last_activity')->nullable();
$table->timestamps();
$table->index('user_id');
});
}
public function down(): void
{
Schema::dropIfExists('user_stats');
}
};

View File

@@ -0,0 +1,78 @@
<?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
{
// ---------------------------------------------------------------
// FRIENDS
// ---------------------------------------------------------------
Schema::create('friends', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('friend_id')->constrained('users')->cascadeOnDelete();
$table->enum('status', ['pending', 'accepted', 'blocked'])->default('pending');
$table->timestamps();
$table->unique(['user_id', 'friend_id']);
$table->index(['friend_id', 'status']);
});
// ---------------------------------------------------------------
// PROFILE LIKES
// ---------------------------------------------------------------
Schema::create('profile_likes', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete(); // The liker
$table->foreignId('profile_id')->constrained('users')->cascadeOnDelete(); // The profile owner
$table->timestamps();
$table->unique(['user_id', 'profile_id']);
$table->index('profile_id');
});
// ---------------------------------------------------------------
// PROFILE COMMENTS
// ---------------------------------------------------------------
Schema::create('profile_comments', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete(); // The commenter
$table->foreignId('profile_id')->constrained('users')->cascadeOnDelete(); // The profile owner
$table->text('content');
$table->timestamps();
$table->index(['profile_id', 'created_at']);
});
// ---------------------------------------------------------------
// PROFILE REPORTS
// ---------------------------------------------------------------
Schema::create('profile_reports', function (Blueprint $table) {
$table->id();
$table->foreignId('reporter_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('profile_id')->constrained('users')->cascadeOnDelete();
$table->string('reason');
$table->text('details')->nullable();
$table->json('snapshot')->nullable();
$table->string('screenshot_path', 500)->nullable();
$table->enum('status', ['pending', 'reviewed', 'dismissed'])->default('pending');
$table->text('admin_note')->nullable();
$table->timestamps();
$table->index(['profile_id', 'status']);
$table->index('reporter_id');
});
}
public function down(): void
{
Schema::dropIfExists('profile_reports');
Schema::dropIfExists('profile_comments');
Schema::dropIfExists('profile_likes');
Schema::dropIfExists('friends');
}
};

View File

@@ -0,0 +1,69 @@
<?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
{
// ---------------------------------------------------------------
// CHAT MESSAGES
// ---------------------------------------------------------------
Schema::create('chat_messages', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('reply_to_id')->nullable()->constrained('chat_messages')->nullOnDelete();
$table->text('message'); // Stored encrypted
$table->boolean('is_deleted')->default(false);
$table->unsignedBigInteger('deleted_by')->nullable(); // user_id of moderator/admin
$table->timestamps();
$table->index(['created_at']);
$table->index('reply_to_id');
$table->index(['user_id', 'created_at']);
});
// ---------------------------------------------------------------
// CHAT MESSAGE REACTIONS
// ---------------------------------------------------------------
Schema::create('chat_message_reactions', function (Blueprint $table) {
$table->id();
$table->foreignId('message_id')->constrained('chat_messages')->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('emoji', 16);
$table->timestamps();
$table->unique(['message_id', 'user_id', 'emoji'], 'uniq_reaction');
$table->index(['message_id', 'user_id']);
});
// ---------------------------------------------------------------
// CHAT MESSAGE REPORTS
// ---------------------------------------------------------------
Schema::create('chat_message_reports', function (Blueprint $table) {
$table->id();
$table->foreignId('reporter_id')->constrained('users')->cascadeOnDelete();
$table->string('message_id');
$table->text('message_text');
$table->unsignedBigInteger('sender_id')->nullable();
$table->string('sender_username')->nullable();
$table->string('reason')->nullable();
$table->json('context_messages')->nullable(); // Previous messages for context
$table->enum('status', ['pending', 'reviewed', 'dismissed'])->default('pending');
$table->text('admin_note')->nullable();
$table->timestamps();
$table->index('reporter_id');
$table->index('status');
});
}
public function down(): void
{
Schema::dropIfExists('chat_message_reports');
Schema::dropIfExists('chat_message_reactions');
Schema::dropIfExists('chat_messages');
}
};

View File

@@ -0,0 +1,51 @@
<?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
{
// ---------------------------------------------------------------
// GUILDS
// ---------------------------------------------------------------
Schema::create('guilds', function (Blueprint $table) {
$table->id();
$table->foreignId('owner_id')->constrained('users')->cascadeOnDelete();
$table->string('name', 64)->unique();
$table->string('tag', 6)->unique();
$table->string('logo_url')->nullable();
$table->string('invite_code', 16)->unique();
$table->string('description', 500)->nullable();
$table->unsignedInteger('points')->default(0);
$table->unsignedInteger('members_count')->default(0);
$table->timestamps();
$table->index(['owner_id']);
$table->index(['points', 'members_count']);
});
// ---------------------------------------------------------------
// GUILD MEMBERS
// ---------------------------------------------------------------
Schema::create('guild_members', function (Blueprint $table) {
$table->id();
$table->foreignId('guild_id')->constrained('guilds')->cascadeOnDelete();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->string('role', 16)->default('member'); // owner | officer | member
$table->timestamp('joined_at')->nullable();
$table->timestamps();
$table->unique(['guild_id', 'user_id']);
$table->index('user_id');
});
}
public function down(): void
{
Schema::dropIfExists('guild_members');
Schema::dropIfExists('guilds');
}
};

View File

@@ -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('kyc_documents', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('category', 24); // identity | address | payment
$table->string('type', 32); // passport | driver_license | id_card | bank_statement | utility_bill | other
$table->string('status', 16)->default('pending'); // pending | approved | rejected
$table->string('rejection_reason', 255)->nullable();
$table->string('file_path');
$table->string('mime', 100);
$table->unsignedBigInteger('size');
$table->timestamp('submitted_at')->nullable();
$table->timestamp('reviewed_at')->nullable();
$table->foreignId('reviewed_by')->nullable()->constrained('users')->nullOnDelete();
$table->softDeletes();
$table->timestamps();
$table->index(['user_id', 'status']);
$table->index('category');
});
}
public function down(): void
{
Schema::dropIfExists('kyc_documents');
}
};

View File

@@ -0,0 +1,122 @@
<?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
{
// ---------------------------------------------------------------
// PERSONAL ACCESS TOKENS (Sanctum)
// ---------------------------------------------------------------
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id();
$table->morphs('tokenable');
$table->text('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable()->index();
$table->timestamps();
});
// ---------------------------------------------------------------
// WALLETS (per-currency balance per user)
// ---------------------------------------------------------------
Schema::create('wallets', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('currency', 10); // BTC | ETH | SOL | BTX | EUR
$table->decimal('balance', 20, 8)->default(0);
$table->string('deposit_address')->nullable();
$table->timestamps();
$table->unique(['user_id', 'currency']);
$table->index(['user_id', 'currency']);
});
// ---------------------------------------------------------------
// WALLET TRANSFERS (main balance <-> vault, audit log)
// ---------------------------------------------------------------
Schema::create('wallet_transfers', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->enum('type', ['deposit', 'withdraw']); // deposit = main->vault, withdraw = vault->main
$table->decimal('amount', 20, 4);
$table->decimal('balance_before', 20, 4);
$table->decimal('balance_after', 20, 4);
$table->decimal('vault_before', 20, 4);
$table->decimal('vault_after', 20, 4);
$table->string('currency', 10)->default('BTX');
$table->string('idempotency_key', 64)->nullable();
$table->json('meta')->nullable();
$table->timestamps();
$table->index(['user_id', 'created_at']);
$table->index(['user_id', 'type']);
$table->unique(['user_id', 'idempotency_key']);
});
// ---------------------------------------------------------------
// VAULT TRANSFERS (encrypted audit trail)
// ---------------------------------------------------------------
Schema::create('vault_transfers', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('direction', 16); // to_vault | from_vault
$table->text('amount'); // Encrypted
$table->text('main_balance_before'); // Encrypted
$table->text('main_balance_after'); // Encrypted
$table->text('vault_balance_before'); // Encrypted
$table->text('vault_balance_after'); // Encrypted
$table->string('idempotency_key', 64)->nullable();
$table->string('source', 16)->default('web'); // web | api
$table->unsignedBigInteger('created_by')->nullable();
$table->text('metadata')->nullable(); // Encrypted
$table->timestamps();
$table->index(['user_id', 'created_at']);
$table->index(['user_id', 'direction']);
$table->unique(['user_id', 'idempotency_key']);
});
// ---------------------------------------------------------------
// CRYPTO PAYMENTS (NOWPayments / on-chain deposits)
// ---------------------------------------------------------------
Schema::create('crypto_payments', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->uuid('order_id')->unique();
$table->string('invoice_id')->unique();
$table->string('payment_id')->unique();
$table->string('pay_currency', 20);
$table->decimal('pay_amount', 36, 18)->nullable();
$table->decimal('actually_paid', 36, 18)->nullable();
$table->string('pay_address')->nullable();
$table->decimal('price_amount', 20, 8)->nullable();
$table->string('price_currency', 10)->default('USD');
$table->decimal('exchange_rate_at_payment', 28, 12)->nullable();
$table->string('status', 40)->index(); // waiting | confirming | finished | failed | expired | partially_paid
$table->unsignedInteger('confirmations')->nullable();
$table->json('tx_hash')->nullable();
$table->decimal('fee', 36, 18)->nullable();
$table->json('raw_payload')->nullable();
$table->decimal('credited_btx', 20, 8)->nullable();
$table->timestamp('credited_at')->nullable();
$table->timestamps();
$table->index(['user_id', 'status']);
});
}
public function down(): void
{
Schema::dropIfExists('crypto_payments');
Schema::dropIfExists('vault_transfers');
Schema::dropIfExists('wallet_transfers');
Schema::dropIfExists('wallets');
Schema::dropIfExists('personal_access_tokens');
}
};

View File

@@ -0,0 +1,129 @@
<?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
{
// ---------------------------------------------------------------
// VIP REWARDS (claimed level-up rewards)
// ---------------------------------------------------------------
Schema::create('vip_rewards', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->integer('level');
$table->decimal('amount', 10, 2);
$table->timestamp('claimed_at');
$table->timestamps();
$table->unique(['user_id', 'level']); // Prevent double-claiming
});
// ---------------------------------------------------------------
// BONUSES (admin-created bonus campaigns)
// ---------------------------------------------------------------
Schema::create('bonuses', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('type')->nullable(); // welcome | reload | spins
$table->decimal('amount_value', 18, 8)->nullable();
$table->string('amount_unit', 32)->nullable(); // USD | BTC | PERCENT | SPINS
$table->decimal('min_deposit', 18, 8)->nullable();
$table->decimal('max_amount', 18, 8)->nullable();
$table->string('currency', 16)->nullable();
$table->string('code')->nullable();
$table->string('status')->default('draft'); // draft | active | paused | expired
$table->timestamp('starts_at')->nullable();
$table->timestamp('expires_at')->nullable();
$table->json('rules')->nullable();
$table->text('description')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
$table->softDeletes();
$table->index(['status', 'starts_at', 'expires_at']);
});
// ---------------------------------------------------------------
// PROMOS (promo code system)
// ---------------------------------------------------------------
Schema::create('promos', function (Blueprint $table) {
$table->id();
$table->string('code')->unique(); // Uppercase normalized
$table->string('description')->nullable();
$table->decimal('bonus_amount', 16, 4)->default(0);
$table->unsignedInteger('wager_multiplier')->default(0);
$table->unsignedInteger('per_user_limit')->default(1);
$table->unsignedInteger('global_limit')->nullable();
$table->timestamp('starts_at')->nullable();
$table->timestamp('ends_at')->nullable();
$table->decimal('min_deposit', 16, 4)->nullable();
$table->unsignedInteger('bonus_expires_days')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->index(['is_active', 'starts_at', 'ends_at']);
});
// ---------------------------------------------------------------
// PROMO USAGES (per-use audit)
// ---------------------------------------------------------------
Schema::create('promo_usages', function (Blueprint $table) {
$table->id();
$table->foreignId('promo_id')->constrained('promos')->cascadeOnDelete();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->timestamp('used_at');
$table->string('ip', 45)->nullable();
$table->text('user_agent')->nullable();
$table->timestamps();
$table->unique(['promo_id', 'user_id', 'used_at']);
$table->index(['user_id', 'promo_id']);
});
// ---------------------------------------------------------------
// PROMO CLAIMS (anti-abuse: one entry per user per promo)
// ---------------------------------------------------------------
Schema::create('promo_claims', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('promo_id')->constrained()->cascadeOnDelete();
$table->string('ip_address')->nullable();
$table->string('device_hash')->nullable();
$table->timestamps();
$table->index(['user_id', 'promo_id']);
});
// ---------------------------------------------------------------
// USER BONUSES (active bonus progress per user)
// ---------------------------------------------------------------
Schema::create('user_bonuses', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('promo_id')->nullable()->constrained('promos')->nullOnDelete();
$table->decimal('amount', 16, 4);
$table->decimal('wager_required', 16, 4)->default(0);
$table->decimal('wager_progress', 16, 4)->default(0);
$table->timestamp('expires_at')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamp('completed_at')->nullable();
$table->timestamps();
$table->index(['user_id', 'is_active']);
});
}
public function down(): void
{
Schema::dropIfExists('user_bonuses');
Schema::dropIfExists('promo_claims');
Schema::dropIfExists('promo_usages');
Schema::dropIfExists('promos');
Schema::dropIfExists('bonuses');
Schema::dropIfExists('vip_rewards');
}
};

View File

@@ -0,0 +1,91 @@
<?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
{
// ---------------------------------------------------------------
// GAME BETS (bet history)
// ---------------------------------------------------------------
Schema::create('game_bets', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('game_name');
$table->decimal('wager_amount', 16, 8)->default(0);
$table->decimal('payout_multiplier', 8, 4)->default(0);
$table->decimal('payout_amount', 16, 8)->default(0);
$table->string('currency', 32)->default('BTX');
$table->timestamps();
$table->index(['user_id', 'created_at']);
});
// ---------------------------------------------------------------
// USER FAVORITES (saved / bookmarked games)
// ---------------------------------------------------------------
Schema::create('user_favorites', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('game_slug', 128);
$table->string('game_name', 255)->nullable();
$table->string('game_image', 512)->nullable();
$table->string('game_provider', 100)->nullable();
$table->timestamps();
$table->unique(['user_id', 'game_slug']);
$table->index('user_id');
});
// ---------------------------------------------------------------
// USER ACHIEVEMENTS
// ---------------------------------------------------------------
Schema::create('user_achievements', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('achievement_key', 64); // e.g. first_bet | big_winner
$table->timestamp('unlocked_at');
$table->timestamps();
$table->unique(['user_id', 'achievement_key']);
$table->index('user_id');
});
// ---------------------------------------------------------------
// USER FEEDBACK (UX ratings & comments)
// ---------------------------------------------------------------
Schema::create('user_feedback', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
$table->string('category', 30)->default('general'); // general | ux | mobile | feature | complaint
$table->unsignedTinyInteger('overall_rating')->nullable(); // 1-5
$table->unsignedTinyInteger('ux_rating')->nullable(); // 1-5
$table->unsignedTinyInteger('comfort_rating')->nullable(); // 1-5
$table->unsignedTinyInteger('mobile_rating')->nullable(); // 1-5
$table->boolean('uses_mobile')->nullable();
$table->unsignedTinyInteger('nps_score')->nullable(); // 1-10
$table->text('ux_comment')->nullable();
$table->text('mobile_comment')->nullable();
$table->text('feature_request')->nullable();
$table->text('improvements')->nullable();
$table->text('general_comment')->nullable();
$table->enum('status', ['new', 'read'])->default('new');
$table->text('admin_note')->nullable();
$table->timestamps();
$table->index(['status', 'created_at']);
$table->index('user_id');
});
}
public function down(): void
{
Schema::dropIfExists('user_feedback');
Schema::dropIfExists('user_achievements');
Schema::dropIfExists('user_favorites');
Schema::dropIfExists('game_bets');
}
};

View File

@@ -0,0 +1,79 @@
<?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
{
// ---------------------------------------------------------------
// APP SETTINGS (key-value store for site configuration)
// ---------------------------------------------------------------
Schema::create('app_settings', function (Blueprint $table) {
$table->id();
$table->string('key')->unique();
$table->json('value');
$table->timestamps();
});
// ---------------------------------------------------------------
// USER RESTRICTIONS (bans, chat bans, etc.)
// ---------------------------------------------------------------
Schema::create('user_restrictions', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->string('type', 64); // account_ban | chat_ban | deposit_block | withdrawal_block | support_block
$table->string('reason', 255)->nullable();
$table->text('notes')->nullable();
$table->foreignId('imposed_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamp('starts_at')->nullable();
$table->timestamp('ends_at')->nullable();
$table->boolean('active')->default(true);
$table->string('source', 64)->nullable(); // api | admin_panel | system
$table->json('metadata')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['user_id', 'type', 'active']);
$table->index(['user_id', 'active']);
$table->index('ends_at');
});
// ---------------------------------------------------------------
// TIPS (peer-to-peer balance transfers)
// ---------------------------------------------------------------
Schema::create('tips', function (Blueprint $table) {
$table->id();
$table->foreignId('from_user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('to_user_id')->constrained('users')->cascadeOnDelete();
$table->string('currency', 10);
$table->decimal('amount', 20, 8);
$table->string('note', 140)->nullable();
$table->timestamps();
$table->index(['from_user_id', 'to_user_id']);
});
// ---------------------------------------------------------------
// NOTIFICATIONS (Laravel default notification table)
// ---------------------------------------------------------------
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();
});
}
public function down(): void
{
Schema::dropIfExists('notifications');
Schema::dropIfExists('tips');
Schema::dropIfExists('user_restrictions');
Schema::dropIfExists('app_settings');
}
};

View File

@@ -0,0 +1,56 @@
<?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
{
// ---------------------------------------------------------------
// OPERATOR CASINOS (B2B license key management)
// ---------------------------------------------------------------
Schema::create('operator_casinos', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('license_key_hash', 64)->unique(); // SHA-256 of plaintext key (never store plaintext)
$table->string('status', 20)->default('active'); // active | inactive | suspended
$table->json('ip_whitelist')->nullable();
$table->json('domain_whitelist')->nullable();
$table->timestamps();
$table->index('status');
});
// ---------------------------------------------------------------
// OPERATOR SESSIONS (provably-fair game sessions)
// ---------------------------------------------------------------
Schema::create('operator_sessions', function (Blueprint $table) {
$table->id();
$table->uuid('session_token')->unique();
$table->foreignId('operator_casino_id')->constrained()->cascadeOnDelete();
$table->string('player_id', 255); // Casino's internal player ID
$table->string('game_slug', 50);
$table->string('currency', 10)->default('EUR');
$table->decimal('start_balance', 20, 4)->default(0);
$table->decimal('current_balance', 20, 4)->default(0);
$table->text('server_seed'); // Encrypted via Laravel encrypt()
$table->string('server_seed_hash', 64); // SHA-256 shown to player for provably-fair
$table->string('client_seed', 255)->nullable();
$table->string('status', 20)->default('active'); // active | expired | ended
$table->timestamp('expires_at');
$table->timestamps();
$table->index(['operator_casino_id', 'player_id']);
$table->index('status');
$table->index('expires_at');
});
}
public function down(): void
{
Schema::dropIfExists('operator_sessions');
Schema::dropIfExists('operator_casinos');
}
};

View File

@@ -0,0 +1,45 @@
<?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('direct_messages', function (Blueprint $table) {
$table->id();
$table->foreignId('sender_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('receiver_id')->constrained('users')->cascadeOnDelete();
$table->text('message');
$table->boolean('is_read')->default(false);
$table->boolean('is_deleted')->default(false);
$table->foreignId('deleted_by')->nullable()->constrained('users')->nullOnDelete();
$table->foreignId('reply_to_id')->nullable()->constrained('direct_messages')->nullOnDelete();
$table->timestamps();
$table->index(['sender_id', 'receiver_id', 'created_at']);
$table->index(['receiver_id', 'is_read']);
});
Schema::create('direct_message_reports', function (Blueprint $table) {
$table->id();
$table->foreignId('reporter_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('message_id')->constrained('direct_messages')->cascadeOnDelete();
$table->string('reason', 64);
$table->text('details')->nullable();
$table->enum('status', ['pending', 'reviewed', 'dismissed'])->default('pending');
$table->timestamps();
$table->unique(['reporter_id', 'message_id']);
$table->index('status');
});
}
public function down(): void
{
Schema::dropIfExists('direct_message_reports');
Schema::dropIfExists('direct_messages');
}
};

View File

@@ -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('guild_messages', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('guild_id');
$table->unsignedBigInteger('user_id');
$table->string('type')->default('message'); // 'message' | 'system'
$table->text('message');
$table->unsignedBigInteger('reply_to_id')->nullable();
$table->boolean('is_deleted')->default(false);
$table->timestamps();
$table->foreign('guild_id')->references('id')->on('guilds')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->index(['guild_id', 'created_at']);
});
}
public function down(): void
{
Schema::dropIfExists('guild_messages');
}
};

View File

@@ -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::table('operator_sessions', function (Blueprint $table) {
// Covers the session-reuse lookup and the stale-session expiration query:
// WHERE player_id = ? AND game_slug = ? AND status = ? AND expires_at > ?
$table->index(['player_id', 'game_slug', 'status', 'expires_at'], 'os_player_game_status_expires');
});
}
public function down(): void
{
Schema::table('operator_sessions', function (Blueprint $table) {
$table->dropIndex('os_player_game_status_expires');
});
}
};

View File

@@ -0,0 +1,27 @@
<?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('game_bets', function (Blueprint $table) {
$table->string('session_token', 36)->nullable()->after('currency');
$table->unsignedInteger('round_number')->nullable()->after('session_token');
$table->string('server_seed_hash', 64)->nullable()->after('round_number');
$table->index('session_token');
});
}
public function down(): void
{
Schema::table('game_bets', function (Blueprint $table) {
$table->dropIndex(['session_token']);
$table->dropColumn(['session_token', 'round_number', 'server_seed_hash']);
});
}
};

View File

@@ -0,0 +1,23 @@
<?php
namespace Database\Seeders;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
}
}